summaryrefslogtreecommitdiffstats
path: root/drivers/media/video
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video')
-rw-r--r--drivers/media/video/Kconfig232
-rw-r--r--drivers/media/video/Makefile19
-rw-r--r--drivers/media/video/dabfirmware.h1408
-rw-r--r--drivers/media/video/dabusb.c874
-rw-r--r--drivers/media/video/dabusb.h85
-rw-r--r--drivers/media/video/dsbr100.c429
-rw-r--r--drivers/media/video/et61x251/Makefile4
-rw-r--r--drivers/media/video/et61x251/et61x251.h234
-rw-r--r--drivers/media/video/et61x251/et61x251_core.c2630
-rw-r--r--drivers/media/video/et61x251/et61x251_sensor.h116
-rw-r--r--drivers/media/video/et61x251/et61x251_tas5130d1b.c141
-rw-r--r--drivers/media/video/ov511.c5932
-rw-r--r--drivers/media/video/ov511.h568
-rw-r--r--drivers/media/video/pwc/Makefile20
-rw-r--r--drivers/media/video/pwc/philips.txt236
-rw-r--r--drivers/media/video/pwc/pwc-ctrl.c1541
-rw-r--r--drivers/media/video/pwc/pwc-if.c2205
-rw-r--r--drivers/media/video/pwc/pwc-ioctl.h292
-rw-r--r--drivers/media/video/pwc/pwc-kiara.c318
-rw-r--r--drivers/media/video/pwc/pwc-kiara.h45
-rw-r--r--drivers/media/video/pwc/pwc-misc.c140
-rw-r--r--drivers/media/video/pwc/pwc-nala.h66
-rw-r--r--drivers/media/video/pwc/pwc-timon.c316
-rw-r--r--drivers/media/video/pwc/pwc-timon.h61
-rw-r--r--drivers/media/video/pwc/pwc-uncompress.c146
-rw-r--r--drivers/media/video/pwc/pwc-uncompress.h41
-rw-r--r--drivers/media/video/pwc/pwc.h272
-rw-r--r--drivers/media/video/se401.c1435
-rw-r--r--drivers/media/video/se401.h234
-rw-r--r--drivers/media/video/sn9c102/Makefile7
-rw-r--r--drivers/media/video/sn9c102/sn9c102.h218
-rw-r--r--drivers/media/video/sn9c102/sn9c102_core.c2919
-rw-r--r--drivers/media/video/sn9c102/sn9c102_hv7131d.c271
-rw-r--r--drivers/media/video/sn9c102/sn9c102_mi0343.c363
-rw-r--r--drivers/media/video/sn9c102/sn9c102_ov7630.c401
-rw-r--r--drivers/media/video/sn9c102/sn9c102_pas106b.c307
-rw-r--r--drivers/media/video/sn9c102/sn9c102_pas202bca.c238
-rw-r--r--drivers/media/video/sn9c102/sn9c102_pas202bcb.c293
-rw-r--r--drivers/media/video/sn9c102/sn9c102_sensor.h389
-rw-r--r--drivers/media/video/sn9c102/sn9c102_tas5110c1b.c159
-rw-r--r--drivers/media/video/sn9c102/sn9c102_tas5130d1b.c169
-rw-r--r--drivers/media/video/stv680.c1508
-rw-r--r--drivers/media/video/stv680.h227
-rw-r--r--drivers/media/video/usbvideo/Makefile4
-rw-r--r--drivers/media/video/usbvideo/ibmcam.c3932
-rw-r--r--drivers/media/video/usbvideo/konicawc.c978
-rw-r--r--drivers/media/video/usbvideo/ultracam.c679
-rw-r--r--drivers/media/video/usbvideo/usbvideo.c2190
-rw-r--r--drivers/media/video/usbvideo/usbvideo.h394
-rw-r--r--drivers/media/video/usbvideo/vicam.c1411
-rw-r--r--drivers/media/video/w9968cf.c3691
-rw-r--r--drivers/media/video/w9968cf.h330
-rw-r--r--drivers/media/video/w9968cf_decoder.h86
-rw-r--r--drivers/media/video/w9968cf_vpp.h40
-rw-r--r--drivers/media/video/zc0301/Makefile3
-rw-r--r--drivers/media/video/zc0301/zc0301.h192
-rw-r--r--drivers/media/video/zc0301/zc0301_core.c2055
-rw-r--r--drivers/media/video/zc0301/zc0301_pas202bcb.c361
-rw-r--r--drivers/media/video/zc0301/zc0301_sensor.h103
59 files changed, 43956 insertions, 2 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index b3d3b22d3f78..1f8a46b5916e 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -291,8 +291,6 @@ config VIDEO_HEXIUM_GEMINI
source "drivers/media/video/cx88/Kconfig"
-source "drivers/media/video/em28xx/Kconfig"
-
config VIDEO_OVCAMCHIP
tristate "OmniVision Camera Chip support"
depends on VIDEO_DEV && I2C
@@ -367,4 +365,234 @@ config VIDEO_SAA7127
To compile this driver as a module, choose M here: the
module will be called saa7127
+#
+# USB Multimedia device configuration
+#
+
+menu "V4L USB devices"
+ depends on USB && VIDEO_DEV
+
+source "drivers/media/video/em28xx/Kconfig"
+
+config USB_VICAM
+ tristate "USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)"
+ depends on USB && VIDEO_DEV && EXPERIMENTAL
+ ---help---
+ Say Y here if you have 3com homeconnect camera (vicam).
+
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" (under Multimedia Devices) to use this driver.
+ Information on this API and pointers to "v4l" programs may be found
+ at <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vicam.
+
+config USB_DSBR
+ tristate "D-Link USB FM radio support (EXPERIMENTAL)"
+ depends on USB && VIDEO_DEV && EXPERIMENTAL
+ ---help---
+ Say Y here if you want to connect this type of radio to your
+ computer's USB port. Note that the audio is not digital, and
+ you must connect the line out connector to a sound card or a
+ set of speakers.
+
+ This driver uses the Video For Linux API. You must enable
+ (Y or M in config) Video For Linux (under Character Devices)
+ to use this driver. Information on this API and pointers to
+ "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dsbr100.
+
+config USB_ET61X251
+ tristate "USB ET61X[12]51 PC Camera Controller support"
+ depends on USB && VIDEO_DEV
+ ---help---
+ Say Y here if you want support for cameras based on Etoms ET61X151
+ or ET61X251 PC Camera Controllers.
+
+ See <file:Documentation/usb/et61x251.txt> for more informations.
+
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" to use this driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called et61x251.
+
+config USB_IBMCAM
+ tristate "USB IBM (Xirlink) C-it Camera support"
+ depends on USB && VIDEO_DEV
+ ---help---
+ Say Y here if you want to connect a IBM "C-It" camera, also known as
+ "Xirlink PC Camera" to your computer's USB port. For more
+ information, read <file:Documentation/usb/ibmcam.txt>.
+
+ This driver uses the Video For Linux API. You must enable
+ (Y or M in config) Video For Linux (under Character Devices)
+ to use this driver. Information on this API and pointers to
+ "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ibmcam.
+
+ This camera has several configuration options which
+ can be specified when you load the module. Read
+ <file:Documentation/usb/ibmcam.txt> to learn more.
+
+config USB_KONICAWC
+ tristate "USB Konica Webcam support"
+ depends on USB && VIDEO_DEV
+ ---help---
+ Say Y here if you want support for webcams based on a Konica
+ chipset. This is known to work with the Intel YC76 webcam.
+
+ This driver uses the Video For Linux API. You must enable
+ (Y or M in config) Video For Linux (under Character Devices)
+ to use this driver. Information on this API and pointers to
+ "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called konicawc.
+
+config USB_OV511
+ tristate "USB OV511 Camera support"
+ depends on USB && VIDEO_DEV
+ ---help---
+ Say Y here if you want to connect this type of camera to your
+ computer's USB port. See <file:Documentation/usb/ov511.txt> for more
+ information and for a list of supported cameras.
+
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" (under Character Devices) to use this driver.
+ Information on this API and pointers to "v4l" programs may be found
+ at <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov511.
+
+config USB_SE401
+ tristate "USB SE401 Camera support"
+ depends on USB && VIDEO_DEV
+ ---help---
+ Say Y here if you want to connect this type of camera to your
+ computer's USB port. See <file:Documentation/usb/se401.txt> for more
+ information and for a list of supported cameras.
+
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" (under Multimedia Devices) to use this driver.
+ Information on this API and pointers to "v4l" programs may be found
+ at <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called se401.
+
+config USB_SN9C102
+ tristate "USB SN9C10x PC Camera Controller support"
+ depends on USB && VIDEO_DEV
+ ---help---
+ Say Y here if you want support for cameras based on SONiX SN9C101,
+ SN9C102 or SN9C103 PC Camera Controllers.
+
+ See <file:Documentation/usb/sn9c102.txt> for more informations.
+
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" to use this driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sn9c102.
+
+config USB_STV680
+ tristate "USB STV680 (Pencam) Camera support"
+ depends on USB && VIDEO_DEV
+ ---help---
+ Say Y here if you want to connect this type of camera to your
+ computer's USB port. This includes the Pencam line of cameras.
+ See <file:Documentation/usb/stv680.txt> for more information and for
+ a list of supported cameras.
+
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" (under Multimedia Devices) to use this driver.
+ Information on this API and pointers to "v4l" programs may be found
+ at <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stv680.
+
+config USB_W9968CF
+ tristate "USB W996[87]CF JPEG Dual Mode Camera support"
+ depends on USB && VIDEO_DEV && I2C && VIDEO_OVCAMCHIP
+ ---help---
+ Say Y here if you want support for cameras based on OV681 or
+ Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips.
+
+ This driver has an optional plugin, which is distributed as a
+ separate module only (released under GPL). It allows to use higher
+ resolutions and framerates, but cannot be included in the official
+ Linux kernel for performance purposes.
+
+ See <file:Documentation/usb/w9968cf.txt> for more informations.
+
+ This driver uses the Video For Linux and the I2C APIs. It needs the
+ OmniVision Camera Chip support as well. You must say Y or M to
+ "Video For Linux", "I2C Support" and "OmniVision Camera Chip
+ support" to use this driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called w9968cf.
+
+config USB_ZC0301
+ tristate "USB ZC0301 Image Processor and Control Chip support"
+ depends on USB && VIDEO_DEV
+ ---help---
+ Say Y here if you want support for cameras based on the ZC0301
+ Image Processor and Control Chip.
+
+ See <file:Documentation/usb/zc0301.txt> for more informations.
+
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" to use this driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zc0301.
+
+config USB_PWC
+ tristate "USB Philips Cameras"
+ depends on USB && VIDEO_DEV
+ ---help---
+ Say Y or M here if you want to use one of these Philips & OEM
+ webcams:
+ * Philips PCA645, PCA646
+ * Philips PCVC675, PCVC680, PCVC690
+ * Philips PCVC720/40, PCVC730, PCVC740, PCVC750
+ * Askey VC010
+ * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro'
+ and 'Orbit'/'Sphere'
+ * Samsung MPC-C10, MPC-C30
+ * Creative Webcam 5, Pro Ex
+ * SOTEC Afina Eye
+ * Visionite VCS-UC300, VCS-UM100
+
+ The PCA635, PCVC665 and PCVC720/20 are not supported by this driver
+ and never will be, but the 665 and 720/20 are supported by other
+ drivers.
+
+ See <file:Documentation/usb/philips.txt> for more information and
+ installation instructions.
+
+ The built-in microphone is enabled by selecting USB Audio support.
+
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" (under Character Devices) to use this driver.
+ Information on this API and pointers to "v4l" programs may be found
+ at <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pwc.
+
+endmenu # V4L USB devices
+
endmenu
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 1a56a2d9e294..1c0e72e5a593 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -65,4 +65,23 @@ obj-$(CONFIG_VIDEO_CX25840) += cx25840/
obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o
obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
+et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o
+zc0301-objs := zc0301_core.o zc0301_pas202bcb.o
+
+obj-$(CONFIG_USB_DABUSB) += dabusb.o
+obj-$(CONFIG_USB_DSBR) += dsbr100.o
+obj-$(CONFIG_USB_OV511) += ov511.o
+obj-$(CONFIG_USB_SE401) += se401.o
+obj-$(CONFIG_USB_STV680) += stv680.o
+obj-$(CONFIG_USB_W9968CF) += w9968cf.o
+
+obj-$(CONFIG_USB_SN9C102) += sn9c102/
+obj-$(CONFIG_USB_ET61X251) += et61x251/
+obj-$(CONFIG_USB_PWC) += pwc/
+obj-$(CONFIG_USB_ZC0301) += zc0301/
+
+obj-$(CONFIG_USB_IBMCAM) += usbvideo/
+obj-$(CONFIG_USB_KONICAWC) += usbvideo/
+obj-$(CONFIG_USB_VICAM) += usbvideo/
+
EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core
diff --git a/drivers/media/video/dabfirmware.h b/drivers/media/video/dabfirmware.h
new file mode 100644
index 000000000000..d14d803566a3
--- /dev/null
+++ b/drivers/media/video/dabfirmware.h
@@ -0,0 +1,1408 @@
+/*
+ * dabdata.h - dab usb firmware and bitstream data
+ */
+
+static INTEL_HEX_RECORD firmware[] = {
+
+{ 2, 0x0000, 0, {0x21,0x57} },
+{ 3, 0x0003, 0, {0x02,0x01,0x66} },
+{ 3, 0x000b, 0, {0x02,0x01,0x66} },
+{ 3, 0x0013, 0, {0x02,0x01,0x66} },
+{ 3, 0x001b, 0, {0x02,0x01,0x66} },
+{ 3, 0x0023, 0, {0x02,0x01,0x66} },
+{ 3, 0x002b, 0, {0x02,0x01,0x66} },
+{ 3, 0x0033, 0, {0x02,0x03,0x0f} },
+{ 3, 0x003b, 0, {0x02,0x01,0x66} },
+{ 3, 0x0043, 0, {0x02,0x01,0x00} },
+{ 3, 0x004b, 0, {0x02,0x01,0x66} },
+{ 3, 0x0053, 0, {0x02,0x01,0x66} },
+{ 3, 0x005b, 0, {0x02,0x04,0xbd} },
+{ 3, 0x0063, 0, {0x02,0x01,0x67} },
+{ 3, 0x0100, 0, {0x02,0x0c,0x5a} },
+{ 3, 0x0104, 0, {0x02,0x01,0xed} },
+{ 3, 0x0108, 0, {0x02,0x02,0x51} },
+{ 3, 0x010c, 0, {0x02,0x02,0x7c} },
+{ 3, 0x0110, 0, {0x02,0x02,0xe4} },
+{ 1, 0x0114, 0, {0x32} },
+{ 1, 0x0118, 0, {0x32} },
+{ 3, 0x011c, 0, {0x02,0x05,0xfd} },
+{ 3, 0x0120, 0, {0x02,0x00,0x00} },
+{ 3, 0x0124, 0, {0x02,0x00,0x00} },
+{ 3, 0x0128, 0, {0x02,0x04,0x3c} },
+{ 3, 0x012c, 0, {0x02,0x04,0x6a} },
+{ 3, 0x0130, 0, {0x02,0x00,0x00} },
+{ 3, 0x0134, 0, {0x02,0x00,0x00} },
+{ 3, 0x0138, 0, {0x02,0x00,0x00} },
+{ 3, 0x013c, 0, {0x02,0x00,0x00} },
+{ 3, 0x0140, 0, {0x02,0x00,0x00} },
+{ 3, 0x0144, 0, {0x02,0x00,0x00} },
+{ 3, 0x0148, 0, {0x02,0x00,0x00} },
+{ 3, 0x014c, 0, {0x02,0x00,0x00} },
+{ 3, 0x0150, 0, {0x02,0x00,0x00} },
+{ 3, 0x0154, 0, {0x02,0x00,0x00} },
+{ 10, 0x0157, 0, {0x75,0x81,0x7f,0xe5,0x82,0x60,0x03,0x02,0x01,0x61} },
+{ 5, 0x0161, 0, {0x12,0x07,0x6f,0x21,0x64} },
+{ 1, 0x0166, 0, {0x32} },
+{ 14, 0x0167, 0, {0xc0,0xd0,0xc0,0x86,0xc0,0x82,0xc0,0x83,0xc0,0xe0,0x90,0x7f,0x97,0xe0} },
+{ 14, 0x0175, 0, {0x44,0x80,0xf0,0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x0183, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x0191, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x90,0x7f,0x97,0xe0} },
+{ 3, 0x019f, 0, {0x55,0x7f,0xf0} },
+{ 14, 0x01a2, 0, {0x90,0x7f,0x9a,0xe0,0x30,0xe4,0x23,0x90,0x7f,0x68,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x01b0, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x01be, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x01cc, 0, {0xe5,0xd8,0xc2,0xe3,0xf5,0xd8,0xd0,0xe0,0xd0,0x83,0xd0,0x82,0xd0,0x86} },
+{ 3, 0x01da, 0, {0xd0,0xd0,0x32} },
+{ 8, 0x01dd, 0, {0x75,0x86,0x00,0x90,0xff,0xc3,0x7c,0x05} },
+{ 7, 0x01e5, 0, {0xa3,0xe5,0x82,0x45,0x83,0x70,0xf9} },
+{ 1, 0x01ec, 0, {0x22} },
+{ 14, 0x01ed, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0xd0} },
+{ 14, 0x01fb, 0, {0x75,0xd0,0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91} },
+{ 13, 0x0209, 0, {0x90,0x88,0x00,0xe0,0xf5,0x41,0x90,0x7f,0xab,0x74,0x02,0xf0,0x90} },
+{ 9, 0x0216, 0, {0x7f,0xab,0x74,0x02,0xf0,0xe5,0x32,0x60,0x21} },
+{ 4, 0x021f, 0, {0x7a,0x00,0x7b,0x00} },
+{ 11, 0x0223, 0, {0xc3,0xea,0x94,0x18,0xeb,0x64,0x80,0x94,0x80,0x50,0x12} },
+{ 14, 0x022e, 0, {0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x0a,0xba,0x00} },
+{ 2, 0x023c, 0, {0x01,0x0b} },
+{ 2, 0x023e, 0, {0x80,0xe3} },
+{ 2, 0x0240, 0, {0xd0,0x86} },
+{ 14, 0x0242, 0, {0xd0,0xd0,0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0} },
+{ 1, 0x0250, 0, {0x32} },
+{ 14, 0x0251, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x025f, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} },
+{ 4, 0x026d, 0, {0x04,0xf0,0xd0,0x86} },
+{ 11, 0x0271, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x027c, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} },
+{ 14, 0x028a, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} },
+{ 13, 0x0298, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} },
+{ 12, 0x02a5, 0, {0x7f,0xab,0x74,0x08,0xf0,0x75,0x6e,0x00,0x75,0x6f,0x02,0x12} },
+{ 6, 0x02b1, 0, {0x11,0x44,0x75,0x70,0x39,0x75} },
+{ 6, 0x02b7, 0, {0x71,0x0c,0x75,0x72,0x02,0x12} },
+{ 12, 0x02bd, 0, {0x11,0x75,0x90,0x7f,0xd6,0xe4,0xf0,0x75,0xd8,0x20,0xd0,0x86} },
+{ 14, 0x02c9, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} },
+{ 13, 0x02d7, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x02e4, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x02f2, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} },
+{ 4, 0x0300, 0, {0x10,0xf0,0xd0,0x86} },
+{ 11, 0x0304, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x030f, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} },
+{ 14, 0x031d, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} },
+{ 12, 0x032b, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0x75,0x6e,0x00,0x75,0x6f,0x02} },
+{ 7, 0x0337, 0, {0x12,0x11,0x44,0x75,0x70,0x40,0x75} },
+{ 6, 0x033e, 0, {0x71,0x0c,0x75,0x72,0x02,0x12} },
+{ 14, 0x0344, 0, {0x11,0x75,0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0} },
+{ 5, 0x0352, 0, {0x75,0xd8,0x10,0xd0,0x86} },
+{ 14, 0x0357, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} },
+{ 13, 0x0365, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 13, 0x0372, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} },
+{ 12, 0x037f, 0, {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x42,0xf0,0x12,0x10,0x1b,0x90} },
+{ 13, 0x038b, 0, {0x7f,0xa6,0xe5,0x43,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40} },
+{ 1, 0x0398, 0, {0xf0} },
+{ 1, 0x0399, 0, {0x22} },
+{ 13, 0x039a, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} },
+{ 12, 0x03a7, 0, {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x44,0xf0,0x12,0x10,0x1b,0x90} },
+{ 12, 0x03b3, 0, {0x7f,0xa6,0xe5,0x45,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0xe5} },
+{ 11, 0x03bf, 0, {0x46,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40,0xf0} },
+{ 1, 0x03ca, 0, {0x22} },
+{ 10, 0x03cb, 0, {0x75,0x44,0x02,0x75,0x45,0x00,0x75,0x46,0x00,0x12} },
+{ 9, 0x03d5, 0, {0x03,0x9a,0x75,0x42,0x03,0x75,0x43,0x00,0x12} },
+{ 2, 0x03de, 0, {0x03,0x72} },
+{ 1, 0x03e0, 0, {0x22} },
+{ 12, 0x03e1, 0, {0x90,0x88,0x00,0xe5,0x36,0xf0,0x90,0x88,0x00,0x74,0x10,0x25} },
+{ 9, 0x03ed, 0, {0x36,0xf0,0x12,0x01,0xdd,0x75,0x42,0x01,0x75} },
+{ 9, 0x03f6, 0, {0x43,0x18,0x12,0x03,0x72,0x75,0x44,0x02,0x75} },
+{ 9, 0x03ff, 0,{0x45,0x00,0x75,0x46,0x00,0x12,0x03,0x9a,0x75} },
+{ 8, 0x0408, 0,{0x42,0x03,0x75,0x43,0x44,0x12,0x03,0x72} },
+{ 1, 0x0410, 0,{0x22} },
+{ 14, 0x0411, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x041f, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} },
+{ 4, 0x042d, 0, {0x02,0xf0,0xd0,0x86} },
+{ 11, 0x0431, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x043c, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x044a, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xa9,0x74} },
+{ 7, 0x0458, 0, {0x04,0xf0,0x75,0x30,0x01,0xd0,0x86} },
+{ 11, 0x045f, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x046a, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x0478, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} },
+{ 7, 0x0486, 0, {0x04,0xf0,0x75,0x31,0x01,0xd0,0x86} },
+{ 11, 0x048d, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x0498, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 12, 0x04a6, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe5,0xf5,0x91,0xd0,0x86} },
+{ 11, 0x04b2, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x04bd, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 12, 0x04cb, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe7,0xf5,0x91,0xd0,0x86} },
+{ 11, 0x04d7, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 12, 0x04e2, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x20,0x90,0x7f,0x96,0xe4,0xf0} },
+{ 1, 0x04ee, 0, {0x22} },
+{ 7, 0x04ef, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x21} },
+{ 1, 0x04f6, 0, {0x22} },
+{ 14, 0x04f7, 0, {0x90,0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xe0,0xfb,0x74,0x80,0x2a,0xfa} },
+{ 14, 0x0505, 0, {0x74,0x80,0x2b,0xfb,0xea,0x03,0x03,0x54,0x3f,0xfc,0xea,0xc4,0x23,0x54} },
+{ 14, 0x0513, 0, {0x1f,0xfa,0x2c,0xfa,0xeb,0x03,0x03,0x54,0x3f,0xfc,0xeb,0xc4,0x23,0x54} },
+{ 11, 0x0521, 0, {0x1f,0xfb,0x2c,0xfb,0x90,0x17,0x0a,0xe0,0xfc,0x60,0x02} },
+{ 2, 0x052c, 0, {0x7a,0x00} },
+{ 7, 0x052e, 0, {0x90,0x17,0x0c,0xe0,0xfc,0x60,0x02} },
+{ 2, 0x0535, 0, {0x7b,0x00} },
+{ 11, 0x0537, 0, {0xea,0x2b,0xfc,0xc3,0x13,0xf5,0x3a,0x75,0x44,0x02,0x8b} },
+{ 7, 0x0542, 0, {0x45,0x8a,0x46,0x12,0x03,0x9a,0x75} },
+{ 9, 0x0549, 0, {0x6e,0x08,0x75,0x6f,0x00,0x12,0x11,0x44,0x75} },
+{ 4, 0x0552, 0, {0x70,0x47,0x75,0x71} },
+{ 8, 0x0556, 0, {0x0c,0x75,0x72,0x02,0x12,0x11,0x75,0x85} },
+{ 5, 0x055e, 0, {0x3a,0x73,0x12,0x11,0xa0} },
+{ 1, 0x0563, 0, {0x22} },
+{ 14, 0x0564, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} },
+{ 14, 0x0572, 0, {0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xef,0xe0,0xfc} },
+{ 14, 0x0580, 0, {0x33,0x95,0xe0,0xfd,0x8c,0x05,0x7c,0x00,0x90,0x7f,0xee,0xe0,0xfe,0x33} },
+{ 14, 0x058e, 0, {0x95,0xe0,0xff,0xec,0x2e,0xfc,0xed,0x3f,0xfd,0x90,0x7f,0xe9,0xe0,0xfe} },
+{ 5, 0x059c, 0, {0xbe,0x01,0x02,0x80,0x03} },
+{ 3, 0x05a1, 0, {0x02,0x05,0xf9} },
+{ 6, 0x05a4, 0, {0xbc,0x01,0x21,0xbd,0x00,0x1e} },
+{ 14, 0x05aa, 0, {0xea,0xc4,0x03,0x54,0xf8,0xfc,0xeb,0x25,0xe0,0xfd,0x2c,0x24,0x00,0xfc} },
+{ 14, 0x05b8, 0, {0xe4,0x34,0x17,0xfd,0x90,0x7e,0xc0,0xe0,0xfe,0x8c,0x82,0x8d,0x83,0xf0} },
+{ 2, 0x05c6, 0, {0x80,0x31} },
+{ 14, 0x05c8, 0, {0xea,0xc4,0x03,0x54,0xf8,0xfa,0xeb,0x25,0xe0,0xfb,0x2a,0xfa,0x24,0x00} },
+{ 14, 0x05d6, 0, {0xfb,0xe4,0x34,0x17,0xfc,0x90,0x7e,0xc0,0xe0,0xfd,0x8b,0x82,0x8c,0x83} },
+{ 14, 0x05e4, 0, {0xf0,0x74,0x01,0x2a,0x24,0x00,0xfa,0xe4,0x34,0x17,0xfb,0x90,0x7e,0xc1} },
+{ 7, 0x05f2, 0, {0xe0,0xfc,0x8a,0x82,0x8b,0x83,0xf0} },
+{ 3, 0x05f9, 0, {0x75,0x38,0x01} },
+{ 1, 0x05fc, 0, {0x22} },
+{ 14, 0x05fd, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} },
+{ 14, 0x060b, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} },
+{ 13, 0x0619, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} },
+{ 13, 0x0626, 0, {0x7f,0xaa,0x74,0x01,0xf0,0x12,0x05,0x64,0x75,0x37,0x00,0xd0,0x86} },
+{ 14, 0x0633, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} },
+{ 13, 0x0641, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x064e, 0, {0x90,0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xee,0xe0} },
+{ 14, 0x065c, 0, {0xfc,0x33,0x95,0xe0,0xfd,0x90,0x7f,0x96,0xe0,0xfe,0x90,0x7f,0x96,0x74} },
+{ 14, 0x066a, 0, {0x80,0x65,0x06,0xf0,0x90,0x7f,0x00,0x74,0x01,0xf0,0xea,0xc4,0x03,0x54} },
+{ 14, 0x0678, 0, {0xf8,0xfe,0xeb,0x25,0xe0,0xfb,0x2e,0xfe,0x24,0x00,0xfb,0xe4,0x34,0x17} },
+{ 14, 0x0686, 0, {0xff,0x8b,0x82,0x8f,0x83,0xe0,0xfb,0x74,0x01,0x2e,0x24,0x00,0xfe,0xe4} },
+{ 14, 0x0694, 0, {0x34,0x17,0xff,0x8e,0x82,0x8f,0x83,0xe0,0xfe,0x90,0x7f,0xe9,0xe0,0xff} },
+{ 3, 0x06a2, 0, {0xbf,0x81,0x0a} },
+{ 10, 0x06a5, 0, {0x90,0x7f,0x00,0xeb,0xf0,0x90,0x7f,0x01,0xee,0xf0} },
+{ 8, 0x06af, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x82,0x1a} },
+{ 3, 0x06b7, 0, {0xba,0x01,0x0c} },
+{ 12, 0x06ba, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} },
+{ 11, 0x06c6, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0xb5,0xf0} },
+{ 8, 0x06d1, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x83,0x1b} },
+{ 3, 0x06d9, 0, {0xba,0x01,0x0d} },
+{ 13, 0x06dc, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} },
+{ 11, 0x06e9, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0x12,0xf0} },
+{ 8, 0x06f4, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x84,0x1c} },
+{ 3, 0x06fc, 0, {0xba,0x01,0x0d} },
+{ 13, 0x06ff, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0c} },
+{ 12, 0x070c, 0, {0x90,0x7f,0x00,0x74,0x80,0xf0,0x90,0x7f,0x01,0x74,0x01,0xf0} },
+{ 5, 0x0718, 0, {0x90,0x7f,0xb5,0xec,0xf0} },
+{ 1, 0x071d, 0, {0x22} },
+{ 12, 0x071e, 0, {0x75,0x36,0x0d,0x90,0x88,0x00,0x74,0x1d,0xf0,0x75,0x6b,0x80} },
+{ 10, 0x072a, 0, {0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} },
+{ 9, 0x0734, 0, {0x6c,0x0f,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} },
+{ 9, 0x073d, 0, {0x6c,0x06,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} },
+{ 7, 0x0746, 0, {0x6c,0x01,0x12,0x10,0xe2,0x7a,0x00} },
+{ 3, 0x074d, 0, {0xba,0xff,0x00} },
+{ 2, 0x0750, 0, {0x50,0x0a} },
+{ 10, 0x0752, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} },
+{ 10, 0x075c, 0, {0x75,0x6b,0x80,0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75} },
+{ 8, 0x0766, 0, {0x6b,0x80,0x75,0x6c,0x0f,0x12,0x10,0xe2} },
+{ 1, 0x076e, 0, {0x22} },
+{ 14, 0x076f, 0, {0x90,0x7f,0xa1,0xe4,0xf0,0x90,0x7f,0xaf,0x74,0x01,0xf0,0x90,0x7f,0x92} },
+{ 14, 0x077d, 0, {0x74,0x02,0xf0,0x75,0x8e,0x31,0x75,0x89,0x21,0x75,0x88,0x00,0x75,0xc8} },
+{ 14, 0x078b, 0, {0x00,0x75,0x8d,0x40,0x75,0x98,0x40,0x75,0xc0,0x40,0x75,0x87,0x00,0x75} },
+{ 9, 0x0799, 0, {0x20,0x00,0x75,0x21,0x00,0x75,0x22,0x00,0x75} },
+{ 5, 0x07a2, 0, {0x23,0x00,0x75,0x47,0x00} },
+{ 7, 0x07a7, 0, {0xc3,0xe5,0x47,0x94,0x20,0x50,0x11} },
+{ 13, 0x07ae, 0, {0xe5,0x47,0x24,0x00,0xf5,0x82,0xe4,0x34,0x17,0xf5,0x83,0xe4,0xf0} },
+{ 4, 0x07bb, 0, {0x05,0x47,0x80,0xe8} },
+{ 9, 0x07bf, 0, {0xe4,0xf5,0x40,0xf5,0x3f,0xe4,0xf5,0x3c,0xf5} },
+{ 7, 0x07c8, 0, {0x3b,0xe4,0xf5,0x3e,0xf5,0x3d,0x75} },
+{ 11, 0x07cf, 0, {0x32,0x00,0x75,0x37,0x00,0x75,0x39,0x00,0x90,0x7f,0x93} },
+{ 14, 0x07da, 0, {0x74,0x3c,0xf0,0x90,0x7f,0x9c,0x74,0xff,0xf0,0x90,0x7f,0x96,0x74,0x80} },
+{ 14, 0x07e8, 0, {0xf0,0x90,0x7f,0x94,0x74,0x70,0xf0,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} },
+{ 14, 0x07f6, 0, {0x7f,0x97,0xe4,0xf0,0x90,0x7f,0x95,0x74,0xc2,0xf0,0x90,0x7f,0x98,0x74} },
+{ 14, 0x0804, 0, {0x28,0xf0,0x90,0x7f,0x9e,0x74,0x28,0xf0,0x90,0x7f,0xf0,0xe4,0xf0,0x90} },
+{ 14, 0x0812, 0, {0x7f,0xf1,0xe4,0xf0,0x90,0x7f,0xf2,0xe4,0xf0,0x90,0x7f,0xf3,0xe4,0xf0} },
+{ 14, 0x0820, 0, {0x90,0x7f,0xf4,0xe4,0xf0,0x90,0x7f,0xf5,0xe4,0xf0,0x90,0x7f,0xf6,0xe4} },
+{ 14, 0x082e, 0, {0xf0,0x90,0x7f,0xf7,0xe4,0xf0,0x90,0x7f,0xf8,0xe4,0xf0,0x90,0x7f,0xf9} },
+{ 14, 0x083c, 0, {0x74,0x38,0xf0,0x90,0x7f,0xfa,0x74,0xa0,0xf0,0x90,0x7f,0xfb,0x74,0xa0} },
+{ 14, 0x084a, 0, {0xf0,0x90,0x7f,0xfc,0x74,0xa0,0xf0,0x90,0x7f,0xfd,0x74,0xa0,0xf0,0x90} },
+{ 14, 0x0858, 0, {0x7f,0xfe,0x74,0xa0,0xf0,0x90,0x7f,0xff,0x74,0xa0,0xf0,0x90,0x7f,0xe0} },
+{ 14, 0x0866, 0, {0x74,0x03,0xf0,0x90,0x7f,0xe1,0x74,0x01,0xf0,0x90,0x7f,0xdd,0x74,0x80} },
+{ 11, 0x0874, 0, {0xf0,0x12,0x12,0x43,0x12,0x07,0x1e,0x7a,0x00,0x7b,0x00} },
+{ 9, 0x087f, 0, {0xc3,0xea,0x94,0x1e,0xeb,0x94,0x00,0x50,0x17} },
+{ 12, 0x0888, 0, {0x90,0x88,0x00,0xe0,0xf5,0x47,0x90,0x88,0x0b,0xe0,0xf5,0x47} },
+{ 9, 0x0894, 0, {0x90,0x7f,0x68,0xf0,0x0a,0xba,0x00,0x01,0x0b} },
+{ 2, 0x089d, 0, {0x80,0xe0} },
+{ 12, 0x089f, 0, {0x12,0x03,0xe1,0x90,0x7f,0xd6,0xe4,0xf0,0x7a,0x00,0x7b,0x00} },
+{ 13, 0x08ab, 0, {0x8a,0x04,0x8b,0x05,0xc3,0xea,0x94,0xe0,0xeb,0x94,0x2e,0x50,0x1a} },
+{ 14, 0x08b8, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} },
+{ 10, 0x08c6, 0, {0x04,0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} },
+{ 2, 0x08d0, 0, {0x80,0xd9} },
+{ 13, 0x08d2, 0, {0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0,0x90} },
+{ 14, 0x08df, 0, {0x7f,0xde,0x74,0x05,0xf0,0x90,0x7f,0xdf,0x74,0x05,0xf0,0x90,0x7f,0xac} },
+{ 14, 0x08ed, 0, {0xe4,0xf0,0x90,0x7f,0xad,0x74,0x05,0xf0,0x75,0xa8,0x80,0x75,0xf8,0x10} },
+{ 13, 0x08fb, 0, {0x90,0x7f,0xae,0x74,0x0b,0xf0,0x90,0x7f,0xe2,0x74,0x88,0xf0,0x90} },
+{ 12, 0x0908, 0, {0x7f,0xab,0x74,0x08,0xf0,0x75,0xe8,0x11,0x75,0x32,0x01,0x75} },
+{ 12, 0x0914, 0, {0x31,0x00,0x75,0x30,0x00,0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7} },
+{ 10, 0x0920, 0, {0xd0,0x05,0xd0,0x04,0x75,0x34,0x00,0x75,0x35,0x01} },
+{ 13, 0x092a, 0, {0x90,0x7f,0xae,0x74,0x03,0xf0,0x8c,0x02,0xba,0x00,0x02,0x80,0x03} },
+{ 3, 0x0937, 0, {0x02,0x0a,0x3f} },
+{ 12, 0x093a, 0, {0x85,0x33,0x34,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90,0x7f,0x97} },
+{ 14, 0x0946, 0, {0x74,0x08,0xf0,0x90,0x7f,0x9d,0x74,0x88,0xf0,0x90,0x7f,0x9a,0xe0,0xfa} },
+{ 12, 0x0954, 0, {0x74,0x05,0x5a,0xf5,0x33,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} },
+{ 13, 0x0960, 0, {0x7f,0x97,0x74,0x02,0xf0,0x90,0x7f,0x9d,0x74,0x82,0xf0,0xe5,0x33} },
+{ 13, 0x096d, 0, {0x25,0xe0,0xfa,0x90,0x7f,0x9a,0xe0,0x54,0x05,0xfb,0x4a,0xf5,0x33} },
+{ 2, 0x097a, 0, {0x60,0x0c} },
+{ 12, 0x097c, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x4a,0xf0} },
+{ 11, 0x0988, 0, {0x75,0x6e,0x00,0x75,0x6f,0x00,0xc0,0x04,0xc0,0x05,0x12} },
+{ 14, 0x0993, 0, {0x11,0x44,0xd0,0x05,0xd0,0x04,0x90,0x17,0x13,0xe0,0xfa,0x74,0x80,0x2a} },
+{ 6, 0x09a1, 0, {0xfa,0xe5,0x33,0xb4,0x04,0x29} },
+{ 3, 0x09a7, 0, {0xba,0xa0,0x00} },
+{ 2, 0x09aa, 0, {0x50,0x24} },
+{ 13, 0x09ac, 0, {0x90,0x17,0x13,0xe0,0x04,0xfb,0x0b,0x90,0x17,0x13,0xeb,0xf0,0x90} },
+{ 14, 0x09b9, 0, {0x17,0x13,0xe0,0xfb,0x90,0x17,0x15,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05} },
+{ 9, 0x09c7, 0, {0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02} },
+{ 5, 0x09d0, 0, {0xe5,0x33,0xb4,0x02,0x26} },
+{ 6, 0x09d5, 0, {0xc3,0x74,0x04,0x9a,0x50,0x20} },
+{ 13, 0x09db, 0, {0x90,0x17,0x13,0xe0,0xfa,0x1a,0x1a,0x90,0x17,0x13,0xea,0xf0,0x90} },
+{ 13, 0x09e8, 0, {0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xf0,0xc0,0x04,0xc0,0x05,0x12} },
+{ 6, 0x09f5, 0, {0x04,0xf7,0xd0,0x05,0xd0,0x04} },
+{ 5, 0x09fb, 0, {0xe5,0x33,0xb4,0x08,0x1d} },
+{ 4, 0x0a00, 0, {0xe5,0x34,0x70,0x19} },
+{ 10, 0x0a04, 0, {0x74,0x01,0x25,0x35,0x54,0x0f,0xf5,0x35,0x85,0x35} },
+{ 12, 0x0a0e, 0, {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} },
+{ 3, 0x0a1a, 0, {0x05,0xd0,0x04} },
+{ 5, 0x0a1d, 0, {0xe5,0x33,0xb4,0x01,0x1d} },
+{ 4, 0x0a22, 0, {0xe5,0x34,0x70,0x19} },
+{ 10, 0x0a26, 0, {0xe5,0x35,0x24,0xff,0x54,0x0f,0xf5,0x35,0x85,0x35} },
+{ 12, 0x0a30, 0, {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} },
+{ 3, 0x0a3c, 0, {0x05,0xd0,0x04} },
+{ 14, 0x0a3f, 0, {0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0,0x04,0x90,0x7f,0x96} },
+{ 14, 0x0a4d, 0, {0xe0,0xfa,0x90,0x7f,0x96,0x74,0x7f,0x5a,0xf0,0x90,0x7f,0x97,0x74,0x08} },
+{ 10, 0x0a5b, 0, {0xf0,0xc3,0xec,0x94,0x00,0xed,0x94,0x02,0x40,0x08} },
+{ 8, 0x0a65, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x20,0xe6,0x08} },
+{ 8, 0x0a6d, 0, {0xc3,0xe4,0x9c,0x74,0x08,0x9d,0x50,0x13} },
+{ 14, 0x0a75, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x40,0x65,0x02,0xf0,0x7c} },
+{ 5, 0x0a83, 0, {0x00,0x7d,0x00,0x80,0x05} },
+{ 5, 0x0a88, 0, {0x0c,0xbc,0x00,0x01,0x0d} },
+{ 5, 0x0a8d, 0, {0xe5,0x38,0xb4,0x01,0x0e} },
+{ 13, 0x0a92, 0, {0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0x75,0x38} },
+{ 1, 0x0a9f, 0, {0x00} },
+{ 7, 0x0aa0, 0, {0xe5,0x31,0x70,0x03,0x02,0x09,0x2a} },
+{ 10, 0x0aa7, 0, {0x90,0x7f,0xc9,0xe0,0xfa,0x70,0x03,0x02,0x0c,0x2d} },
+{ 14, 0x0ab1, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} },
+{ 9, 0x0abf, 0, {0x7d,0xc0,0xe0,0xfa,0xba,0x2c,0x02,0x80,0x03} },
+{ 3, 0x0ac8, 0, {0x02,0x0b,0x36} },
+{ 5, 0x0acb, 0, {0x75,0x32,0x00,0x7b,0x00} },
+{ 3, 0x0ad0, 0, {0xbb,0x64,0x00} },
+{ 2, 0x0ad3, 0, {0x50,0x1c} },
+{ 14, 0x0ad5, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} },
+{ 13, 0x0ae3, 0, {0x04,0xd0,0x03,0xd0,0x02,0x90,0x88,0x0f,0xe0,0xf5,0x47,0x0b,0x80} },
+{ 1, 0x0af0, 0, {0xdf} },
+{ 13, 0x0af1, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x07,0x1e,0x12,0x03,0xe1,0x12} },
+{ 12, 0x0afe, 0, {0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x75,0x6e,0x00,0x75} },
+{ 13, 0x0b0a, 0, {0x6f,0x01,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0x44,0xd0,0x05} },
+{ 9, 0x0b17, 0, {0xd0,0x04,0xd0,0x02,0x75,0x70,0x4d,0x75,0x71} },
+{ 11, 0x0b20, 0, {0x0c,0x75,0x72,0x02,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} },
+{ 11, 0x0b2b, 0, {0x11,0x75,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x02,0x0c,0x2d} },
+{ 3, 0x0b36, 0, {0xba,0x2a,0x3b} },
+{ 13, 0x0b39, 0, {0x90,0x7f,0x98,0x74,0x20,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} },
+{ 14, 0x0b46, 0, {0x01,0xdd,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x90,0x7f,0x98,0x74,0x28,0xf0} },
+{ 2, 0x0b54, 0, {0x7b,0x00} },
+{ 3, 0x0b56, 0, {0xbb,0x0a,0x00} },
+{ 5, 0x0b59, 0, {0x40,0x03,0x02,0x0c,0x2d} },
+{ 14, 0x0b5e, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} },
+{ 8, 0x0b6c, 0, {0x04,0xd0,0x03,0xd0,0x02,0x0b,0x80,0xe2} },
+{ 3, 0x0b74, 0, {0xba,0x2b,0x1a} },
+{ 8, 0x0b77, 0, {0x90,0x7f,0xc9,0xe0,0xfb,0xbb,0x40,0x12} },
+{ 14, 0x0b7f, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x12,0x05,0xd0,0x05,0xd0,0x04,0xd0} },
+{ 4, 0x0b8d, 0, {0x02,0x02,0x0c,0x2d} },
+{ 3, 0x0b91, 0, {0xba,0x10,0x1f} },
+{ 14, 0x0b94, 0, {0x90,0x7f,0x96,0xe0,0xfb,0x90,0x7f,0x96,0x74,0x80,0x65,0x03,0xf0,0xc0} },
+{ 14, 0x0ba2, 0, {0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x3d,0xd0,0x05,0xd0,0x04,0xd0,0x02} },
+{ 3, 0x0bb0, 0, {0x02,0x0c,0x2d} },
+{ 3, 0x0bb3, 0, {0xba,0x11,0x12} },
+{ 14, 0x0bb6, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x6a,0xd0,0x05,0xd0,0x04,0xd0} },
+{ 4, 0x0bc4, 0, {0x02,0x02,0x0c,0x2d} },
+{ 3, 0x0bc8, 0, {0xba,0x12,0x12} },
+{ 14, 0x0bcb, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x8f,0xd0,0x05,0xd0,0x04,0xd0} },
+{ 4, 0x0bd9, 0, {0x02,0x02,0x0c,0x2d} },
+{ 3, 0x0bdd, 0, {0xba,0x13,0x0b} },
+{ 11, 0x0be0, 0, {0x90,0x7d,0xc1,0xe0,0xfb,0x90,0x88,0x00,0xf0,0x80,0x42} },
+{ 3, 0x0beb, 0, {0xba,0x14,0x11} },
+{ 14, 0x0bee, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0xdd,0xd0,0x05,0xd0,0x04,0xd0} },
+{ 3, 0x0bfc, 0, {0x02,0x80,0x2e} },
+{ 3, 0x0bff, 0, {0xba,0x15,0x1d} },
+{ 12, 0x0c02, 0, {0x90,0x7d,0xc1,0xe0,0xf5,0x75,0x90,0x7d,0xc2,0xe0,0xf5,0x76} },
+{ 14, 0x0c0e, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0,0x05,0xd0,0x04,0xd0} },
+{ 3, 0x0c1c, 0, {0x02,0x80,0x0e} },
+{ 3, 0x0c1f, 0, {0xba,0x16,0x0b} },
+{ 11, 0x0c22, 0, {0xc0,0x04,0xc0,0x05,0x12,0x13,0xa3,0xd0,0x05,0xd0,0x04} },
+{ 11, 0x0c2d, 0, {0x90,0x7f,0xc9,0xe4,0xf0,0x75,0x31,0x00,0x02,0x09,0x2a} },
+{ 1, 0x0c38, 0, {0x22} },
+{ 7, 0x0c39, 0, {0x53,0x55,0x50,0x45,0x4e,0x44,0x00} },
+{ 7, 0x0c40, 0, {0x52,0x45,0x53,0x55,0x4d,0x45,0x00} },
+{ 6, 0x0c47, 0, {0x20,0x56,0x6f,0x6c,0x20,0x00} },
+{ 13, 0x0c4d, 0, {0x44,0x41,0x42,0x55,0x53,0x42,0x20,0x76,0x31,0x2e,0x30,0x30,0x00} },
+{ 14, 0x0c5a, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} },
+{ 14, 0x0c68, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} },
+{ 13, 0x0c76, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} },
+{ 14, 0x0c83, 0, {0x7f,0xab,0x74,0x01,0xf0,0x90,0x7f,0xe8,0xe0,0xfa,0x90,0x7f,0xe9,0xe0} },
+{ 6, 0x0c91, 0, {0xfb,0xbb,0x00,0x02,0x80,0x03} },
+{ 3, 0x0c97, 0, {0x02,0x0d,0x38} },
+{ 3, 0x0c9a, 0, {0xba,0x80,0x14} },
+{ 14, 0x0c9d, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5} },
+{ 6, 0x0cab, 0, {0x74,0x02,0xf0,0x02,0x0e,0xcd} },
+{ 5, 0x0cb1, 0, {0xba,0x82,0x02,0x80,0x03} },
+{ 3, 0x0cb6, 0, {0x02,0x0d,0x1d} },
+{ 8, 0x0cb9, 0, {0x90,0x7f,0xec,0xe0,0xfc,0xbc,0x01,0x00} },
+{ 2, 0x0cc1, 0, {0x40,0x21} },
+{ 6, 0x0cc3, 0, {0xc3,0x74,0x07,0x9c,0x40,0x1b} },
+{ 14, 0x0cc9, 0, {0xec,0x24,0xff,0x25,0xe0,0xfd,0x24,0xc6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} },
+{ 13, 0x0cd7, 0, {0x83,0xe0,0xfd,0x53,0x05,0x01,0x90,0x7f,0x00,0xed,0xf0,0x80,0x2b} },
+{ 3, 0x0ce4, 0, {0xbc,0x81,0x00} },
+{ 2, 0x0ce7, 0, {0x40,0x21} },
+{ 6, 0x0ce9, 0, {0xc3,0x74,0x87,0x9c,0x40,0x1b} },
+{ 14, 0x0cef, 0, {0xec,0x24,0x7f,0x25,0xe0,0xfc,0x24,0xb6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} },
+{ 13, 0x0cfd, 0, {0x83,0xe0,0xfc,0x53,0x04,0x01,0x90,0x7f,0x00,0xec,0xf0,0x80,0x05} },
+{ 5, 0x0d0a, 0, {0x90,0x7f,0x00,0xe4,0xf0} },
+{ 14, 0x0d0f, 0, {0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74,0x02,0xf0,0x02,0x0e,0xcd} },
+{ 5, 0x0d1d, 0, {0xba,0x81,0x02,0x80,0x03} },
+{ 3, 0x0d22, 0, {0x02,0x0e,0xc5} },
+{ 14, 0x0d25, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74} },
+{ 5, 0x0d33, 0, {0x02,0xf0,0x02,0x0e,0xcd} },
+{ 3, 0x0d38, 0, {0xbb,0x01,0x2d} },
+{ 6, 0x0d3b, 0, {0xba,0x00,0x03,0x02,0x0e,0xcd} },
+{ 3, 0x0d41, 0, {0xba,0x02,0x11} },
+{ 13, 0x0d44, 0, {0x75,0x59,0x00,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} },
+{ 4, 0x0d51, 0, {0x02,0x02,0x0e,0xcd} },
+{ 5, 0x0d55, 0, {0xba,0x21,0x02,0x80,0x03} },
+{ 3, 0x0d5a, 0, {0x02,0x0e,0xcd} },
+{ 11, 0x0d5d, 0, {0x75,0x37,0x01,0x90,0x7f,0xc5,0xe4,0xf0,0x02,0x0e,0xcd} },
+{ 3, 0x0d68, 0, {0xbb,0x03,0x1f} },
+{ 6, 0x0d6b, 0, {0xba,0x00,0x03,0x02,0x0e,0xcd} },
+{ 5, 0x0d71, 0, {0xba,0x02,0x02,0x80,0x03} },
+{ 3, 0x0d76, 0, {0x02,0x0e,0xcd} },
+{ 13, 0x0d79, 0, {0x75,0x59,0x01,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} },
+{ 4, 0x0d86, 0, {0x02,0x02,0x0e,0xcd} },
+{ 3, 0x0d8a, 0, {0xbb,0x06,0x54} },
+{ 5, 0x0d8d, 0, {0xba,0x80,0x02,0x80,0x03} },
+{ 3, 0x0d92, 0, {0x02,0x0e,0xc5} },
+{ 8, 0x0d95, 0, {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x01,0x15} },
+{ 12, 0x0d9d, 0, {0x7c,0xfb,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} },
+{ 9, 0x0da9, 0, {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} },
+{ 10, 0x0db2, 0, {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x02,0x02,0x80,0x03} },
+{ 3, 0x0dbc, 0, {0x02,0x0e,0xc5} },
+{ 10, 0x0dbf, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x02,0x80,0x03} },
+{ 3, 0x0dc9, 0, {0x02,0x0e,0xc5} },
+{ 12, 0x0dcc, 0, {0x7c,0x3b,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} },
+{ 9, 0x0dd8, 0, {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} },
+{ 6, 0x0de1, 0, {0xbb,0x07,0x03,0x02,0x0e,0xc5} },
+{ 3, 0x0de7, 0, {0xbb,0x08,0x10} },
+{ 13, 0x0dea, 0, {0xac,0x48,0x90,0x7f,0x00,0xec,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} },
+{ 3, 0x0df7, 0, {0x02,0x0e,0xcd} },
+{ 3, 0x0dfa, 0, {0xbb,0x09,0x31} },
+{ 5, 0x0dfd, 0, {0xba,0x00,0x02,0x80,0x03} },
+{ 3, 0x0e02, 0, {0x02,0x0e,0xc5} },
+{ 14, 0x0e05, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xc3,0x74,0x01,0x9c,0x50,0x03,0x02,0x0e,0xc5} },
+{ 8, 0x0e13, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x0a} },
+{ 10, 0x0e1b, 0, {0x90,0x17,0x21,0xe4,0xf0,0x90,0x17,0x22,0xe4,0xf0} },
+{ 9, 0x0e25, 0, {0x90,0x7f,0xea,0xe0,0xf5,0x48,0x02,0x0e,0xcd} },
+{ 3, 0x0e2e, 0, {0xbb,0x0a,0x27} },
+{ 5, 0x0e31, 0, {0xba,0x81,0x02,0x80,0x03} },
+{ 3, 0x0e36, 0, {0x02,0x0e,0xc5} },
+{ 14, 0x0e39, 0, {0x90,0x7f,0xec,0xe0,0xfa,0x24,0x20,0xfa,0xe4,0x34,0x17,0xfc,0x8a,0x82} },
+{ 14, 0x0e47, 0, {0x8c,0x83,0xe0,0xfa,0x90,0x7f,0x00,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} },
+{ 3, 0x0e55, 0, {0x02,0x0e,0xcd} },
+{ 5, 0x0e58, 0, {0xbb,0x0b,0x02,0x80,0x03} },
+{ 3, 0x0e5d, 0, {0x02,0x0e,0xa9} },
+{ 13, 0x0e60, 0, {0x90,0x17,0x20,0xe4,0xf0,0x90,0x7f,0xec,0xe0,0xfa,0xba,0x01,0x1a} },
+{ 8, 0x0e6d, 0, {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x12} },
+{ 14, 0x0e75, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x90,0x17,0x21,0xf0,0xc0,0x03,0x12,0x04,0xe2} },
+{ 4, 0x0e83, 0, {0xd0,0x03,0x80,0x46} },
+{ 8, 0x0e87, 0, {0x90,0x7f,0xec,0xe0,0xfa,0xba,0x02,0x3e} },
+{ 8, 0x0e8f, 0, {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x36} },
+{ 13, 0x0e97, 0, {0xc0,0x03,0x12,0x04,0xef,0xd0,0x03,0x90,0x7f,0xea,0xe0,0xfa,0x90} },
+{ 5, 0x0ea4, 0, {0x17,0x22,0xf0,0x80,0x24} },
+{ 5, 0x0ea9, 0, {0xbb,0x12,0x02,0x80,0x17} },
+{ 5, 0x0eae, 0, {0xbb,0x81,0x02,0x80,0x0d} },
+{ 5, 0x0eb3, 0, {0xbb,0x83,0x02,0x80,0x08} },
+{ 5, 0x0eb8, 0, {0xbb,0x82,0x02,0x80,0x03} },
+{ 3, 0x0ebd, 0, {0xbb,0x84,0x05} },
+{ 5, 0x0ec0, 0, {0x12,0x06,0x4e,0x80,0x08} },
+{ 8, 0x0ec5, 0, {0x90,0x7f,0xb4,0x74,0x03,0xf0,0x80,0x06} },
+{ 6, 0x0ecd, 0, {0x90,0x7f,0xb4,0x74,0x02,0xf0} },
+{ 2, 0x0ed3, 0, {0xd0,0x86} },
+{ 14, 0x0ed5, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} },
+{ 13, 0x0ee3, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 11, 0x0ef0, 0, {0x90,0x7f,0xec,0xe0,0xf5,0x5a,0xc3,0x94,0x01,0x40,0x1d} },
+{ 7, 0x0efb, 0, {0xc3,0x74,0x07,0x95,0x5a,0x40,0x16} },
+{ 13, 0x0f02, 0, {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xc6,0xf5,0x82,0xe4,0x34} },
+{ 9, 0x0f0f, 0, {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0,0x80,0x22} },
+{ 7, 0x0f18, 0, {0xc3,0xe5,0x5a,0x94,0x81,0x40,0x1b} },
+{ 7, 0x0f1f, 0, {0xc3,0x74,0x87,0x95,0x5a,0x40,0x14} },
+{ 13, 0x0f26, 0, {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xb6,0xf5,0x82,0xe4,0x34} },
+{ 7, 0x0f33, 0, {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0} },
+{ 1, 0x0f3a, 0, {0x22} },
+{ 14, 0x0f3b, 0, {0x09,0x02,0xba,0x00,0x03,0x01,0x00,0x40,0x00,0x09,0x04,0x00,0x00,0x00} },
+{ 14, 0x0f49, 0, {0x01,0x01,0x00,0x00,0x09,0x24,0x01,0x00,0x01,0x3d,0x00,0x01,0x01,0x0c} },
+{ 14, 0x0f57, 0, {0x24,0x02,0x01,0x10,0x07,0x00,0x02,0x03,0x00,0x00,0x00,0x0d,0x24,0x06} },
+{ 14, 0x0f65, 0, {0x03,0x01,0x02,0x15,0x00,0x03,0x00,0x03,0x00,0x00,0x09,0x24,0x03,0x02} },
+{ 14, 0x0f73, 0, {0x01,0x01,0x00,0x01,0x00,0x09,0x24,0x03,0x04,0x02,0x03,0x00,0x03,0x00} },
+{ 14, 0x0f81, 0, {0x09,0x24,0x03,0x05,0x03,0x06,0x00,0x01,0x00,0x09,0x04,0x01,0x00,0x00} },
+{ 14, 0x0f8f, 0, {0x01,0x02,0x00,0x00,0x09,0x04,0x01,0x01,0x01,0x01,0x02,0x00,0x00,0x07} },
+{ 14, 0x0f9d, 0, {0x24,0x01,0x02,0x01,0x01,0x00,0x0b,0x24,0x02,0x01,0x02,0x02,0x10,0x01} },
+{ 14, 0x0fab, 0, {0x80,0xbb,0x00,0x09,0x05,0x88,0x05,0x00,0x01,0x01,0x00,0x00,0x07,0x25} },
+{ 14, 0x0fb9, 0, {0x01,0x00,0x00,0x00,0x00,0x09,0x04,0x02,0x00,0x02,0x00,0x00,0x00,0x00} },
+{ 14, 0x0fc7, 0, {0x07,0x05,0x82,0x02,0x40,0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00} },
+{ 14, 0x0fd5, 0, {0x09,0x04,0x02,0x01,0x03,0x00,0x00,0x00,0x00,0x07,0x05,0x82,0x02,0x40} },
+{ 14, 0x0fe3, 0, {0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00,0x09,0x05,0x89,0x05,0xa0} },
+{ 10, 0x0ff1, 0, {0x01,0x01,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00} },
+{ 14, 0x0ffb, 0, {0x12,0x01,0x00,0x01,0x00,0x00,0x00,0x40,0x47,0x05,0x99,0x99,0x00,0x01} },
+{ 14, 0x1009, 0, {0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x02,0xba} },
+{ 4, 0x1017, 0, {0x00,0x03,0x01,0x00} },
+{ 2, 0x101b, 0, {0x7a,0x00} },
+{ 3, 0x101d, 0, {0xba,0x05,0x00} },
+{ 2, 0x1020, 0, {0x50,0x17} },
+{ 8, 0x1022, 0, {0x90,0x7f,0xa5,0xe0,0xfb,0x30,0xe0,0x05} },
+{ 5, 0x102a, 0, {0x90,0x00,0x01,0x80,0x0d} },
+{ 10, 0x102f, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xe4} },
+{ 3, 0x1039, 0, {0x90,0x00,0x01} },
+{ 1, 0x103c, 0, {0x22} },
+{ 14, 0x103d, 0, {0x90,0x7d,0xc1,0xe0,0xf9,0xa3,0xe0,0xfa,0xa3,0xe0,0xfb,0x7c,0x00,0x7d} },
+{ 4, 0x104b, 0, {0x7e,0xeb,0x60,0x12} },
+{ 14, 0x104f, 0, {0x89,0x82,0x8a,0x83,0xe0,0xa3,0xa9,0x82,0xaa,0x83,0x8c,0x82,0x8d,0x83} },
+{ 4, 0x105d, 0, {0xf0,0x0c,0xdb,0xee} },
+{ 8, 0x1061, 0, {0x90,0x7d,0xc3,0xe0,0x90,0x7f,0xb9,0xf0} },
+{ 1, 0x1069, 0, {0x22} },
+{ 14, 0x106a, 0, {0x90,0x7d,0xc1,0xe0,0xf9,0xa3,0xe0,0xfa,0xa3,0xe0,0xfb,0x7c,0xc4,0x7d} },
+{ 4, 0x1078, 0, {0x7d,0xeb,0x60,0xe5} },
+{ 14, 0x107c, 0, {0x8c,0x82,0x8d,0x83,0xe0,0x0c,0x89,0x82,0x8a,0x83,0xf0,0xa3,0xa9,0x82} },
+{ 4, 0x108a, 0, {0xaa,0x83,0xdb,0xee} },
+{ 1, 0x108e, 0, {0x22} },
+{ 14, 0x108f, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x05,0x86,0x90,0x7d,0xc1,0xe0,0x05,0x86} },
+{ 14, 0x109d, 0, {0xa3,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0x05,0x86,0xa3,0xa3,0xe0,0xf9} },
+{ 5, 0x10ab, 0, {0x60,0x16,0xa3,0x05,0x86} },
+{ 13, 0x10b0, 0, {0x90,0x7f,0xa6,0x05,0x86,0xe0,0xa3,0x05,0x86,0xf0,0xc0,0x01,0x12} },
+{ 6, 0x10bd, 0, {0x10,0x1b,0xd0,0x01,0xd9,0xed} },
+{ 6, 0x10c3, 0, {0x90,0x7f,0xa5,0x74,0x40,0xf0} },
+{ 1, 0x10c9, 0, {0x22} },
+{ 8, 0x10ca, 0, {0x90,0x88,0x02,0x74,0x01,0xf0,0x7a,0x00} },
+{ 3, 0x10d2, 0, {0xba,0xff,0x00} },
+{ 2, 0x10d5, 0, {0x50,0x0a} },
+{ 10, 0x10d7, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} },
+{ 1, 0x10e1, 0, {0x22} },
+{ 5, 0x10e2, 0, {0xe5,0x6b,0xb4,0xc0,0x08} },
+{ 8, 0x10e7, 0, {0x90,0x88,0x03,0xe5,0x6c,0xf0,0x80,0x06} },
+{ 6, 0x10ef, 0, {0x90,0x88,0x02,0xe5,0x6c,0xf0} },
+{ 4, 0x10f5, 0, {0x7a,0x00,0x7b,0x00} },
+{ 11, 0x10f9, 0, {0xc3,0xea,0x94,0x32,0xeb,0x64,0x80,0x94,0x80,0x50,0x07} },
+{ 5, 0x1104, 0, {0x0a,0xba,0x00,0x01,0x0b} },
+{ 2, 0x1109, 0, {0x80,0xee} },
+{ 1, 0x110b, 0, {0x22} },
+{ 10, 0x110c, 0, {0x90,0x88,0x03,0xe5,0x6d,0xf0,0x05,0x39,0x7a,0x00} },
+{ 3, 0x1116, 0, {0xba,0x28,0x00} },
+{ 2, 0x1119, 0, {0x50,0x03} },
+{ 3, 0x111b, 0, {0x0a,0x80,0xf8} },
+{ 5, 0x111e, 0, {0xe5,0x39,0xb4,0x10,0x08} },
+{ 8, 0x1123, 0, {0x90,0x88,0x02,0x74,0xc0,0xf0,0x80,0x0e} },
+{ 5, 0x112b, 0, {0xe5,0x39,0xb4,0x20,0x09} },
+{ 9, 0x1130, 0, {0x90,0x88,0x02,0x74,0x80,0xf0,0x75,0x39,0x00} },
+{ 2, 0x1139, 0, {0x7a,0x00} },
+{ 3, 0x113b, 0, {0xba,0x28,0x00} },
+{ 2, 0x113e, 0, {0x50,0x03} },
+{ 3, 0x1140, 0, {0x0a,0x80,0xf8} },
+{ 1, 0x1143, 0, {0x22} },
+{ 4, 0x1144, 0, {0xe5,0x6f,0x60,0x02} },
+{ 2, 0x1148, 0, {0x80,0x07} },
+{ 7, 0x114a, 0, {0x7a,0x00,0x75,0x39,0x00,0x80,0x05} },
+{ 5, 0x1151, 0, {0x7a,0x40,0x75,0x39,0x10} },
+{ 9, 0x1156, 0, {0xe5,0x6e,0x2a,0xfa,0xe5,0x6e,0x25,0x39,0xf5} },
+{ 10, 0x115f, 0, {0x39,0x90,0x88,0x02,0x74,0x80,0x2a,0xf0,0x7a,0x00} },
+{ 8, 0x1169, 0, {0xc3,0xea,0x64,0x80,0x94,0xa8,0x50,0x03} },
+{ 3, 0x1171, 0, {0x0a,0x80,0xf5} },
+{ 1, 0x1174, 0, {0x22} },
+{ 6, 0x1175, 0, {0xaa,0x70,0xab,0x71,0xac,0x72} },
+{ 12, 0x117b, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x12,0x14,0xee,0xfd,0x60,0x18} },
+{ 13, 0x1187, 0, {0x8d,0x6d,0xc0,0x02,0xc0,0x03,0xc0,0x04,0x12,0x11,0x0c,0xd0,0x04} },
+{ 9, 0x1194, 0, {0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} },
+{ 2, 0x119d, 0, {0x80,0xdc} },
+{ 1, 0x119f, 0, {0x22} },
+{ 13, 0x11a0, 0, {0xe5,0x73,0xc4,0x54,0x0f,0xfa,0x53,0x02,0x0f,0xc3,0x74,0x09,0x9a} },
+{ 2, 0x11ad, 0, {0x50,0x06} },
+{ 6, 0x11af, 0, {0x74,0x37,0x2a,0xfb,0x80,0x04} },
+{ 4, 0x11b5, 0, {0x74,0x30,0x2a,0xfb} },
+{ 12, 0x11b9, 0, {0x8b,0x6d,0xc0,0x03,0x12,0x11,0x0c,0xd0,0x03,0xaa,0x73,0x53} },
+{ 8, 0x11c5, 0, {0x02,0x0f,0xc3,0x74,0x09,0x9a,0x50,0x06} },
+{ 6, 0x11cd, 0, {0x74,0x37,0x2a,0xfb,0x80,0x04} },
+{ 4, 0x11d3, 0, {0x74,0x30,0x2a,0xfb} },
+{ 5, 0x11d7, 0, {0x8b,0x6d,0x12,0x11,0x0c} },
+{ 1, 0x11dc, 0, {0x22} },
+{ 7, 0x11dd, 0, {0x90,0x7d,0xc3,0xe0,0xfa,0x60,0x0f} },
+{ 12, 0x11e4, 0, {0x90,0x7d,0xc1,0xe0,0xf5,0x6e,0x90,0x7d,0xc2,0xe0,0xf5,0x6f} },
+{ 3, 0x11f0, 0, {0x12,0x11,0x44} },
+{ 12, 0x11f3, 0, {0x90,0x7d,0xff,0xe4,0xf0,0x75,0x70,0xc4,0x75,0x71,0x7d,0x75} },
+{ 5, 0x11ff, 0, {0x72,0x01,0x12,0x11,0x75} },
+{ 1, 0x1204, 0, {0x22} },
+{ 2, 0x1205, 0, {0x7a,0x04} },
+{ 3, 0x1207, 0, {0xba,0x40,0x00} },
+{ 2, 0x120a, 0, {0x50,0x36} },
+{ 14, 0x120c, 0, {0xea,0x24,0xc0,0xf5,0x82,0xe4,0x34,0x7d,0xf5,0x83,0xe0,0xfb,0x7c,0x00} },
+{ 3, 0x121a, 0, {0xbc,0x08,0x00} },
+{ 2, 0x121d, 0, {0x50,0x20} },
+{ 6, 0x121f, 0, {0x8b,0x05,0xed,0x30,0xe7,0x0b} },
+{ 11, 0x1225, 0, {0x90,0x7f,0x96,0x74,0x42,0xf0,0x74,0xc3,0xf0,0x80,0x08} },
+{ 8, 0x1230, 0, {0x90,0x7f,0x96,0xe4,0xf0,0x74,0x81,0xf0} },
+{ 7, 0x1238, 0, {0xeb,0x25,0xe0,0xfb,0x0c,0x80,0xdb} },
+{ 3, 0x123f, 0, {0x0a,0x80,0xc5} },
+{ 1, 0x1242, 0, {0x22} },
+{ 4, 0x1243, 0, {0x7a,0x00,0x7b,0xef} },
+{ 3, 0x1247, 0, {0xba,0x10,0x00} },
+{ 2, 0x124a, 0, {0x50,0x20} },
+{ 14, 0x124c, 0, {0x74,0x11,0x2b,0xfb,0x24,0x00,0xfc,0xe4,0x34,0x18,0xfd,0x8c,0x82,0x8d} },
+{ 14, 0x125a, 0, {0x83,0xe4,0xf0,0xea,0x24,0x00,0xf5,0x82,0xe4,0x34,0x19,0xf5,0x83,0xe4} },
+{ 4, 0x1268, 0, {0xf0,0x0a,0x80,0xdb} },
+{ 1, 0x126c, 0, {0x22} },
+{ 14, 0x126d, 0, {0x74,0xf8,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 14, 0x127b, 0, {0x74,0xf9,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 14, 0x1289, 0, {0x74,0xfa,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 14, 0x1297, 0, {0x74,0xfb,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 14, 0x12a5, 0, {0x74,0xff,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 1, 0x12b3, 0, {0x22} },
+{ 14, 0x12b4, 0, {0x12,0x03,0xcb,0x12,0x12,0x6d,0x7a,0xc0,0x7b,0x87,0x7c,0x01,0x74,0x01} },
+{ 14, 0x12c2, 0, {0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x74} },
+{ 14, 0x12d0, 0, {0x01,0x12,0x14,0xbf,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e} },
+{ 14, 0x12de, 0, {0x83,0x8f,0xf0,0x74,0x06,0x12,0x14,0xbf,0x74,0x01,0x2a,0xfd,0xe4,0x3b} },
+{ 14, 0x12ec, 0, {0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83,0x8c,0xf0,0xe4,0x12,0x14,0xbf,0x74} },
+{ 14, 0x12fa, 0, {0x01,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0} },
+{ 14, 0x1308, 0, {0x74,0x0b,0x12,0x14,0xbf,0x74,0x01,0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07} },
+{ 14, 0x1316, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x74,0x08,0x12,0x14,0xbf,0x74,0x01,0x2d} },
+{ 14, 0x1324, 0, {0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0,0x74,0x01} },
+{ 14, 0x1332, 0, {0x12,0x14,0xbf,0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83} },
+{ 14, 0x1340, 0, {0x8c,0xf0,0xe4,0x12,0x14,0xbf,0x74,0x01,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f} },
+{ 14, 0x134e, 0, {0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0,0x74,0x03,0x12,0x14,0xbf,0x7d,0x00} },
+{ 3, 0x135c, 0, {0xbd,0x06,0x00} },
+{ 2, 0x135f, 0, {0x50,0x12} },
+{ 11, 0x1361, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x0a,0xba,0x00,0x01,0x0b} },
+{ 7, 0x136c, 0, {0xe4,0x12,0x14,0xbf,0x0d,0x80,0xe9} },
+{ 13, 0x1373, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0xe5,0x74,0x12,0x14,0xbf,0x74,0xf9} },
+{ 14, 0x1380, 0, {0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0x74,0x0f,0xf0,0x74} },
+{ 14, 0x138e, 0, {0xfe,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0x74,0x01,0xf0} },
+{ 6, 0x139c, 0, {0x12,0x03,0xe1,0x12,0x04,0xf7} },
+{ 1, 0x13a2, 0, {0x22} },
+{ 13, 0x13a3, 0, {0x90,0x7d,0xc1,0xe0,0xfa,0x24,0x00,0xfb,0xe4,0x34,0x19,0xfc,0x90} },
+{ 14, 0x13b0, 0, {0x7d,0xc2,0xe0,0xfd,0x8b,0x82,0x8c,0x83,0xf0,0x75,0xf0,0x11,0xea,0xa4} },
+{ 3, 0x13be, 0, {0xfa,0x7b,0x00} },
+{ 3, 0x13c1, 0, {0xbb,0x10,0x00} },
+{ 2, 0x13c4, 0, {0x50,0x24} },
+{ 14, 0x13c6, 0, {0xea,0x24,0x00,0xfc,0xe4,0x34,0x18,0xfd,0xeb,0x2c,0xfc,0xe4,0x3d,0xfd} },
+{ 14, 0x13d4, 0, {0x74,0x04,0x2b,0x24,0xc0,0xf5,0x82,0xe4,0x34,0x7d,0xf5,0x83,0xe0,0xfe} },
+{ 8, 0x13e2, 0, {0x8c,0x82,0x8d,0x83,0xf0,0x0b,0x80,0xd7} },
+{ 14, 0x13ea, 0, {0xea,0x24,0x00,0xfa,0xe4,0x34,0x18,0xfb,0x74,0x10,0x2a,0xf5,0x82,0xe4} },
+{ 5, 0x13f8, 0, {0x3b,0xf5,0x83,0xe4,0xf0} },
+{ 1, 0x13fd, 0, {0x22} },
+{ 4, 0x13fe, 0, {0xe5,0x76,0x60,0x02} },
+{ 2, 0x1402, 0, {0x80,0x16} },
+{ 12, 0x1404, 0, {0x74,0x0f,0x55,0x75,0xfa,0x8a,0x75,0x24,0x00,0xf5,0x82,0xe4} },
+{ 10, 0x1410, 0, {0x34,0x19,0xf5,0x83,0xe0,0xf5,0x74,0x12,0x12,0xb4} },
+{ 10, 0x141a, 0, {0x12,0x10,0xca,0x75,0x6e,0x00,0x75,0x6f,0x00,0x12} },
+{ 6, 0x1424, 0, {0x11,0x44,0x75,0x70,0xb9,0x75} },
+{ 6, 0x142a, 0, {0x71,0x14,0x75,0x72,0x02,0x12} },
+{ 11, 0x1430, 0, {0x11,0x75,0xe5,0x76,0xb4,0x02,0x04,0x74,0x01,0x80,0x01} },
+{ 1, 0x143b, 0, {0xe4} },
+{ 3, 0x143c, 0, {0xfa,0x70,0x0f} },
+{ 12, 0x143f, 0, {0x74,0x01,0x25,0x75,0xf5,0x73,0xc0,0x02,0x12,0x11,0xa0,0xd0} },
+{ 3, 0x144b, 0, {0x02,0x80,0x0a} },
+{ 10, 0x144e, 0, {0x85,0x75,0x73,0xc0,0x02,0x12,0x11,0xa0,0xd0,0x02} },
+{ 12, 0x1458, 0, {0x75,0x6e,0x00,0x75,0x6f,0x01,0xc0,0x02,0x12,0x11,0x44,0xd0} },
+{ 4, 0x1464, 0, {0x02,0xea,0x70,0x1a} },
+{ 13, 0x1468, 0, {0x75,0xf0,0x11,0xe5,0x75,0xa4,0xfa,0x24,0x00,0xfa,0xe4,0x34,0x18} },
+{ 9, 0x1475, 0, {0xfb,0x8a,0x70,0x8b,0x71,0x75,0x72,0x01,0x12} },
+{ 4, 0x147e, 0, {0x11,0x75,0x80,0x36} },
+{ 2, 0x1482, 0, {0x7a,0x00} },
+{ 3, 0x1484, 0, {0xba,0x10,0x00} },
+{ 2, 0x1487, 0, {0x50,0x2f} },
+{ 13, 0x1489, 0, {0xea,0x24,0x00,0xf5,0x82,0xe4,0x34,0x19,0xf5,0x83,0xe0,0xfb,0xe5} },
+{ 4, 0x1496, 0, {0x75,0xb5,0x03,0x1b} },
+{ 14, 0x149a, 0, {0x75,0xf0,0x11,0xea,0xa4,0xfb,0x24,0x00,0xfb,0xe4,0x34,0x18,0xfc,0x8b} },
+{ 9, 0x14a8, 0, {0x70,0x8c,0x71,0x75,0x72,0x01,0xc0,0x02,0x12} },
+{ 4, 0x14b1, 0, {0x11,0x75,0xd0,0x02} },
+{ 3, 0x14b5, 0, {0x0a,0x80,0xcc} },
+{ 1, 0x14b8, 0, {0x22} },
+{ 6, 0x14b9, 0, {0x50,0x72,0x6f,0x67,0x20,0x00} },
+{ 14, 0x14bf, 0, {0xc8,0xc0,0xe0,0xc8,0xc0,0xe0,0xe5,0xf0,0x60,0x0b,0x14,0x60,0x0f,0x14} },
+{ 7, 0x14cd, 0, {0x60,0x11,0x14,0x60,0x12,0x80,0x15} },
+{ 7, 0x14d4, 0, {0xd0,0xe0,0xa8,0x82,0xf6,0x80,0x0e} },
+{ 5, 0x14db, 0, {0xd0,0xe0,0xf0,0x80,0x09} },
+{ 4, 0x14e0, 0, {0xd0,0xe0,0x80,0x05} },
+{ 5, 0x14e4, 0, {0xd0,0xe0,0xa8,0x82,0xf2} },
+{ 4, 0x14e9, 0, {0xc8,0xd0,0xe0,0xc8} },
+{ 1, 0x14ed, 0, {0x22} },
+{ 14, 0x14ee, 0, {0xc8,0xc0,0xe0,0xe5,0xf0,0x60,0x0d,0x14,0x60,0x0f,0x14,0x60,0x0f,0x14} },
+{ 6, 0x14fc, 0, {0x60,0x10,0x74,0xff,0x80,0x0f} },
+{ 5, 0x1502, 0, {0xa8,0x82,0xe6,0x80,0x0a} },
+{ 3, 0x1507, 0, {0xe0,0x80,0x07} },
+{ 4, 0x150a, 0, {0xe4,0x93,0x80,0x03} },
+{ 3, 0x150e, 0, {0xa8,0x82,0xe2} },
+{ 4, 0x1511, 0, {0xf8,0xd0,0xe0,0xc8} },
+{ 1, 0x1515, 0, {0x22} },
+{ 0, 0x0000, 1, {0} }
+
+};
+
+static unsigned char bitstream[] = {
+
+0x00,0x09,0x0F,0xF0,0x0F,0xF0,0x0F,0xF0, 0x0F,0xF0,0x00,0x00,0x01,0x61,0x00,0x0D,
+0x64,0x61,0x62,0x75,0x73,0x62,0x74,0x72, 0x2E,0x6E,0x63,0x64,0x00,0x62,0x00,0x0B,
+0x73,0x31,0x30,0x78,0x6C,0x76,0x71,0x31, 0x30,0x30,0x00,0x63,0x00,0x0B,0x31,0x39,
+0x39,0x39,0x2F,0x30,0x39,0x2F,0x32,0x34, 0x00,0x64,0x00,0x09,0x31,0x30,0x3A,0x34,
+0x32,0x3A,0x34,0x36,0x00,0x65,0x00,0x00, 0x2E,0xC0,0xFF,0x20,0x17,0x5F,0x9F,0x5B,
+0xFE,0xFB,0xBB,0xB7,0xBB,0xBB,0xFB,0xBF, 0xAF,0xEF,0xFB,0xDF,0xB7,0xFB,0xFB,0x7F,
+0xBF,0xB7,0xEF,0xF2,0xFF,0xFB,0xFE,0xFF, 0xFF,0xEF,0xFF,0xFE,0xFF,0xBF,0xFF,0xFF,
+0xFF,0xFF,0xAF,0xFF,0xFA,0xFF,0xFF,0xFF, 0xC9,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFB,0xFF,0xA3,0xFF,0xFB,
+0xFE,0xFF,0xBF,0xEF,0xE3,0xFE,0xFF,0xBF, 0xE3,0xFE,0xFF,0xBF,0x6F,0xFB,0xF6,0xFF,
+0xBF,0xFF,0x47,0xFF,0xFF,0x9F,0xEE,0xF9, 0xFE,0xCF,0x9F,0xEF,0xFB,0xCF,0x9B,0xEE,
+0xF8,0xFE,0xEF,0x8F,0xEE,0xFB,0xFE,0x0B, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xBF,0xFF,0xFF,0xFB,0xFF,0xFF, 0xBF,0xFF,0xFF,0xFC,0x17,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xFB,0xFF,0xFF,0x7F,0xFF,0xFF,
+0xFC,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x5F,0xFF, 0xFF,0xFD,0xFF,0xFF,0xDB,0xFF,0xFD,0xFF,
+0x77,0xFF,0xFD,0xFF,0xFF,0xDF,0xFE,0xFD, 0xFF,0xFF,0xF2,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFD,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE1,
+0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xE3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF,
+0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x67,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0x7F,0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xDF,0xFF,0xFF,0xFF,0x2F,0xFF,
+0xF3,0xFD,0xFF,0x7F,0xDE,0xF7,0xFD,0xFF, 0x7F,0xF7,0x7D,0xFF,0x7F,0xDF,0xF7,0xBD,
+0xFF,0x7F,0xFF,0x1F,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xEF,0xFB,
+0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFF, 0x3F,0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,
+0x9F,0xE7,0xFA,0x7F,0x9F,0xE7,0xF9,0xFE, 0x7F,0x9F,0xE7,0xFF,0xFC,0x7F,0xBF,0xBF,
+0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xB7, 0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,
+0xFF,0xE0,0xFD,0xF9,0xFE,0x7F,0x9F,0xE7, 0xF9,0xFE,0x7F,0x9D,0xF9,0xFE,0x7D,0x9D,
+0xE7,0xF9,0xFE,0x7F,0x9F,0xED,0xED,0xFF, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,
+0xDF,0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF, 0x7F,0xDF,0xFF,0x9B,0xFF,0xEF,0xFB,0xFE,
+0xFB,0xBF,0xEF,0xBB,0xFE,0xFF,0xAF,0xBB, 0xBE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFF,
+0xB7,0xBF,0xDB,0xF6,0xBD,0xBF,0x6B,0xDB, 0xF6,0xF9,0xBF,0x5B,0xD6,0xF9,0xBF,0x6F,
+0xDB,0xF6,0xFD,0xBF,0xFF,0x0E,0xFF,0xFF, 0xFF,0xFF,0x5F,0xFF,0xF7,0xFF,0xFF,0x7F,
+0xF7,0xBD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xDF,0x9F,0xFF,0xFF,0xFF,0xFE,0xFF,
+0xFF,0xEF,0xFE,0xFE,0xFF,0xFF,0x77,0xFF, 0xFB,0xFB,0xFF,0xFF,0xFF,0xFF,0xF8,0x3F,
+0xFF,0xFD,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xF4,0x7F,0xFF,0xFE,0xFD, 0xBE,0xFF,0xDF,0xFE,0xFF,0xFF,0xEF,0x7F,
+0xFF,0xCF,0xFF,0xCF,0xFF,0xFF,0xFF,0xDF, 0xE6,0xFF,0xFF,0x7F,0xDF,0xF7,0xDD,0x7F,
+0x7F,0xDF,0xF7,0xFF,0x7F,0xDF,0xD7,0xFD, 0xFF,0x7F,0xDF,0xF7,0xFF,0xCD,0xFF,0xF2,
+0xFF,0xFF,0x4F,0x7F,0xF4,0xFF,0xFF,0xFF, 0xE7,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xBB,0xFF,0xEF,0xFF,0xFE,0xFF, 0xFF,0xFF,0xEF,0xFF,0xFF,0xEF,0xFF,0xFB,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x65, 0xEF,0xFF,0xFF,0x7F,0xFF,0xFD,0xEF,0xFF,
+0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xCF,0xDF,0xFE,0xFF,
+0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xF3,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFE,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xBF,0xFF, 0xFF,0xFF,0xE3,0x7F,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xEF,0xEB,0xFF,0xFE,0xBF,0xFF, 0xEB,0xFF,0xFC,0x7F,0xFF,0xFF,0xFF,0xEE,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDD,0xFF, 0xD6,0xFF,0xFD,0xBF,0xFF,0xFB,0xFF,0xFE,
+0xFD,0xFF,0xFF,0xFD,0xEF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xDE,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xBF,0xFF,0xFD,0xFF,0x7F,0xBF, 0xFF,0x5F,0xDF,0xFF,0xFF,0xBF,0x77,0xFF,
+0xFF,0xFF,0x7F,0xD7,0xFF,0xFF,0xFF,0xFF, 0xFF,0xC3,0xFF,0xFF,0xFF,0xFF,0xDF,0xEF,
+0xFF,0xFF,0xFE,0xFB,0xFF,0xFF,0xDF,0xBF, 0xFF,0xFF,0xFF,0xFF,0xED,0xFF,0xB7,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xAF,0x7F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xDF,0xBF,0xDF,0xF3,0xFD,0xFB,0xFF,0x5B,
+0xFD,0xFF,0xBF,0xEF,0xF7,0xFF,0xFF,0x7D, 0xFF,0xFF,0xFF,0xFF,0xF8,0x3B,0xFF,0xBF,
+0x6F,0xFF,0xFE,0xFF,0xBF,0xFF,0xEB,0x7D, 0xFF,0xEF,0xFB,0xFE,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xF2,0x7F,0xFC,0xFF,0x3F,0xDF,0xED, 0xFE,0xFF,0xFF,0xFF,0xFF,0xEF,0x5F,0xF7,
+0xB5,0xFF,0xEF,0xFF,0xFF,0xFF,0xE0,0x3F, 0x9F,0x9E,0xFF,0xFF,0xEF,0xFF,0xDF,0xFF,
+0xBF,0x5F,0xBF,0xCF,0xF3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x69,0xAF,0x33,0xFD,0xFF,
+0xFB,0xFF,0xFF,0xFF,0xFF,0xFC,0xFF,0x7F, 0xD9,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xF5,
+0xA3,0xDF,0x6E,0xDE,0xFF,0xFF,0xBD,0xFF, 0xFF,0xFE,0xFF,0xFF,0xFF,0xFE,0xE7,0xFD,
+0xFF,0xFF,0xFF,0xF9,0xEF,0xC6,0xFE,0xB7, 0xAD,0xE5,0xF9,0xFF,0xFF,0xFF,0xCF,0xFF,
+0xFF,0xFF,0xCD,0xFB,0x7F,0xFF,0xFF,0xFF, 0xF9,0xF6,0x0F,0xDF,0xEC,0xCF,0x7F,0xFF,
+0xFB,0x7F,0xFF,0xFF,0xFF,0xFD,0xFF,0xFE, 0xF9,0xFD,0x7F,0xFF,0x7F,0xFF,0xF9,0x5B,
+0xFF,0x73,0xDC,0xFD,0x7B,0xDF,0xFF,0xFF, 0xFF,0x7B,0xFF,0xFF,0xF7,0x53,0xD6,0xFF,
+0xFF,0xFF,0xFF,0xD8,0x9F,0xFE,0xFF,0xEF, 0x7F,0xEE,0xFF,0xFF,0xFF,0xFB,0xED,0xED,
+0xFD,0xFF,0xFE,0xFF,0xFF,0xFB,0x7F,0xFF, 0xE2,0x7F,0xFF,0x6F,0xD8,0x57,0xF7,0xFF,
+0xFF,0xFF,0xDF,0xFF,0xE8,0xFF,0xFF,0xFD, 0xFF,0xFF,0xFC,0x7F,0xFF,0xE4,0xFF,0xFB,
+0xEF,0xFB,0xFE,0xDF,0xB7,0xED,0xFF,0xFE, 0xDF,0x7F,0xFF,0xFE,0x7F,0xB7,0xFF,0xFF,
+0xFF,0xFF,0x89,0xFF,0xFF,0xCF,0xF3,0xFE, 0x7F,0xFF,0xEF,0xFF,0xFE,0x7E,0x7F,0xFB,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF1, 0xFF,0xEB,0x7A,0xD5,0xBF,0x6F,0xDB,0xBE,
+0xFD,0xB7,0xD8,0xF6,0xE5,0xBF,0x6F,0xFB, 0xFE,0xF5,0xBD,0x7E,0x06,0xFF,0xDF,0xF7,
+0xFB,0xF6,0xFF,0x3F,0xFF,0xDB,0xFF,0xFF, 0x6F,0xFB,0xF7,0xFF,0xFF,0xFF,0xFB,0xFE,
+0xF7,0xAF,0xFF,0xB7,0xED,0xEF,0xF7,0xFE, 0xFF,0xFF,0xDF,0xFF,0xFE,0xFF,0xEF,0xFF,
+0xFF,0xFF,0xFF,0xBF,0xF7,0xFC,0x1F,0xEE, 0xFB,0xFE,0xBD,0xFF,0x7F,0x5F,0xD7,0xFD,
+0xFB,0x43,0xFF,0xFF,0xFD,0xFF,0x5F,0xFF, 0xF7,0xFF,0xF9,0x3F,0xFF,0xCF,0xF3,0xFD,
+0xF7,0x7E,0xEF,0xA7,0xF9,0xFE,0x8F,0xA7, 0xE9,0xF3,0x7E,0x9F,0xFB,0xF8,0xFF,0xFF,
+0x3F,0xFD,0x7F,0x5F,0xDF,0xFD,0xFF,0xFF, 0x5F,0xFF,0xFD,0x5F,0xFF,0xFF,0x7F,0xFD,
+0x7F,0xFD,0x9F,0xFF,0xE0,0xFF,0xFA,0xF8, 0xBE,0x6F,0x9F,0xE6,0xF8,0xBE,0x3F,0x9A,
+0xF9,0xBE,0x6F,0x9F,0xE2,0xF9,0xFE,0x6F, 0x9F,0xF9,0xFF,0xF5,0xFD,0x7F,0xCF,0xDF,
+0xFD,0xFD,0x7F,0xFF,0xF5,0xFF,0xFF,0xFF, 0xF7,0xF5,0xFD,0x0F,0xDB,0xFF,0xD3,0xFF,
+0xEB,0xFA,0xFF,0xFF,0xBF,0xFF,0xFA,0xFF, 0xFF,0xCB,0xFB,0xFE,0xFF,0xFF,0xEB,0xFA,
+0xFE,0xFF,0xFF,0xB7,0xFF,0xFF,0xFF,0xFF, 0xBF,0xFF,0xDF,0xF5,0xFF,0xFF,0xD7,0xFF,
+0xFF,0xFF,0xDF,0xD7,0xF5,0xFF,0x7F,0xFE, 0x4F,0xFF,0xFD,0xFF,0x7F,0x7F,0xFF,0xAD,
+0xEB,0xFB,0xFF,0xAD,0xFF,0xFF,0xFF,0xFF, 0xAF,0xEB,0xFB,0xFF,0xFC,0x0D,0xFF,0xFF,
+0xDF,0xD2,0xFD,0xFF,0xFF,0xFD,0xF6,0xFF, 0xFF,0x7F,0xFF,0xFF,0x1F,0xFF,0xFF,0xFF,
+0xFF,0xFB,0x3F,0x7D,0xEB,0x32,0xFE,0xBF, 0x2F,0xEB,0xFA,0xAE,0xBD,0xE0,0xFA,0x7E,
+0xBF,0xAD,0xEB,0xFA,0xFE,0xBF,0xF5,0x7F, 0xFF,0xDE,0xFE,0xE3,0xFB,0xFF,0xFF,0xFF,
+0xDF,0xEF,0x4F,0xDF,0xFF,0x7F,0xDF,0xFF, 0xF7,0xFF,0xFF,0xF8,0x7F,0xFF,0xFF,0xEF,
+0xFB,0xFF,0xFF,0xFF,0xEF,0xFF,0xFF,0xDF, 0xED,0xFB,0xDF,0xFF,0xBF,0xFF,0xFF,0xFF,
+0x81,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF, 0xFF,0xFF,0xFE,0xDD,0xFE,0xEF,0xFD,0xFF,
+0xFF,0xFB,0xFE,0xF7,0xFF,0x93,0xFD,0xFB, 0x7E,0xFF,0xFE,0x87,0xE9,0xFF,0x7F,0xB3,
+0x9F,0xFE,0xFE,0xFF,0xAF,0xFD,0xFE,0x7E, 0x3F,0xFE,0x67,0xFF,0xFF,0xF7,0xFF,0xFF,
+0xFC,0xF7,0xDF,0xFD,0xFF,0x7F,0xFF,0xFF, 0x7F,0x6D,0xFF,0xFF,0xFE,0xFF,0xFF,0x2F,
+0xFF,0xBF,0xFF,0xFF,0xEE,0xFF,0xBE,0xFF, 0xFF,0xFE,0xFF,0xEF,0xFF,0xFF,0xFE,0xFF,
+0xEF,0xFF,0xFF,0xFA,0x5F,0xFF,0xFF,0xFB, 0xFF,0xFF,0xEF,0xFF,0xFB,0xFE,0xFD,0xFF,
+0xFE,0xFF,0xFB,0xFF,0xFF,0xFF,0x7F,0xFF, 0xFE,0xBF,0xDF,0xFF,0xFB,0xFF,0xFF,0xF7,
+0xFC,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF2,0x7F,0xFF,
+0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF, 0xF3,0xFF,0xFF,0xFF,0xEF,0xFB,0xFF,0xFF,
+0xFF,0xDF,0xE2,0xFF,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFB,0xE7,0xFF,0xFD,
+0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xED, 0xEF,0xFD,0xFF,0xFF,0xDF,0xD7,0xF5,0xFD,
+0x7F,0x5D,0xFD,0xFF,0x7F,0xDF,0x97,0xF4, 0xFD,0x7B,0x5F,0xFF,0xC9,0xFF,0xFB,0xFE,
+0xFF,0xBF,0xFF,0x5F,0xFF,0xFF,0xF7,0xFF, 0xEF,0xFD,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xF7,0xFF,0xD7,0xFD,0x7D,0x7F,0xFF, 0xFF,0xFF,0xFF,0xEF,0xDF,0xF7,0xFD,0xFF,
+0xBB,0xFF,0xFF,0x7F,0xFF,0xFE,0xE3,0xFF, 0xF9,0xFE,0x7F,0xBF,0xEF,0xFB,0xFE,0xFF,
+0xBF,0xF9,0xFE,0xFF,0x9F,0xEF,0xF9,0xFE, 0xFF,0xBF,0xF3,0xDA,0xFF,0x37,0xCD,0xF3,
+0x7C,0xDF,0x37,0xCD,0xF3,0x7F,0x37,0xCD, 0xF3,0x7C,0xDF,0x37,0xCC,0xF3,0x7F,0x5A,
+0xBD,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,0xFD, 0xBF,0x6F,0xDE,0xFD,0xBF,0x6F,0xDB,0xF6,
+0xFD,0xBF,0x6F,0xFE,0xF1,0x6F,0xEB,0x7A, 0xDE,0xB7,0xAD,0xEB,0x7A,0xDE,0xB7,0xAF,
+0x7A,0xDE,0xB7,0xAD,0xEB,0x7A,0xDE,0xB7, 0xFF,0x7E,0xFF,0xFE,0xCD,0xB3,0x6C,0xDB,
+0x36,0xCD,0xB3,0x6C,0xDE,0xCD,0xB3,0x6C, 0xDB,0x36,0xCD,0xB3,0x6C,0xDF,0xC9,0xBF,
+0xF7,0xBD,0xEF,0x7A,0x9E,0xA7,0xA9,0xEA, 0x7A,0xB7,0xBD,0xEA,0x7B,0xDE,0xA7,0xBD,
+0xCA,0x72,0x8D,0x91,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFE,0xF7,0xEF,0xFB,
+0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFE, 0x87,0xFF,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,
+0xFD,0xBF,0x6F,0xF6,0xFD,0xBF,0x6F,0xDB, 0xF6,0xFD,0xBF,0x6F,0xFE,0x4F,0xFF,0xBF,
+0xEF,0xBB,0xEE,0xFB,0xBE,0xEF,0xBB,0xEF, 0xBE,0xEF,0xBB,0xEE,0xFB,0xBE,0xEF,0xBB,
+0xEF,0xFC,0x5F,0xFF,0xFF,0xFF,0x3F,0xCF, 0xF3,0xFC,0xFF,0x3F,0xCF,0xFC,0xFF,0x3F,
+0xCF,0xF3,0xFC,0xFF,0x3F,0xCF,0xFD,0x9F, 0xFE,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,
+0xEB,0xFE,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF, 0xAF,0xEB,0xFF,0xE1,0x6F,0xFD,0xFF,0x7F,
+0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFD,0xFF, 0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF,
+0x7A,0xBF,0xFB,0xFE,0xDF,0xB7,0xED,0xFB, 0x7E,0xDF,0xB7,0xFB,0x7E,0xDF,0xB7,0xED,
+0xFB,0x7E,0xDF,0xB7,0xFF,0xC9,0xFF,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,
+0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEE, 0xFB,0xFE,0xBB,0xFF,0xFE,0xFF,0xBF,0xEF,
+0xFB,0xFE,0xFF,0xBF,0xEF,0xFE,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0x3F,0xCF,0xFF,0xE7,
+0xFE,0xFF,0xF5,0xFD,0x77,0x5D,0xD7,0x35, 0xDD,0x77,0xD7,0xF5,0xCD,0x7B,0x5D,0xD7,
+0xF5,0xDD,0x77,0xFE,0x27,0xFF,0xFF,0x8B, 0xE2,0xF8,0xBE,0x2F,0x8B,0xE2,0xF9,0xAF,
+0x8B,0xE2,0xF8,0xBE,0x2F,0x8B,0xE2,0xF9, 0xFE,0x1F,0xFF,0x5F,0xD7,0xF5,0xFD,0x7F,
+0x5F,0xD7,0xF5,0xFF,0x5F,0xD7,0xF5,0xFD, 0x7F,0x5F,0xD7,0xF5,0xFF,0xFA,0x3F,0xFE,
+0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,0xEB, 0xEC,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,
+0xEB,0xFF,0xFE,0x7F,0xFD,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE6, 0xFF,0xFA,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,
+0xF7,0xFC,0xFF,0xDF,0xF7,0xFD,0xFF,0x7F, 0xDF,0xF7,0xFD,0xFF,0xF5,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0x02,0xFF,0xFE,0xBF,0xAB,0xEB,0xFA, 0xBE,0xBF,0x23,0xEB,0xDE,0x1F,0xAF,0xEA,
+0xFA,0xFE,0xAF,0xAF,0xEB,0xFD,0x97,0xFF, 0xF3,0xFC,0x7B,0x1F,0xCF,0xF1,0xFC,0x7F,
+0x1F,0xF1,0xFC,0x77,0x1F,0xCD,0xF1,0xFC, 0xFF,0x1F,0xFE,0x87,0xFF,0xAF,0xEF,0xFA,
+0xFE,0xFF,0xAF,0xEF,0xFA,0xFD,0xBF,0x2B, 0xFB,0x7E,0xBF,0xBF,0xEB,0xFB,0xFB,0xFB,
+0xDF,0xFF,0xFB,0xF7,0xFF,0xFF,0x7F,0xF7, 0xF7,0xFF,0xFD,0xDF,0xFE,0xFC,0xDF,0xFF,
+0xDF,0xFF,0xFD,0xFF,0xDA,0xBF,0xFF,0xBB, 0xEF,0xFB,0xF9,0xFF,0xBE,0xEF,0xFB,0xFB,
+0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xF7,0x7F,0xFD,0xD7,0xFF,0xFF,0x7F,
+0xFF,0xFF,0xFF,0xFE,0xF7,0xFF,0xFE,0xFF, 0xF7,0xFF,0xFF,0x7F,0xFF,0xFF,0xEC,0xFF,
+0xFF,0xFE,0xDF,0xBF,0xFF,0xFB,0xFE,0xFF, 0xBB,0x68,0xAE,0x1F,0xAE,0xFB,0xFB,0xFF,
+0xFF,0xBF,0xFF,0xD5,0xFF,0x7F,0xFF,0xFF, 0xF7,0xFE,0xFE,0xFF,0xBF,0xEF,0x9F,0xFD,
+0x7F,0xFF,0xCB,0xFF,0xFF,0xDF,0xFF,0xFF, 0xBB,0xF7,0xBF,0xFF,0xFF,0xFF,0xFF,0xDF,
+0xFF,0xBF,0xFB,0xFF,0xFF,0xFF,0xDE,0x3F, 0xFF,0xFF,0xFF,0xFF,0xFF,0xA7,0xFF,0xFF,
+0xFF,0xFF,0xEF,0xFF,0x7F,0xFB,0xFD,0xFB, 0x7F,0xFF,0xFF,0xFF,0xFF,0xCF,0xF3,0x7C,
+0xFF,0x7F,0x8D,0x7F,0xFF,0xFF,0xFF,0xFF, 0xFB,0xFF,0xF7,0xFB,0xFE,0xFD,0xFF,0xFF,
+0xFF,0xFF,0xF7,0xFD,0xFF,0x7F,0xFD,0x1F, 0xFD,0xFF,0xFF,0xFF,0xFF,0xBF,0xDF,0xFF,
+0xFF,0xFE,0x5C,0xFF,0x6D,0xFF,0x7F,0xAB, 0xE7,0xF1,0xFF,0xFD,0x9F,0xFF,0xFF,0xAD,
+0xEB,0x7A,0x3F,0x1F,0xFF,0xFF,0xFE,0xBF, 0xAF,0xF3,0xDE,0xF5,0xFF,0x8F,0xFB,0xDF,
+0xE6,0x7F,0xFF,0xDF,0xF3,0xFD,0xFF,0x7E, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xF7,0xF3,
+0x7F,0xDF,0xF7,0xEF,0xFF,0xF6,0x3F,0x9F, 0xDF,0xFF,0xFF,0xEE,0xFF,0xFF,0xEF,0xFB,
+0xFF,0xFF,0xF9,0xFB,0xFE,0x4F,0xBF,0xEF, 0xBB,0xFF,0x69,0xAF,0xAF,0xFC,0xFF,0x3F,
+0xDD,0xFF,0xFC,0xBF,0x8F,0xFF,0xFD,0xF3, 0xBF,0xED,0x9E,0xFC,0xBF,0x6F,0xF5,0xD3,
+0xDF,0xFF,0xDB,0xD6,0xF5,0xEF,0xFD,0xFE, 0xFF,0xB9,0xFF,0x1F,0xD2,0xA9,0xAF,0xFF,
+0xDB,0xF7,0xBF,0xEF,0x46,0xFF,0xFF,0xAD, 0xEB,0x7A,0xDF,0xEF,0xF7,0xFF,0x7F,0xF7,
+0x9F,0xED,0xFF,0x7F,0xFF,0xAD,0xEB,0x7F, 0xF5,0x6F,0xFF,0xFD,0xFB,0xD6,0xF4,0xF7,
+0xFB,0xF9,0x7E,0x7F,0xFF,0x5F,0xC2,0xFE, 0xBF,0xFD,0xFB,0x33,0xDF,0xF9,0x5B,0xFF,
+0xFF,0xDD,0x67,0x7D,0xCF,0xEF,0xDB,0xEC, 0xFF,0x77,0xDD,0xF7,0xFD,0xFF,0xFF,0xDE,
+0xA7,0xBF,0xD4,0x9F,0xFF,0xFF,0xBF,0xEF, 0xFE,0xFF,0xDF,0xEF,0xBB,0xFF,0xFF,0xEF,
+0xEB,0xFA,0xFF,0xEF,0xBD,0xFB,0xFF,0xE2, 0x7F,0xFF,0xDF,0xDF,0xF7,0xFD,0xBF,0xBB,
+0x73,0xF7,0xFD,0x7F,0xDF,0xDE,0xF7,0xBF, 0xEA,0xDB,0xF6,0xFF,0xD6,0xFF,0xFF,0x66,
+0xFF,0xBE,0xFF,0xBF,0x6B,0xD9,0xF6,0xDF, 0xFF,0xFB,0x7E,0x7F,0xB7,0x7E,0xFF,0xFE,
+0xFF,0xCD,0xFF,0xFE,0x7F,0xFF,0xFC,0xFD, 0x3F,0xFB,0xFB,0xF7,0xFF,0xFF,0xFB,0xF6,
+0x7D,0xFE,0x7F,0xFF,0xFC,0xFF,0xB9,0xFF, 0xF9,0xFA,0xFE,0xBF,0xAF,0x5B,0xD6,0xED,
+0xAD,0x7B,0xF6,0xF9,0xBF,0xEF,0xF8,0xFA, 0xFE,0xBF,0xFE,0xE6,0xFF,0xFF,0xF7,0xFD,
+0xFF,0x7F,0xBF,0xEF,0xF3,0xFF,0xFF,0x6F, 0xF7,0xFE,0xFF,0xFF,0xF7,0xFD,0xFE,0xF7,
+0xEF,0xFF,0xFB,0xEF,0xFB,0x7E,0xDE,0xFE, 0xFF,0xBF,0xFF,0xFE,0xFF,0xFF,0xFB,0xFF,
+0xFF,0xEF,0xFB,0x6F,0xFC,0x1F,0xFE,0xE7, 0xFF,0xFF,0xFF,0xEF,0xFF,0xD3,0xB4,0xBB,
+0xFF,0xFF,0xFD,0xBF,0x6F,0xE3,0xFE,0xFF, 0xBF,0xFC,0xBF,0xF7,0xCF,0xF7,0xFD,0xFF,
+0x2F,0xDF,0xAB,0xEA,0xFF,0xDF,0xE7,0xEA, 0x9A,0xAF,0xEF,0xFB,0xFE,0xFF,0xF5,0x3F,
+0xFD,0x7E,0xFF,0xD7,0xF5,0xFB,0xFF,0xFD, 0xF7,0xFF,0x7F,0xFE,0xF7,0xFD,0xFF,0xD7,
+0xFF,0xD7,0x7F,0xEE,0x7F,0xFA,0x79,0xFE, 0x2F,0x8B,0xE6,0xF9,0xFE,0x3F,0x9E,0xF9,
+0xBE,0x2F,0x0B,0xE7,0xF9,0xFE,0x2F,0x9F, 0xFD,0xFF,0xFE,0x7D,0x7F,0x5F,0xD7,0xFF,
+0xFF,0x7F,0xFF,0xFD,0xFF,0x7F,0x5F,0x97, 0xFF,0xFD,0x7F,0x5F,0xFF,0xE3,0xFF,0xFF,
+0xFA,0xFE,0xBF,0xAF,0xFB,0xFB,0xFF,0xFF, 0xCF,0xEB,0xFE,0xBF,0xAF,0xFF,0xFA,0xFE,
+0xBF,0xFF,0x87,0xFF,0xFF,0xF5,0xFF,0xFF, 0xFF,0xFF,0xFD,0xFF,0x7F,0xFF,0xFF,0xFF,
+0xFB,0xFF,0xFF,0xF5,0xFF,0xFF,0xFE,0x0F, 0xFF,0xFD,0xEB,0xFF,0xFF,0xF7,0xFF,0xEF,
+0x7B,0xDF,0xFE,0xFF,0xFF,0xDF,0xF7,0xFD, 0xEB,0x7F,0xDF,0xFF,0x5F,0xFF,0xFF,0xFF,
+0xFF,0xFD,0xBF,0xFF,0x7E,0xFA,0xBF,0xC7, 0xDB,0xF7,0xBD,0x3F,0xFB,0xFF,0xF6,0xFF,
+0xFA,0xAF,0xFF,0xEB,0xFA,0xFE,0x3F,0x2F, 0xEA,0xFA,0x3E,0xAD,0xC9,0xBA,0xF6,0xAD,
+0xAF,0xEB,0xFA,0xF6,0xBF,0xFE,0x7F,0xFF, 0xFF,0xFD,0xFF,0xF1,0x7F,0x3F,0xCF,0xF1,
+0xEF,0xFF,0x7F,0xFF,0xBC,0xDF,0xDF,0xF7, 0xDD,0xFF,0xE0,0x7F,0xFF,0xFF,0xFE,0xFF,
+0xFA,0xEC,0xBB,0x7F,0x5F,0xFF,0xFB,0xEC, 0xFF,0xEF,0xB7,0xFF,0xF7,0xFF,0xFF,0xB5,
+0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xEE,0xDF, 0x5F,0xDF,0xDE,0xFF,0xAE,0xE7,0x77,0xFF,
+0xFF,0xDF,0xF7,0xFF,0xE3,0xFF,0xFA,0xBB, 0xFE,0xFF,0xAF,0xFD,0xFB,0xFE,0xBF,0xAB,
+0xF9,0xFE,0xFF,0xBF,0x7F,0xBF,0xFE,0xBD, 0xFE,0xD7,0xFF,0x9F,0xFD,0xFF,0xBE,0xEF,
+0xFF,0xEE,0xFD,0xBB,0x5B,0xEF,0xFF,0x7F, 0xEF,0xFF,0xEF,0xFF,0x7F,0xFF,0x4F,0xFF,
+0xEF,0xFB,0xBC,0xFC,0xFF,0xFF,0xFF,0xFE, 0xFE,0xFD,0xFA,0xFE,0xFB,0xFF,0xFD,0xF3,
+0xFB,0xFF,0xF8,0x5F,0xFF,0xFF,0xD7,0xF5, 0xFD,0xDF,0xEF,0xFF,0xF3,0xDC,0x5F,0xCE,
+0xF5,0xBD,0xFF,0xFF,0xD7,0xFF,0xFF,0xF9, 0x3F,0xFF,0xDF,0xF7,0xFF,0xFE,0xFF,0xFD,
+0xFF,0xFB,0xFF,0xF7,0xB9,0x7D,0xFE,0xDF, 0xFF,0xFF,0xFF,0xFF,0xF9,0x7F,0xFF,0xFE,
+0xFF,0xFF,0x7F,0xFF,0xFE,0xFF,0xFF,0xF7, 0xF6,0xFF,0xBF,0xF1,0xF8,0xFF,0xFF,0xFF,
+0xFF,0xE0,0xFF,0xFF,0xFF,0xFF,0xF9,0xFF, 0xFF,0xFF,0xFF,0xFF,0xEF,0xEF,0xFF,0xFF,
+0x9B,0xFB,0x7F,0xFF,0xFF,0xFF,0xC1,0xFF, 0xDF,0xFF,0x3F,0x5F,0xD7,0xBF,0xEF,0xBB,
+0xDE,0xEE,0xFF,0x7F,0xDF,0xFF,0xFE,0xF5, 0x7F,0xDF,0xFF,0x99,0xFF,0xFF,0xFA,0xFF,
+0xBF,0xFD,0xEB,0x7A,0xFF,0xB7,0xFE,0xFE, 0xFF,0xFF,0xEF,0xFF,0xFF,0xFD,0xBF,0xFF,
+0x97,0xFF,0xFD,0xF7,0xFF,0x7F,0xF7,0xFF, 0xFF,0xFD,0x5F,0xFE,0xF3,0xF9,0xDF,0xDF,
+0xFF,0xFF,0xFC,0xFF,0xFF,0x83,0xFF,0xFF, 0xFE,0xFF,0x9E,0xEC,0xFB,0xEE,0xFF,0x9F,
+0xBF,0xEF,0xFF,0xFE,0xED,0x7B,0xFF,0xFF, 0xFF,0xF1,0x5A,0xFF,0xFF,0xFD,0xFF,0x7C,
+0x69,0x3B,0xDF,0xFF,0x7F,0x1F,0xDF,0xFF, 0xFD,0xBA,0xFF,0xFF,0xFB,0xFF,0x5B,0xBD,
+0xFF,0xFF,0xFF,0xFF,0xD7,0xB6,0xED,0xE9, 0xFF,0xD6,0xBD,0x6F,0x5F,0xFB,0xFF,0xEF,
+0xFF,0x5F,0xFE,0xF6,0x6F,0xFF,0xFF,0xFF, 0xFF,0xF7,0xEB,0x7A,0xDF,0xFF,0x9F,0x7F,
+0x7F,0xFF,0xB7,0xFF,0xFF,0xFE,0xDF,0xFF, 0x6C,0xFF,0xFB,0xFF,0xBB,0x6F,0xEB,0xFE,
+0xCC,0xF7,0xA5,0xFA,0x5C,0xF5,0x75,0xBB, 0xB7,0xDF,0xFE,0x6F,0x5F,0xC5,0xBF,0xFD,
+0x7B,0xFE,0xFF,0x95,0xE7,0x29,0xCF,0x4F, 0xF5,0x91,0xEE,0x6B,0xDF,0xEF,0xFD,0x54,
+0xF5,0xBD,0xB1,0xFF,0xEF,0xEE,0xFB,0xBE, 0xBF,0xAF,0xFE,0xDE,0xBD,0x6F,0xDA,0xF2,
+0xFF,0xAF,0xBE,0xFF,0xFF,0xFD,0x7E,0xA7, 0xFF,0xF7,0xFF,0xBF,0xEF,0x7B,0xF6,0xFD,
+0xBD,0x4A,0xF2,0x85,0x85,0xBF,0x5B,0xFE, 0xB5,0xFD,0xFA,0xFF,0x4F,0xFF,0xFE,0xDF,
+0xFF,0xED,0xFF,0xBF,0xFF,0xBF,0x7F,0xFE, 0xFF,0xB7,0x6D,0xFF,0xF7,0xBF,0xBF,0xEF,
+0xFD,0x1F,0xFF,0xFE,0x7D,0xFF,0x67,0xFF, 0xFF,0xFF,0x3F,0x7F,0xFE,0xBF,0xFF,0xE7,
+0xDF,0xE7,0xFF,0xEF,0x6B,0xFC,0x1F,0xFF, 0xBF,0xEF,0xFB,0xFE,0xDE,0xBF,0xAF,0xFA,
+0xFF,0xB6,0xEF,0xF9,0xFE,0xFF,0x8F,0xEF, 0xDB,0xEF,0xAB,0x6F,0xFB,0xFE,0xFF,0xFF,
+0xEF,0xFD,0xFF,0x7F,0xFF,0xFF,0xDE,0xFF, 0xFF,0xEF,0xFF,0xFF,0xFF,0x3F,0xFF,0x6C,
+0xFF,0xBF,0xFB,0xFF,0xFE,0xFF,0xFB,0xFE, 0xDF,0xFF,0xFF,0xEF,0xFF,0xFF,0xBF,0xFF,
+0xFF,0xFE,0xFB,0xFF,0xD5,0x7F,0xFF,0xFF, 0xEF,0xFB,0xFF,0xFF,0xBF,0xEF,0x43,0xB5,
+0xFD,0x6F,0xCF,0xD6,0xBE,0x3F,0x7F,0xDB, 0xFE,0xC3,0xFF,0xFD,0xFF,0xAF,0xEB,0xFB,
+0xFC,0xFF,0x3E,0xEF,0xE8,0xFA,0xBD,0xCD, 0xAA,0xFE,0xFE,0x7D,0xCF,0xFF,0xB7,0xFF,
+0xF7,0xFF,0xFF,0xFF,0xFD,0xFF,0x75,0xCD, 0x52,0xD7,0xFD,0xFB,0xF7,0xDD,0xFB,0xEF,
+0xEB,0xFF,0xFF,0x4F,0xFF,0xBF,0x9F,0xE7, 0xF9,0xFC,0x7F,0x8B,0xC3,0xF9,0xAF,0x8F,
+0xE7,0xE9,0xBE,0x7F,0x9F,0xE6,0xF9,0xFC, 0x5F,0xFF,0xFF,0xF7,0xFD,0xFF,0x7A,0x5F,
+0xD7,0xED,0xFF,0xFF,0xD7,0xFF,0xDD,0x7F, 0xE7,0xFF,0xFC,0xFF,0xFC,0x3F,0xFF,0xFF,
+0xFF,0xFB,0xFF,0xFE,0xBF,0xAF,0xFF,0xFD, 0xFF,0xEF,0xFF,0xEB,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xF7,0x7F,0xFF,0x7F,0xDF,0xFF,0xFD, 0xFD,0x7F,0xFE,0xF7,0xFD,0x7F,0xDF,0xFF,
+0xFD,0xFF,0xFF,0xDF,0xFB,0xFF,0xEE,0xFF, 0xFB,0xFF,0xF7,0xFD,0xFF,0x7A,0xDF,0xF5,
+0xFD,0xFA,0xDF,0xF7,0xFC,0xFF,0x7F,0xDF, 0xBF,0xED,0xFF,0xC9,0xFF,0xDF,0xFF,0xBF,
+0x2F,0xFB,0xFF,0xBC,0xAD,0xFF,0xF7,0xFF, 0xFF,0xEF,0xD3,0xFF,0x7D,0xBF,0x6F,0xFF,
+0xFA,0xFF,0xFE,0xBF,0xAE,0xEA,0xFA,0xBE, 0xAD,0xA5,0xEB,0xCE,0xBF,0xA7,0xEB,0x5A,
+0xDE,0xBD,0xAF,0x6B,0xFD,0x57,0xFF,0xFF, 0xF4,0x7F,0x1F,0x7F,0xFD,0xFF,0x7F,0x36,
+0xF0,0xDF,0x79,0xFF,0xFF,0xFF,0xF7,0xFD, 0xBF,0xFF,0x87,0xFF,0xFB,0xF3,0xFC,0xFF,
+0xFF,0xFF,0xFF,0x7E,0xFF,0xBF,0xDF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xF8,0x9F,
+0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFD, 0xF7,0xFC,0xBD,0xFF,0xFE,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFB,0xF9,0xBF,0xFF,0xFF,0xEB, 0xE2,0xFE,0xFF,0xBF,0xEF,0xA9,0xBA,0x2F,
+0xEB,0xF9,0xFE,0x77,0xDF,0xF7,0xFF,0xFF, 0xF9,0x7F,0xFF,0xFF,0x7F,0xEF,0xD7,0xFF,
+0xFD,0xFF,0xFB,0xF5,0xFF,0xBF,0x6F,0xDF, 0xFF,0xFF,0xFD,0xFF,0xFF,0xF0,0xFF,0xFF,
+0xFF,0x3F,0xCF,0xFF,0xBA,0xEE,0x9B,0xBF, 0xEE,0xD7,0xFE,0xCD,0xEF,0xFF,0xDF,0xBF,
+0xFF,0xFF,0xC5,0xFF,0xFF,0xFD,0x7F,0x4F, 0xFD,0xF6,0xD9,0xFF,0x4F,0xD6,0xFD,0xBF,
+0x6E,0xFF,0xFF,0xF4,0x7F,0xFF,0x7F,0x8B, 0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xF9,0xFE,
+0x37,0xFF,0xD9,0xFB,0xF5,0xAF,0xFD,0xFF, 0xFF,0xFB,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,
+0xFB,0xF7,0xFF,0xFD,0xFF,0x7C,0xFA,0x7E, 0x4F,0xFC,0xDF,0x1D,0xC7,0xFF,0xFF,0xFF,
+0xFF,0xAE,0xFF,0xFF,0xFF,0xFF,0xFD,0xFB, 0xFF,0xFF,0xFE,0xFE,0xFC,0xFF,0x7F,0x7F,
+0xBF,0xEF,0xFE,0xFF,0xFF,0xFF,0x5F,0xFD, 0xFF,0xFF,0xFF,0xFD,0x6F,0x5A,0xD7,0x7B,
+0xBE,0x5F,0xFE,0x39,0xFF,0xF7,0xFF,0xF7, 0xFD,0xFE,0xAA,0x1F,0xFF,0xFF,0xFF,0xFF,
+0xFE,0xFE,0xAB,0xAF,0xFD,0xFE,0xBF,0xFF, 0xF7,0xFF,0x7F,0xFE,0x8F,0xE3,0xFB,0xEE,
+0x7F,0xFF,0xFF,0xFF,0xFF,0xEB,0xFB,0xFF, 0xFD,0xBF,0xEF,0xDF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFB,0xE4,0x3F,0xFF,0xDF, 0xFF,0xFF,0xFF,0xFF,0xF3,0xEF,0xBB,0xFB,
+0xBF,0xEF,0xBB,0xFF,0xD7,0xBF,0xFF,0xFF, 0xFF,0x29,0xAF,0xF7,0xFF,0xFF,0xFB,0xFF,
+0xFB,0xE6,0xFF,0x0F,0xFB,0x3F,0xDF,0x0F, 0xFF,0xAF,0xFF,0xFF,0xFF,0xF5,0xC3,0xDF,
+0x5F,0xFF,0xFF,0xFF,0xFE,0x6B,0xCA,0xBE, 0xBC,0xFF,0x9F,0xF2,0xBF,0xFF,0xFE,0xFA,
+0xFF,0xFF,0xEF,0x16,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFC,0xDF,0x97,0xFD,0x79,0xFF,0x37,
+0xE7,0x7F,0xFF,0xFF,0xB5,0xFF,0xFF,0xF6, 0x2F,0xFF,0xFD,0xFB,0xFE,0xFF,0xFF,0xFD,
+0x5F,0x57,0x5F,0xFF,0xDB,0x52,0xDF,0xFF, 0xFD,0xBF,0xFF,0xFF,0xFC,0xDB,0xFF,0x7B,
+0xB5,0xFD,0x7F,0xFF,0x71,0x9C,0x6E,0xFF, 0xF6,0x35,0xA5,0x9B,0xFF,0xFF,0xFD,0xFF,
+0xFF,0xDB,0x9E,0x7F,0xFE,0xEF,0xFB,0xFF, 0xFF,0xBD,0xEF,0xFF,0xDE,0xB7,0xF9,0x4B,
+0xFF,0xF5,0xEF,0xFF,0xFF,0xFF,0xE8,0x7E, 0xFF,0xEA,0xDF,0xF7,0xFF,0xFD,0x69,0x5B,
+0xFC,0x9F,0xEF,0x78,0xD6,0xFF,0xEB,0xEF, 0xFF,0xFF,0xFF,0xE8,0xFF,0xFF,0xED,0xFF,
+0xFF,0xFF,0xFF,0xE3,0xF9,0xF6,0xBF,0xFF, 0xFF,0xFE,0xDF,0xFF,0x7F,0xFF,0xFF,0xFF,
+0xD1,0xFF,0xFF,0xE7,0xFF,0xFF,0xFF,0xFF, 0xE7,0xF9,0xFF,0xBF,0x7F,0xD9,0xFF,0xFD,
+0xFE,0x7F,0xFF,0xFE,0xFF,0xF9,0xFF,0xFB, 0xD6,0xDF,0xBF,0xEF,0x5B,0xD6,0xFF,0xBF,
+0xFB,0xF6,0xFF,0xBF,0xEF,0xF8,0xF6,0xDD, 0xBE,0xFE,0x16,0xFF,0xBF,0xEF,0xFF,0xFE,
+0xFF,0xBF,0xEF,0xFF,0xFF,0xFF,0x6F,0xFB, 0xFF,0xFF,0xFF,0x6F,0xF3,0xFF,0xF7,0xEF,
+0xFB,0xFF,0xBF,0xFF,0xEF,0xFE,0xFF,0xBF, 0xFF,0xFF,0xFF,0xBE,0xBF,0xFF,0xEF,0xFF,
+0x7F,0xEF,0xFF,0xFD,0x17,0xFB,0x7B,0xFF, 0xFF,0xFD,0x7F,0xDB,0xF6,0xF4,0x7F,0xFA,
+0xFE,0xF5,0xBF,0xEB,0xE3,0xF7,0xFF,0xFF, 0xE9,0xBF,0xFF,0xAF,0xF7,0xFD,0xF3,0x7E,
+0x8F,0xA3,0xEA,0xFF,0xCB,0xF3,0xEE,0xFF, 0xBF,0xEF,0xF7,0xF9,0xFF,0xFE,0x7F,0xFF,
+0xFF,0xFF,0xFF,0xF5,0xFB,0xF6,0xFF,0xF5, 0x2F,0xFE,0xFB,0xD7,0xBF,0xFF,0xBE,0xDF,
+0x9F,0xFF,0xF0,0xFF,0xFF,0xF9,0xFE,0x7F, 0x8F,0xA3,0xF8,0xFE,0x6F,0x9F,0xF9,0xF6,
+0x2F,0x9F,0xE7,0xF9,0xFE,0x2F,0x9F,0xE1, 0xFF,0xFF,0xFF,0x7F,0xDF,0xF7,0xF5,0xFD,
+0x7F,0x7F,0xF5,0xFF,0x9F,0x5F,0xFB,0xFE, 0xFF,0x7F,0xFF,0xFF,0xCB,0xFF,0xFF,0xFB,
+0xFE,0xFF,0xBF,0xAF,0xFB,0xFE,0xFF,0xDF, 0xFE,0xFE,0xBF,0xF7,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xC7,0xFF,0xFF,0xFD,0xFF,0x7F,0xDD, 0xF7,0xFD,0xFF,0xFF,0xD7,0xFF,0xFD,0x7F,
+0xFF,0xFB,0xFD,0xFF,0xFF,0xFE,0xEF,0x7F, 0xFD,0xEF,0xFB,0xFE,0xFB,0xFD,0xFF,0x7F,
+0xDF,0xFD,0xFF,0x7A,0xDF,0xF7,0xFD,0xFF, 0xFF,0xFF,0xFF,0x1F,0xFF,0xFF,0xD3,0xF7,
+0xFF,0xFF,0x6F,0xDB,0xFF,0xFF,0xEF,0xCB, 0xF4,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
+0x29,0xFF,0xE8,0xDA,0x76,0x9F,0xAF,0x6A, 0xDA,0xFE,0x35,0xEB,0xDA,0xD6,0xBF,0xAB,
+0xEB,0x7A,0xDE,0xBF,0xD7,0x7F,0xFF,0xFE, 0xFF,0xBF,0xEF,0xFD,0xDF,0x77,0xBF,0xFD,
+0x37,0xEF,0xFF,0xEF,0xFF,0x3F,0xFF,0xFF, 0xFF,0xFE,0x7F,0xFF,0xFF,0xFF,0xF7,0x7E,
+0xDF,0xFF,0xFF,0xFF,0xFA,0xB7,0x7F,0xFF, 0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0x89,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x9F,0xFB,0xFF,0xFF,0xFF,0xE7,0xFF,
+0xFF,0xFF,0xFF,0xAA,0xFF,0xAB,0xFB,0xFA, 0xEF,0xBF,0xFF,0xDF,0xFA,0x7B,0xB9,0xFE,
+0xFE,0xFF,0xFD,0xFF,0xF7,0xFE,0x3F,0xFF, 0xB7,0xFF,0xF7,0xEE,0xFF,0x7F,0xEF,0xFF,
+0xFF,0x7F,0xFF,0x1F,0xFB,0xFF,0xBF,0xFB, 0xFE,0xFF,0xBD,0xFF,0xFF,0x2F,0xFF,0xBF,
+0xFF,0x7F,0xDF,0xFA,0xFF,0xFF,0xFC,0xEE, 0xF5,0xF3,0xBE,0xFB,0x0F,0xEF,0xF3,0xBE,
+0xEF,0xFC,0x5F,0xFF,0x5A,0xFF,0xF7,0xDF, 0xFF,0xFF,0xFE,0xD5,0xFC,0x5F,0xFB,0xF2,
+0xFF,0xFF,0x2F,0xBB,0xF3,0xFF,0xFF,0xBF, 0xFF,0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF,
+0xBF,0xFF,0xFF,0xFD,0x7B,0xFF,0xDF,0xB9, 0xFF,0xFB,0xFF,0xD8,0x7F,0xFF,0xFF,0xFF,
+0xFB,0xFF,0xFC,0x7F,0x1F,0xBF,0xE0,0xDF, 0xF7,0xEF,0xFF,0xFD,0x7F,0xFE,0xDF,0xFF,
+0xE0,0xFF,0xFF,0xFD,0xEF,0xFB,0xFF,0xFE, 0xF7,0xDF,0xFF,0xEB,0x5F,0xFF,0xF7,0xFF,
+0xFF,0xFF,0xFF,0xBF,0xFF,0xFD,0xFF,0xFD, 0xFF,0xFF,0xFF,0xF7,0xFD,0xFF,0x3B,0xDC,
+0xFD,0x6D,0x7B,0x5F,0x57,0xF5,0xFD,0x7F, 0x5F,0xFF,0xB1,0xFF,0xEB,0xFF,0xFF,0xFF,
+0xFB,0xFB,0xFE,0xFF,0xBF,0xFB,0xBE,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFF,0xAF,0xFE,0xF7,
+0xDF,0xDF,0xFF,0xFF,0xFF,0x7F,0xCF,0xF3, 0xF8,0xFF,0xD7,0xFB,0xFF,0x5F,0xBF,0xF7,
+0xFB,0xFF,0x7F,0xFE,0x23,0xFF,0xFF,0xFE, 0x7F,0xF3,0xFF,0xFB,0xFE,0xFF,0xFF,0xF3,
+0xFF,0xFF,0xF5,0xF9,0xFF,0x3F,0xFF,0xFF, 0xF0,0x9A,0xFF,0xBE,0x7F,0xFF,0xFC,0xF9,
+0xFF,0xFD,0xAF,0xEB,0xFE,0xBF,0xFF,0xCF, 0xF3,0xFE,0x7F,0xFF,0xFF,0x5B,0xBD,0xFF,
+0xBC,0xEB,0xFF,0xD7,0xD4,0xAF,0xAF,0xFD, 0xFF,0xCF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7,
+0xFD,0xFE,0xFF,0x6F,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFD,0x7F,0x5E,0xFD,0xBF,0xDB,0xF6,
+0xFD,0xBF,0x6F,0xFB,0xEE,0xFD,0xFF,0x7A, 0xFF,0xFA,0xFB,0xFF,0x3F,0xFB,0xB7,0x5F,
+0xD6,0xF7,0x1F,0x71,0xDC,0x77,0x1D,0xC7, 0x31,0xDC,0x77,0xDF,0xF9,0xBF,0xF5,0x5B,
+0xF4,0xD7,0x9D,0xAE,0xFF,0xBF,0xFD,0xBF, 0xDB,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,0xFE,
+0x3D,0x81,0xFF,0xEB,0xFE,0xFE,0xFE,0xFF, 0xEB,0x7A,0xDF,0x7D,0x77,0x7D,0xF5,0x79,
+0xDF,0x57,0xDD,0xF5,0x7D,0x7E,0xE6,0xFF, 0xD6,0x3F,0xBF,0x7F,0xFF,0xD4,0xF5,0x3F,
+0xBF,0xFB,0xBE,0xEF,0xB3,0xEE,0xFB,0x9E, 0xEF,0xBB,0xFE,0x8B,0xFF,0xFE,0xDF,0xB7,
+0xED,0xFF,0xF7,0xFD,0xFE,0xFF,0xEF,0xBB, 0xEE,0xFF,0xBE,0xEF,0xBB,0xEE,0xEB,0xFC,
+0x1F,0xFF,0xFF,0xFD,0xFF,0xE7,0xFF,0xF7, 0xFD,0xFF,0xEF,0xFE,0xFF,0xBF,0xEF,0xFB,
+0xFE,0xFF,0xBF,0xEB,0xFA,0x1F,0xFF,0xB7, 0xEF,0x5B,0xFE,0xFF,0xAF,0xEB,0xDD,0xE7,
+0xDE,0x77,0x9D,0xE7,0x79,0xDE,0x77,0x9D, 0xBF,0xE6,0x6F,0xFF,0xFE,0xFF,0xBF,0xEF,
+0xFB,0xFE,0xFD,0xBF,0x6F,0xF6,0xFD,0xBF, 0x6F,0xDB,0xF6,0xFD,0xBF,0xFF,0x7E,0xFF,
+0xFF,0xFB,0xFE,0xFE,0xFF,0xEF,0xFB,0xFD, 0xEF,0x7E,0xF7,0xBD,0xEF,0x7B,0xDE,0xF7,
+0xBD,0xEF,0xFF,0xD5,0xFF,0xBF,0xFF,0xEF, 0xFE,0xFF,0xFC,0x3F,0x0F,0xE7,0xFE,0x7F,
+0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7,0xFE, 0xF3,0xFF,0xFE,0xDF,0xAD,0xDF,0x67,0xEE,
+0xFB,0xBF,0xEF,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFE,0xFF,0xBF,0xEF,0xFF,0x23,0xFF,0xFF,
+0xFF,0xFF,0x7F,0xFF,0xF3,0xBC,0xDB,0xFE, 0xFB,0xFF,0xFB,0xBE,0xF7,0xFB,0xFF,0x7F,
+0xDF,0xFF,0xCF,0xFB,0xFF,0x9F,0xE3,0xF9, 0xBE,0x3F,0x8F,0xE7,0x79,0xFF,0x9D,0xE7,
+0xF9,0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x5F, 0xFF,0xCF,0xF7,0xFF,0xFF,0xFF,0xDF,0xF7,
+0xFE,0x7F,0xE7,0xF9,0xFE,0x7F,0xFF,0xFF, 0xFB,0xFE,0xFF,0xFF,0xBF,0xFF,0xBF,0xBF,
+0xFF,0xFE,0xFF,0xBF,0xEF,0xFF,0xFD,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFD,0xFF,
+0xFF,0x3F,0xFF,0xBF,0xFF,0xF7,0xFF,0xFF, 0x7F,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xE8,0xEF,0xFF, 0x5F,0xF7,0xBF,0xF9,0xFE,0xDF,0xB7,0xFD,
+0xFF,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0xDD,0xFF,0xF2,0xFF,0xBF,0xFF,
+0xFF,0xBF,0xFF,0xFF,0x2F,0xF2,0xFF,0xBF, 0x2F,0x7B,0xD2,0xF7,0xBF,0x2F,0xFF,0xBB,
+0xFF,0xEE,0x8F,0xAF,0xEB,0xFA,0xFE,0x3F, 0xA7,0x69,0xCE,0x8F,0xA4,0xEA,0xFA,0xEE,
+0xB7,0xAE,0xEB,0xFD,0xC7,0xFF,0xF7,0xF7, 0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x3E,0xF3,
+0x74,0xFF,0x3F,0x4F,0xFF,0xE7,0xFF,0x3F, 0xFE,0xA7,0xFF,0xFF,0xDF,0xF7,0xB7,0xFF,
+0xF7,0xFF,0xBA,0xEF,0x37,0xEB,0xFB,0xFE, 0xBF,0xFB,0xFE,0xF3,0xFF,0xF9,0xDF,0xFF,
+0xBF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF, 0xFD,0xDF,0xFF,0xFD,0xFF,0xFF,0xFB,0xFE,
+0xFD,0xFF,0xFB,0xBF,0xFE,0x3F,0xED,0xFF, 0xDF,0xBE,0x3D,0xA7,0xFB,0xFA,0x3F,0xE6,
+0xE1,0xFE,0xFE,0x3F,0xEF,0xE3,0xDF,0xF5, 0x7F,0xFE,0xFF,0x7E,0xFF,0xFF,0xFF,0xFF,
+0xEF,0x6F,0xF6,0xFF,0x7D,0xEF,0xD7,0xDE, 0xFF,0x7D,0xEF,0xFF,0xF2,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0x7B,0xDE,0xFB,0xE6,0xEE, 0xEF,0x37,0x6E,0xF3,0x7E,0xEB,0x37,0xEF,
+0xFF,0xC1,0xFF,0xFE,0xFF,0xF7,0xEF,0xFF, 0xFF,0xFF,0xBF,0x3F,0xD2,0xDF,0xBF,0x2F,
+0x7B,0xE2,0xFF,0xFE,0x3B,0xBD,0xDB,0xFF, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFE,
+0xFF,0xFB,0xFF,0xFF,0xBF,0xFF,0xFB,0xDF, 0xFF,0xBF,0xFF,0xB7,0xFF,0xFF,0xBF,0xEF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF, 0x7F,0xFF,0x1F,0xEF,0xF1,0xFD,0xFF,0xF6,
+0xAF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF, 0xFF,0xFF,0xFE,0x9F,0xFF,0xFF,0xFF,0x77,
+0xEF,0xF7,0xFB,0xFF,0xFE,0x5F,0xFF,0xFF, 0xBF,0xCF,0xFB,0xF7,0xDD,0xF7,0xF5,0xFF,
+0x5F,0xD5,0xF5,0xFD,0x7F,0x5F,0xD7,0xF5, 0xFF,0xFB,0x0F,0xFF,0xFF,0xA9,0xEA,0x7A,
+0xFF,0xAF,0x8F,0xFE,0xDF,0xAF,0xEF,0xFB, 0xFE,0xFF,0xBF,0xEF,0xFB,0xDF,0xE5,0x5F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xBD,0x57,0xFF, 0xFF,0x6F,0x77,0xBF,0xF7,0xFB,0xFF,0x7F,
+0xBF,0xF7,0xFF,0xFC,0xBF,0xFF,0x9F,0xFF, 0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xFF,0x1F,
+0xCF,0xFF,0xFC,0xFF,0xFF,0xFF,0xFF,0xFB, 0x65,0xAF,0xF3,0x7C,0xFF,0x3F,0xDF,0xFF,
+0xFD,0xE9,0xFE,0x7F,0xE7,0xFF,0xFE,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFD,0xE3,0xDF,0xFB,
+0xDB,0xF6,0xFD,0xEF,0x5B,0xFB,0xFF,0xDF, 0xFC,0xFF,0x3F,0xDF,0xF3,0xFD,0xFF,0x7F,
+0xDF,0xEF,0x66,0xFF,0xDF,0xAD,0xEB,0x7A, 0xDE,0xF7,0xF7,0xE7,0xD9,0xFD,0x9F,0x67,
+0xD9,0xF6,0x7D,0x9F,0xE7,0xDF,0xF5,0x47, 0xFD,0x65,0x5B,0xD6,0xF4,0xFE,0xFF,0xEF,
+0xFF,0x6D,0xF6,0xDD,0xB7,0x6D,0xDB,0x76, 0xDC,0xB7,0x7D,0xFA,0x9B,0xF6,0x6D,0x9D,
+0x67,0x59,0xDF,0xF7,0xDD,0xFF,0xEB,0xFE, 0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,0xE3,
+0xD1,0x9F,0xFF,0xBD,0xBF,0xEF,0xFE,0xF7, 0xBF,0xBF,0xF7,0xD7,0x7F,0xDD,0xF7,0x9D,
+0xDF,0x7F,0xDF,0xF7,0xFF,0xE0,0x7F,0xFD, 0xC1,0xDF,0xF7,0xFD,0xC7,0x7F,0x7F,0xFB,
+0xFF,0xBB,0xEC,0xFB,0x3E,0xFF,0xBF,0xEC, 0xFB,0xFF,0xD8,0x7F,0xBF,0x6C,0xFF,0xBE,
+0xFF,0xBF,0xED,0xFF,0xEF,0xFE,0xFB,0xBF, 0xEF,0xFB,0xFE,0xFF,0xBF,0xEE,0xFF,0xC5,
+0xFF,0xAF,0x6F,0xFF,0xFC,0xFD,0x3F,0xE7, 0xFF,0xFE,0xFF,0xEF,0xFB,0xFE,0xFF,0xBF,
+0xEF,0xFB,0xFE,0xBF,0x89,0xFE,0xFA,0xBA, 0xFE,0xBF,0xAF,0xFB,0xF6,0xF5,0xD9,0x7D,
+0x97,0x65,0xD9,0x74,0x5D,0x97,0x65,0xD3, 0xFE,0xD6,0xFF,0xBF,0xF7,0xFD,0xFF,0x7F,
+0xBF,0xCF,0xFB,0xFE,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFF,0xF6,0x8F,0xFB,
+0xFF,0xEF,0xFB,0x7E,0xDB,0xFE,0xFF,0xBE, 0xEF,0xEE,0xFB,0xBE,0xEF,0xBB,0xEE,0xFB,
+0xBE,0xFF,0xFF,0xDF,0xFF,0x43,0xFF,0xFF, 0xFB,0xEF,0x5F,0xB7,0xFE,0x7F,0xE7,0xF9,
+0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0xF9, 0xBF,0xFE,0xAF,0x77,0xFD,0xFF,0x2F,0xAF,
+0xA7,0xFE,0xFF,0xEF,0xFB,0xFE,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0xF1,0x7F,0xEF,0xDF,
+0xFF,0x97,0xF5,0xEF,0xFF,0xDF,0xFF,0xFF, 0xBF,0xFF,0xBF,0xFF,0xFF,0xFE,0xFF,0xFF,
+0xFF,0xE0,0xFF,0xFF,0xF9,0xFE,0x2F,0x8B, 0xE3,0xF8,0xBE,0x77,0x9F,0xF9,0xDA,0x77,
+0x9D,0xE7,0x79,0xDE,0x77,0x9F,0xDD,0xFF, 0xFD,0xFD,0x7F,0x5F,0xD7,0xFD,0xFF,0x7F,
+0xE7,0xFE,0x7F,0x97,0xE7,0xFB,0xFE,0xFF, 0xBF,0xEF,0xFF,0xAB,0xFF,0xEF,0xFA,0xFE,
+0xBF,0xAF,0xFF,0xFA,0xFF,0xFF,0xDF,0xFF, 0xFB,0xFF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF,
+0x67,0xFF,0xF7,0xF5,0xFF,0xFF,0xFF,0xDF, 0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF,0xBD, 0xEB,0xFF,0xFF,0xF7,0xAD,0xEB,0xFF,0xDF,
+0xFD,0xFF,0x3F,0xDF,0xF7,0xFD,0xFF,0x7F, 0xDF,0xFF,0x5F,0xFF,0xF7,0xFF,0xFF,0xFD,
+0xBF,0xFF,0xCB,0xF4,0xFF,0x7F,0xD3,0xF7, 0xFD,0x3F,0x7F,0xD3,0xF7,0xFF,0xFC,0x3F,
+0xFF,0xEA,0xFA,0xBE,0xAF,0xAB,0xEB,0xBA, 0xF4,0x95,0x6B,0x52,0xD4,0xAD,0x2F,0x4A,
+0xD2,0xF6,0xBF,0xD2,0x7F,0xF7,0x3F,0xFF, 0xFF,0xF3,0x7F,0xFF,0xFF,0xF7,0xFF,0xBA,
+0xDF,0xFB,0xFD,0xFF,0xBF,0xFF,0xFB,0xFF, 0xF8,0x7F,0xEA,0xFF,0xFE,0xFE,0xDF,0xFF,
+0xF7,0xFF,0x7F,0xBB,0xFF,0xFF,0xBF,0xDF, 0xFB,0xFF,0xFF,0xBF,0xFF,0xB1,0x7F,0xFF,
+0xFB,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF, 0xCF,0xFE,0xFF,0xFF,0xEF,0xFF,0xF7,0xFF,
+0xFF,0xFF,0xF1,0xFF,0x69,0xBE,0xFA,0xBF, 0xAF,0xE2,0xFF,0xFE,0xFD,0xAF,0xF3,0xFE,
+0xFF,0xBF,0xEF,0xFB,0xFC,0xFF,0xFF,0x07, 0xFD,0x95,0xDB,0xDF,0x7F,0xDF,0xAF,0xFF,
+0xF7,0xAF,0x36,0xFE,0xBF,0x65,0xEB,0xF6, 0xFE,0x9F,0x6F,0xFE,0x07,0xFF,0xCF,0xFF,
+0xF8,0xFE,0xFF,0xCF,0xFF,0xF6,0xFA,0xE7, 0xFB,0xFE,0xFF,0xBB,0xED,0xF9,0xFF,0xFF,
+0xFF,0x5F,0xFF,0xFF,0xFF,0x75,0xFF,0xEF, 0x7E,0xFD,0xE0,0xE8,0x5E,0xD3,0xE5,0xF9,
+0x3E,0x5F,0xD7,0xF7,0xFF,0xFA,0x2F,0xFB, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0x7F,
+0x7F,0xD7,0xF5,0x7D,0x5F,0x57,0xD5,0xF5, 0xEF,0xFF,0xF3,0x7F,0xFC,0x7F,0xFF,0xC7,
+0xF1,0xFF,0xFF,0x1F,0xCF,0xB0,0xFF,0x3F, 0xCF,0xF3,0xFC,0xFF,0x3F,0xCE,0xFF,0xE4,
+0xFF,0xDF,0x7F,0xFE,0xF7,0xBB,0xFF,0xFF, 0xDF,0xEF,0xEE,0xFF,0xBF,0xEF,0xFB,0xFE,
+0xBF,0xBF,0xEF,0xFF,0xD1,0xFF,0xFF,0xFF, 0xFD,0xFB,0xFF,0xFD,0xFF,0xFB,0x9F,0xE9,
+0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0xBF, 0xFF,0xB3,0xFF,0xFF,0xF7,0xFF,0xFF,0xAF,
+0xF7,0xFF,0xB6,0x3F,0xEB,0xFA,0xFE,0xBF, 0xAF,0xEB,0xFA,0xFE,0xBF,0xFE,0xA7,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF, 0xFE,0x9F,0xF7,0xF9,0xFF,0x7F,0x9F,0xE7,
+0xFF,0xFF,0xFE,0xAF,0x6F,0xFF,0xFF,0xFF, 0x9F,0xFF,0xDF,0xFF,0x7D,0x5F,0xDD,0xFF,
+0xFB,0xBF,0xE7,0xBB,0xFF,0xFB,0xDF,0x6D, 0x5F,0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xEB,0xF7,0xFF,0xE7,0xEF,0xF7,0xFF,0xFF, 0x7F,0xFF,0xF7,0xFF,0xFC,0x8F,0xFF,0xEF,
+0xFD,0xFE,0xFF,0xBE,0xF4,0xF2,0x7D,0xD7, 0xCF,0xFF,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xCF,0x6B,0xFF,0xBF,0x3F,0xFB,0xF2, 0xFC,0x7F,0xEB,0xFF,0x9F,0xFA,0xFF,0xFF,
+0x3F,0xFF,0xF3,0xFF,0xFF,0xFD,0x70,0xF7, 0xFF,0xFF,0xBF,0xFF,0xFB,0xD7,0xFE,0xF5,
+0x77,0xFF,0x15,0xDD,0x77,0xFD,0xFF,0x7F, 0xDF,0xF7,0xFB,0xCD,0xBF,0xFF,0xFD,0xFF,
+0xFF,0xDF,0x37,0xCD,0xF9,0xEC,0xFE,0xEF, 0xBB,0xF4,0xFB,0x3F,0x4F,0xB3,0xFF,0xFD,
+0xCB,0xFF,0xE9,0x7E,0x54,0x9F,0xE5,0x4B, 0xB7,0xFF,0xDD,0x7D,0xC7,0x71,0xDD,0x77,
+0x5D,0xD7,0x75,0xCD,0x7F,0xD6,0xFF,0xD3, 0xF6,0xF9,0x3F,0x6D,0x95,0xAF,0x7F,0xFE,
+0xFF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFE,0xF6,0xC7,0xFF,0xAD,0x7B,0xCA,0xFF,
+0xBF,0xBF,0xEF,0xFD,0xE3,0xDF,0xB7,0xED, 0xFB,0x7E,0xDF,0x37,0xED,0xE3,0xFB,0xDF,
+0xFF,0x52,0x5C,0x15,0xFD,0xCF,0x7F,0xDF, 0xFE,0xEF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEC,
+0x7B,0xFE,0xFF,0xFE,0x3E,0x7F,0xDA,0xF7, 0xFD,0xFF,0x7F,0xFF,0xFF,0xFB,0xEF,0xBB,
+0x6F,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFF, 0xF7,0x7D,0xFF,0xD8,0xFF,0xFD,0xBF,0x7F,
+0xFB,0xFF,0xFF,0x9F,0xFB,0xFE,0x7F,0x9F, 0xE7,0xF9,0xFE,0x7F,0x9F,0xEA,0x7F,0xF6,
+0xBF,0xBD,0x6A,0x5A,0xF6,0xE5,0xBF,0x77, 0x5F,0x6D,0xDD,0x77,0x5D,0xD7,0x75,0xDD,
+0x77,0xFF,0xA5,0xBF,0xCF,0xFB,0xFF,0xFF, 0xBF,0xCF,0xFB,0xFD,0xFF,0xBF,0xF3,0xFE,
+0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFD,0xAB, 0xFF,0xBF,0xBF,0xFF,0xFB,0xFF,0x7F,0xEF,
+0xFF,0xBE,0xFB,0xEE,0xFB,0xBE,0xEF,0xBB, 0xEE,0xFB,0xBF,0xFF,0xB5,0xFF,0xD0,0xBC,
+0xFD,0x2F,0x4B,0xF7,0xFF,0xFF,0x9F,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,
+0xFA,0x8F,0xFD,0xAB,0xFA,0xDA,0xBF,0xAF, 0xB3,0xFD,0xFF,0xBF,0xFB,0xFE,0xFF,0xBF,
+0xEF,0xFB,0xFE,0xF7,0xBF,0xFF,0x9F,0xFF, 0x77,0xF7,0xBD,0xFD,0x77,0xDF,0xFF,0x7E,
+0xDF,0xED,0xBB,0xFE,0xFF,0xBE,0xEF,0xFB, 0xFE,0xFF,0xFA,0x3F,0xFF,0xBE,0x6F,0x8F,
+0xE6,0xF9,0xFE,0x7F,0x9F,0xC7,0xFE,0x7F, 0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7,0xFB,
+0x7F,0xFF,0x7F,0xCF,0xFF,0xFD,0xFF,0xFF, 0xDF,0xFB,0xAF,0xBF,0xEF,0xFF,0xFE,0xFF,
+0x9F,0xEF,0xFB,0xFF,0xFC,0xFF,0xFB,0xFE, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xF7,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF5,0xFF,0xFF,0xFF,0x3F,0xDF,0xF7,
+0xFF,0xFF,0x7F,0xEF,0xFE,0xFF,0xBF,0xFF, 0xFB,0xFF,0xFF,0xBF,0xEF,0xFF,0xB3,0x7F,
+0xFF,0x7B,0x5E,0xF7,0xFD,0xFF,0x7B,0x7F, 0xF7,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,
+0xDF,0xF7,0xFF,0x17,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xDD,0xF6,0xFC,0xBF,0xCB,0xF2,
+0xBC,0xBF,0x2F,0xCB,0xF2,0xFC,0xBF,0xFE, 0x8F,0xFF,0xFA,0x7E,0xBF,0xA7,0xEB,0xDA,
+0xFC,0xBF,0xAF,0x7A,0xFE,0xBF,0xAF,0xEA, 0xFA,0xFE,0xBF,0xAF,0xF4,0xDF,0xFE,0xFF,
+0xF3,0x3C,0x7F,0x3E,0xFF,0xCF,0xF8,0xBF, 0x8F,0xE3,0xF8,0xFE,0x3F,0x8F,0xE7,0xE8,
+0xFF,0xFC,0x9F,0xFF,0xFF,0xCF,0xEB,0xB3, 0xE7,0xFB,0x7B,0xF3,0xFE,0xFF,0xCF,0xDB,
+0xFB,0xFB,0xBF,0x6F,0x6F,0xDF,0xEC,0x7F, 0xFF,0xFF,0xF7,0xFD,0xFD,0xFF,0xFF,0xFF,
+0xFF,0xB2,0xBF,0xFF,0xDE,0xFD,0xBD,0xEF, 0xFB,0xF6,0xDF,0xEA,0xE7,0xDB,0xFE,0xBB,
+0xFF,0xEB,0xFB,0xBF,0x9F,0x8F,0xE8,0xFE, 0x3F,0x8F,0xA3,0xF8,0xFE,0x3F,0x8F,0xFF,
+0xF8,0x7E,0xFD,0xFD,0x7F,0xFF,0xFB,0xCD, 0xFF,0xFD,0xFF,0x5F,0xEF,0xFD,0xFF,0xFF,
+0xDF,0xF7,0xFD,0xFF,0xBE,0x90,0xFF,0xFF, 0xEE,0xFF,0x3F,0xBF,0xF3,0xBB,0xFE,0xB7,
+0xAB,0xFA,0xFE,0xAF,0xAD,0xEA,0xFA,0xDE, 0xAB,0xFF,0x63,0xFF,0xFE,0xF2,0xFF,0xB3,
+0xFF,0xDF,0xEE,0x7D,0xFF,0x03,0xF1,0xF4, 0x3F,0x1F,0xC3,0xF1,0xEC,0x7F,0xFE,0x6F,
+0xFF,0xFB,0xFB,0xFF,0x9F,0xFF,0xBF,0xFF, 0x7B,0x5F,0xFD,0xFF,0xDF,0xF7,0xFD,0xFD,
+0x7F,0x7F,0xDF,0xFE,0xCF,0xFB,0xFF,0xFF, 0xAF,0xFB,0xFF,0x1F,0xEF,0xA5,0xFD,0xBF,
+0xDF,0xFB,0x7D,0xFF,0xBF,0xDF,0xFB,0xFF, 0xFD,0x3B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,
+0xAF,0xF3,0xFF,0xFB,0x7F,0xBF,0xD7,0xFB, 0xBF,0x7F,0xBB,0xF7,0xFF,0xF8,0x7F,0xFF,
+0xFA,0x5F,0xD7,0xFF,0xDF,0x7F,0xEF,0xFF, 0xFF,0x7F,0xDB,0xF7,0xFD,0xFF,0x7F,0xDF,
+0xB7,0xFB,0xEC,0xFF,0xFF,0xF7,0xBF,0xEF, 0xFD,0xFC,0xFB,0xFF,0xEF,0xF0,0xFE,0x3F,
+0x8F,0xE3,0xF8,0xFE,0x3F,0x8F,0xEF,0x8D, 0xFF,0xFF,0xEF,0x7F,0xBF,0xFF,0xFB,0xFF,
+0xDB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xEF,0xD8,0xFF,0x2E,0x7F,
+0xBE,0xEF,0xFE,0x6E,0xFF,0xBF,0xF9,0xFF, 0xFF,0xF3,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFC,0x66,0xBE,0x47,0xF3,0x7F,0xDF,0xFE, 0x87,0x9F,0xFF,0xFF,0xFF,0xFF,0xE7,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xD6,0x6F,0x7C, 0xFB,0x4F,0xD2,0xFF,0xFD,0x2B,0xFE,0xFF,
+0xFF,0xFD,0x5F,0xD7,0xD5,0xF5,0x7D,0xFF, 0xFF,0xFF,0xBF,0x9B,0xFF,0xFF,0xDF,0xB7,
+0xFF,0xFF,0xDF,0xFF,0x3F,0xCF,0xFE,0x7F, 0xBF,0xEF,0xFB,0xFC,0xFF,0x3F,0xFF,0xD9,
+0xBF,0xFE,0x97,0xEC,0x8F,0xB7,0xFE,0x9B, 0x7D,0xFD,0xB7,0xDD,0x77,0x1D,0xC7,0x71,
+0xDD,0x77,0x5D,0xD7,0xF3,0x6F,0xFD,0x3F, 0x73,0xDD,0xAF,0xFD,0x7A,0xFF,0xFF,0xAF,
+0xFE,0xFD,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF, 0xEF,0x66,0x7F,0xFF,0xFF,0xBF,0xBF,0xFF,
+0xFB,0xFF,0xF7,0xDF,0xFD,0xFB,0x7D,0xDF, 0xB7,0xCD,0xF3,0x7C,0x5F,0x3F,0x91,0x3F,
+0xFF,0x3D,0xEF,0x7B,0xFF,0xFC,0xFF,0xCA, 0xEF,0xFE,0xFF,0xBD,0xEF,0xFB,0x1E,0xE7,
+0xBB,0xEC,0x7F,0xB3,0xFF,0xFD,0x9F,0xFF, 0xFF,0xFE,0xFF,0xFF,0x7F,0xBF,0xFB,0xFE,
+0xFF,0xBF,0xEF,0xFB,0xEE,0xFB,0xBF,0xDF, 0x67,0xFF,0xFF,0xBF,0xEF,0xDB,0xFF,0xBC,
+0xFE,0x7F,0xFB,0xFF,0x9F,0xEF,0xF9,0xFE, 0x7F,0x9F,0xE7,0xF9,0xFE,0x87,0xFF,0xEE,
+0xFB,0xBE,0xE5,0xBF,0xEF,0xF9,0xD7,0x65, 0xF7,0xDD,0xE7,0x7D,0xDF,0x77,0x5D,0xD7,
+0x7F,0xF8,0x9B,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xFF,0xBF,0xEF,0xFB,0xFF,0x7F,0xCF,
+0xF3,0xFC,0xFF,0xBF,0xEF,0xFF,0xDB,0x3F, 0xEF,0xFB,0xFE,0xFF,0xDF,0xFF,0xFE,0xFB,
+0xBB,0xEF,0xBF,0xEF,0xBB,0xEE,0xFB,0xBE, 0xEF,0xBB,0xFF,0xFC,0x7F,0xFD,0x3B,0x5B,
+0xD6,0xE5,0xFD,0x4F,0xC3,0xFB,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFF,
+0xB4,0xFF,0xFA,0xBC,0x8F,0xB2,0xE9,0xD2, 0x2E,0xCF,0xFB,0xFF,0xBF,0xEF,0xFB,0xFE,
+0xFF,0xBF,0xEF,0xFB,0xFF,0xEC,0xFF,0xFD, 0xFD,0x7F,0xDF,0xF7,0xE4,0xDF,0x5F,0xFF,
+0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xC3,0xFF,0xEF,0xE6,0xF8,0xFE,
+0x3F,0x8B,0x83,0xF9,0xFE,0x7F,0xE7,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x17,
+0xFD,0xFF,0xFF,0xFF,0x7F,0x5F,0xF7,0x2C, 0xFF,0xFF,0xFF,0xFE,0x7F,0xFF,0xE7,0xF9,
+0xFE,0x7F,0x9F,0xFE,0x2F,0xFF,0xFF,0xEF, 0xFF,0xFE,0xBF,0xEF,0xAD,0xFF,0xFF,0x7F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFE,0xDF,0xFF,0xDF,0xFF,0xFD,0xFD,0x7F,
+0xDF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFA,0x3F,0xFE,
+0xF7,0xFD,0xEF,0x7A,0xFF,0xB1,0xBD,0xFF, 0x7F,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,
+0xFF,0x7F,0xF3,0x27,0xFF,0xDF,0xFF,0xDD, 0xFF,0xFC,0x9B,0xFF,0xCB,0xFC,0xBF,0x2F,
+0xCB,0xF2,0xFC,0xBF,0x2F,0xC9,0xFF,0xDE, 0xFF,0xDF,0xAF,0xEB,0xDA,0xFE,0xBB,0xAF,
+0xEB,0xF8,0xF7,0xAF,0xE8,0xFA,0xFE,0xBF, 0xAF,0xEB,0xF2,0xFF,0xFD,0xFF,0xFF,0xEF,
+0xBD,0xD7,0xBF,0xFF,0xFF,0xDE,0x8F,0xB8, 0xDE,0x37,0x8D,0xA3,0x78,0xDA,0x3F,0x8F,
+0xFF,0xA1,0xFF,0xFF,0xFB,0xFB,0xFF,0xFF, 0xFF,0xFF,0xA7,0xBD,0xFB,0x76,0xFD,0xBF,
+0xEF,0xDB,0xFE,0xBB,0xBF,0xFE,0x27,0x7F, 0xFF,0xFE,0xFE,0xFD,0xF5,0xFF,0xEF,0xF5,
+0xDF,0x1F,0xE7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0xFF,0xCD,0xFD,0xAE,0xFF,0xFA,
+0x3E,0x3F,0xAB,0xFD,0xF8,0x7E,0x8F,0xE3, 0xF8,0xFE,0x3E,0x8F,0xE3,0xF8,0xFF,0xFE,
+0x1F,0xEF,0xDF,0xBF,0xFE,0xDE,0xDF,0xD9, 0xFF,0xDF,0xBC,0xFF,0xFF,0x7F,0xFF,0xEF,
+0xFD,0x7F,0xDF,0xF7,0xF9,0x3F,0xFE,0xFF, 0xFF,0x6F,0xFE,0xDE,0xBF,0xF7,0xED,0xEA,
+0xFD,0x8F,0x83,0xF8,0xEA,0x3F,0x8F,0xEF, 0xFF,0xF4,0x7F,0xFF,0xEF,0xEF,0x7B,0xF3,
+0xF1,0x5F,0xFF,0xFF,0xF1,0x3B,0x7F,0xDF, 0xF7,0xFD,0xFF,0xFF,0xFF,0xFF,0xE0,0xFF,
+0xFF,0xFF,0xF7,0xFF,0x6F,0xFF,0x7F,0xFF, 0xFF,0xF7,0xDE,0xF7,0xBF,0xEF,0xFB,0xF7,
+0xFD,0xFF,0xFF,0xF5,0xFA,0xFF,0xFF,0xFB, 0xE7,0xFF,0xF3,0xF8,0x7F,0xF3,0xDF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0xEF, 0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,
+0xFF,0x7F,0xFF,0x9F,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xCF,0xFF,0x37,0xFF,0xFF,
+0x7F,0xDF,0x77,0x5D,0xE7,0xFC,0xFF,0xBF, 0xF7,0xF5,0xFB,0xFF,0xFF,0xD7,0xF5,0xFB,
+0xFF,0xFF,0x45,0xFD,0x7F,0xEA,0xFD,0xBE, 0xBF,0xDF,0xF7,0xFF,0xFF,0xDB,0xFB,0xFE,
+0xFF,0xBF,0xEF,0xFF,0xFF,0xFF,0xFB,0x5F, 0x7F,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFE,0xFF,0xEF,0xFD,0xFF,0x7F,0xDF, 0xFF,0xEF,0xFB,0xF8,0x0F,0xF3,0xFF,0xF9,
+0x2E,0xFB,0xFE,0xFC,0xF3,0xEF,0xFF,0xFF, 0xBF,0xFF,0xFB,0xE7,0xFF,0xFE,0x7E,0xFF,
+0xC0,0x6B,0xCF,0xFF,0x34,0xDF,0xF1,0xFD, 0xFF,0xEF,0xFF,0xFF,0xFF,0xDF,0xF7,0xFD,
+0xCF,0x7F,0x9C,0xFD,0xFD,0x6C,0xF7,0xFF, 0xF6,0xFD,0xEB,0x2B,0x9F,0xFF,0xFC,0xFE,
+0x7E,0xFF,0xFF,0xFF,0xFF,0xD7,0xF3,0xF7, 0xFF,0xFB,0xE1,0xBF,0xFF,0xEB,0x7A,0xDE,
+0xD7,0xFB,0xFF,0xF9,0xFE,0xFF,0xFF,0xF3, 0xDE,0x7F,0xFD,0xE7,0x7F,0xFF,0xFD,0xBB,
+0xFF,0xFF,0x7E,0xCC,0xF6,0xAF,0x5F,0x7F, 0xFE,0xF4,0x7D,0xF7,0xFD,0xBB,0x6E,0xDB,
+0xB7,0xFF,0xF7,0xDF,0x66,0xFF,0xFF,0xF7, 0x3D,0xCF,0xDE,0xBD,0xFF,0xFF,0xDE,0xDB,
+0x8D,0xF7,0x7E,0xDF,0xB7,0xEF,0x7F,0xFF, 0xF6,0x87,0xFF,0xFF,0xEF,0xFE,0xDE,0xBF,
+0xFF,0xFF,0xFF,0xBB,0xEF,0xFD,0xFF,0x7B, 0xDE,0xF7,0x3F,0xFF,0xBF,0xFB,0xDB,0xFF,
+0xF2,0xB6,0xFD,0xBD,0x7F,0xE7,0xFF,0xFF, 0xFF,0x6F,0xF7,0xFF,0xFF,0xFF,0xFE,0x77,
+0xFF,0xBF,0xF8,0xAF,0xFF,0xDF,0xBF,0xFF, 0xBF,0x7F,0xFB,0xFF,0xFF,0xFF,0xDB,0xFE,
+0xFF,0xBF,0xFF,0xFA,0xFF,0xFD,0xFF,0xF6, 0x7F,0xFF,0x9F,0xFF,0xFF,0x3F,0xEF,0xF8,
+0xEE,0x7E,0x9F,0xBA,0xFE,0xBF,0x8F,0xEF, 0xFE,0xFE,0xF9,0xFF,0xFA,0x7F,0xFE,0x7E,
+0xBF,0xAF,0xFB,0x96,0xFD,0x9F,0xEF,0x5E, 0x65,0xBE,0xEF,0x5B,0xB6,0xFF,0xBE,0xE3,
+0xFF,0xB5,0xBF,0xFF,0xFD,0xFF,0x7F,0xFF, 0xEF,0xDF,0xFE,0xFF,0xBF,0xFB,0xFE,0xFF,
+0xBF,0xCF,0xFF,0xFF,0xFF,0xFD,0x9B,0xFF, 0xFE,0xFB,0xFE,0xDF,0xFF,0x7F,0xFF,0xF7,
+0xFE,0xFF,0xDF,0xFB,0xFB,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xB7,0xFE,0xFA,0xFF,0xAB,
+0xEF,0xFF,0xFD,0xB5,0x7B,0x7F,0xFB,0xF7, 0xFD,0xFF,0xFF,0xDD,0xFF,0xEF,0x8F,0xFF,
+0x2F,0xFF,0xFB,0x7C,0xFF,0x3F,0xDF,0x73, 0xEB,0xFE,0x3F,0xFF,0xEF,0xFB,0xFE,0xFF,
+0xEF,0xFD,0xFF,0xBF,0xFD,0x0F,0xFF,0xFF, 0xFF,0xF5,0xF9,0xFF,0x7F,0xD7,0xFD,0xFF,
+0xDF,0xFF,0xF7,0xFB,0xFF,0x7F,0xBF,0xFF, 0xFF,0xF0,0x9F,0xFF,0xFE,0x7F,0x8B,0xE3,
+0xF9,0xDE,0x27,0x9B,0xE6,0xBE,0x7F,0x9B, 0xC3,0xF8,0xDE,0x7F,0x9D,0xE7,0xFE,0x7F,
+0xFF,0xFF,0x5F,0xD7,0xFF,0xFF,0xFF,0x4F, 0xFB,0xFF,0xFF,0x7F,0xFF,0xAF,0xFF,0x9F,
+0x7F,0xFB,0xFF,0xE8,0xFF,0xFF,0xFE,0xBF, 0xAF,0xFF,0xFF,0xFE,0xBF,0xEF,0xF7,0xFF,
+0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF, 0xFC,0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xFF,
+0xFD,0x3F,0xCF,0xFF,0xFF,0xFF,0xFF,0xF7, 0xFF,0xFD,0x7F,0xFF,0xFF,0x93,0xFF,0xFF,
+0x7A,0xDF,0xF7,0xFF,0xFF,0x7B,0x7F,0xB7, 0xEF,0xFF,0xFF,0xFD,0xBF,0xFD,0xFB,0xFF,
+0xF7,0xFF,0xD7,0xFF,0xFF,0xFF,0xFC,0x9F, 0x6F,0xCB,0xFF,0xF4,0xBB,0xDF,0xD6,0xFD,
+0xBF,0x2F,0xD3,0xF7,0xFF,0xDF,0xFF,0xCF, 0xFF,0xFA,0xBE,0xBD,0xAF,0x6A,0xDA,0xBE,
+0xBB,0xAB,0x3A,0xBE,0x2D,0xAE,0xEB,0xDA, 0xF6,0x3F,0xAD,0xF5,0xDD,0xFF,0xCF,0xF1,
+0xFF,0xF9,0x7F,0xFF,0x73,0xFE,0xFF,0xCF, 0xC3,0xF4,0xF7,0x2F,0xF3,0xFF,0xFC,0xFF,
+0x7C,0x1F,0xFF,0x3F,0x4F,0xFF,0x7E,0xFF, 0xEF,0xBD,0xF6,0xFE,0xFF,0x2B,0xEF,0xDC,
+0xFB,0xFD,0xFF,0xFB,0xFF,0xEA,0x7B,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFB,0xF7,0xDF,0xFF,
+0xE3,0x7D,0xFF,0xB7,0xFF,0xBF,0xFF,0xFF, 0xDF,0xFF,0xF8,0xFF,0xBF,0xFF,0xBF,0xEB,
+0xE7,0xFA,0xFE,0x3D,0xBF,0xE9,0xFC,0xBF, 0xFF,0xFA,0xFB,0xFE,0xFF,0xFF,0xFF,0xD9,
+0xFF,0xFF,0xFF,0xF6,0x7F,0xFF,0xF6,0x7D, 0xFF,0xDF,0xCF,0xFD,0xBF,0xFB,0xEF,0x7E,
+0xFF,0x7F,0xFF,0xFF,0xD3,0xFF,0xFD,0xFB, 0xFF,0xFB,0xFF,0xFF,0xFF,0xEF,0xFF,0xBF,
+0xFE,0xFF,0xF7,0xEF,0xFF,0xFF,0xFF,0xFB, 0xFF,0x87,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,
+0x7B,0xFE,0xFF,0xFE,0x3B,0xF7,0xF7,0xFF, 0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF,
+0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xFF,0xF7, 0xFF,0xFF,0xAD,0xFF,0xFE,0xF7,0xFF,0xFF,
+0x5F,0xFF,0xFF,0xDF,0xFF,0xFD,0xFF,0xF5, 0xFF,0xDF,0xFF,0xBD,0xFF,0xE9,0xFF,0xC7,
+0xF3,0xFF,0xFF,0xF7,0xFF,0xF3,0xFF,0xF8, 0x3B,0xFF,0xFF,0x7B,0xDF,0xBF,0xFB,0xEF,
+0xFB,0xFF,0xFB,0xF7,0xF7,0xBB,0xFF,0xFF, 0xFF,0xFF,0xFB,0xFF,0xFE,0x7F,0xF3,0x7F,
+0x5E,0xB7,0xBF,0xFD,0x7F,0xFF,0xF9,0x7F, 0xFB,0xFF,0xEB,0xFD,0x7F,0x7F,0xFF,0xEF,
+0xFB,0xE0,0x3F,0xFE,0xBF,0xBF,0xDF,0xFF, 0x7E,0xFF,0xF7,0xFF,0xFF,0xFE,0xBF,0xFF,
+0xDB,0x78,0xFF,0xFF,0xFF,0xEE,0xA1,0xBF, 0xF5,0xDE,0xFB,0xF7,0xFF,0xFB,0xFF,0xFF,
+0xFF,0xFF,0xFB,0xFF,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xEF,0xF0,0xFF,0xFF,0xFF,0xF3,
+0xF7,0xFF,0xEF,0xFF,0xE7,0xCF,0xFF,0xFB, 0xFF,0xEF,0xFF,0xFF,0x9F,0x9F,0xEF,0xFC,
+0x16,0xBF,0xFE,0xF3,0xE4,0xFF,0xFF,0xC6, 0xFF,0xE7,0xFF,0xFF,0xFD,0xFF,0xBF,0xFF,
+0xFF,0x3F,0xFF,0xBF,0xD6,0xAF,0x7F,0xFE, 0x6B,0x7E,0x7F,0xFF,0xAF,0xFF,0xFF,0xBF,
+0xFF,0x5F,0xFF,0xFE,0xFF,0xFF,0xFE,0xFF, 0xFF,0xBD,0xDB,0xFF,0xFE,0x5F,0xF2,0xFF,
+0xFF,0x5F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xEF,0x7F,0xFF,0xFF,0xFF,0xFF,0xDE,0xBF,
+0xFF,0xFF,0xEF,0xFB,0x77,0xFE,0xBD,0x7F, 0x5F,0xFF,0xFF,0xFF,0xDF,0x6F,0xED,0xFF,
+0xFD,0xFF,0x7F,0xFD,0x6F,0xFF,0xFF,0x77, 0xDA,0xCF,0xFD,0x5F,0xFF,0xBF,0xFF,0xFF,
+0xDF,0x7F,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF, 0x66,0x7F,0xFF,0xFE,0xBF,0xE7,0xBF,0xFA,
+0xFF,0xFE,0xFF,0xFF,0xFF,0xDF,0xFF,0x59, 0xEF,0xFF,0xEF,0xFB,0x7F,0x89,0xFF,0xFF,
+0xE9,0xFF,0x6F,0xFF,0xF5,0xFF,0xFF,0xFF, 0xFF,0xFF,0x7F,0xF2,0xF7,0xFF,0xFF,0xEF,
+0xF8,0x7F,0xFB,0xFF,0xFD,0xFF,0xFF,0xD9, 0xFF,0xEF,0xBB,0xFF,0xFF,0xFF,0xBF,0xEF,
+0xDE,0xFF,0xFF,0x9F,0x7F,0xDF,0xFF,0xF7, 0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,0xAF,
+0xFF,0xFF,0xF7,0x3F,0xEB,0x9F,0xFE,0x7F, 0x9E,0x7F,0x9F,0xFE,0x87,0xFF,0xED,0xDB,
+0x56,0xFF,0xBF,0xAF,0x0B,0xD2,0xFF,0xEF, 0xDB,0x6E,0x7D,0xBD,0x6F,0xF8,0xFE,0x3F,
+0xFA,0x5B,0xFF,0xFD,0xBF,0xEF,0xFF,0xBF, 0x6F,0xDB,0xE6,0xFF,0xFF,0x3F,0xFF,0xDF,
+0xFE,0xFF,0xFF,0xFF,0xFF,0xDA,0x3F,0xFF, 0xFB,0xFE,0xFE,0xFF,0xFF,0xDF,0xF7,0xBD,
+0xFF,0xFD,0xFF,0xFE,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFF,0xF1,0x5F,0xFD,0x9F,0xDF,0xFD,
+0xFF,0xFD,0x7F,0xFF,0xFF,0xFF,0xFF,0x76, 0xFA,0xFF,0xFF,0x7F,0xE3,0xF8,0xFF,0xAE,
+0xFF,0xFB,0x7E,0x9D,0x73,0xFF,0xFA,0x7F, 0xDF,0xFF,0xFF,0x7F,0xFF,0xFB,0xCD,0xFF,
+0x7F,0xEF,0xFB,0xFF,0xFD,0xFF,0xF7,0x7F, 0x7F,0xEF,0xFF,0xED,0xFF,0xFF,0xFF,0xB5,
+0xFF,0xBF,0xFF,0xBF,0xFD,0xEF,0xDB,0xF7, 0xFF,0x93,0xFF,0xEF,0xE2,0xF9,0xBE,0x7F,
+0x8B,0xE7,0xF9,0xFE,0x6B,0xE7,0xF9,0xFE, 0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x47,0xFF,
+0xFF,0xFD,0xFF,0x9F,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xF5,0xFF,0x9F,0xFF,0xF7,0xFE,
+0xFF,0xBF,0xFE,0x6F,0xFF,0xFF,0xFB,0xFF, 0xFF,0xFF,0xAF,0xFF,0xFF,0xFF,0x7F,0xFB,
+0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, 0xDF,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF,0xDF,
+0xFF,0xFF,0xFF,0x5F,0xFF,0xFF,0xFF,0xFF, 0x5F,0xFB,0xFE,0xFF,0xF8,0x37,0xFF,0xFF,
+0xEF,0xFF,0x7F,0xFE,0xBF,0xFF,0xFF,0xFE, 0xBF,0xFF,0xFF,0x7F,0xFF,0xBF,0xFD,0xFF,
+0x7F,0xFA,0x7F,0xFF,0xFF,0x6F,0xFF,0xFF, 0x7D,0xFF,0xCF,0xFF,0xFF,0xFF,0x4F,0xFF,
+0xF2,0xFF,0xFF,0xFF,0xFF,0xFF,0xFA,0xBF, 0xFF,0xAE,0xEB,0xFA,0xFE,0xBB,0xAD,0xEB,
+0xFA,0xF7,0xAF,0x6B,0xFA,0xF6,0xBF,0x25, 0xE9,0xF2,0x7F,0x45,0xFF,0xFF,0xFD,0xF7,
+0xF7,0xBF,0xFF,0xDF,0xFF,0xFF,0xBF,0xFB, 0xFF,0xDF,0xF3,0xFF,0xF7,0x3F,0xCF,0xFF,
+0xA1,0xFF,0xFF,0xBF,0xE7,0xFF,0xFF,0x7F, 0xFF,0x3D,0xFF,0xFF,0xFF,0xF7,0xFF,0x2F,
+0xFF,0xFB,0xF5,0x7F,0xFE,0x57,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,
+0x3F,0xFF,0xFE,0xFF,0xFF,0xFF,0xFD,0xFE, 0xF7,0xEE,0xAF,0xFE,0xEE,0xE7,0xFA,0xFF,
+0xFE,0x9D,0xF9,0x5E,0xFE,0xFF,0xEB,0xFF, 0xFF,0xDF,0xA7,0xFF,0xFF,0xFF,0xFC,0xDB,
+0xFF,0xFF,0xFF,0x7E,0xFB,0xFF,0xFF,0xEF, 0xFB,0xFD,0xFF,0xDB,0xFF,0xFF,0xFF,0xEF,
+0xFF,0xFF,0xFF,0xFD,0xBF,0xFE,0xBF,0xFF, 0x6F,0x7F,0xFF,0xF7,0xFF,0xFF,0xF9,0xFF,
+0xF7,0xFF,0xBF,0xDE,0xF7,0xFF,0xFF,0xFF, 0xFA,0x7F,0xFD,0xBF,0x5F,0xFF,0xFF,0xBF,
+0xFF,0xED,0xFF,0xF7,0xBF,0xFF,0xFF,0xEF, 0xFF,0xDF,0xFF,0xFF,0xFF,0xE6,0xFF,0xFB,
+0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,
+0xFD,0xFF,0xF5,0xFF,0xF6,0x7F,0xDF,0xBD, 0xCF,0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,
+0xFF,0xF9,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3, 0xFF,0xEE,0xBF,0xFF,0x7D,0xEF,0xFE,0xFF,
+0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFE, 0xFF,0xFF,0xFF,0xFF,0xE7,0xFF,0xB5,0xAE,
+0xFF,0xFF,0xB6,0xFE,0xBF,0xFF,0xFF,0xBF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0x27,0xFF,0xEF,0xFE,0x7F,0xDF,0xFF, 0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFD,0xFF,0xF7,0xF9,0x9F,0xFF, 0x5F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,
+0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x0F,0xFF,0xE7,0xBF,0xFE,
+0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFC,0xBF, 0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xC4,
+0x6B,0xFF,0x29,0x1F,0xFB,0xAF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xEF,0x1B,0xFE,0xFF,0xFC,
+0x6F,0xFF,0xFF,0xFD,0x6A,0xF7,0xD7,0xF5, 0xBF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFE,0xBF,0xFF,0xFF,0xFA,0xFF,0xFF,0xF7, 0xFB,0xDD,0xBF,0xFF,0xE7,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0x7F,0xFF, 0xFF,0xF5,0xFF,0xFF,0xF7,0xFD,0xB3,0xEF,
+0xFD,0x7E,0x5D,0xFF,0xFD,0xFF,0xFF,0xFF, 0xFD,0x7F,0xD2,0xF5,0xFB,0x7E,0xCB,0xB7,
+0xFF,0xFF,0xFF,0xC6,0xFF,0xFD,0xEE,0x63, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,0xFD,0x65,
+0x5B,0xDF,0xFF,0xD5,0xFF,0xFF,0xFF,0xF6, 0xE7,0xBF,0xF7,0xA9,0xFF,0xFF,0xED,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,0xFF,0xFF, 0xAF,0xFF,0xFF,0xFF,0xF8,0x1B,0xFF,0xE3,
+0xD0,0xBF,0xFF,0xE1,0xFF,0xFF,0xFF,0xFF, 0xFF,0xD7,0xFF,0xFF,0xFF,0x5F,0xFF,0xFF,
+0xFF,0xFF,0xAF,0xFF,0xDB,0x76,0xBF,0xFF, 0x7F,0xFF,0xBF,0xEF,0xFE,0xFF,0xBF,0xEF,
+0xFB,0xFE,0xFF,0xFF,0xFF,0xBF,0xF2,0x7F, 0xFF,0x9F,0xFE,0xBD,0xFE,0x7F,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF7,0x3F,0xEC,0x7F,0xF6,0x95,0xBB,
+0xEF,0xF8,0xFE,0xFC,0xBF,0x2F,0xDA,0xFC, 0xBF,0x2F,0xCB,0xF2,0xFC,0xBF,0xEF,0xFF,
+0xA9,0xBF,0xCF,0xFB,0xFF,0xFF,0xFF,0xFE, 0xDD,0xB7,0x6D,0xF6,0xD9,0xB6,0x6D,0x9B,
+0x76,0xD9,0xBF,0xFB,0xFD,0xA3,0xFF,0xBF, 0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0x7F,0xDF,
+0xFD,0xEF,0x7B,0xDE,0xF7,0xFD,0xEF,0x7F, 0xFF,0xFF,0x05,0xFF,0xFA,0xFE,0x7F,0xEF,
+0xE3,0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xFF, 0xFF,0x5F,0xFF,0xFF,0xFD,0x7F,0xFB,0xAF,
+0xFF,0x63,0xC8,0xFF,0xBF,0xEF,0xFF,0xFF, 0xFA,0x7F,0xFF,0xFF,0xFF,0xFE,0x9F,0xF7,
+0xFF,0xFA,0xBF,0xFE,0x9F,0xFB,0x7F,0xFF, 0xFF,0xEF,0xD7,0xFF,0xFF,0xF5,0xFF,0xFF,
+0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xBF,0xFF, 0xF9,0xBF,0xFF,0xBE,0x27,0x9F,0xE7,0xF9,
+0xFE,0x7F,0x8B,0xE7,0xFE,0x7F,0x9F,0xE2, 0xF9,0xFE,0x7F,0x9F,0xE7,0xF1,0x7F,0xFF,
+0xFF,0xFF,0xFB,0xFE,0xFF,0xFF,0xFF,0xD7, 0xFF,0xFF,0xFF,0xFF,0xF5,0xFF,0xFF,0xFF,
+0xD7,0xFF,0xFA,0xFF,0xFE,0xFF,0xFF,0xFF, 0xFD,0xFF,0xFF,0xFF,0xAF,0xF7,0xFF,0xFF,
+0xFF,0xEB,0xFF,0xFF,0xFF,0xAF,0xFF,0xC4, 0xFF,0xF7,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF,
+0xFF,0x5F,0xFF,0xFF,0xFF,0xFF,0xD7,0xFF, 0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,0xFB,0x7A,
+0xDF,0xF7,0xFD,0xFF,0xFF,0xFE,0xBF,0xFF, 0xFF,0x7F,0xFF,0xAF,0xFF,0xFF,0xFF,0xF7,
+0xEF,0xE3,0xFF,0xDD,0xD2,0xFF,0xDF,0xFF, 0xFF,0xF2,0xFC,0xBF,0xCB,0xF6,0xFD,0xBF,
+0x2F,0xCB,0xFF,0x7F,0xDF,0xDE,0xAF,0xFF, 0xDA,0xEE,0xBF,0xAF,0xE9,0xFA,0xF4,0xBD,
+0xAF,0x5A,0xAE,0xBB,0xAB,0x6B,0xDA,0xDE, 0xBF,0xAD,0xD7,0x5E,0xFF,0xFF,0xBF,0xFC,
+0xFF,0xDF,0xFD,0xFF,0xFF,0xFF,0xFF,0xDF, 0xF7,0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFA,
+0x1F,0xFF,0xFE,0xFB,0xEF,0xBF,0xFD,0xFF, 0xFD,0xBD,0x77,0xFF,0xFF,0xFF,0xFF,0x9D,
+0xEF,0xFF,0xFF,0xFF,0xEF,0x7D,0xFF,0xFB, 0xFE,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEE, 0xBF,0xE4,0xFB,0xFF,0xFE,0x3F,0xFE,0xFF,
+0xFF,0xFF,0xFF,0xAF,0xEA,0xFE,0xBF,0xAF, 0xEB,0xFA,0xFE,0xFF,0xFF,0xFF,0x55,0xF6,
+0xFF,0xFE,0xF7,0xFF,0x7F,0xFF,0xEB,0xF7, 0x5F,0xC5,0xFD,0x7F,0x5F,0xD7,0xF5,0xFF,
+0x6F,0xFB,0xFF,0x8A,0xFF,0xFF,0xFF,0xFF, 0xEB,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0xBF,
+0xEF,0xFB,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF, 0x77,0xDF,0xFB,0xFF,0xFD,0x7F,0xEF,0xFF,
+0xFF,0xFF,0xBF,0x7F,0xFF,0xDF,0xBF,0xFF, 0xFB,0xFF,0xFF,0xFF,0xFE,0xEF,0xDF,0xFF,
+0xFE,0xFF,0x9F,0xEF,0x7D,0xFF,0xF7,0xFF, 0x7F,0xFF,0xFF,0xDF,0xF7,0xFD,0xFF,0xEF,
+0xDF,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0xFB,
+0xFD,0xFF,0xBF,0xDF,0xD1,0xFF,0xF8,0x3B, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0x7E,0xDB,0xFD,0xFF,0x77,0xDB,0xB7,0x7D, 0xBF,0xFB,0xFF,0xF8,0x7F,0xED,0x7B,0x5E,
+0xFF,0xFE,0xFF,0xFF,0x4F,0xD7,0xFD,0x7F, 0xDF,0xD7,0xF5,0xFF,0x7F,0xFF,0xFF,0xFF,
+0xF2,0x3F,0xFE,0xFF,0xBF,0xFF,0xFF,0xFF, 0xFF,0xBF,0xEF,0xFE,0xFF,0x3B,0xEE,0xFF,
+0xFC,0xEF,0xFF,0xFF,0xFF,0x85,0xFF,0xFD, 0xFE,0xFF,0xF5,0xFF,0xFF,0xFE,0xFF,0xDF,
+0xFB,0xFF,0x5F,0xBF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xA8,0xFF,0xFF,0x9F,0x9E,0xFF,
+0xFF,0xFF,0x7F,0xF3,0xFF,0xFF,0xCF,0xFF, 0xF7,0xFD,0xFF,0x7F,0xFF,0xFF,0xFC,0x16,
+0xBF,0xCF,0xA3,0xE5,0xEF,0x7F,0xFF,0xF3, 0xE4,0xFF,0xCF,0x93,0xFC,0xFF,0x3F,0xCF,
+0xFF,0xFF,0xFF,0xD6,0x0F,0x7D,0xBF,0x6E, 0xFB,0xF4,0xFC,0xAF,0x6D,0xDB,0x77,0xB7,
+0x6D,0xDB,0xF6,0xFD,0xBF,0xFF,0xFF,0xFF, 0xBF,0x9B,0xFA,0xDE,0xB7,0xB7,0xED,0xF9,
+0x7E,0xB7,0xAC,0xEB,0xD6,0xB3,0xAD,0xEB, 0x7A,0xDF,0xFF,0xFF,0xFF,0xD8,0xBF,0xFF,
+0xB7,0xED,0x9F,0x6F,0xDD,0xF7,0x68,0xDB, 0x37,0xB3,0x6C,0xDB,0x36,0xCD,0xB3,0x7F,
+0xFF,0x7F,0xF5,0x6F,0xFD,0xEF,0x79,0x3D, 0xF7,0x93,0xE4,0x7A,0x9E,0xAD,0xEA,0x7A,
+0x9E,0xF7,0xBD,0xEF,0xFF,0xFF,0xFF,0x76, 0x7F,0xFB,0xC6,0xFF,0xBB,0xEF,0xDA,0xFE,
+0xFD,0xBF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xFF,0xFB,0xFF,0xA5,0xFF,0xFD,0xAB,
+0x6F,0x78,0xDE,0x17,0x8F,0x79,0xDF,0xFD, 0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0xFF,0xFB,
+0xFF,0xFB,0xFF,0xEF,0xFB,0xEF,0xFB,0xFE, 0xFF,0xBB,0xDA,0xF3,0xEF,0x3B,0xCE,0xF3,
+0xBC,0xEF,0x3F,0xCF,0xDF,0xFF,0xB7,0xFF, 0xFF,0xFF,0xCF,0x73,0xFF,0xBF,0xEF,0xFF,
+0xF3,0xFF,0x3F,0xCF,0xF3,0xFC,0xFF,0x3D, 0xCF,0x9F,0xFE,0x07,0xFF,0xAF,0xEB,0xFE,
+0xFD,0xBF,0xEF,0xEB,0xFA,0xFF,0xAF,0xEB, 0xFA,0xFE,0xBF,0xAF,0xFB,0xFE,0x3F,0xFB,
+0x9B,0xFF,0x7F,0xDF,0xFF,0xF3,0xFE,0xFF, 0xDE,0xF7,0xBF,0x7B,0xDE,0xF7,0xBD,0xEF,
+0x7B,0xFE,0xFF,0xFF,0xDF,0x3F,0xFE,0xFF, 0xB7,0xFF,0xEF,0xF7,0xFF,0xBF,0xED,0xFE,
+0xDF,0xB7,0xED,0xFB,0x7E,0xDF,0xFF,0xFF, 0xFF,0xFD,0x5F,0xEF,0xEB,0xFA,0xFE,0xF5,
+0xBF,0x6F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xF8,0xFF,0xA8,0xFF,
+0xFF,0xBF,0xEF,0xFB,0x6A,0xFB,0xB7,0xEF, 0xFB,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,
+0xEF,0xFB,0xFF,0xE0,0xFF,0xFF,0xFD,0x7F, 0x5C,0xD7,0x7D,0xDF,0xF3,0x5C,0xF5,0xCD,
+0x73,0x5E,0xD7,0xB5,0xFD,0x7F,0xEF,0xFF, 0xDB,0xFF,0xFF,0xE2,0xF8,0xBE,0x2F,0x8F,
+0xE7,0xF8,0xBE,0x6B,0xE2,0xF8,0xBE,0x2F, 0x8B,0xE2,0xF9,0xFE,0x7F,0xE7,0xFF,0xD7,
+0xF5,0xFD,0x7F,0xFF,0xF7,0xF5,0xFD,0x7F, 0xD7,0xF5,0xFD,0x7F,0x5F,0xD7,0xF5,0xFF,
+0xFF,0xFF,0x8F,0xFF,0xAF,0xEB,0xFA,0xFF, 0xFF,0xBF,0xEB,0xFA,0xFF,0x2F,0xEB,0xFA,
+0xFE,0xBF,0xAF,0xEB,0xFF,0xFF,0xFE,0x5F, 0xFF,0x5F,0xFF,0xFF,0xFD,0xFF,0xFF,0xD7,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xBF,0xFE,0xB7,0xFD,
+0xFF,0x7E,0xDF,0xF7,0xAD,0xFF,0x7F,0xF7, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,
+0xF6,0x7F,0xFF,0xFF,0xFF,0xDB,0xF6,0xFC, 0xAF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xEC,0xBF,0xFF, 0xAF,0xEB,0xFA,0xF6,0xAB,0x8F,0xEB,0xFA,
+0xF7,0xA5,0xEB,0xFA,0xBE,0xBF,0xAF,0xEB, 0xFA,0xFF,0x6D,0xFF,0xFF,0x7F,0xDF,0x33,
+0xDD,0xFF,0x7F,0xFE,0xF7,0xFC,0x7F,0xFB, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xA9,
+0xFF,0xFD,0xFF,0xFF,0xFE,0xFF,0xFF,0xDF, 0xFF,0xFF,0xEF,0xEF,0xFD,0xFF,0x7F,0xFF,
+0xFF,0xFF,0xFF,0xFE,0xA7,0xFF,0xFF,0xFF, 0x77,0xDF,0xF7,0xFD,0x9F,0x7F,0xFE,0x77,
+0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xAF,0xBF,0xAF,0xFF,0xF9,0xBE,0xBF,
+0x8F,0xFB,0xFE,0xFE,0xEF,0xFB,0xFE,0xFF, 0xBF,0xEF,0xFB,0xFF,0xFF,0xFD,0xDF,0x6F,
+0xEF,0xFF,0x7F,0xFF,0xBF,0xBF,0xDF,0xFF, 0xFC,0xFF,0xDF,0xF7,0xFD,0xEF,0x7F,0xDF,
+0xFF,0xFF,0xFF,0x3F,0xF6,0xFF,0xCF,0xFF, 0xDB,0xFB,0xF7,0xFF,0xEB,0x7A,0xFF,0xFF,
+0xFF,0xBF,0xEF,0xFB,0xFF,0xFF,0xFF,0xFE, 0x6D,0xFD,0xFF,0x5F,0xFB,0xFF,0xFF,0xF7,
+0xFF,0x5F,0xF5,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xF8,0xFF,0xFB,0xFF,
+0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xE7,0xF6, 0xBF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,
+0xFF,0xC9,0xFF,0xFF,0xFF,0xBD,0xFF,0xBF, 0xAF,0xEF,0xEF,0x3F,0xD1,0xFC,0x7F,0xFB,
+0xC7,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3,0xFF, 0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0x77,0xFF,
+0xDF,0xB7,0xFD,0xF7,0xFD,0xF7,0xFF,0xFF, 0xFF,0xFF,0xFF,0x57,0xFF,0xF7,0xA5,0xFD,
+0x3F,0xDF,0xBF,0xBF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xDF,0xFA,0xFD,0xFF,0xFF,0xFF,0xFE,
+0x87,0xFF,0xE9,0xFF,0xFE,0xEF,0xBF,0xEF, 0xFE,0xFE,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFA,0x9F,0xFF,0x3F, 0xFF,0xFD,0xFD,0x57,0xDF,0xFD,0xF3,0xFF,
+0xDF,0xFD,0xFF,0x5F,0xDF,0xF5,0xFD,0xFF, 0xFF,0xF9,0x8F,0xFF,0xFF,0xFF,0xEE,0x7F,
+0xFF,0xFF,0xBF,0x5E,0xFE,0xEC,0xFB,0x3F, 0x7F,0x9F,0xEF,0xF9,0xFF,0xFF,0xCD,0x6B,
+0xFF,0xFF,0xFF,0xC5,0xF3,0xFC,0xFA,0x38, 0xFF,0xAF,0x3F,0xEE,0x7F,0x9F,0xFF,0xD9,
+0xFF,0xFF,0xFD,0x7A,0xF7,0xFF,0xF3,0xFF, 0xAF,0x6F,0xDB,0xF2,0xB9,0xE9,0xFB,0xFF,
+0xFF,0xFF,0xFE,0xFF,0xFF,0xEF,0xFF,0xFB, 0xC5,0xBF,0xFF,0xEF,0xFF,0x5E,0xB7,0xAD,
+0xCD,0x79,0x7C,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0x93,0xFF,0xEF,
+0xEA,0xFE,0xBF,0xEF,0x5B,0xD2,0xCD,0xF5, 0x6D,0x77,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,
+0xFF,0xFF,0x66,0xFF,0xD5,0x65,0x7D,0x5F, 0x75,0x9D,0x65,0x7F,0xD6,0xFB,0x4F,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,0xC7, 0xFF,0xBF,0xEF,0xFA,0xFE,0xFF,0xBF,0xEB,
+0xFF,0xDF,0xFF,0x7E,0xFF,0xFF,0xEF,0xFD, 0x7E,0xD7,0xFF,0x78,0xDF,0xFF,0x5F,0xDF,
+0xF5,0xBF,0x7F,0xDF,0xC5,0xFF,0x3F,0xF6, 0x7E,0xFF,0x0F,0xEF,0xF2,0x3E,0xBF,0xFF,
+0xFB,0x3F,0xFF,0xFB,0x7F,0xFF,0xB3,0xFE, 0xFB,0xF6,0xFD,0xFF,0xDA,0xF7,0xFD,0xFF,
+0x7F,0xDF,0xF7,0xBF,0xFF,0xFA,0x7F,0xFF, 0xFF,0xFF,0xFF,0x9F,0xFF,0xF3,0xDC,0xF9,
+0xBF,0xCE,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7, 0xFF,0xFF,0xE2,0x7F,0xFE,0xFF,0xBF,0xEF,
+0xEB,0xFA,0xFF,0x9F,0x67,0x1E,0xFF,0x8F, 0xE7,0xF8,0xFE,0x7F,0x8F,0xEF,0xFF,0xBD,
+0xBF,0xFF,0xFB,0xFF,0xFF,0xDF,0xF7,0xFF, 0xFC,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFD,0xB3,0xFF,0xFF,0xEF, 0xFF,0xFF,0xBF,0xED,0xFF,0xFB,0xEE,0xFE,
+0xFF,0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xFF, 0xFF,0xB5,0xFF,0xB7,0xFD,0xFD,0x6E,0xFF,
+0xFF,0xFE,0xFD,0x2F,0xD8,0xFE,0xBF,0x8F, 0xEB,0xF9,0xFE,0x3F,0xFF,0xFA,0xCF,0xFF,
+0xE7,0xD9,0xFA,0xBF,0xDF,0x77,0xFC,0xFB, 0x3F,0xAB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,
+0xFF,0xFF,0xEE,0x1F,0xFF,0xDF,0xF7,0xFF, 0xFF,0xFF,0x5F,0x97,0x35,0xBF,0x5E,0xFE,
+0xBF,0xEF,0xFF,0xF7,0xFD,0xFF,0xFF,0xFA, 0xBF,0xFF,0xBE,0x6F,0x9F,0xE7,0xF8,0xBE,
+0x2F,0x8B,0x66,0x94,0x7D,0x9D,0xE7,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF1,0x7F,0xFF,0xFF,
+0xFF,0xF7,0xF5,0xFD,0x7F,0x5F,0xFB,0xFD, 0x9E,0xFF,0xFB,0xFE,0xFF,0xFF,0xEF,0xFF,
+0xFF,0xA0,0xFF,0xFF,0xFF,0xBF,0xEF,0xEB, 0xFA,0xFE,0xBF,0xB7,0xF7,0xF7,0xFF,0xFF,
+0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xDD,0xFF, 0xFD,0xFF,0xFF,0xFF,0xD7,0xFF,0xFF,0xFF,
+0x7F,0xF5,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF, 0xBF,0xFF,0xFF,0xAB,0xFE,0xFB,0xFE,0xFF,
+0xF7,0xAF,0xFF,0xFF,0xDE,0xF7,0xEB,0x5F, 0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF,0xFF,
+0xB3,0xFF,0xC9,0xFE,0xFF,0xFF,0xFF,0xFF, 0xD6,0xFF,0xFF,0xCB,0xFF,0xFF,0xDF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFC,0x8F,0xFF,0xBA, 0xBE,0xBF,0xAF,0xEB,0x78,0xFE,0xB7,0xAD,
+0x3A,0xFE,0xB7,0xAF,0xEB,0x7A,0xFE,0xBF, 0xAF,0xFF,0x9F,0xFF,0xFF,0xDF,0xFC,0xFF,
+0xFF,0xFE,0xC3,0xFE,0xFF,0xFF,0x33,0xFC, 0xFF,0xBF,0xDF,0xF3,0xFF,0xFF,0xBB,0x9F,
+0xFF,0xFF,0xFF,0xEB,0xDF,0xFF,0xFF,0xAF, 0xF7,0x6F,0xF9,0xBF,0xEF,0xFD,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xE3,0x7F,0xFF,0xFF,0xFF, 0xFB,0xFF,0xFF,0xBF,0xFD,0xFB,0xF7,0xFF,
+0xDF,0xF7,0xFF,0xFE,0xEF,0x5F,0xBD,0xFF, 0xFA,0xFF,0xF8,0xFF,0xBF,0xAF,0xFB,0xFE,
+0xFE,0x3F,0xEF,0xE8,0xFF,0xDF,0xF3,0xFD, 0xFF,0xFF,0xFF,0xFF,0xFF,0xED,0xFF,0xFB,
+0xFD,0xFF,0xAF,0xFF,0xFF,0xFE,0xFE,0xBF, 0xDB,0xFF,0xFF,0xFF,0xBF,0xFF,0xDF,0xFF,
+0xFD,0xFF,0xCB,0xFF,0xFF,0xFF,0xFF,0xFF, 0xBF,0x6F,0xFF,0x7F,0xB7,0xB3,0xFF,0xFF,
+0xDF,0xFF,0xFB,0xEF,0xFF,0xFF,0xFF,0x07, 0xFF,0xFB,0xFF,0xFF,0xFF,0xED,0xFF,0xF5,
+0x7C,0xFF,0x7F,0xFE,0xFF,0xFF,0xEF,0xCF, 0xFF,0xFB,0xFF,0xFF,0x2F,0xFF,0xFF,0xFF,
+0xFF,0xF3,0xFF,0xFB,0xFF,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,
+0xFD,0x1B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7C,0xFF,0xFF,0xFF,0xFF,
+0xEF,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0x7F, 0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xDB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, 0xFF,0xFF,0xF0,0x7F,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xDF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xFE,
+0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xEF,0xFE,0xFF,0xBF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xEF,0xFA,0xB5,0xFF,0xFF,0xFF, 0xF7,0xF7,0xFF,0xFF,0xFF,0xFF,0xDF,0xFB,
+0xFC,0xFF,0xFF,0xFE,0xFF,0x7F,0xDF,0xBF, 0xFF,0xCB,0xBF,0xF9,0xFE,0x7F,0x9F,0xE7,
+0xF9,0xFE,0x7F,0x97,0xE1,0xFE,0x79,0x9F, 0xE7,0xFD,0xFE,0x7F,0xDF,0xFE,0x37,0xFF,
+0xFB,0xDE,0xDE,0xBD,0xEF,0xF3,0xFE,0xFB, 0xAF,0xEB,0xFE,0xFF,0xFF,0xCF,0xFF,0xFE,
+0xFF,0xBF,0xFF,0x8F,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xE7,0xF9,0x5E,0x7F,0xEF,0xFB,
+0xDA,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFD, 0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDF,
+0xFF,0xFF,0x7F,0xFF,0xFF,0xF7,0xFB,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFC,0x3F,0xFF,0xBF,
+0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0x7B,0x7F, 0xBF,0xEF,0xFB,0xFE,0xFF,0xB5,0xEF,0xFB,
+0xBF,0xFA,0x7F,0xFC,0xFF,0x3F,0xCF,0xF3, 0xFC,0xFF,0x3F,0xCF,0xBC,0xFF,0x3F,0xEF,
+0xF3,0xFC,0xFE,0x3F,0xCF,0xFF,0xEE,0xEF, 0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0x6A,0xD7,
+0xB7,0xFB,0xF8,0xFF,0xB7,0xEF,0xBA,0xFE, 0xFF,0xBF,0x7F,0xE9,0xFF,0xF9,0x7E,0x5F,
+0x97,0xE5,0xF9,0xFE,0x7F,0xBF,0xF9,0x7E, 0x5F,0x9F,0xE5,0xFB,0xFE,0x5F,0xB7,0xFF,
+0xA3,0xFF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0x5E,0xF7,0x7D,0xFF,0x77,0xDF,
+0xF7,0xFD,0xFF,0x7F,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xDF,0xFB,0x7F,
+0xFF,0xFF,0xEF,0xFF,0xFE,0xFB,0xFF,0xFF, 0xBF,0xFE,0x8F,0xFF,0xDF,0xF7,0xFD,0xFD,
+0x7F,0xDF,0xF7,0xFD,0x3E,0xDF,0xF5,0xBD, 0xFF,0x7F,0xDF,0xF7,0xFD,0xF7,0xFF,0x9F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFD,0xFF,0xBE,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFD,0x3F,0xFF,0xDF,0xF7, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0xCF,
+0x77,0xFC,0xFF,0x5F,0xDF,0xF7,0xFD,0xFF, 0xF4,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFD,0xFF,0xFF,0xFF,0xEE,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xED,0xFB,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xE9,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFB,0xFF,0xFF,0xFF,0xD3,0xFF,0xFF,
+0xBF,0x3F,0xFB,0xFF,0xFF,0xFF,0xFB,0xF3, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xF7, 0xFF,0xFF,0xFF,0xFF,0x17,0xFF,0xFF,0xFF,
+0xDF,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF, 0xDF,0xDF,0xFF,0xFD,0xFF,0xFF,0xDF,0xF7,
+0xFF,0x4F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFD,
+0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0x9F,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF, 0xFF,0xFF,0x7A,0x3F,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF2,
+0x7F,0xFF,0xFB,0xFE,0xFF,0xBF,0xEF,0xF8, 0xFE,0xFF,0xBF,0xFB,0xFE,0xFF,0x8F,0xEC,
+0xFB,0xFE,0xFF,0xBF,0xF8,0xF7,0xFE,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFD,0xBF,0xCF,0xEC,
+0xFF,0x3F,0xEF,0xDB,0xF8,0xFF,0xBF,0xCF, 0xFF,0xF9,0xFF,0xFF,0xBF,0xFF,0xFB,0xFF,
+0xFF,0xFF,0xEF,0xFB,0xDF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xBB,0xFF,
+0xEF,0xFB,0xFE,0xEF,0xBF,0xEE,0xEB,0xFB, 0xFE,0xFF,0xEF,0xFE,0xEE,0xBF,0xFE,0xEB,
+0xFF,0xEF,0xFF,0x17,0xFF,0x7E,0xEB,0xBB, 0xFE,0xBF,0xBE,0xFB,0xEF,0x5B,0xF7,0xBD,
+0xFB,0xCF,0xBF,0xBF,0xBB,0xFB,0x7E,0xCC, 0xEF,0xFF
+
+};
diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c
new file mode 100644
index 000000000000..1774ab7a40d2
--- /dev/null
+++ b/drivers/media/video/dabusb.c
@@ -0,0 +1,874 @@
+/*****************************************************************************/
+
+/*
+ * dabusb.c -- dab usb driver.
+ *
+ * Copyright (C) 1999 Deti Fliegl (deti@fliegl.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *
+ * $Id: dabusb.c,v 1.54 2000/07/24 21:39:39 deti Exp $
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/delay.h>
+#include <linux/usb.h>
+#include <linux/smp_lock.h>
+#include <linux/mutex.h>
+
+#include "dabusb.h"
+#include "dabfirmware.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.54"
+#define DRIVER_AUTHOR "Deti Fliegl, deti@fliegl.de"
+#define DRIVER_DESC "DAB-USB Interface Driver for Linux (c)1999"
+
+/* --------------------------------------------------------------------- */
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define NRDABUSB 256
+#else
+#define NRDABUSB 4
+#endif
+
+/*-------------------------------------------------------------------*/
+
+static dabusb_t dabusb[NRDABUSB];
+static int buffers = 256;
+static struct usb_driver dabusb_driver;
+
+/*-------------------------------------------------------------------*/
+
+static int dabusb_add_buf_tail (pdabusb_t s, struct list_head *dst, struct list_head *src)
+{
+ unsigned long flags;
+ struct list_head *tmp;
+ int ret = 0;
+
+ spin_lock_irqsave (&s->lock, flags);
+
+ if (list_empty (src)) {
+ // no elements in source buffer
+ ret = -1;
+ goto err;
+ }
+ tmp = src->next;
+ list_move_tail (tmp, dst);
+
+ err: spin_unlock_irqrestore (&s->lock, flags);
+ return ret;
+}
+/*-------------------------------------------------------------------*/
+#ifdef DEBUG
+static void dump_urb (struct urb *urb)
+{
+ dbg("urb :%p", urb);
+ dbg("dev :%p", urb->dev);
+ dbg("pipe :%08X", urb->pipe);
+ dbg("status :%d", urb->status);
+ dbg("transfer_flags :%08X", urb->transfer_flags);
+ dbg("transfer_buffer :%p", urb->transfer_buffer);
+ dbg("transfer_buffer_length:%d", urb->transfer_buffer_length);
+ dbg("actual_length :%d", urb->actual_length);
+ dbg("setup_packet :%p", urb->setup_packet);
+ dbg("start_frame :%d", urb->start_frame);
+ dbg("number_of_packets :%d", urb->number_of_packets);
+ dbg("interval :%d", urb->interval);
+ dbg("error_count :%d", urb->error_count);
+ dbg("context :%p", urb->context);
+ dbg("complete :%p", urb->complete);
+}
+#endif
+/*-------------------------------------------------------------------*/
+static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q)
+{
+ unsigned long flags;
+ pbuff_t b;
+
+ dbg("dabusb_cancel_queue");
+
+ spin_lock_irqsave (&s->lock, flags);
+
+ list_for_each_entry(b, q, buff_list) {
+#ifdef DEBUG
+ dump_urb(b->purb);
+#endif
+ usb_unlink_urb (b->purb);
+ }
+ spin_unlock_irqrestore (&s->lock, flags);
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+static int dabusb_free_queue (struct list_head *q)
+{
+ struct list_head *tmp;
+ struct list_head *p;
+ pbuff_t b;
+
+ dbg("dabusb_free_queue");
+ for (p = q->next; p != q;) {
+ b = list_entry (p, buff_t, buff_list);
+
+#ifdef DEBUG
+ dump_urb(b->purb);
+#endif
+ kfree(b->purb->transfer_buffer);
+ usb_free_urb(b->purb);
+ tmp = p->next;
+ list_del (p);
+ kfree (b);
+ p = tmp;
+ }
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+static int dabusb_free_buffers (pdabusb_t s)
+{
+ unsigned long flags;
+ dbg("dabusb_free_buffers");
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ dabusb_free_queue (&s->free_buff_list);
+ dabusb_free_queue (&s->rec_buff_list);
+
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ s->got_mem = 0;
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+static void dabusb_iso_complete (struct urb *purb, struct pt_regs *regs)
+{
+ pbuff_t b = purb->context;
+ pdabusb_t s = b->s;
+ int i;
+ int len;
+ int dst = 0;
+ void *buf = purb->transfer_buffer;
+
+ dbg("dabusb_iso_complete");
+
+ // process if URB was not killed
+ if (purb->status != -ENOENT) {
+ unsigned int pipe = usb_rcvisocpipe (purb->dev, _DABUSB_ISOPIPE);
+ int pipesize = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe));
+ for (i = 0; i < purb->number_of_packets; i++)
+ if (!purb->iso_frame_desc[i].status) {
+ len = purb->iso_frame_desc[i].actual_length;
+ if (len <= pipesize) {
+ memcpy (buf + dst, buf + purb->iso_frame_desc[i].offset, len);
+ dst += len;
+ }
+ else
+ err("dabusb_iso_complete: invalid len %d", len);
+ }
+ else
+ warn("dabusb_iso_complete: corrupted packet status: %d", purb->iso_frame_desc[i].status);
+ if (dst != purb->actual_length)
+ err("dst!=purb->actual_length:%d!=%d", dst, purb->actual_length);
+ }
+
+ if (atomic_dec_and_test (&s->pending_io) && !s->remove_pending && s->state != _stopped) {
+ s->overruns++;
+ err("overrun (%d)", s->overruns);
+ }
+ wake_up (&s->wait);
+}
+/*-------------------------------------------------------------------*/
+static int dabusb_alloc_buffers (pdabusb_t s)
+{
+ int buffers = 0;
+ pbuff_t b;
+ unsigned int pipe = usb_rcvisocpipe (s->usbdev, _DABUSB_ISOPIPE);
+ int pipesize = usb_maxpacket (s->usbdev, pipe, usb_pipeout (pipe));
+ int packets = _ISOPIPESIZE / pipesize;
+ int transfer_buffer_length = packets * pipesize;
+ int i;
+
+ dbg("dabusb_alloc_buffers pipesize:%d packets:%d transfer_buffer_len:%d",
+ pipesize, packets, transfer_buffer_length);
+
+ while (buffers < (s->total_buffer_size << 10)) {
+ b = (pbuff_t) kzalloc (sizeof (buff_t), GFP_KERNEL);
+ if (!b) {
+ err("kzalloc(sizeof(buff_t))==NULL");
+ goto err;
+ }
+ b->s = s;
+ b->purb = usb_alloc_urb(packets, GFP_KERNEL);
+ if (!b->purb) {
+ err("usb_alloc_urb == NULL");
+ kfree (b);
+ goto err;
+ }
+
+ b->purb->transfer_buffer = kmalloc (transfer_buffer_length, GFP_KERNEL);
+ if (!b->purb->transfer_buffer) {
+ kfree (b->purb);
+ kfree (b);
+ err("kmalloc(%d)==NULL", transfer_buffer_length);
+ goto err;
+ }
+
+ b->purb->transfer_buffer_length = transfer_buffer_length;
+ b->purb->number_of_packets = packets;
+ b->purb->complete = dabusb_iso_complete;
+ b->purb->context = b;
+ b->purb->dev = s->usbdev;
+ b->purb->pipe = pipe;
+ b->purb->transfer_flags = URB_ISO_ASAP;
+
+ for (i = 0; i < packets; i++) {
+ b->purb->iso_frame_desc[i].offset = i * pipesize;
+ b->purb->iso_frame_desc[i].length = pipesize;
+ }
+
+ buffers += transfer_buffer_length;
+ list_add_tail (&b->buff_list, &s->free_buff_list);
+ }
+ s->got_mem = buffers;
+
+ return 0;
+
+ err:
+ dabusb_free_buffers (s);
+ return -ENOMEM;
+}
+/*-------------------------------------------------------------------*/
+static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb)
+{
+ int ret;
+ unsigned int pipe;
+ int actual_length;
+
+ dbg("dabusb_bulk");
+
+ if (!pb->pipe)
+ pipe = usb_rcvbulkpipe (s->usbdev, 2);
+ else
+ pipe = usb_sndbulkpipe (s->usbdev, 2);
+
+ ret=usb_bulk_msg(s->usbdev, pipe, pb->data, pb->size, &actual_length, 100);
+ if(ret<0) {
+ err("dabusb: usb_bulk_msg failed(%d)",ret);
+
+ if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) {
+ err("set_interface failed");
+ return -EINVAL;
+ }
+
+ }
+
+ if( ret == -EPIPE ) {
+ warn("CLEAR_FEATURE request to remove STALL condition.");
+ if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe)))
+ err("request failed");
+ }
+
+ pb->size = actual_length;
+ return ret;
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_writemem (pdabusb_t s, int pos, unsigned char *data, int len)
+{
+ int ret;
+ unsigned char *transfer_buffer = kmalloc (len, GFP_KERNEL);
+
+ if (!transfer_buffer) {
+ err("dabusb_writemem: kmalloc(%d) failed.", len);
+ return -ENOMEM;
+ }
+
+ memcpy (transfer_buffer, data, len);
+
+ ret=usb_control_msg(s->usbdev, usb_sndctrlpipe( s->usbdev, 0 ), 0xa0, 0x40, pos, 0, transfer_buffer, len, 300);
+
+ kfree (transfer_buffer);
+ return ret;
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_8051_reset (pdabusb_t s, unsigned char reset_bit)
+{
+ dbg("dabusb_8051_reset: %d",reset_bit);
+ return dabusb_writemem (s, CPUCS_REG, &reset_bit, 1);
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_loadmem (pdabusb_t s, const char *fname)
+{
+ int ret;
+ PINTEL_HEX_RECORD ptr = firmware;
+
+ dbg("Enter dabusb_loadmem (internal)");
+
+ ret = dabusb_8051_reset (s, 1);
+ while (ptr->Type == 0) {
+
+ dbg("dabusb_writemem: %04X %p %d)", ptr->Address, ptr->Data, ptr->Length);
+
+ ret = dabusb_writemem (s, ptr->Address, ptr->Data, ptr->Length);
+ if (ret < 0) {
+ err("dabusb_writemem failed (%d %04X %p %d)", ret, ptr->Address, ptr->Data, ptr->Length);
+ break;
+ }
+ ptr++;
+ }
+ ret = dabusb_8051_reset (s, 0);
+
+ dbg("dabusb_loadmem: exit");
+
+ return ret;
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_fpga_clear (pdabusb_t s, pbulk_transfer_t b)
+{
+ b->size = 4;
+ b->data[0] = 0x2a;
+ b->data[1] = 0;
+ b->data[2] = 0;
+ b->data[3] = 0;
+
+ dbg("dabusb_fpga_clear");
+
+ return dabusb_bulk (s, b);
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_fpga_init (pdabusb_t s, pbulk_transfer_t b)
+{
+ b->size = 4;
+ b->data[0] = 0x2c;
+ b->data[1] = 0;
+ b->data[2] = 0;
+ b->data[3] = 0;
+
+ dbg("dabusb_fpga_init");
+
+ return dabusb_bulk (s, b);
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_fpga_download (pdabusb_t s, const char *fname)
+{
+ pbulk_transfer_t b = kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL);
+ unsigned int blen, n;
+ int ret;
+ unsigned char *buf = bitstream;
+
+ dbg("Enter dabusb_fpga_download (internal)");
+
+ if (!b) {
+ err("kmalloc(sizeof(bulk_transfer_t))==NULL");
+ return -ENOMEM;
+ }
+
+ b->pipe = 1;
+ ret = dabusb_fpga_clear (s, b);
+ mdelay (10);
+ blen = buf[73] + (buf[72] << 8);
+
+ dbg("Bitstream len: %i", blen);
+
+ b->data[0] = 0x2b;
+ b->data[1] = 0;
+ b->data[2] = 0;
+ b->data[3] = 60;
+
+ for (n = 0; n <= blen + 60; n += 60) {
+ // some cclks for startup
+ b->size = 64;
+ memcpy (b->data + 4, buf + 74 + n, 60);
+ ret = dabusb_bulk (s, b);
+ if (ret < 0) {
+ err("dabusb_bulk failed.");
+ break;
+ }
+ mdelay (1);
+ }
+
+ ret = dabusb_fpga_init (s, b);
+ kfree (b);
+
+ dbg("exit dabusb_fpga_download");
+
+ return ret;
+}
+
+static int dabusb_stop (pdabusb_t s)
+{
+ dbg("dabusb_stop");
+
+ s->state = _stopped;
+ dabusb_cancel_queue (s, &s->rec_buff_list);
+
+ dbg("pending_io: %d", s->pending_io.counter);
+
+ s->pending_io.counter = 0;
+ return 0;
+}
+
+static int dabusb_startrek (pdabusb_t s)
+{
+ if (!s->got_mem && s->state != _started) {
+
+ dbg("dabusb_startrek");
+
+ if (dabusb_alloc_buffers (s) < 0)
+ return -ENOMEM;
+ dabusb_stop (s);
+ s->state = _started;
+ s->readptr = 0;
+ }
+
+ if (!list_empty (&s->free_buff_list)) {
+ pbuff_t end;
+ int ret;
+
+ while (!dabusb_add_buf_tail (s, &s->rec_buff_list, &s->free_buff_list)) {
+
+ dbg("submitting: end:%p s->rec_buff_list:%p", s->rec_buff_list.prev, &s->rec_buff_list);
+
+ end = list_entry (s->rec_buff_list.prev, buff_t, buff_list);
+
+ ret = usb_submit_urb (end->purb, GFP_KERNEL);
+ if (ret) {
+ err("usb_submit_urb returned:%d", ret);
+ if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list))
+ err("startrek: dabusb_add_buf_tail failed");
+ break;
+ }
+ else
+ atomic_inc (&s->pending_io);
+ }
+ dbg("pending_io: %d",s->pending_io.counter);
+ }
+
+ return 0;
+}
+
+static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, loff_t * ppos)
+{
+ pdabusb_t s = (pdabusb_t) file->private_data;
+ unsigned long flags;
+ unsigned ret = 0;
+ int rem;
+ int cnt;
+ pbuff_t b;
+ struct urb *purb = NULL;
+
+ dbg("dabusb_read");
+
+ if (*ppos)
+ return -ESPIPE;
+
+ if (s->remove_pending)
+ return -EIO;
+
+
+ if (!s->usbdev)
+ return -EIO;
+
+ while (count > 0) {
+ dabusb_startrek (s);
+
+ spin_lock_irqsave (&s->lock, flags);
+
+ if (list_empty (&s->rec_buff_list)) {
+
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ err("error: rec_buf_list is empty");
+ goto err;
+ }
+
+ b = list_entry (s->rec_buff_list.next, buff_t, buff_list);
+ purb = b->purb;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ if (purb->status == -EINPROGRESS) {
+ if (file->f_flags & O_NONBLOCK) // return nonblocking
+ {
+ if (!ret)
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ interruptible_sleep_on (&s->wait);
+
+ if (signal_pending (current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ goto err;
+ }
+
+ spin_lock_irqsave (&s->lock, flags);
+
+ if (list_empty (&s->rec_buff_list)) {
+ spin_unlock_irqrestore(&s->lock, flags);
+ err("error: still no buffer available.");
+ goto err;
+ }
+ spin_unlock_irqrestore(&s->lock, flags);
+ s->readptr = 0;
+ }
+ if (s->remove_pending) {
+ ret = -EIO;
+ goto err;
+ }
+
+ rem = purb->actual_length - s->readptr; // set remaining bytes to copy
+
+ if (count >= rem)
+ cnt = rem;
+ else
+ cnt = count;
+
+ dbg("copy_to_user:%p %p %d",buf, purb->transfer_buffer + s->readptr, cnt);
+
+ if (copy_to_user (buf, purb->transfer_buffer + s->readptr, cnt)) {
+ err("read: copy_to_user failed");
+ if (!ret)
+ ret = -EFAULT;
+ goto err;
+ }
+
+ s->readptr += cnt;
+ count -= cnt;
+ buf += cnt;
+ ret += cnt;
+
+ if (s->readptr == purb->actual_length) {
+ // finished, take next buffer
+ if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list))
+ err("read: dabusb_add_buf_tail failed");
+ s->readptr = 0;
+ }
+ }
+ err: //mutex_unlock(&s->mutex);
+ return ret;
+}
+
+static int dabusb_open (struct inode *inode, struct file *file)
+{
+ int devnum = iminor(inode);
+ pdabusb_t s;
+
+ if (devnum < DABUSB_MINOR || devnum >= (DABUSB_MINOR + NRDABUSB))
+ return -EIO;
+
+ s = &dabusb[devnum - DABUSB_MINOR];
+
+ dbg("dabusb_open");
+ mutex_lock(&s->mutex);
+
+ while (!s->usbdev || s->opened) {
+ mutex_unlock(&s->mutex);
+
+ if (file->f_flags & O_NONBLOCK) {
+ return -EBUSY;
+ }
+ msleep_interruptible(500);
+
+ if (signal_pending (current)) {
+ return -EAGAIN;
+ }
+ mutex_lock(&s->mutex);
+ }
+ if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) {
+ mutex_unlock(&s->mutex);
+ err("set_interface failed");
+ return -EINVAL;
+ }
+ s->opened = 1;
+ mutex_unlock(&s->mutex);
+
+ file->f_pos = 0;
+ file->private_data = s;
+
+ return nonseekable_open(inode, file);
+}
+
+static int dabusb_release (struct inode *inode, struct file *file)
+{
+ pdabusb_t s = (pdabusb_t) file->private_data;
+
+ dbg("dabusb_release");
+
+ mutex_lock(&s->mutex);
+ dabusb_stop (s);
+ dabusb_free_buffers (s);
+ mutex_unlock(&s->mutex);
+
+ if (!s->remove_pending) {
+ if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0)
+ err("set_interface failed");
+ }
+ else
+ wake_up (&s->remove_ok);
+
+ s->opened = 0;
+ return 0;
+}
+
+static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ pdabusb_t s = (pdabusb_t) file->private_data;
+ pbulk_transfer_t pbulk;
+ int ret = 0;
+ int version = DABUSB_VERSION;
+
+ dbg("dabusb_ioctl");
+
+ if (s->remove_pending)
+ return -EIO;
+
+ mutex_lock(&s->mutex);
+
+ if (!s->usbdev) {
+ mutex_unlock(&s->mutex);
+ return -EIO;
+ }
+
+ switch (cmd) {
+
+ case IOCTL_DAB_BULK:
+ pbulk = (pbulk_transfer_t) kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL);
+
+ if (!pbulk) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ if (copy_from_user (pbulk, (void __user *) arg, sizeof (bulk_transfer_t))) {
+ ret = -EFAULT;
+ kfree (pbulk);
+ break;
+ }
+
+ ret=dabusb_bulk (s, pbulk);
+ if(ret==0)
+ if (copy_to_user((void __user *)arg, pbulk,
+ sizeof(bulk_transfer_t)))
+ ret = -EFAULT;
+ kfree (pbulk);
+ break;
+
+ case IOCTL_DAB_OVERRUNS:
+ ret = put_user (s->overruns, (unsigned int __user *) arg);
+ break;
+
+ case IOCTL_DAB_VERSION:
+ ret = put_user (version, (unsigned int __user *) arg);
+ break;
+
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+ mutex_unlock(&s->mutex);
+ return ret;
+}
+
+static struct file_operations dabusb_fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = dabusb_read,
+ .ioctl = dabusb_ioctl,
+ .open = dabusb_open,
+ .release = dabusb_release,
+};
+
+static struct usb_class_driver dabusb_class = {
+ .name = "dabusb%d",
+ .fops = &dabusb_fops,
+ .minor_base = DABUSB_MINOR,
+};
+
+
+/* --------------------------------------------------------------------- */
+static int dabusb_probe (struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ int retval;
+ pdabusb_t s;
+
+ dbg("dabusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d",
+ le16_to_cpu(usbdev->descriptor.idVendor),
+ le16_to_cpu(usbdev->descriptor.idProduct),
+ intf->altsetting->desc.bInterfaceNumber);
+
+ /* We don't handle multiple configurations */
+ if (usbdev->descriptor.bNumConfigurations != 1)
+ return -ENODEV;
+
+ if (intf->altsetting->desc.bInterfaceNumber != _DABUSB_IF &&
+ le16_to_cpu(usbdev->descriptor.idProduct) == 0x9999)
+ return -ENODEV;
+
+
+
+ s = &dabusb[intf->minor];
+
+ mutex_lock(&s->mutex);
+ s->remove_pending = 0;
+ s->usbdev = usbdev;
+ s->devnum = intf->minor;
+
+ if (usb_reset_configuration (usbdev) < 0) {
+ err("reset_configuration failed");
+ goto reject;
+ }
+ if (le16_to_cpu(usbdev->descriptor.idProduct) == 0x2131) {
+ dabusb_loadmem (s, NULL);
+ goto reject;
+ }
+ else {
+ dabusb_fpga_download (s, NULL);
+
+ if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) {
+ err("set_interface failed");
+ goto reject;
+ }
+ }
+ dbg("bound to interface: %d", intf->altsetting->desc.bInterfaceNumber);
+ usb_set_intfdata (intf, s);
+ mutex_unlock(&s->mutex);
+
+ retval = usb_register_dev(intf, &dabusb_class);
+ if (retval) {
+ usb_set_intfdata (intf, NULL);
+ return -ENOMEM;
+ }
+
+ return 0;
+
+ reject:
+ mutex_unlock(&s->mutex);
+ s->usbdev = NULL;
+ return -ENODEV;
+}
+
+static void dabusb_disconnect (struct usb_interface *intf)
+{
+ wait_queue_t __wait;
+ pdabusb_t s = usb_get_intfdata (intf);
+
+ dbg("dabusb_disconnect");
+
+ init_waitqueue_entry(&__wait, current);
+
+ usb_set_intfdata (intf, NULL);
+ if (s) {
+ usb_deregister_dev (intf, &dabusb_class);
+ s->remove_pending = 1;
+ wake_up (&s->wait);
+ add_wait_queue(&s->remove_ok, &__wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (s->state == _started)
+ schedule();
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&s->remove_ok, &__wait);
+
+ s->usbdev = NULL;
+ s->overruns = 0;
+ }
+}
+
+static struct usb_device_id dabusb_ids [] = {
+ // { USB_DEVICE(0x0547, 0x2131) }, /* An2131 chip, no boot ROM */
+ { USB_DEVICE(0x0547, 0x9999) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, dabusb_ids);
+
+static struct usb_driver dabusb_driver = {
+ .name = "dabusb",
+ .probe = dabusb_probe,
+ .disconnect = dabusb_disconnect,
+ .id_table = dabusb_ids,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int __init dabusb_init (void)
+{
+ int retval;
+ unsigned u;
+
+ /* initialize struct */
+ for (u = 0; u < NRDABUSB; u++) {
+ pdabusb_t s = &dabusb[u];
+ memset (s, 0, sizeof (dabusb_t));
+ mutex_init (&s->mutex);
+ s->usbdev = NULL;
+ s->total_buffer_size = buffers;
+ init_waitqueue_head (&s->wait);
+ init_waitqueue_head (&s->remove_ok);
+ spin_lock_init (&s->lock);
+ INIT_LIST_HEAD (&s->free_buff_list);
+ INIT_LIST_HEAD (&s->rec_buff_list);
+ }
+
+ /* register misc device */
+ retval = usb_register(&dabusb_driver);
+ if (retval)
+ goto out;
+
+ dbg("dabusb_init: driver registered");
+
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+
+out:
+ return retval;
+}
+
+static void __exit dabusb_cleanup (void)
+{
+ dbg("dabusb_cleanup");
+
+ usb_deregister (&dabusb_driver);
+}
+
+/* --------------------------------------------------------------------- */
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+module_param(buffers, int, 0);
+MODULE_PARM_DESC (buffers, "Number of buffers (default=256)");
+
+module_init (dabusb_init);
+module_exit (dabusb_cleanup);
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/media/video/dabusb.h b/drivers/media/video/dabusb.h
new file mode 100644
index 000000000000..96b03e4af8b9
--- /dev/null
+++ b/drivers/media/video/dabusb.h
@@ -0,0 +1,85 @@
+#define _BULK_DATA_LEN 64
+typedef struct
+{
+ unsigned char data[_BULK_DATA_LEN];
+ unsigned int size;
+ unsigned int pipe;
+}bulk_transfer_t,*pbulk_transfer_t;
+
+#define DABUSB_MINOR 240 /* some unassigned USB minor */
+#define DABUSB_VERSION 0x1000
+#define IOCTL_DAB_BULK _IOWR('d', 0x30, bulk_transfer_t)
+#define IOCTL_DAB_OVERRUNS _IOR('d', 0x15, int)
+#define IOCTL_DAB_VERSION _IOR('d', 0x3f, int)
+
+#ifdef __KERNEL__
+
+typedef enum { _stopped=0, _started } driver_state_t;
+
+typedef struct
+{
+ struct mutex mutex;
+ struct usb_device *usbdev;
+ wait_queue_head_t wait;
+ wait_queue_head_t remove_ok;
+ spinlock_t lock;
+ atomic_t pending_io;
+ driver_state_t state;
+ int remove_pending;
+ int got_mem;
+ int total_buffer_size;
+ unsigned int overruns;
+ int readptr;
+ int opened;
+ int devnum;
+ struct list_head free_buff_list;
+ struct list_head rec_buff_list;
+} dabusb_t,*pdabusb_t;
+
+typedef struct
+{
+ pdabusb_t s;
+ struct urb *purb;
+ struct list_head buff_list;
+} buff_t,*pbuff_t;
+
+typedef struct
+{
+ wait_queue_head_t wait;
+} bulk_completion_context_t, *pbulk_completion_context_t;
+
+
+#define _DABUSB_IF 2
+#define _DABUSB_ISOPIPE 0x09
+#define _ISOPIPESIZE 16384
+
+#define _BULK_DATA_LEN 64
+// Vendor specific request code for Anchor Upload/Download
+// This one is implemented in the core
+#define ANCHOR_LOAD_INTERNAL 0xA0
+
+// EZ-USB Control and Status Register. Bit 0 controls 8051 reset
+#define CPUCS_REG 0x7F92
+#define _TOTAL_BUFFERS 384
+
+#define MAX_INTEL_HEX_RECORD_LENGTH 16
+
+#ifndef _BYTE_DEFINED
+#define _BYTE_DEFINED
+typedef unsigned char BYTE;
+#endif // !_BYTE_DEFINED
+
+#ifndef _WORD_DEFINED
+#define _WORD_DEFINED
+typedef unsigned short WORD;
+#endif // !_WORD_DEFINED
+
+typedef struct _INTEL_HEX_RECORD
+{
+ BYTE Length;
+ WORD Address;
+ BYTE Type;
+ BYTE Data[MAX_INTEL_HEX_RECORD_LENGTH];
+} INTEL_HEX_RECORD, *PINTEL_HEX_RECORD;
+
+#endif
diff --git a/drivers/media/video/dsbr100.c b/drivers/media/video/dsbr100.c
new file mode 100644
index 000000000000..25646804d5be
--- /dev/null
+++ b/drivers/media/video/dsbr100.c
@@ -0,0 +1,429 @@
+/* A driver for the D-Link DSB-R100 USB radio. The R100 plugs
+ into both the USB and an analog audio input, so this thing
+ only deals with initialisation and frequency setting, the
+ audio data has to be handled by a sound driver.
+
+ Major issue: I can't find out where the device reports the signal
+ strength, and indeed the windows software appearantly just looks
+ at the stereo indicator as well. So, scanning will only find
+ stereo stations. Sad, but I can't help it.
+
+ Also, the windows program sends oodles of messages over to the
+ device, and I couldn't figure out their meaning. My suspicion
+ is that they don't have any:-)
+
+ You might find some interesting stuff about this module at
+ http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
+
+ Copyright (c) 2000 Markus Demleitner <msdemlei@cl.uni-heidelberg.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ History:
+
+ Version 0.40:
+ Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing
+
+ Version 0.30:
+ Markus: Updates for 2.5.x kernel and more ISO compliant source
+
+ Version 0.25:
+ PSL and Markus: Cleanup, radio now doesn't stop on device close
+
+ Version 0.24:
+ Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally
+ right. Some minor cleanup, improved standalone compilation
+
+ Version 0.23:
+ Markus: Sign extension bug fixed by declaring transfer_buffer unsigned
+
+ Version 0.22:
+ Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns,
+ thanks to Mike Cox for pointing the problem out.
+
+ Version 0.21:
+ Markus: Minor cleanup, warnings if something goes wrong, lame attempt
+ to adhere to Documentation/CodingStyle
+
+ Version 0.2:
+ Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
+ Markus: Copyright clarification
+
+ Version 0.01: Markus: initial release
+
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/videodev.h>
+#include <linux/usb.h>
+#include <linux/smp_lock.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.40"
+#define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
+#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
+
+#define DSB100_VENDOR 0x04b4
+#define DSB100_PRODUCT 0x1002
+
+/* Commands the device appears to understand */
+#define DSB100_TUNE 1
+#define DSB100_ONOFF 2
+
+#define TB_LEN 16
+
+/* Frequency limits in MHz -- these are European values. For Japanese
+devices, that would be 76 and 91. */
+#define FREQ_MIN 87.5
+#define FREQ_MAX 108.0
+#define FREQ_MUL 16000
+
+
+static int usb_dsbr100_probe(struct usb_interface *intf,
+ const struct usb_device_id *id);
+static void usb_dsbr100_disconnect(struct usb_interface *intf);
+static int usb_dsbr100_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static int usb_dsbr100_open(struct inode *inode, struct file *file);
+static int usb_dsbr100_close(struct inode *inode, struct file *file);
+
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+
+/* Data for one (physical) device */
+typedef struct {
+ struct usb_device *usbdev;
+ struct video_device *videodev;
+ unsigned char transfer_buffer[TB_LEN];
+ int curfreq;
+ int stereo;
+ int users;
+ int removed;
+} dsbr100_device;
+
+
+/* File system interface */
+static struct file_operations usb_dsbr100_fops = {
+ .owner = THIS_MODULE,
+ .open = usb_dsbr100_open,
+ .release = usb_dsbr100_close,
+ .ioctl = usb_dsbr100_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+/* V4L interface */
+static struct video_device dsbr100_videodev_template=
+{
+ .owner = THIS_MODULE,
+ .name = "D-Link DSB-R 100",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_AZTECH,
+ .fops = &usb_dsbr100_fops,
+ .release = video_device_release,
+};
+
+static struct usb_device_id usb_dsbr100_device_table [] = {
+ { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table);
+
+/* USB subsystem interface */
+static struct usb_driver usb_dsbr100_driver = {
+ .name = "dsbr100",
+ .probe = usb_dsbr100_probe,
+ .disconnect = usb_dsbr100_disconnect,
+ .id_table = usb_dsbr100_device_table,
+};
+
+/* Low-level device interface begins here */
+
+/* switch on radio */
+static int dsbr100_start(dsbr100_device *radio)
+{
+ if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_ONOFF,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
+ return -1;
+ return (radio->transfer_buffer)[0];
+}
+
+
+/* switch off radio */
+static int dsbr100_stop(dsbr100_device *radio)
+{
+ if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_ONOFF,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
+ return -1;
+ return (radio->transfer_buffer)[0];
+}
+
+/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
+static int dsbr100_setfreq(dsbr100_device *radio, int freq)
+{
+ freq = (freq/16*80)/1000+856;
+ if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_TUNE,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ (freq>>8)&0x00ff, freq&0xff,
+ radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 ||
+ usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) {
+ radio->stereo = -1;
+ return -1;
+ }
+ radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
+ return (radio->transfer_buffer)[0];
+}
+
+/* return the device status. This is, in effect, just whether it
+sees a stereo signal or not. Pity. */
+static void dsbr100_getstat(dsbr100_device *radio)
+{
+ if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0)
+ radio->stereo = -1;
+ else
+ radio->stereo = ! (radio->transfer_buffer[0]&0x01);
+}
+
+
+/* USB subsystem interface begins here */
+
+/* check if the device is present and register with v4l and
+usb if it is */
+static int usb_dsbr100_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ dsbr100_device *radio;
+
+ if (!(radio = kmalloc(sizeof(dsbr100_device), GFP_KERNEL)))
+ return -ENOMEM;
+ if (!(radio->videodev = video_device_alloc())) {
+ kfree(radio);
+ return -ENOMEM;
+ }
+ memcpy(radio->videodev, &dsbr100_videodev_template,
+ sizeof(dsbr100_videodev_template));
+ radio->removed = 0;
+ radio->users = 0;
+ radio->usbdev = interface_to_usbdev(intf);
+ radio->curfreq = FREQ_MIN*FREQ_MUL;
+ video_set_drvdata(radio->videodev, radio);
+ if (video_register_device(radio->videodev, VFL_TYPE_RADIO,
+ radio_nr)) {
+ warn("Could not register video device");
+ video_device_release(radio->videodev);
+ kfree(radio);
+ return -EIO;
+ }
+ usb_set_intfdata(intf, radio);
+ return 0;
+}
+
+/* handle unplugging of the device, release data structures
+if nothing keeps us from doing it. If something is still
+keeping us busy, the release callback of v4l will take care
+of releasing it. stv680.c does not relase its private
+data, so I don't do this here either. Checking out the
+code I'd expect I better did that, but if there's a memory
+leak here it's tiny (~50 bytes per disconnect) */
+static void usb_dsbr100_disconnect(struct usb_interface *intf)
+{
+ dsbr100_device *radio = usb_get_intfdata(intf);
+
+ usb_set_intfdata (intf, NULL);
+ if (radio) {
+ video_unregister_device(radio->videodev);
+ radio->videodev = NULL;
+ if (radio->users) {
+ kfree(radio);
+ } else {
+ radio->removed = 1;
+ }
+ }
+}
+
+
+/* Video for Linux interface */
+
+static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ dsbr100_device *radio=video_get_drvdata(video_devdata(file));
+
+ if (!radio)
+ return -EIO;
+
+ switch(cmd) {
+ case VIDIOCGCAP: {
+ struct video_capability *v = arg;
+
+ memset(v, 0, sizeof(*v));
+ v->type = VID_TYPE_TUNER;
+ v->channels = 1;
+ v->audios = 1;
+ strcpy(v->name, "D-Link R-100 USB FM Radio");
+ return 0;
+ }
+ case VIDIOCGTUNER: {
+ struct video_tuner *v = arg;
+
+ dsbr100_getstat(radio);
+ if(v->tuner) /* Only 1 tuner */
+ return -EINVAL;
+ v->rangelow = FREQ_MIN*FREQ_MUL;
+ v->rangehigh = FREQ_MAX*FREQ_MUL;
+ v->flags = VIDEO_TUNER_LOW;
+ v->mode = VIDEO_MODE_AUTO;
+ v->signal = radio->stereo*0x7000;
+ /* Don't know how to get signal strength */
+ v->flags |= VIDEO_TUNER_STEREO_ON*radio->stereo;
+ strcpy(v->name, "DSB R-100");
+ return 0;
+ }
+ case VIDIOCSTUNER: {
+ struct video_tuner *v = arg;
+
+ if(v->tuner!=0)
+ return -EINVAL;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ: {
+ int *freq = arg;
+
+ if (radio->curfreq==-1)
+ return -EINVAL;
+ *freq = radio->curfreq;
+ return 0;
+ }
+ case VIDIOCSFREQ: {
+ int *freq = arg;
+
+ radio->curfreq = *freq;
+ if (dsbr100_setfreq(radio, radio->curfreq)==-1)
+ warn("Set frequency failed");
+ return 0;
+ }
+ case VIDIOCGAUDIO: {
+ struct video_audio *v = arg;
+
+ memset(v, 0, sizeof(*v));
+ v->flags |= VIDEO_AUDIO_MUTABLE;
+ v->mode = VIDEO_SOUND_STEREO;
+ v->volume = 1;
+ v->step = 1;
+ strcpy(v->name, "Radio");
+ return 0;
+ }
+ case VIDIOCSAUDIO: {
+ struct video_audio *v = arg;
+
+ if (v->audio)
+ return -EINVAL;
+ if (v->flags&VIDEO_AUDIO_MUTE) {
+ if (dsbr100_stop(radio)==-1)
+ warn("Radio did not respond properly");
+ }
+ else
+ if (dsbr100_start(radio)==-1)
+ warn("Radio did not respond properly");
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int usb_dsbr100_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, usb_dsbr100_do_ioctl);
+}
+
+static int usb_dsbr100_open(struct inode *inode, struct file *file)
+{
+ dsbr100_device *radio=video_get_drvdata(video_devdata(file));
+
+ radio->users = 1;
+ if (dsbr100_start(radio)<0) {
+ warn("Radio did not start up properly");
+ radio->users = 0;
+ return -EIO;
+ }
+ dsbr100_setfreq(radio, radio->curfreq);
+ return 0;
+}
+
+static int usb_dsbr100_close(struct inode *inode, struct file *file)
+{
+ dsbr100_device *radio=video_get_drvdata(video_devdata(file));
+
+ if (!radio)
+ return -ENODEV;
+ radio->users = 0;
+ if (radio->removed) {
+ kfree(radio);
+ }
+ return 0;
+}
+
+static int __init dsbr100_init(void)
+{
+ int retval = usb_register(&usb_dsbr100_driver);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+ return retval;
+}
+
+static void __exit dsbr100_exit(void)
+{
+ usb_deregister(&usb_dsbr100_driver);
+}
+
+module_init (dsbr100_init);
+module_exit (dsbr100_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/et61x251/Makefile b/drivers/media/video/et61x251/Makefile
new file mode 100644
index 000000000000..2ff4db9ec882
--- /dev/null
+++ b/drivers/media/video/et61x251/Makefile
@@ -0,0 +1,4 @@
+et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o
+
+obj-$(CONFIG_USB_ET61X251) += et61x251.o
+
diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h
new file mode 100644
index 000000000000..eee8afc9be72
--- /dev/null
+++ b/drivers/media/video/et61x251/et61x251.h
@@ -0,0 +1,234 @@
+/***************************************************************************
+ * V4L2 driver for ET61X[12]51 PC Camera Controllers *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _ET61X251_H_
+#define _ET61X251_H_
+
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/rwsem.h>
+#include <linux/mutex.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+
+#include "et61x251_sensor.h"
+
+/*****************************************************************************/
+
+#define ET61X251_DEBUG
+#define ET61X251_DEBUG_LEVEL 2
+#define ET61X251_MAX_DEVICES 64
+#define ET61X251_PRESERVE_IMGSCALE 0
+#define ET61X251_FORCE_MUNMAP 0
+#define ET61X251_MAX_FRAMES 32
+#define ET61X251_COMPRESSION_QUALITY 0
+#define ET61X251_URBS 2
+#define ET61X251_ISO_PACKETS 7
+#define ET61X251_ALTERNATE_SETTING 13
+#define ET61X251_URB_TIMEOUT msecs_to_jiffies(2 * ET61X251_ISO_PACKETS)
+#define ET61X251_CTRL_TIMEOUT 100
+#define ET61X251_FRAME_TIMEOUT 2
+
+/*****************************************************************************/
+
+static const struct usb_device_id et61x251_id_table[] = {
+ { USB_DEVICE(0x102c, 0x6151), },
+ { USB_DEVICE(0x102c, 0x6251), },
+ { USB_DEVICE(0x102c, 0x6253), },
+ { USB_DEVICE(0x102c, 0x6254), },
+ { USB_DEVICE(0x102c, 0x6255), },
+ { USB_DEVICE(0x102c, 0x6256), },
+ { USB_DEVICE(0x102c, 0x6257), },
+ { USB_DEVICE(0x102c, 0x6258), },
+ { USB_DEVICE(0x102c, 0x6259), },
+ { USB_DEVICE(0x102c, 0x625a), },
+ { USB_DEVICE(0x102c, 0x625b), },
+ { USB_DEVICE(0x102c, 0x625c), },
+ { USB_DEVICE(0x102c, 0x625d), },
+ { USB_DEVICE(0x102c, 0x625e), },
+ { USB_DEVICE(0x102c, 0x625f), },
+ { USB_DEVICE(0x102c, 0x6260), },
+ { USB_DEVICE(0x102c, 0x6261), },
+ { USB_DEVICE(0x102c, 0x6262), },
+ { USB_DEVICE(0x102c, 0x6263), },
+ { USB_DEVICE(0x102c, 0x6264), },
+ { USB_DEVICE(0x102c, 0x6265), },
+ { USB_DEVICE(0x102c, 0x6266), },
+ { USB_DEVICE(0x102c, 0x6267), },
+ { USB_DEVICE(0x102c, 0x6268), },
+ { USB_DEVICE(0x102c, 0x6269), },
+ { }
+};
+
+ET61X251_SENSOR_TABLE
+
+/*****************************************************************************/
+
+enum et61x251_frame_state {
+ F_UNUSED,
+ F_QUEUED,
+ F_GRABBING,
+ F_DONE,
+ F_ERROR,
+};
+
+struct et61x251_frame_t {
+ void* bufmem;
+ struct v4l2_buffer buf;
+ enum et61x251_frame_state state;
+ struct list_head frame;
+ unsigned long vma_use_count;
+};
+
+enum et61x251_dev_state {
+ DEV_INITIALIZED = 0x01,
+ DEV_DISCONNECTED = 0x02,
+ DEV_MISCONFIGURED = 0x04,
+};
+
+enum et61x251_io_method {
+ IO_NONE,
+ IO_READ,
+ IO_MMAP,
+};
+
+enum et61x251_stream_state {
+ STREAM_OFF,
+ STREAM_INTERRUPT,
+ STREAM_ON,
+};
+
+struct et61x251_sysfs_attr {
+ u8 reg, i2c_reg;
+};
+
+struct et61x251_module_param {
+ u8 force_munmap;
+ u16 frame_timeout;
+};
+
+static DEFINE_MUTEX(et61x251_sysfs_lock);
+static DECLARE_RWSEM(et61x251_disconnect);
+
+struct et61x251_device {
+ struct video_device* v4ldev;
+
+ struct et61x251_sensor sensor;
+
+ struct usb_device* usbdev;
+ struct urb* urb[ET61X251_URBS];
+ void* transfer_buffer[ET61X251_URBS];
+ u8* control_buffer;
+
+ struct et61x251_frame_t *frame_current, frame[ET61X251_MAX_FRAMES];
+ struct list_head inqueue, outqueue;
+ u32 frame_count, nbuffers, nreadbuffers;
+
+ enum et61x251_io_method io;
+ enum et61x251_stream_state stream;
+
+ struct v4l2_jpegcompression compression;
+
+ struct et61x251_sysfs_attr sysfs;
+ struct et61x251_module_param module_param;
+
+ enum et61x251_dev_state state;
+ u8 users;
+
+ struct mutex dev_mutex, fileop_mutex;
+ spinlock_t queue_lock;
+ wait_queue_head_t open, wait_frame, wait_stream;
+};
+
+/*****************************************************************************/
+
+struct et61x251_device*
+et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id)
+{
+ if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id))
+ return cam;
+
+ return NULL;
+}
+
+
+void
+et61x251_attach_sensor(struct et61x251_device* cam,
+ struct et61x251_sensor* sensor)
+{
+ memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor));
+}
+
+/*****************************************************************************/
+
+#undef DBG
+#undef KDBG
+#ifdef ET61X251_DEBUG
+# define DBG(level, fmt, args...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1) \
+ dev_err(&cam->usbdev->dev, fmt "\n", ## args); \
+ else if ((level) == 2) \
+ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \
+ else if ((level) >= 3) \
+ dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args); \
+ } \
+} while (0)
+# define KDBG(level, fmt, args...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1 || (level) == 2) \
+ pr_info("et61x251: " fmt "\n", ## args); \
+ else if ((level) == 3) \
+ pr_debug("et61x251: [%s:%d] " fmt "\n", __FUNCTION__, \
+ __LINE__ , ## args); \
+ } \
+} while (0)
+# define V4LDBG(level, name, cmd) \
+do { \
+ if (debug >= (level)) \
+ v4l_print_ioctl(name, cmd); \
+} while (0)
+#else
+# define DBG(level, fmt, args...) do {;} while(0)
+# define KDBG(level, fmt, args...) do {;} while(0)
+# define V4LDBG(level, name, cmd) do {;} while(0)
+#endif
+
+#undef PDBG
+#define PDBG(fmt, args...) \
+dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args)
+
+#undef PDBGG
+#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
+
+#endif /* _ET61X251_H_ */
diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c
new file mode 100644
index 000000000000..7cc01b828b3d
--- /dev/null
+++ b/drivers/media/video/et61x251/et61x251_core.c
@@ -0,0 +1,2630 @@
+/***************************************************************************
+ * V4L2 driver for ET61X[12]51 PC Camera Controllers *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/compiler.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/page-flags.h>
+#include <linux/byteorder/generic.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "et61x251.h"
+
+/*****************************************************************************/
+
+#define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \
+ "PC Camera Controllers"
+#define ET61X251_MODULE_AUTHOR "(C) 2006 Luca Risolia"
+#define ET61X251_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
+#define ET61X251_MODULE_LICENSE "GPL"
+#define ET61X251_MODULE_VERSION "1:1.02"
+#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 2)
+
+/*****************************************************************************/
+
+MODULE_DEVICE_TABLE(usb, et61x251_id_table);
+
+MODULE_AUTHOR(ET61X251_MODULE_AUTHOR " " ET61X251_AUTHOR_EMAIL);
+MODULE_DESCRIPTION(ET61X251_MODULE_NAME);
+MODULE_VERSION(ET61X251_MODULE_VERSION);
+MODULE_LICENSE(ET61X251_MODULE_LICENSE);
+
+static short video_nr[] = {[0 ... ET61X251_MAX_DEVICES-1] = -1};
+module_param_array(video_nr, short, NULL, 0444);
+MODULE_PARM_DESC(video_nr,
+ "\n<-1|n[,...]> Specify V4L2 minor mode number."
+ "\n -1 = use next available (default)"
+ "\n n = use minor number n (integer >= 0)"
+ "\nYou can specify up to "
+ __MODULE_STRING(ET61X251_MAX_DEVICES) " cameras this way."
+ "\nFor example:"
+ "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+ "\nthe second registered camera and use auto for the first"
+ "\none and for every other camera."
+ "\n");
+
+static short force_munmap[] = {[0 ... ET61X251_MAX_DEVICES-1] =
+ ET61X251_FORCE_MUNMAP};
+module_param_array(force_munmap, bool, NULL, 0444);
+MODULE_PARM_DESC(force_munmap,
+ "\n<0|1[,...]> Force the application to unmap previously"
+ "\nmapped buffer memory before calling any VIDIOC_S_CROP or"
+ "\nVIDIOC_S_FMT ioctl's. Not all the applications support"
+ "\nthis feature. This parameter is specific for each"
+ "\ndetected camera."
+ "\n 0 = do not force memory unmapping"
+ "\n 1 = force memory unmapping (save memory)"
+ "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
+ "\n");
+
+static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] =
+ ET61X251_FRAME_TIMEOUT};
+module_param_array(frame_timeout, uint, NULL, 0644);
+MODULE_PARM_DESC(frame_timeout,
+ "\n<n[,...]> Timeout for a video frame in seconds."
+ "\nThis parameter is specific for each detected camera."
+ "\nDefault value is "
+ __MODULE_STRING(ET61X251_FRAME_TIMEOUT)"."
+ "\n");
+
+#ifdef ET61X251_DEBUG
+static unsigned short debug = ET61X251_DEBUG_LEVEL;
+module_param(debug, ushort, 0644);
+MODULE_PARM_DESC(debug,
+ "\n<n> Debugging information level, from 0 to 3:"
+ "\n0 = none (use carefully)"
+ "\n1 = critical errors"
+ "\n2 = significant informations"
+ "\n3 = more verbose messages"
+ "\nLevel 3 is useful for testing only, when only "
+ "one device is used."
+ "\nDefault value is "__MODULE_STRING(ET61X251_DEBUG_LEVEL)"."
+ "\n");
+#endif
+
+/*****************************************************************************/
+
+static u32
+et61x251_request_buffers(struct et61x251_device* cam, u32 count,
+ enum et61x251_io_method io)
+{
+ struct v4l2_pix_format* p = &(cam->sensor.pix_format);
+ struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
+ const size_t imagesize = cam->module_param.force_munmap ||
+ io == IO_READ ?
+ (p->width * p->height * p->priv) / 8 :
+ (r->width * r->height * p->priv) / 8;
+ void* buff = NULL;
+ u32 i;
+
+ if (count > ET61X251_MAX_FRAMES)
+ count = ET61X251_MAX_FRAMES;
+
+ cam->nbuffers = count;
+ while (cam->nbuffers > 0) {
+ if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize))))
+ break;
+ cam->nbuffers--;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
+ cam->frame[i].buf.index = i;
+ cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
+ cam->frame[i].buf.length = imagesize;
+ cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->frame[i].buf.sequence = 0;
+ cam->frame[i].buf.field = V4L2_FIELD_NONE;
+ cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
+ cam->frame[i].buf.flags = 0;
+ }
+
+ return cam->nbuffers;
+}
+
+
+static void et61x251_release_buffers(struct et61x251_device* cam)
+{
+ if (cam->nbuffers) {
+ vfree(cam->frame[0].bufmem);
+ cam->nbuffers = 0;
+ }
+ cam->frame_current = NULL;
+}
+
+
+static void et61x251_empty_framequeues(struct et61x251_device* cam)
+{
+ u32 i;
+
+ INIT_LIST_HEAD(&cam->inqueue);
+ INIT_LIST_HEAD(&cam->outqueue);
+
+ for (i = 0; i < ET61X251_MAX_FRAMES; i++) {
+ cam->frame[i].state = F_UNUSED;
+ cam->frame[i].buf.bytesused = 0;
+ }
+}
+
+
+static void et61x251_requeue_outqueue(struct et61x251_device* cam)
+{
+ struct et61x251_frame_t *i;
+
+ list_for_each_entry(i, &cam->outqueue, frame) {
+ i->state = F_QUEUED;
+ list_add(&i->frame, &cam->inqueue);
+ }
+
+ INIT_LIST_HEAD(&cam->outqueue);
+}
+
+
+static void et61x251_queue_unusedframes(struct et61x251_device* cam)
+{
+ unsigned long lock_flags;
+ u32 i;
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].state == F_UNUSED) {
+ cam->frame[i].state = F_QUEUED;
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[i].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ }
+}
+
+/*****************************************************************************/
+
+int et61x251_write_reg(struct et61x251_device* cam, u8 value, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int res;
+
+ *buff = value;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, index, buff, 1, ET61X251_CTRL_TIMEOUT);
+ if (res < 0) {
+ DBG(3, "Failed to write a register (value 0x%02X, index "
+ "0x%02X, error %d)", value, index, res);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int et61x251_read_reg(struct et61x251_device* cam, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+ 0, index, buff, 1, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ DBG(3, "Failed to read a register (index 0x%02X, error %d)",
+ index, res);
+
+ return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+static int
+et61x251_i2c_wait(struct et61x251_device* cam, struct et61x251_sensor* sensor)
+{
+ int i, r;
+
+ for (i = 1; i <= 8; i++) {
+ if (sensor->interface == ET61X251_I2C_3WIRES) {
+ r = et61x251_read_reg(cam, 0x8e);
+ if (!(r & 0x02) && (r >= 0))
+ return 0;
+ } else {
+ r = et61x251_read_reg(cam, 0x8b);
+ if (!(r & 0x01) && (r >= 0))
+ return 0;
+ }
+ if (r < 0)
+ return -EIO;
+ udelay(8*8); /* minimum for sensors at 400kHz */
+ }
+
+ return -EBUSY;
+}
+
+
+int
+et61x251_i2c_try_read(struct et61x251_device* cam,
+ struct et61x251_sensor* sensor, u8 address)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ data[0] = address;
+ data[1] = cam->sensor.i2c_slave_id;
+ data[2] = cam->sensor.rsta | 0x10;
+ data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02);
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += et61x251_i2c_wait(cam, sensor);
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+ 0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ if (err)
+ DBG(3, "I2C read failed for %s image sensor", sensor->name);
+
+ PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]);
+
+ return err ? -1 : (int)data[0];
+}
+
+
+int
+et61x251_i2c_try_write(struct et61x251_device* cam,
+ struct et61x251_sensor* sensor, u8 address, u8 value)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ data[0] = address;
+ data[1] = cam->sensor.i2c_slave_id;
+ data[2] = cam->sensor.rsta | 0x12;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ data[0] = value;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += et61x251_i2c_wait(cam, sensor);
+
+ if (err)
+ DBG(3, "I2C write failed for %s image sensor", sensor->name);
+
+ PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value);
+
+ return err ? -1 : 0;
+}
+
+
+int
+et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2,
+ u8 data3, u8 data4, u8 data5, u8 data6, u8 data7,
+ u8 data8, u8 address)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ data[0] = data2;
+ data[1] = data3;
+ data[2] = data4;
+ data[3] = data5;
+ data[4] = data6;
+ data[5] = data7;
+ data[6] = data8;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x81, data, n-1, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ data[0] = address;
+ data[1] = cam->sensor.i2c_slave_id;
+ data[2] = cam->sensor.rsta | 0x02 | (n << 4);
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ /* Start writing through the serial interface */
+ data[0] = data1;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += et61x251_i2c_wait(cam, &cam->sensor);
+
+ if (err)
+ DBG(3, "I2C raw write failed for %s image sensor",
+ cam->sensor.name);
+
+ PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, "
+ "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X,"
+ " data6 = 0x%02X, data7 = 0x%02X, data8 = 0x%02X", n, address,
+ data1, data2, data3, data4, data5, data6, data7, data8);
+
+ return err ? -1 : 0;
+
+}
+
+
+int et61x251_i2c_read(struct et61x251_device* cam, u8 address)
+{
+ return et61x251_i2c_try_read(cam, &cam->sensor, address);
+}
+
+
+int et61x251_i2c_write(struct et61x251_device* cam, u8 address, u8 value)
+{
+ return et61x251_i2c_try_write(cam, &cam->sensor, address, value);
+}
+
+/*****************************************************************************/
+
+static void et61x251_urb_complete(struct urb *urb, struct pt_regs* regs)
+{
+ struct et61x251_device* cam = urb->context;
+ struct et61x251_frame_t** f;
+ size_t imagesize;
+ u8 i;
+ int err = 0;
+
+ if (urb->status == -ENOENT)
+ return;
+
+ f = &cam->frame_current;
+
+ if (cam->stream == STREAM_INTERRUPT) {
+ cam->stream = STREAM_OFF;
+ if ((*f))
+ (*f)->state = F_QUEUED;
+ DBG(3, "Stream interrupted");
+ wake_up(&cam->wait_stream);
+ }
+
+ if (cam->state & DEV_DISCONNECTED)
+ return;
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ wake_up_interruptible(&cam->wait_frame);
+ return;
+ }
+
+ if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
+ goto resubmit_urb;
+
+ if (!(*f))
+ (*f) = list_entry(cam->inqueue.next, struct et61x251_frame_t,
+ frame);
+
+ imagesize = (cam->sensor.pix_format.width *
+ cam->sensor.pix_format.height *
+ cam->sensor.pix_format.priv) / 8;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int len, status;
+ void *pos;
+ u8* b1, * b2, sof;
+ const u8 VOID_BYTES = 6;
+ size_t imglen;
+
+ len = urb->iso_frame_desc[i].actual_length;
+ status = urb->iso_frame_desc[i].status;
+ pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+ if (status) {
+ DBG(3, "Error in isochronous frame");
+ (*f)->state = F_ERROR;
+ continue;
+ }
+
+ b1 = pos++;
+ b2 = pos++;
+ sof = ((*b1 & 0x3f) == 63);
+ imglen = ((*b1 & 0xc0) << 2) | *b2;
+
+ PDBGG("Isochrnous frame: length %u, #%u i, image length %zu",
+ len, i, imglen);
+
+ if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR)
+start_of_frame:
+ if (sof) {
+ (*f)->state = F_GRABBING;
+ (*f)->buf.bytesused = 0;
+ do_gettimeofday(&(*f)->buf.timestamp);
+ pos += 22;
+ DBG(3, "SOF detected: new video frame");
+ }
+
+ if ((*f)->state == F_GRABBING) {
+ if (sof && (*f)->buf.bytesused) {
+ if (cam->sensor.pix_format.pixelformat ==
+ V4L2_PIX_FMT_ET61X251)
+ goto end_of_frame;
+ else {
+ DBG(3, "Not expected SOF detected "
+ "after %lu bytes",
+ (unsigned long)(*f)->buf.bytesused);
+ (*f)->state = F_ERROR;
+ continue;
+ }
+ }
+
+ if ((*f)->buf.bytesused + imglen > imagesize) {
+ DBG(3, "Video frame size exceeded");
+ (*f)->state = F_ERROR;
+ continue;
+ }
+
+ pos += VOID_BYTES;
+
+ memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, imglen);
+ (*f)->buf.bytesused += imglen;
+
+ if ((*f)->buf.bytesused == imagesize) {
+ u32 b;
+end_of_frame:
+ b = (*f)->buf.bytesused;
+ (*f)->state = F_DONE;
+ (*f)->buf.sequence= ++cam->frame_count;
+ spin_lock(&cam->queue_lock);
+ list_move_tail(&(*f)->frame, &cam->outqueue);
+ if (!list_empty(&cam->inqueue))
+ (*f) = list_entry(cam->inqueue.next,
+ struct et61x251_frame_t,
+ frame);
+ else
+ (*f) = NULL;
+ spin_unlock(&cam->queue_lock);
+ DBG(3, "Video frame captured: : %lu bytes",
+ (unsigned long)(b));
+
+ if (!(*f))
+ goto resubmit_urb;
+
+ if (sof &&
+ cam->sensor.pix_format.pixelformat ==
+ V4L2_PIX_FMT_ET61X251)
+ goto start_of_frame;
+ }
+ }
+ }
+
+resubmit_urb:
+ urb->dev = cam->usbdev;
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0 && err != -EPERM) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "usb_submit_urb() failed");
+ }
+
+ wake_up_interruptible(&cam->wait_frame);
+}
+
+
+static int et61x251_start_transfer(struct et61x251_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ struct urb* urb;
+ const unsigned int wMaxPacketSize[] = {0, 256, 384, 512, 640, 768, 832,
+ 864, 896, 920, 956, 980, 1000,
+ 1022};
+ const unsigned int psz = wMaxPacketSize[ET61X251_ALTERNATE_SETTING];
+ s8 i, j;
+ int err = 0;
+
+ for (i = 0; i < ET61X251_URBS; i++) {
+ cam->transfer_buffer[i] = kzalloc(ET61X251_ISO_PACKETS * psz,
+ GFP_KERNEL);
+ if (!cam->transfer_buffer[i]) {
+ err = -ENOMEM;
+ DBG(1, "Not enough memory");
+ goto free_buffers;
+ }
+ }
+
+ for (i = 0; i < ET61X251_URBS; i++) {
+ urb = usb_alloc_urb(ET61X251_ISO_PACKETS, GFP_KERNEL);
+ cam->urb[i] = urb;
+ if (!urb) {
+ err = -ENOMEM;
+ DBG(1, "usb_alloc_urb() failed");
+ goto free_urbs;
+ }
+ urb->dev = udev;
+ urb->context = cam;
+ urb->pipe = usb_rcvisocpipe(udev, 1);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->number_of_packets = ET61X251_ISO_PACKETS;
+ urb->complete = et61x251_urb_complete;
+ urb->transfer_buffer = cam->transfer_buffer[i];
+ urb->transfer_buffer_length = psz * ET61X251_ISO_PACKETS;
+ urb->interval = 1;
+ for (j = 0; j < ET61X251_ISO_PACKETS; j++) {
+ urb->iso_frame_desc[j].offset = psz * j;
+ urb->iso_frame_desc[j].length = psz;
+ }
+ }
+
+ err = et61x251_write_reg(cam, 0x01, 0x03);
+ err = et61x251_write_reg(cam, 0x00, 0x03);
+ err = et61x251_write_reg(cam, 0x08, 0x03);
+ if (err) {
+ err = -EIO;
+ DBG(1, "I/O hardware error");
+ goto free_urbs;
+ }
+
+ err = usb_set_interface(udev, 0, ET61X251_ALTERNATE_SETTING);
+ if (err) {
+ DBG(1, "usb_set_interface() failed");
+ goto free_urbs;
+ }
+
+ cam->frame_current = NULL;
+
+ for (i = 0; i < ET61X251_URBS; i++) {
+ err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+ if (err) {
+ for (j = i-1; j >= 0; j--)
+ usb_kill_urb(cam->urb[j]);
+ DBG(1, "usb_submit_urb() failed, error %d", err);
+ goto free_urbs;
+ }
+ }
+
+ return 0;
+
+free_urbs:
+ for (i = 0; (i < ET61X251_URBS) && cam->urb[i]; i++)
+ usb_free_urb(cam->urb[i]);
+
+free_buffers:
+ for (i = 0; (i < ET61X251_URBS) && cam->transfer_buffer[i]; i++)
+ kfree(cam->transfer_buffer[i]);
+
+ return err;
+}
+
+
+static int et61x251_stop_transfer(struct et61x251_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ s8 i;
+ int err = 0;
+
+ if (cam->state & DEV_DISCONNECTED)
+ return 0;
+
+ for (i = ET61X251_URBS-1; i >= 0; i--) {
+ usb_kill_urb(cam->urb[i]);
+ usb_free_urb(cam->urb[i]);
+ kfree(cam->transfer_buffer[i]);
+ }
+
+ err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+ if (err)
+ DBG(3, "usb_set_interface() failed");
+
+ return err;
+}
+
+
+static int et61x251_stream_interrupt(struct et61x251_device* cam)
+{
+ long timeout;
+
+ cam->stream = STREAM_INTERRUPT;
+ timeout = wait_event_timeout(cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED),
+ ET61X251_URB_TIMEOUT);
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ else if (cam->stream != STREAM_OFF) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "URB timeout reached. The camera is misconfigured. To "
+ "use it, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count)
+{
+ char str[5];
+ char* endp;
+ unsigned long val;
+
+ if (len < 4) {
+ strncpy(str, buff, len);
+ str[len+1] = '\0';
+ } else {
+ strncpy(str, buff, 4);
+ str[4] = '\0';
+ }
+
+ val = simple_strtoul(str, &endp, 0);
+
+ *count = 0;
+ if (val <= 0xff)
+ *count = (ssize_t)(endp - str);
+ if ((*count) && (len == *count+1) && (buff[*count] == '\n'))
+ *count += 1;
+
+ return (u8)val;
+}
+
+/*
+ NOTE 1: being inside one of the following methods implies that the v4l
+ device exists for sure (see kobjects and reference counters)
+ NOTE 2: buffers are PAGE_SIZE long
+*/
+
+static ssize_t et61x251_show_reg(struct class_device* cd, char* buf)
+{
+ struct et61x251_device* cam;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ count = sprintf(buf, "%u\n", cam->sysfs.reg);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+et61x251_store_reg(struct class_device* cd, const char* buf, size_t len)
+{
+ struct et61x251_device* cam;
+ u8 index;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ index = et61x251_strtou8(buf, len, &count);
+ if (index > 0x8e || !count) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EINVAL;
+ }
+
+ cam->sysfs.reg = index;
+
+ DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t et61x251_show_val(struct class_device* cd, char* buf)
+{
+ struct et61x251_device* cam;
+ ssize_t count;
+ int val;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EIO;
+ }
+
+ count = sprintf(buf, "%d\n", val);
+
+ DBG(3, "Read bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+et61x251_store_val(struct class_device* cd, const char* buf, size_t len)
+{
+ struct et61x251_device* cam;
+ u8 value;
+ ssize_t count;
+ int err;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ value = et61x251_strtou8(buf, len, &count);
+ if (!count) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EINVAL;
+ }
+
+ err = et61x251_write_reg(cam, value, cam->sysfs.reg);
+ if (err) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EIO;
+ }
+
+ DBG(2, "Written ET61X[12]51 reg. 0x%02X, val. 0x%02X",
+ cam->sysfs.reg, value);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t et61x251_show_i2c_reg(struct class_device* cd, char* buf)
+{
+ struct et61x251_device* cam;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg);
+
+ DBG(3, "Read bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+et61x251_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
+{
+ struct et61x251_device* cam;
+ u8 index;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ index = et61x251_strtou8(buf, len, &count);
+ if (!count) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EINVAL;
+ }
+
+ cam->sysfs.i2c_reg = index;
+
+ DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t et61x251_show_i2c_val(struct class_device* cd, char* buf)
+{
+ struct et61x251_device* cam;
+ ssize_t count;
+ int val;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENOSYS;
+ }
+
+ if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EIO;
+ }
+
+ count = sprintf(buf, "%d\n", val);
+
+ DBG(3, "Read bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+et61x251_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
+{
+ struct et61x251_device* cam;
+ u8 value;
+ ssize_t count;
+ int err;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENOSYS;
+ }
+
+ value = et61x251_strtou8(buf, len, &count);
+ if (!count) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EINVAL;
+ }
+
+ err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value);
+ if (err) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EIO;
+ }
+
+ DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X",
+ cam->sysfs.i2c_reg, value);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR,
+ et61x251_show_reg, et61x251_store_reg);
+static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR,
+ et61x251_show_val, et61x251_store_val);
+static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
+ et61x251_show_i2c_reg, et61x251_store_i2c_reg);
+static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
+ et61x251_show_i2c_val, et61x251_store_i2c_val);
+
+
+static void et61x251_create_sysfs(struct et61x251_device* cam)
+{
+ struct video_device *v4ldev = cam->v4ldev;
+
+ video_device_create_file(v4ldev, &class_device_attr_reg);
+ video_device_create_file(v4ldev, &class_device_attr_val);
+ if (cam->sensor.sysfs_ops) {
+ video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
+ video_device_create_file(v4ldev, &class_device_attr_i2c_val);
+ }
+}
+#endif /* CONFIG_VIDEO_ADV_DEBUG */
+
+/*****************************************************************************/
+
+static int
+et61x251_set_pix_format(struct et61x251_device* cam,
+ struct v4l2_pix_format* pix)
+{
+ int r, err = 0;
+
+ if ((r = et61x251_read_reg(cam, 0x12)) < 0)
+ err += r;
+ if (pix->pixelformat == V4L2_PIX_FMT_ET61X251)
+ err += et61x251_write_reg(cam, r & 0xfd, 0x12);
+ else
+ err += et61x251_write_reg(cam, r | 0x02, 0x12);
+
+ return err ? -EIO : 0;
+}
+
+
+static int
+et61x251_set_compression(struct et61x251_device* cam,
+ struct v4l2_jpegcompression* compression)
+{
+ int r, err = 0;
+
+ if ((r = et61x251_read_reg(cam, 0x12)) < 0)
+ err += r;
+ if (compression->quality == 0)
+ err += et61x251_write_reg(cam, r & 0xfb, 0x12);
+ else
+ err += et61x251_write_reg(cam, r | 0x04, 0x12);
+
+ return err ? -EIO : 0;
+}
+
+
+static int et61x251_set_scale(struct et61x251_device* cam, u8 scale)
+{
+ int r = 0, err = 0;
+
+ r = et61x251_read_reg(cam, 0x12);
+ if (r < 0)
+ err += r;
+
+ if (scale == 1)
+ err += et61x251_write_reg(cam, r & ~0x01, 0x12);
+ else if (scale == 2)
+ err += et61x251_write_reg(cam, r | 0x01, 0x12);
+
+ if (err)
+ return -EIO;
+
+ PDBGG("Scaling factor: %u", scale);
+
+ return 0;
+}
+
+
+static int
+et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left +
+ s->active_pixel.left),
+ fmw_sy = (u16)(rect->top - s->cropcap.bounds.top +
+ s->active_pixel.top),
+ fmw_length = (u16)(rect->width),
+ fmw_height = (u16)(rect->height);
+ int err = 0;
+
+ err += et61x251_write_reg(cam, fmw_sx & 0xff, 0x69);
+ err += et61x251_write_reg(cam, fmw_sy & 0xff, 0x6a);
+ err += et61x251_write_reg(cam, fmw_length & 0xff, 0x6b);
+ err += et61x251_write_reg(cam, fmw_height & 0xff, 0x6c);
+ err += et61x251_write_reg(cam, (fmw_sx >> 8) | ((fmw_sy & 0x300) >> 6)
+ | ((fmw_length & 0x300) >> 4)
+ | ((fmw_height & 0x300) >> 2), 0x6d);
+ if (err)
+ return -EIO;
+
+ PDBGG("fmw_sx, fmw_sy, fmw_length, fmw_height: %u %u %u %u",
+ fmw_sx, fmw_sy, fmw_length, fmw_height);
+
+ return 0;
+}
+
+
+static int et61x251_init(struct et61x251_device* cam)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ struct v4l2_queryctrl *qctrl;
+ struct v4l2_rect* rect;
+ u8 i = 0;
+ int err = 0;
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ init_waitqueue_head(&cam->open);
+ qctrl = s->qctrl;
+ rect = &(s->cropcap.defrect);
+ cam->compression.quality = ET61X251_COMPRESSION_QUALITY;
+ } else { /* use current values */
+ qctrl = s->_qctrl;
+ rect = &(s->_rect);
+ }
+
+ err += et61x251_set_scale(cam, rect->width / s->pix_format.width);
+ err += et61x251_set_crop(cam, rect);
+ if (err)
+ return err;
+
+ if (s->init) {
+ err = s->init(cam);
+ if (err) {
+ DBG(3, "Sensor initialization failed");
+ return err;
+ }
+ }
+
+ err += et61x251_set_compression(cam, &cam->compression);
+ err += et61x251_set_pix_format(cam, &s->pix_format);
+ if (s->set_pix_format)
+ err += s->set_pix_format(cam, &s->pix_format);
+ if (err)
+ return err;
+
+ if (s->pix_format.pixelformat == V4L2_PIX_FMT_ET61X251)
+ DBG(3, "Compressed video format is active, quality %d",
+ cam->compression.quality);
+ else
+ DBG(3, "Uncompressed video format is active");
+
+ if (s->set_crop)
+ if ((err = s->set_crop(cam, rect))) {
+ DBG(3, "set_crop() failed");
+ return err;
+ }
+
+ if (s->set_ctrl) {
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (s->qctrl[i].id != 0 &&
+ !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) {
+ ctrl.id = s->qctrl[i].id;
+ ctrl.value = qctrl[i].default_value;
+ err = s->set_ctrl(cam, &ctrl);
+ if (err) {
+ DBG(3, "Set %s control failed",
+ s->qctrl[i].name);
+ return err;
+ }
+ DBG(3, "Image sensor supports '%s' control",
+ s->qctrl[i].name);
+ }
+ }
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ mutex_init(&cam->fileop_mutex);
+ spin_lock_init(&cam->queue_lock);
+ init_waitqueue_head(&cam->wait_frame);
+ init_waitqueue_head(&cam->wait_stream);
+ cam->nreadbuffers = 2;
+ memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
+ memcpy(&(s->_rect), &(s->cropcap.defrect),
+ sizeof(struct v4l2_rect));
+ cam->state |= DEV_INITIALIZED;
+ }
+
+ DBG(2, "Initialization succeeded");
+ return 0;
+}
+
+
+static void et61x251_release_resources(struct et61x251_device* cam)
+{
+ mutex_lock(&et61x251_sysfs_lock);
+
+ DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
+ video_set_drvdata(cam->v4ldev, NULL);
+ video_unregister_device(cam->v4ldev);
+
+ usb_put_dev(cam->usbdev);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ kfree(cam->control_buffer);
+}
+
+/*****************************************************************************/
+
+static int et61x251_open(struct inode* inode, struct file* filp)
+{
+ struct et61x251_device* cam;
+ int err = 0;
+
+ /*
+ This is the only safe way to prevent race conditions with
+ disconnect
+ */
+ if (!down_read_trylock(&et61x251_disconnect))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(video_devdata(filp));
+
+ if (mutex_lock_interruptible(&cam->dev_mutex)) {
+ up_read(&et61x251_disconnect);
+ return -ERESTARTSYS;
+ }
+
+ if (cam->users) {
+ DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor);
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (filp->f_flags & O_NDELAY)) {
+ err = -EWOULDBLOCK;
+ goto out;
+ }
+ mutex_unlock(&cam->dev_mutex);
+ err = wait_event_interruptible_exclusive(cam->open,
+ cam->state & DEV_DISCONNECTED
+ || !cam->users);
+ if (err) {
+ up_read(&et61x251_disconnect);
+ return err;
+ }
+ if (cam->state & DEV_DISCONNECTED) {
+ up_read(&et61x251_disconnect);
+ return -ENODEV;
+ }
+ mutex_lock(&cam->dev_mutex);
+ }
+
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ err = et61x251_init(cam);
+ if (err) {
+ DBG(1, "Initialization failed again. "
+ "I will retry on next open().");
+ goto out;
+ }
+ cam->state &= ~DEV_MISCONFIGURED;
+ }
+
+ if ((err = et61x251_start_transfer(cam)))
+ goto out;
+
+ filp->private_data = cam;
+ cam->users++;
+ cam->io = IO_NONE;
+ cam->stream = STREAM_OFF;
+ cam->nbuffers = 0;
+ cam->frame_count = 0;
+ et61x251_empty_framequeues(cam);
+
+ DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
+
+out:
+ mutex_unlock(&cam->dev_mutex);
+ up_read(&et61x251_disconnect);
+ return err;
+}
+
+
+static int et61x251_release(struct inode* inode, struct file* filp)
+{
+ struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+
+ mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */
+
+ et61x251_stop_transfer(cam);
+
+ et61x251_release_buffers(cam);
+
+ if (cam->state & DEV_DISCONNECTED) {
+ et61x251_release_resources(cam);
+ mutex_unlock(&cam->dev_mutex);
+ kfree(cam);
+ return 0;
+ }
+
+ cam->users--;
+ wake_up_interruptible_nr(&cam->open, 1);
+
+ DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);
+
+ mutex_unlock(&cam->dev_mutex);
+
+ return 0;
+}
+
+
+static ssize_t
+et61x251_read(struct file* filp, char __user * buf,
+ size_t count, loff_t* f_pos)
+{
+ struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+ struct et61x251_frame_t* f, * i;
+ unsigned long lock_flags;
+ long timeout;
+ int err = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (cam->io == IO_MMAP) {
+ DBG(3, "Close and open the device again to choose the read "
+ "method");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!et61x251_request_buffers(cam, cam->nreadbuffers,
+ IO_READ)) {
+ DBG(1, "read() failed, not enough memory");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENOMEM;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (list_empty(&cam->inqueue)) {
+ if (!list_empty(&cam->outqueue))
+ et61x251_empty_framequeues(cam);
+ et61x251_queue_unusedframes(cam);
+ }
+
+ if (!count) {
+ mutex_unlock(&cam->fileop_mutex);
+ return 0;
+ }
+
+ if (list_empty(&cam->outqueue)) {
+ if (filp->f_flags & O_NONBLOCK) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EAGAIN;
+ }
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0) {
+ mutex_unlock(&cam->fileop_mutex);
+ return timeout;
+ }
+ if (cam->state & DEV_DISCONNECTED) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+ if (!timeout || (cam->state & DEV_MISCONFIGURED)) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+ }
+
+ f = list_entry(cam->outqueue.prev, struct et61x251_frame_t, frame);
+
+ if (count > f->buf.bytesused)
+ count = f->buf.bytesused;
+
+ if (copy_to_user(buf, f->bufmem, count)) {
+ err = -EFAULT;
+ goto exit;
+ }
+ *f_pos += count;
+
+exit:
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(i, &cam->outqueue, frame)
+ i->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ et61x251_queue_unusedframes(cam);
+
+ PDBGG("Frame #%lu, bytes read: %zu",
+ (unsigned long)f->buf.index, count);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return err ? err : count;
+}
+
+
+static unsigned int et61x251_poll(struct file *filp, poll_table *wait)
+{
+ struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+ struct et61x251_frame_t* f;
+ unsigned long lock_flags;
+ unsigned int mask = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return POLLERR;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ goto error;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ goto error;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!et61x251_request_buffers(cam, cam->nreadbuffers,
+ IO_READ)) {
+ DBG(1, "poll() failed, not enough memory");
+ goto error;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (cam->io == IO_READ) {
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(f, &cam->outqueue, frame)
+ f->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ et61x251_queue_unusedframes(cam);
+ }
+
+ poll_wait(filp, &cam->wait_frame, wait);
+
+ if (!list_empty(&cam->outqueue))
+ mask |= POLLIN | POLLRDNORM;
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return mask;
+
+error:
+ mutex_unlock(&cam->fileop_mutex);
+ return POLLERR;
+}
+
+
+static void et61x251_vm_open(struct vm_area_struct* vma)
+{
+ struct et61x251_frame_t* f = vma->vm_private_data;
+ f->vma_use_count++;
+}
+
+
+static void et61x251_vm_close(struct vm_area_struct* vma)
+{
+ /* NOTE: buffers are not freed here */
+ struct et61x251_frame_t* f = vma->vm_private_data;
+ f->vma_use_count--;
+}
+
+
+static struct vm_operations_struct et61x251_vm_ops = {
+ .open = et61x251_vm_open,
+ .close = et61x251_vm_close,
+};
+
+
+static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+ struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+ unsigned long size = vma->vm_end - vma->vm_start,
+ start = vma->vm_start;
+ void *pos;
+ u32 i;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
+ size != PAGE_ALIGN(cam->frame[0].buf.length)) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+ if (i == cam->nbuffers) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED;
+
+ pos = cam->frame[i].bufmem;
+ while (size > 0) { /* size is page-aligned */
+ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vma->vm_ops = &et61x251_vm_ops;
+ vma->vm_private_data = &cam->frame[i];
+
+ et61x251_vm_open(vma);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+static int
+et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_capability cap = {
+ .driver = "et61x251",
+ .version = ET61X251_MODULE_VERSION_CODE,
+ .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING,
+ };
+
+ strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
+ if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
+ strlcpy(cap.bus_info, cam->usbdev->dev.bus_id,
+ sizeof(cap.bus_info));
+
+ if (copy_to_user(arg, &cap, sizeof(cap)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_input i;
+
+ if (copy_from_user(&i, arg, sizeof(i)))
+ return -EFAULT;
+
+ if (i.index)
+ return -EINVAL;
+
+ memset(&i, 0, sizeof(i));
+ strcpy(i.name, "Camera");
+ i.type = V4L2_INPUT_TYPE_CAMERA;
+
+ if (copy_to_user(arg, &i, sizeof(i)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_g_input(struct et61x251_device* cam, void __user * arg)
+{
+ int index = 0;
+
+ if (copy_to_user(arg, &index, sizeof(index)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_s_input(struct et61x251_device* cam, void __user * arg)
+{
+ int index;
+
+ if (copy_from_user(&index, arg, sizeof(index)))
+ return -EFAULT;
+
+ if (index != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_queryctrl qc;
+ u8 i;
+
+ if (copy_from_user(&qc, arg, sizeof(qc)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (qc.id && qc.id == s->qctrl[i].id) {
+ memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
+ if (copy_to_user(arg, &qc, sizeof(qc)))
+ return -EFAULT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+
+static int
+et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ int err = 0;
+ u8 i;
+
+ if (!s->get_ctrl && !s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ if (!s->get_ctrl) {
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (ctrl.id == s->qctrl[i].id) {
+ ctrl.value = s->_qctrl[i].default_value;
+ goto exit;
+ }
+ return -EINVAL;
+ } else
+ err = s->get_ctrl(cam, &ctrl);
+
+exit:
+ if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
+ return -EFAULT;
+
+ return err;
+}
+
+
+static int
+et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ u8 i;
+ int err = 0;
+
+ if (!s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (ctrl.id == s->qctrl[i].id) {
+ if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+ if (ctrl.value < s->qctrl[i].minimum ||
+ ctrl.value > s->qctrl[i].maximum)
+ return -ERANGE;
+ ctrl.value -= ctrl.value % s->qctrl[i].step;
+ break;
+ }
+
+ if ((err = s->set_ctrl(cam, &ctrl)))
+ return err;
+
+ s->_qctrl[i].default_value = ctrl.value;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_cropcap* cc = &(cam->sensor.cropcap);
+
+ cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cc->pixelaspect.numerator = 1;
+ cc->pixelaspect.denominator = 1;
+
+ if (copy_to_user(arg, cc, sizeof(*cc)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_crop crop = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ };
+
+ memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
+
+ if (copy_to_user(arg, &crop, sizeof(crop)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_crop crop;
+ struct v4l2_rect* rect;
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ struct v4l2_pix_format* pix_format = &(s->pix_format);
+ u8 scale;
+ const enum et61x251_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&crop, arg, sizeof(crop)))
+ return -EFAULT;
+
+ rect = &(crop.c);
+
+ if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (cam->module_param.force_munmap)
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_CROP failed. "
+ "Unmap the buffers first.");
+ return -EINVAL;
+ }
+
+ /* Preserve R,G or B origin */
+ rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L;
+ rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L;
+
+ if (rect->width < 4)
+ rect->width = 4;
+ if (rect->height < 4)
+ rect->height = 4;
+ if (rect->width > bounds->width)
+ rect->width = bounds->width;
+ if (rect->height > bounds->height)
+ rect->height = bounds->height;
+ if (rect->left < bounds->left)
+ rect->left = bounds->left;
+ if (rect->top < bounds->top)
+ rect->top = bounds->top;
+ if (rect->left + rect->width > bounds->left + bounds->width)
+ rect->left = bounds->left+bounds->width - rect->width;
+ if (rect->top + rect->height > bounds->top + bounds->height)
+ rect->top = bounds->top+bounds->height - rect->height;
+
+ rect->width &= ~3L;
+ rect->height &= ~3L;
+
+ if (ET61X251_PRESERVE_IMGSCALE) {
+ /* Calculate the actual scaling factor */
+ u32 a, b;
+ a = rect->width * rect->height;
+ b = pix_format->width * pix_format->height;
+ scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
+ } else
+ scale = 1;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = et61x251_stream_interrupt(cam)))
+ return err;
+
+ if (copy_to_user(arg, &crop, sizeof(crop))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ if (cam->module_param.force_munmap || cam->io == IO_READ)
+ et61x251_release_buffers(cam);
+
+ err = et61x251_set_crop(cam, rect);
+ if (s->set_crop)
+ err += s->set_crop(cam, rect);
+ err += et61x251_set_scale(cam, scale);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ s->pix_format.width = rect->width/scale;
+ s->pix_format.height = rect->height/scale;
+ memcpy(&(s->_rect), rect, sizeof(*rect));
+
+ if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+ nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -ENOMEM;
+ }
+
+ if (cam->io == IO_READ)
+ et61x251_empty_framequeues(cam);
+ else if (cam->module_param.force_munmap)
+ et61x251_requeue_outqueue(cam);
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_fmtdesc fmtd;
+
+ if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
+ return -EFAULT;
+
+ if (fmtd.index == 0) {
+ strcpy(fmtd.description, "bayer rgb");
+ fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8;
+ } else if (fmtd.index == 1) {
+ strcpy(fmtd.description, "compressed");
+ fmtd.pixelformat = V4L2_PIX_FMT_ET61X251;
+ fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
+ } else
+ return -EINVAL;
+
+ fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
+
+ if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_format format;
+ struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format);
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251)
+ ? 0 : (pfmt->width * pfmt->priv) / 8;
+ pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
+ pfmt->field = V4L2_FIELD_NONE;
+ memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
+
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
+ void __user * arg)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_format format;
+ struct v4l2_pix_format* pix;
+ struct v4l2_pix_format* pfmt = &(s->pix_format);
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ struct v4l2_rect rect;
+ u8 scale;
+ const enum et61x251_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ pix = &(format.fmt.pix);
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memcpy(&rect, &(s->_rect), sizeof(rect));
+
+ { /* calculate the actual scaling factor */
+ u32 a, b;
+ a = rect.width * rect.height;
+ b = pix->width * pix->height;
+ scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
+ }
+
+ rect.width = scale * pix->width;
+ rect.height = scale * pix->height;
+
+ if (rect.width < 4)
+ rect.width = 4;
+ if (rect.height < 4)
+ rect.height = 4;
+ if (rect.width > bounds->left + bounds->width - rect.left)
+ rect.width = bounds->left + bounds->width - rect.left;
+ if (rect.height > bounds->top + bounds->height - rect.top)
+ rect.height = bounds->top + bounds->height - rect.top;
+
+ rect.width &= ~3L;
+ rect.height &= ~3L;
+
+ { /* adjust the scaling factor */
+ u32 a, b;
+ a = rect.width * rect.height;
+ b = pix->width * pix->height;
+ scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
+ }
+
+ pix->width = rect.width / scale;
+ pix->height = rect.height / scale;
+
+ if (pix->pixelformat != V4L2_PIX_FMT_ET61X251 &&
+ pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
+ pix->pixelformat = pfmt->pixelformat;
+ pix->priv = pfmt->priv; /* bpp */
+ pix->colorspace = pfmt->colorspace;
+ pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251)
+ ? 0 : (pix->width * pix->priv) / 8;
+ pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8);
+ pix->field = V4L2_FIELD_NONE;
+
+ if (cmd == VIDIOC_TRY_FMT) {
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+ return 0;
+ }
+
+ if (cam->module_param.force_munmap)
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_FMT failed. "
+ "Unmap the buffers first.");
+ return -EINVAL;
+ }
+
+ if (cam->stream == STREAM_ON)
+ if ((err = et61x251_stream_interrupt(cam)))
+ return err;
+
+ if (copy_to_user(arg, &format, sizeof(format))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ if (cam->module_param.force_munmap || cam->io == IO_READ)
+ et61x251_release_buffers(cam);
+
+ err += et61x251_set_pix_format(cam, pix);
+ err += et61x251_set_crop(cam, &rect);
+ if (s->set_pix_format)
+ err += s->set_pix_format(cam, pix);
+ if (s->set_crop)
+ err += s->set_crop(cam, &rect);
+ err += et61x251_set_scale(cam, scale);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ memcpy(pfmt, pix, sizeof(*pix));
+ memcpy(&(s->_rect), &rect, sizeof(rect));
+
+ if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+ nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -ENOMEM;
+ }
+
+ if (cam->io == IO_READ)
+ et61x251_empty_framequeues(cam);
+ else if (cam->module_param.force_munmap)
+ et61x251_requeue_outqueue(cam);
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_g_jpegcomp(struct et61x251_device* cam, void __user * arg)
+{
+ if (copy_to_user(arg, &cam->compression,
+ sizeof(cam->compression)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_s_jpegcomp(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_jpegcompression jc;
+ const enum et61x251_stream_state stream = cam->stream;
+ int err = 0;
+
+ if (copy_from_user(&jc, arg, sizeof(jc)))
+ return -EFAULT;
+
+ if (jc.quality != 0 && jc.quality != 1)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = et61x251_stream_interrupt(cam)))
+ return err;
+
+ err += et61x251_set_compression(cam, &jc);
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
+ "problems. To use the camera, close and open "
+ "/dev/video%d again.", cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ cam->compression.quality = jc.quality;
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_requestbuffers rb;
+ u32 i;
+ int err;
+
+ if (copy_from_user(&rb, arg, sizeof(rb)))
+ return -EFAULT;
+
+ if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ rb.memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (cam->io == IO_READ) {
+ DBG(3, "Close and open the device again to choose the mmap "
+ "I/O method");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_REQBUFS failed. "
+ "Previous buffers are still mapped.");
+ return -EINVAL;
+ }
+
+ if (cam->stream == STREAM_ON)
+ if ((err = et61x251_stream_interrupt(cam)))
+ return err;
+
+ et61x251_empty_framequeues(cam);
+
+ et61x251_release_buffers(cam);
+ if (rb.count)
+ rb.count = et61x251_request_buffers(cam, rb.count, IO_MMAP);
+
+ if (copy_to_user(arg, &rb, sizeof(rb))) {
+ et61x251_release_buffers(cam);
+ cam->io = IO_NONE;
+ return -EFAULT;
+ }
+
+ cam->io = rb.count ? IO_MMAP : IO_NONE;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_querybuf(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_buffer b;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
+
+ if (cam->frame[b.index].vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (cam->frame[b.index].state == F_DONE)
+ b.flags |= V4L2_BUF_FLAG_DONE;
+ else if (cam->frame[b.index].state != F_UNUSED)
+ b.flags |= V4L2_BUF_FLAG_QUEUED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_qbuf(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_buffer b;
+ unsigned long lock_flags;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->frame[b.index].state != F_UNUSED)
+ return -EINVAL;
+
+ cam->frame[b.index].state = F_QUEUED;
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ PDBGG("Frame #%lu queued", (unsigned long)b.index);
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp,
+ void __user * arg)
+{
+ struct v4l2_buffer b;
+ struct et61x251_frame_t *f;
+ unsigned long lock_flags;
+ long timeout;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&cam->outqueue)) {
+ if (cam->stream == STREAM_OFF)
+ return -EINVAL;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0)
+ return timeout;
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ if (!timeout || (cam->state & DEV_MISCONFIGURED))
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ f = list_entry(cam->outqueue.next, struct et61x251_frame_t, frame);
+ list_del(cam->outqueue.next);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ f->state = F_UNUSED;
+
+ memcpy(&b, &f->buf, sizeof(b));
+ if (f->vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index);
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg)
+{
+ int type;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&cam->inqueue))
+ return -EINVAL;
+
+ cam->stream = STREAM_ON;
+
+ DBG(3, "Stream on");
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_streamoff(struct et61x251_device* cam, void __user * arg)
+{
+ int type, err;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = et61x251_stream_interrupt(cam)))
+ return err;
+
+ et61x251_empty_framequeues(cam);
+
+ DBG(3, "Stream off");
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_g_parm(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_streamparm sp;
+
+ if (copy_from_user(&sp, arg, sizeof(sp)))
+ return -EFAULT;
+
+ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp.parm.capture.extendedmode = 0;
+ sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_s_parm(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_streamparm sp;
+
+ if (copy_from_user(&sp, arg, sizeof(sp)))
+ return -EFAULT;
+
+ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp.parm.capture.extendedmode = 0;
+
+ if (sp.parm.capture.readbuffers == 0)
+ sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+ if (sp.parm.capture.readbuffers > ET61X251_MAX_FRAMES)
+ sp.parm.capture.readbuffers = ET61X251_MAX_FRAMES;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ cam->nreadbuffers = sp.parm.capture.readbuffers;
+
+ return 0;
+}
+
+
+static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp,
+ unsigned int cmd, void __user * arg)
+{
+ struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+
+ switch (cmd) {
+
+ case VIDIOC_QUERYCAP:
+ return et61x251_vidioc_querycap(cam, arg);
+
+ case VIDIOC_ENUMINPUT:
+ return et61x251_vidioc_enuminput(cam, arg);
+
+ case VIDIOC_G_INPUT:
+ return et61x251_vidioc_g_input(cam, arg);
+
+ case VIDIOC_S_INPUT:
+ return et61x251_vidioc_s_input(cam, arg);
+
+ case VIDIOC_QUERYCTRL:
+ return et61x251_vidioc_query_ctrl(cam, arg);
+
+ case VIDIOC_G_CTRL:
+ return et61x251_vidioc_g_ctrl(cam, arg);
+
+ case VIDIOC_S_CTRL_OLD:
+ case VIDIOC_S_CTRL:
+ return et61x251_vidioc_s_ctrl(cam, arg);
+
+ case VIDIOC_CROPCAP_OLD:
+ case VIDIOC_CROPCAP:
+ return et61x251_vidioc_cropcap(cam, arg);
+
+ case VIDIOC_G_CROP:
+ return et61x251_vidioc_g_crop(cam, arg);
+
+ case VIDIOC_S_CROP:
+ return et61x251_vidioc_s_crop(cam, arg);
+
+ case VIDIOC_ENUM_FMT:
+ return et61x251_vidioc_enum_fmt(cam, arg);
+
+ case VIDIOC_G_FMT:
+ return et61x251_vidioc_g_fmt(cam, arg);
+
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT:
+ return et61x251_vidioc_try_s_fmt(cam, cmd, arg);
+
+ case VIDIOC_G_JPEGCOMP:
+ return et61x251_vidioc_g_jpegcomp(cam, arg);
+
+ case VIDIOC_S_JPEGCOMP:
+ return et61x251_vidioc_s_jpegcomp(cam, arg);
+
+ case VIDIOC_REQBUFS:
+ return et61x251_vidioc_reqbufs(cam, arg);
+
+ case VIDIOC_QUERYBUF:
+ return et61x251_vidioc_querybuf(cam, arg);
+
+ case VIDIOC_QBUF:
+ return et61x251_vidioc_qbuf(cam, arg);
+
+ case VIDIOC_DQBUF:
+ return et61x251_vidioc_dqbuf(cam, filp, arg);
+
+ case VIDIOC_STREAMON:
+ return et61x251_vidioc_streamon(cam, arg);
+
+ case VIDIOC_STREAMOFF:
+ return et61x251_vidioc_streamoff(cam, arg);
+
+ case VIDIOC_G_PARM:
+ return et61x251_vidioc_g_parm(cam, arg);
+
+ case VIDIOC_S_PARM_OLD:
+ case VIDIOC_S_PARM:
+ return et61x251_vidioc_s_parm(cam, arg);
+
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_QUERYSTD:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_QUERYMENU:
+ return -EINVAL;
+
+ default:
+ return -EINVAL;
+
+ }
+}
+
+
+static int et61x251_ioctl(struct inode* inode, struct file* filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+ int err = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ V4LDBG(3, "et61x251", cmd);
+
+ err = et61x251_ioctl_v4l2(inode, filp, cmd, (void __user *)arg);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return err;
+}
+
+
+static struct file_operations et61x251_fops = {
+ .owner = THIS_MODULE,
+ .open = et61x251_open,
+ .release = et61x251_release,
+ .ioctl = et61x251_ioctl,
+ .read = et61x251_read,
+ .poll = et61x251_poll,
+ .mmap = et61x251_mmap,
+ .llseek = no_llseek,
+};
+
+/*****************************************************************************/
+
+/* It exists a single interface only. We do not need to validate anything. */
+static int
+et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct et61x251_device* cam;
+ static unsigned int dev_nr = 0;
+ unsigned int i;
+ int err = 0;
+
+ if (!(cam = kzalloc(sizeof(struct et61x251_device), GFP_KERNEL)))
+ return -ENOMEM;
+
+ cam->usbdev = udev;
+
+ if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) {
+ DBG(1, "kmalloc() failed");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ if (!(cam->v4ldev = video_device_alloc())) {
+ DBG(1, "video_device_alloc() failed");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ mutex_init(&cam->dev_mutex);
+
+ DBG(2, "ET61X[12]51 PC Camera Controller detected "
+ "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct);
+
+ for (i = 0; et61x251_sensor_table[i]; i++) {
+ err = et61x251_sensor_table[i](cam);
+ if (!err)
+ break;
+ }
+
+ if (!err)
+ DBG(2, "%s image sensor detected", cam->sensor.name);
+ else {
+ DBG(1, "No supported image sensor detected");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (et61x251_init(cam)) {
+ DBG(1, "Initialization failed. I will retry on open().");
+ cam->state |= DEV_MISCONFIGURED;
+ }
+
+ strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera");
+ cam->v4ldev->owner = THIS_MODULE;
+ cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
+ cam->v4ldev->hardware = 0;
+ cam->v4ldev->fops = &et61x251_fops;
+ cam->v4ldev->minor = video_nr[dev_nr];
+ cam->v4ldev->release = video_device_release;
+ video_set_drvdata(cam->v4ldev, cam);
+
+ mutex_lock(&cam->dev_mutex);
+
+ err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
+ video_nr[dev_nr]);
+ if (err) {
+ DBG(1, "V4L2 device registration failed");
+ if (err == -ENFILE && video_nr[dev_nr] == -1)
+ DBG(1, "Free /dev/videoX node not found");
+ video_nr[dev_nr] = -1;
+ dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
+ mutex_unlock(&cam->dev_mutex);
+ goto fail;
+ }
+
+ DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
+
+ cam->module_param.force_munmap = force_munmap[dev_nr];
+ cam->module_param.frame_timeout = frame_timeout[dev_nr];
+
+ dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ et61x251_create_sysfs(cam);
+ DBG(2, "Optional device control through 'sysfs' interface ready");
+#endif
+
+ usb_set_intfdata(intf, cam);
+
+ mutex_unlock(&cam->dev_mutex);
+
+ return 0;
+
+fail:
+ if (cam) {
+ kfree(cam->control_buffer);
+ if (cam->v4ldev)
+ video_device_release(cam->v4ldev);
+ kfree(cam);
+ }
+ return err;
+}
+
+
+static void et61x251_usb_disconnect(struct usb_interface* intf)
+{
+ struct et61x251_device* cam = usb_get_intfdata(intf);
+
+ if (!cam)
+ return;
+
+ down_write(&et61x251_disconnect);
+
+ mutex_lock(&cam->dev_mutex);
+
+ DBG(2, "Disconnecting %s...", cam->v4ldev->name);
+
+ wake_up_interruptible_all(&cam->open);
+
+ if (cam->users) {
+ DBG(2, "Device /dev/video%d is open! Deregistration and "
+ "memory deallocation are deferred on close.",
+ cam->v4ldev->minor);
+ cam->state |= DEV_MISCONFIGURED;
+ et61x251_stop_transfer(cam);
+ cam->state |= DEV_DISCONNECTED;
+ wake_up_interruptible(&cam->wait_frame);
+ wake_up(&cam->wait_stream);
+ usb_get_dev(cam->usbdev);
+ } else {
+ cam->state |= DEV_DISCONNECTED;
+ et61x251_release_resources(cam);
+ }
+
+ mutex_unlock(&cam->dev_mutex);
+
+ if (!cam->users)
+ kfree(cam);
+
+ up_write(&et61x251_disconnect);
+}
+
+
+static struct usb_driver et61x251_usb_driver = {
+ .name = "et61x251",
+ .id_table = et61x251_id_table,
+ .probe = et61x251_usb_probe,
+ .disconnect = et61x251_usb_disconnect,
+};
+
+/*****************************************************************************/
+
+static int __init et61x251_module_init(void)
+{
+ int err = 0;
+
+ KDBG(2, ET61X251_MODULE_NAME " v" ET61X251_MODULE_VERSION);
+ KDBG(3, ET61X251_MODULE_AUTHOR);
+
+ if ((err = usb_register(&et61x251_usb_driver)))
+ KDBG(1, "usb_register() failed");
+
+ return err;
+}
+
+
+static void __exit et61x251_module_exit(void)
+{
+ usb_deregister(&et61x251_usb_driver);
+}
+
+
+module_init(et61x251_module_init);
+module_exit(et61x251_module_exit);
diff --git a/drivers/media/video/et61x251/et61x251_sensor.h b/drivers/media/video/et61x251/et61x251_sensor.h
new file mode 100644
index 000000000000..56841ae8a207
--- /dev/null
+++ b/drivers/media/video/et61x251/et61x251_sensor.h
@@ -0,0 +1,116 @@
+/***************************************************************************
+ * API for image sensors connected to ET61X[12]51 PC Camera Controllers *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _ET61X251_SENSOR_H_
+#define _ET61X251_SENSOR_H_
+
+#include <linux/usb.h>
+#include <linux/videodev.h>
+#include <linux/device.h>
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <asm/types.h>
+
+struct et61x251_device;
+struct et61x251_sensor;
+
+/*****************************************************************************/
+
+extern int et61x251_probe_tas5130d1b(struct et61x251_device* cam);
+
+#define ET61X251_SENSOR_TABLE \
+/* Weak detections must go at the end of the list */ \
+static int (*et61x251_sensor_table[])(struct et61x251_device*) = { \
+ &et61x251_probe_tas5130d1b, \
+ NULL, \
+};
+
+extern struct et61x251_device*
+et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id);
+
+extern void
+et61x251_attach_sensor(struct et61x251_device* cam,
+ struct et61x251_sensor* sensor);
+
+/*****************************************************************************/
+
+extern int et61x251_write_reg(struct et61x251_device*, u8 value, u16 index);
+extern int et61x251_read_reg(struct et61x251_device*, u16 index);
+extern int et61x251_i2c_write(struct et61x251_device*, u8 address, u8 value);
+extern int et61x251_i2c_read(struct et61x251_device*, u8 address);
+extern int et61x251_i2c_try_write(struct et61x251_device*,
+ struct et61x251_sensor*, u8 address,
+ u8 value);
+extern int et61x251_i2c_try_read(struct et61x251_device*,
+ struct et61x251_sensor*, u8 address);
+extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1,
+ u8 data2, u8 data3, u8 data4, u8 data5,
+ u8 data6, u8 data7, u8 data8, u8 address);
+
+/*****************************************************************************/
+
+enum et61x251_i2c_sysfs_ops {
+ ET61X251_I2C_READ = 0x01,
+ ET61X251_I2C_WRITE = 0x02,
+};
+
+enum et61x251_i2c_interface {
+ ET61X251_I2C_2WIRES,
+ ET61X251_I2C_3WIRES,
+};
+
+/* Repeat start condition when RSTA is high */
+enum et61x251_i2c_rsta {
+ ET61X251_I2C_RSTA_STOP = 0x00, /* stop then start */
+ ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */
+};
+
+#define ET61X251_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10
+
+struct et61x251_sensor {
+ char name[32];
+
+ enum et61x251_i2c_sysfs_ops sysfs_ops;
+
+ enum et61x251_i2c_interface interface;
+ u8 i2c_slave_id;
+ enum et61x251_i2c_rsta rsta;
+ struct v4l2_rect active_pixel; /* left and top define FVSX and FVSY */
+
+ struct v4l2_queryctrl qctrl[ET61X251_MAX_CTRLS];
+ struct v4l2_cropcap cropcap;
+ struct v4l2_pix_format pix_format;
+
+ int (*init)(struct et61x251_device* cam);
+ int (*get_ctrl)(struct et61x251_device* cam,
+ struct v4l2_control* ctrl);
+ int (*set_ctrl)(struct et61x251_device* cam,
+ const struct v4l2_control* ctrl);
+ int (*set_crop)(struct et61x251_device* cam,
+ const struct v4l2_rect* rect);
+ int (*set_pix_format)(struct et61x251_device* cam,
+ const struct v4l2_pix_format* pix);
+
+ /* Private */
+ struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS];
+ struct v4l2_rect _rect;
+};
+
+#endif /* _ET61X251_SENSOR_H_ */
diff --git a/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/drivers/media/video/et61x251/et61x251_tas5130d1b.c
new file mode 100644
index 000000000000..3998d76a307a
--- /dev/null
+++ b/drivers/media/video/et61x251/et61x251_tas5130d1b.c
@@ -0,0 +1,141 @@
+/***************************************************************************
+ * Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51 *
+ * PC Camera Controllers *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "et61x251_sensor.h"
+
+
+static int tas5130d1b_init(struct et61x251_device* cam)
+{
+ int err = 0;
+
+ err += et61x251_write_reg(cam, 0x14, 0x01);
+ err += et61x251_write_reg(cam, 0x1b, 0x02);
+ err += et61x251_write_reg(cam, 0x02, 0x12);
+ err += et61x251_write_reg(cam, 0x0e, 0x60);
+ err += et61x251_write_reg(cam, 0x80, 0x61);
+ err += et61x251_write_reg(cam, 0xf0, 0x62);
+ err += et61x251_write_reg(cam, 0x03, 0x63);
+ err += et61x251_write_reg(cam, 0x14, 0x64);
+ err += et61x251_write_reg(cam, 0xf4, 0x65);
+ err += et61x251_write_reg(cam, 0x01, 0x66);
+ err += et61x251_write_reg(cam, 0x05, 0x67);
+ err += et61x251_write_reg(cam, 0x8f, 0x68);
+ err += et61x251_write_reg(cam, 0x0f, 0x8d);
+ err += et61x251_write_reg(cam, 0x08, 0x8e);
+
+ return err;
+}
+
+
+static int tas5130d1b_set_ctrl(struct et61x251_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ err += et61x251_i2c_raw_write(cam, 2, 0x20,
+ 0xf6-ctrl->value, 0, 0, 0,
+ 0, 0, 0, 0);
+ break;
+ case V4L2_CID_EXPOSURE:
+ err += et61x251_i2c_raw_write(cam, 2, 0x40,
+ 0x47-ctrl->value, 0, 0, 0,
+ 0, 0, 0, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static struct et61x251_sensor tas5130d1b = {
+ .name = "TAS5130D1B",
+ .interface = ET61X251_I2C_3WIRES,
+ .rsta = ET61X251_I2C_RSTA_STOP,
+ .active_pixel = {
+ .left = 106,
+ .top = 13,
+ },
+ .init = &tas5130d1b_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0xf6,
+ .step = 0x02,
+ .default_value = 0x0d,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0x47,
+ .step = 0x01,
+ .default_value = 0x23,
+ .flags = 0,
+ },
+ },
+ .set_ctrl = &tas5130d1b_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+};
+
+
+int et61x251_probe_tas5130d1b(struct et61x251_device* cam)
+{
+ const struct usb_device_id tas5130d1b_id_table[] = {
+ { USB_DEVICE(0x102c, 0x6251), },
+ { }
+ };
+
+ /* Sensor detection is based on USB pid/vid */
+ if (!et61x251_match_id(cam, tas5130d1b_id_table))
+ return -ENODEV;
+
+ et61x251_attach_sensor(cam, &tas5130d1b);
+
+ return 0;
+}
diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c
new file mode 100644
index 000000000000..da44579d6f29
--- /dev/null
+++ b/drivers/media/video/ov511.c
@@ -0,0 +1,5932 @@
+/*
+ * OmniVision OV511 Camera-to-USB Bridge Driver
+ *
+ * Copyright (c) 1999-2003 Mark W. McClelland
+ * Original decompression code Copyright 1998-2000 OmniVision Technologies
+ * Many improvements by Bret Wallach <bwallac1@san.rr.com>
+ * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
+ * Snapshot code by Kevin Moore
+ * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
+ * Changes by Claudio Matsuoka <claudio@conectiva.com>
+ * Original SAA7111A code by Dave Perks <dperks@ibm.net>
+ * URB error messages from pwc driver by Nemosoft
+ * generic_ioctl() code from videodev.c by Gerd Knorr and Alan Cox
+ * Memory management (rvmalloc) code from bttv driver, by Gerd Knorr and others
+ *
+ * Based on the Linux CPiA driver written by Peter Pregler,
+ * Scott J. Bertin and Johannes Erdfelt.
+ *
+ * Please see the file: Documentation/usb/ov511.txt
+ * and the website at: http://alpha.dyndns.org/ov511
+ * for more info.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+
+#if defined (__i386__)
+ #include <asm/cpufeature.h>
+#endif
+
+#include "ov511.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.64 for Linux 2.5"
+#define EMAIL "mark@alpha.dyndns.org"
+#define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.org> & Bret Wallach \
+ & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \
+ <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"
+#define DRIVER_DESC "ov511 USB Camera Driver"
+
+#define OV511_I2C_RETRIES 3
+#define ENABLE_Y_QUANTABLE 1
+#define ENABLE_UV_QUANTABLE 1
+
+#define OV511_MAX_UNIT_VIDEO 16
+
+/* Pixel count * bytes per YUV420 pixel (1.5) */
+#define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3 / 2)
+
+#define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval))
+
+/* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */
+#define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 3 / 2 + 1024)
+
+#define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)
+
+/**********************************************************************
+ * Module Parameters
+ * (See ov511.txt for detailed descriptions of these)
+ **********************************************************************/
+
+/* These variables (and all static globals) default to zero */
+static int autobright = 1;
+static int autogain = 1;
+static int autoexp = 1;
+static int debug;
+static int snapshot;
+static int cams = 1;
+static int compress;
+static int testpat;
+static int dumppix;
+static int led = 1;
+static int dump_bridge;
+static int dump_sensor;
+static int printph;
+static int phy = 0x1f;
+static int phuv = 0x05;
+static int pvy = 0x06;
+static int pvuv = 0x06;
+static int qhy = 0x14;
+static int qhuv = 0x03;
+static int qvy = 0x04;
+static int qvuv = 0x04;
+static int lightfreq;
+static int bandingfilter;
+static int clockdiv = -1;
+static int packetsize = -1;
+static int framedrop = -1;
+static int fastset;
+static int force_palette;
+static int backlight;
+static int unit_video[OV511_MAX_UNIT_VIDEO];
+static int remove_zeros;
+static int mirror;
+static int ov518_color;
+
+module_param(autobright, int, 0);
+MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness");
+module_param(autogain, int, 0);
+MODULE_PARM_DESC(autogain, "Sensor automatically changes gain");
+module_param(autoexp, int, 0);
+MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure");
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug,
+ "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max");
+module_param(snapshot, int, 0);
+MODULE_PARM_DESC(snapshot, "Enable snapshot mode");
+module_param(cams, int, 0);
+MODULE_PARM_DESC(cams, "Number of simultaneous cameras");
+module_param(compress, int, 0);
+MODULE_PARM_DESC(compress, "Turn on compression");
+module_param(testpat, int, 0);
+MODULE_PARM_DESC(testpat,
+ "Replace image with vertical bar testpattern (only partially working)");
+module_param(dumppix, int, 0);
+MODULE_PARM_DESC(dumppix, "Dump raw pixel data");
+module_param(led, int, 0);
+MODULE_PARM_DESC(led,
+ "LED policy (OV511+ or later). 0=off, 1=on (default), 2=auto (on when open)");
+module_param(dump_bridge, int, 0);
+MODULE_PARM_DESC(dump_bridge, "Dump the bridge registers");
+module_param(dump_sensor, int, 0);
+MODULE_PARM_DESC(dump_sensor, "Dump the sensor registers");
+module_param(printph, int, 0);
+MODULE_PARM_DESC(printph, "Print frame start/end headers");
+module_param(phy, int, 0);
+MODULE_PARM_DESC(phy, "Prediction range (horiz. Y)");
+module_param(phuv, int, 0);
+MODULE_PARM_DESC(phuv, "Prediction range (horiz. UV)");
+module_param(pvy, int, 0);
+MODULE_PARM_DESC(pvy, "Prediction range (vert. Y)");
+module_param(pvuv, int, 0);
+MODULE_PARM_DESC(pvuv, "Prediction range (vert. UV)");
+module_param(qhy, int, 0);
+MODULE_PARM_DESC(qhy, "Quantization threshold (horiz. Y)");
+module_param(qhuv, int, 0);
+MODULE_PARM_DESC(qhuv, "Quantization threshold (horiz. UV)");
+module_param(qvy, int, 0);
+MODULE_PARM_DESC(qvy, "Quantization threshold (vert. Y)");
+module_param(qvuv, int, 0);
+MODULE_PARM_DESC(qvuv, "Quantization threshold (vert. UV)");
+module_param(lightfreq, int, 0);
+MODULE_PARM_DESC(lightfreq,
+ "Light frequency. Set to 50 or 60 Hz, or zero for default settings");
+module_param(bandingfilter, int, 0);
+MODULE_PARM_DESC(bandingfilter,
+ "Enable banding filter (to reduce effects of fluorescent lighting)");
+module_param(clockdiv, int, 0);
+MODULE_PARM_DESC(clockdiv, "Force pixel clock divisor to a specific value");
+module_param(packetsize, int, 0);
+MODULE_PARM_DESC(packetsize, "Force a specific isoc packet size");
+module_param(framedrop, int, 0);
+MODULE_PARM_DESC(framedrop, "Force a specific frame drop register setting");
+module_param(fastset, int, 0);
+MODULE_PARM_DESC(fastset, "Allows picture settings to take effect immediately");
+module_param(force_palette, int, 0);
+MODULE_PARM_DESC(force_palette, "Force the palette to a specific value");
+module_param(backlight, int, 0);
+MODULE_PARM_DESC(backlight, "For objects that are lit from behind");
+static int num_uv;
+module_param_array(unit_video, int, &num_uv, 0);
+MODULE_PARM_DESC(unit_video,
+ "Force use of specific minor number(s). 0 is not allowed.");
+module_param(remove_zeros, int, 0);
+MODULE_PARM_DESC(remove_zeros,
+ "Remove zero-padding from uncompressed incoming data");
+module_param(mirror, int, 0);
+MODULE_PARM_DESC(mirror, "Reverse image horizontally");
+module_param(ov518_color, int, 0);
+MODULE_PARM_DESC(ov518_color, "Enable OV518 color (experimental)");
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/**********************************************************************
+ * Miscellaneous Globals
+ **********************************************************************/
+
+static struct usb_driver ov511_driver;
+
+/* Number of times to retry a failed I2C transaction. Increase this if you
+ * are getting "Failed to read sensor ID..." */
+static const int i2c_detect_tries = 5;
+
+static struct usb_device_id device_table [] = {
+ { USB_DEVICE(VEND_OMNIVISION, PROD_OV511) },
+ { USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) },
+ { USB_DEVICE(VEND_OMNIVISION, PROD_OV518) },
+ { USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) },
+ { USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, device_table);
+
+static unsigned char yQuanTable511[] = OV511_YQUANTABLE;
+static unsigned char uvQuanTable511[] = OV511_UVQUANTABLE;
+static unsigned char yQuanTable518[] = OV518_YQUANTABLE;
+static unsigned char uvQuanTable518[] = OV518_UVQUANTABLE;
+
+/**********************************************************************
+ * Symbolic Names
+ **********************************************************************/
+
+/* Known OV511-based cameras */
+static struct symbolic_list camlist[] = {
+ { 0, "Generic Camera (no ID)" },
+ { 1, "Mustek WCam 3X" },
+ { 3, "D-Link DSB-C300" },
+ { 4, "Generic OV511/OV7610" },
+ { 5, "Puretek PT-6007" },
+ { 6, "Lifeview USB Life TV (NTSC)" },
+ { 21, "Creative Labs WebCam 3" },
+ { 22, "Lifeview USB Life TV (PAL D/K+B/G)" },
+ { 36, "Koala-Cam" },
+ { 38, "Lifeview USB Life TV (PAL)" },
+ { 41, "Samsung Anycam MPC-M10" },
+ { 43, "Mtekvision Zeca MV402" },
+ { 46, "Suma eON" },
+ { 70, "Lifeview USB Life TV (PAL/SECAM)" },
+ { 100, "Lifeview RoboCam" },
+ { 102, "AverMedia InterCam Elite" },
+ { 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */
+ { 134, "Ezonics EZCam II" },
+ { 192, "Webeye 2000B" },
+ { 253, "Alpha Vision Tech. AlphaCam SE" },
+ { -1, NULL }
+};
+
+/* Video4Linux1 Palettes */
+static struct symbolic_list v4l1_plist[] = {
+ { VIDEO_PALETTE_GREY, "GREY" },
+ { VIDEO_PALETTE_HI240, "HI240" },
+ { VIDEO_PALETTE_RGB565, "RGB565" },
+ { VIDEO_PALETTE_RGB24, "RGB24" },
+ { VIDEO_PALETTE_RGB32, "RGB32" },
+ { VIDEO_PALETTE_RGB555, "RGB555" },
+ { VIDEO_PALETTE_YUV422, "YUV422" },
+ { VIDEO_PALETTE_YUYV, "YUYV" },
+ { VIDEO_PALETTE_UYVY, "UYVY" },
+ { VIDEO_PALETTE_YUV420, "YUV420" },
+ { VIDEO_PALETTE_YUV411, "YUV411" },
+ { VIDEO_PALETTE_RAW, "RAW" },
+ { VIDEO_PALETTE_YUV422P,"YUV422P" },
+ { VIDEO_PALETTE_YUV411P,"YUV411P" },
+ { VIDEO_PALETTE_YUV420P,"YUV420P" },
+ { VIDEO_PALETTE_YUV410P,"YUV410P" },
+ { -1, NULL }
+};
+
+static struct symbolic_list brglist[] = {
+ { BRG_OV511, "OV511" },
+ { BRG_OV511PLUS, "OV511+" },
+ { BRG_OV518, "OV518" },
+ { BRG_OV518PLUS, "OV518+" },
+ { -1, NULL }
+};
+
+static struct symbolic_list senlist[] = {
+ { SEN_OV76BE, "OV76BE" },
+ { SEN_OV7610, "OV7610" },
+ { SEN_OV7620, "OV7620" },
+ { SEN_OV7620AE, "OV7620AE" },
+ { SEN_OV6620, "OV6620" },
+ { SEN_OV6630, "OV6630" },
+ { SEN_OV6630AE, "OV6630AE" },
+ { SEN_OV6630AF, "OV6630AF" },
+ { SEN_OV8600, "OV8600" },
+ { SEN_KS0127, "KS0127" },
+ { SEN_KS0127B, "KS0127B" },
+ { SEN_SAA7111A, "SAA7111A" },
+ { -1, NULL }
+};
+
+/* URB error codes: */
+static struct symbolic_list urb_errlist[] = {
+ { -ENOSR, "Buffer error (overrun)" },
+ { -EPIPE, "Stalled (device not responding)" },
+ { -EOVERFLOW, "Babble (bad cable?)" },
+ { -EPROTO, "Bit-stuff error (bad cable?)" },
+ { -EILSEQ, "CRC/Timeout" },
+ { -ETIMEDOUT, "NAK (device does not respond)" },
+ { -1, NULL }
+};
+
+/**********************************************************************
+ * Memory management
+ **********************************************************************/
+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 (size > 0) {
+ SetPageReserved(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;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
+/**********************************************************************
+ *
+ * Register I/O
+ *
+ **********************************************************************/
+
+/* Write an OV51x register */
+static int
+reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value)
+{
+ int rc;
+
+ PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+ mutex_lock(&ov->cbuf_lock);
+ ov->cbuf[0] = value;
+ rc = usb_control_msg(ov->dev,
+ usb_sndctrlpipe(ov->dev, 0),
+ (ov->bclass == BCL_OV518)?1:2 /* REG_IO */,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, (__u16)reg, &ov->cbuf[0], 1, 1000);
+ mutex_unlock(&ov->cbuf_lock);
+
+ if (rc < 0)
+ err("reg write: error %d: %s", rc, symbolic(urb_errlist, rc));
+
+ return rc;
+}
+
+/* Read from an OV51x register */
+/* returns: negative is error, pos or zero is data */
+static int
+reg_r(struct usb_ov511 *ov, unsigned char reg)
+{
+ int rc;
+
+ mutex_lock(&ov->cbuf_lock);
+ rc = usb_control_msg(ov->dev,
+ usb_rcvctrlpipe(ov->dev, 0),
+ (ov->bclass == BCL_OV518)?1:3 /* REG_IO */,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, (__u16)reg, &ov->cbuf[0], 1, 1000);
+
+ if (rc < 0) {
+ err("reg read: error %d: %s", rc, symbolic(urb_errlist, rc));
+ } else {
+ rc = ov->cbuf[0];
+ PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]);
+ }
+
+ mutex_unlock(&ov->cbuf_lock);
+
+ return rc;
+}
+
+/*
+ * Writes bits at positions specified by mask to an OV51x reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless
+ * of their respective state in "value".
+ */
+static int
+reg_w_mask(struct usb_ov511 *ov,
+ unsigned char reg,
+ unsigned char value,
+ unsigned char mask)
+{
+ int ret;
+ unsigned char oldval, newval;
+
+ ret = reg_r(ov, reg);
+ if (ret < 0)
+ return ret;
+
+ oldval = (unsigned char) ret;
+ oldval &= (~mask); /* Clear the masked bits */
+ value &= mask; /* Enforce mask on value */
+ newval = oldval | value; /* Set the desired bits */
+
+ return (reg_w(ov, reg, newval));
+}
+
+/*
+ * Writes multiple (n) byte value to a single register. Only valid with certain
+ * registers (0x30 and 0xc4 - 0xce).
+ */
+static int
+ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n)
+{
+ int rc;
+
+ PDEBUG(5, "0x%02X:%7d, n=%d", reg, val, n);
+
+ mutex_lock(&ov->cbuf_lock);
+
+ *((__le32 *)ov->cbuf) = __cpu_to_le32(val);
+
+ rc = usb_control_msg(ov->dev,
+ usb_sndctrlpipe(ov->dev, 0),
+ 1 /* REG_IO */,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, (__u16)reg, ov->cbuf, n, 1000);
+ mutex_unlock(&ov->cbuf_lock);
+
+ if (rc < 0)
+ err("reg write multiple: error %d: %s", rc,
+ symbolic(urb_errlist, rc));
+
+ return rc;
+}
+
+static int
+ov511_upload_quan_tables(struct usb_ov511 *ov)
+{
+ unsigned char *pYTable = yQuanTable511;
+ unsigned char *pUVTable = uvQuanTable511;
+ unsigned char val0, val1;
+ int i, rc, reg = R511_COMP_LUT_BEGIN;
+
+ PDEBUG(4, "Uploading quantization tables");
+
+ for (i = 0; i < OV511_QUANTABLESIZE / 2; i++) {
+ if (ENABLE_Y_QUANTABLE) {
+ val0 = *pYTable++;
+ val1 = *pYTable++;
+ val0 &= 0x0f;
+ val1 &= 0x0f;
+ val0 |= val1 << 4;
+ rc = reg_w(ov, reg, val0);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (ENABLE_UV_QUANTABLE) {
+ val0 = *pUVTable++;
+ val1 = *pUVTable++;
+ val0 &= 0x0f;
+ val1 &= 0x0f;
+ val0 |= val1 << 4;
+ rc = reg_w(ov, reg + OV511_QUANTABLESIZE/2, val0);
+ if (rc < 0)
+ return rc;
+ }
+
+ reg++;
+ }
+
+ return 0;
+}
+
+/* OV518 quantization tables are 8x4 (instead of 8x8) */
+static int
+ov518_upload_quan_tables(struct usb_ov511 *ov)
+{
+ unsigned char *pYTable = yQuanTable518;
+ unsigned char *pUVTable = uvQuanTable518;
+ unsigned char val0, val1;
+ int i, rc, reg = R511_COMP_LUT_BEGIN;
+
+ PDEBUG(4, "Uploading quantization tables");
+
+ for (i = 0; i < OV518_QUANTABLESIZE / 2; i++) {
+ if (ENABLE_Y_QUANTABLE) {
+ val0 = *pYTable++;
+ val1 = *pYTable++;
+ val0 &= 0x0f;
+ val1 &= 0x0f;
+ val0 |= val1 << 4;
+ rc = reg_w(ov, reg, val0);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (ENABLE_UV_QUANTABLE) {
+ val0 = *pUVTable++;
+ val1 = *pUVTable++;
+ val0 &= 0x0f;
+ val1 &= 0x0f;
+ val0 |= val1 << 4;
+ rc = reg_w(ov, reg + OV518_QUANTABLESIZE/2, val0);
+ if (rc < 0)
+ return rc;
+ }
+
+ reg++;
+ }
+
+ return 0;
+}
+
+static int
+ov51x_reset(struct usb_ov511 *ov, unsigned char reset_type)
+{
+ int rc;
+
+ /* Setting bit 0 not allowed on 518/518Plus */
+ if (ov->bclass == BCL_OV518)
+ reset_type &= 0xfe;
+
+ PDEBUG(4, "Reset: type=0x%02X", reset_type);
+
+ rc = reg_w(ov, R51x_SYS_RESET, reset_type);
+ rc = reg_w(ov, R51x_SYS_RESET, 0);
+
+ if (rc < 0)
+ err("reset: command failed");
+
+ return rc;
+}
+
+/**********************************************************************
+ *
+ * Low-level I2C I/O functions
+ *
+ **********************************************************************/
+
+/* NOTE: Do not call this function directly!
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from i2c_w(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int
+ov518_i2c_write_internal(struct usb_ov511 *ov,
+ unsigned char reg,
+ unsigned char value)
+{
+ int rc;
+
+ PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+ /* Select camera register */
+ rc = reg_w(ov, R51x_I2C_SADDR_3, reg);
+ if (rc < 0)
+ return rc;
+
+ /* Write "value" to I2C data port of OV511 */
+ rc = reg_w(ov, R51x_I2C_DATA, value);
+ if (rc < 0)
+ return rc;
+
+ /* Initiate 3-byte write cycle */
+ rc = reg_w(ov, R518_I2C_CTL, 0x01);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+/* NOTE: Do not call this function directly! */
+static int
+ov511_i2c_write_internal(struct usb_ov511 *ov,
+ unsigned char reg,
+ unsigned char value)
+{
+ int rc, retries;
+
+ PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+ /* Three byte write cycle */
+ for (retries = OV511_I2C_RETRIES; ; ) {
+ /* Select camera register */
+ rc = reg_w(ov, R51x_I2C_SADDR_3, reg);
+ if (rc < 0)
+ break;
+
+ /* Write "value" to I2C data port of OV511 */
+ rc = reg_w(ov, R51x_I2C_DATA, value);
+ if (rc < 0)
+ break;
+
+ /* Initiate 3-byte write cycle */
+ rc = reg_w(ov, R511_I2C_CTL, 0x01);
+ if (rc < 0)
+ break;
+
+ /* Retry until idle */
+ do
+ rc = reg_r(ov, R511_I2C_CTL);
+ while (rc > 0 && ((rc&1) == 0));
+ if (rc < 0)
+ break;
+
+ /* Ack? */
+ if ((rc&2) == 0) {
+ rc = 0;
+ break;
+ }
+#if 0
+ /* I2C abort */
+ reg_w(ov, R511_I2C_CTL, 0x10);
+#endif
+ if (--retries < 0) {
+ err("i2c write retries exhausted");
+ rc = -1;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/* NOTE: Do not call this function directly!
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from i2c_r(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int
+ov518_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg)
+{
+ int rc, value;
+
+ /* Select camera register */
+ rc = reg_w(ov, R51x_I2C_SADDR_2, reg);
+ if (rc < 0)
+ return rc;
+
+ /* Initiate 2-byte write cycle */
+ rc = reg_w(ov, R518_I2C_CTL, 0x03);
+ if (rc < 0)
+ return rc;
+
+ /* Initiate 2-byte read cycle */
+ rc = reg_w(ov, R518_I2C_CTL, 0x05);
+ if (rc < 0)
+ return rc;
+
+ value = reg_r(ov, R51x_I2C_DATA);
+
+ PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+ return value;
+}
+
+/* NOTE: Do not call this function directly!
+ * returns: negative is error, pos or zero is data */
+static int
+ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg)
+{
+ int rc, value, retries;
+
+ /* Two byte write cycle */
+ for (retries = OV511_I2C_RETRIES; ; ) {
+ /* Select camera register */
+ rc = reg_w(ov, R51x_I2C_SADDR_2, reg);
+ if (rc < 0)
+ return rc;
+
+ /* Initiate 2-byte write cycle */
+ rc = reg_w(ov, R511_I2C_CTL, 0x03);
+ if (rc < 0)
+ return rc;
+
+ /* Retry until idle */
+ do
+ rc = reg_r(ov, R511_I2C_CTL);
+ while (rc > 0 && ((rc&1) == 0));
+ if (rc < 0)
+ return rc;
+
+ if ((rc&2) == 0) /* Ack? */
+ break;
+
+ /* I2C abort */
+ reg_w(ov, R511_I2C_CTL, 0x10);
+
+ if (--retries < 0) {
+ err("i2c write retries exhausted");
+ return -1;
+ }
+ }
+
+ /* Two byte read cycle */
+ for (retries = OV511_I2C_RETRIES; ; ) {
+ /* Initiate 2-byte read cycle */
+ rc = reg_w(ov, R511_I2C_CTL, 0x05);
+ if (rc < 0)
+ return rc;
+
+ /* Retry until idle */
+ do
+ rc = reg_r(ov, R511_I2C_CTL);
+ while (rc > 0 && ((rc&1) == 0));
+ if (rc < 0)
+ return rc;
+
+ if ((rc&2) == 0) /* Ack? */
+ break;
+
+ /* I2C abort */
+ rc = reg_w(ov, R511_I2C_CTL, 0x10);
+ if (rc < 0)
+ return rc;
+
+ if (--retries < 0) {
+ err("i2c read retries exhausted");
+ return -1;
+ }
+ }
+
+ value = reg_r(ov, R51x_I2C_DATA);
+
+ PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+ /* This is needed to make i2c_w() work */
+ rc = reg_w(ov, R511_I2C_CTL, 0x05);
+ if (rc < 0)
+ return rc;
+
+ return value;
+}
+
+/* returns: negative is error, pos or zero is data */
+static int
+i2c_r(struct usb_ov511 *ov, unsigned char reg)
+{
+ int rc;
+
+ mutex_lock(&ov->i2c_lock);
+
+ if (ov->bclass == BCL_OV518)
+ rc = ov518_i2c_read_internal(ov, reg);
+ else
+ rc = ov511_i2c_read_internal(ov, reg);
+
+ mutex_unlock(&ov->i2c_lock);
+
+ return rc;
+}
+
+static int
+i2c_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value)
+{
+ int rc;
+
+ mutex_lock(&ov->i2c_lock);
+
+ if (ov->bclass == BCL_OV518)
+ rc = ov518_i2c_write_internal(ov, reg, value);
+ else
+ rc = ov511_i2c_write_internal(ov, reg, value);
+
+ mutex_unlock(&ov->i2c_lock);
+
+ return rc;
+}
+
+/* Do not call this function directly! */
+static int
+ov51x_i2c_write_mask_internal(struct usb_ov511 *ov,
+ unsigned char reg,
+ unsigned char value,
+ unsigned char mask)
+{
+ int rc;
+ unsigned char oldval, newval;
+
+ if (mask == 0xff) {
+ newval = value;
+ } else {
+ if (ov->bclass == BCL_OV518)
+ rc = ov518_i2c_read_internal(ov, reg);
+ else
+ rc = ov511_i2c_read_internal(ov, reg);
+ if (rc < 0)
+ return rc;
+
+ oldval = (unsigned char) rc;
+ oldval &= (~mask); /* Clear the masked bits */
+ value &= mask; /* Enforce mask on value */
+ newval = oldval | value; /* Set the desired bits */
+ }
+
+ if (ov->bclass == BCL_OV518)
+ return (ov518_i2c_write_internal(ov, reg, newval));
+ else
+ return (ov511_i2c_write_internal(ov, reg, newval));
+}
+
+/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless
+ * of their respective state in "value".
+ */
+static int
+i2c_w_mask(struct usb_ov511 *ov,
+ unsigned char reg,
+ unsigned char value,
+ unsigned char mask)
+{
+ int rc;
+
+ mutex_lock(&ov->i2c_lock);
+ rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask);
+ mutex_unlock(&ov->i2c_lock);
+
+ return rc;
+}
+
+/* Set the read and write slave IDs. The "slave" argument is the write slave,
+ * and the read slave will be set to (slave + 1). ov->i2c_lock should be held
+ * when calling this. This should not be called from outside the i2c I/O
+ * functions.
+ */
+static int
+i2c_set_slave_internal(struct usb_ov511 *ov, unsigned char slave)
+{
+ int rc;
+
+ rc = reg_w(ov, R51x_I2C_W_SID, slave);
+ if (rc < 0)
+ return rc;
+
+ rc = reg_w(ov, R51x_I2C_R_SID, slave + 1);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+/* Write to a specific I2C slave ID and register, using the specified mask */
+static int
+i2c_w_slave(struct usb_ov511 *ov,
+ unsigned char slave,
+ unsigned char reg,
+ unsigned char value,
+ unsigned char mask)
+{
+ int rc = 0;
+
+ mutex_lock(&ov->i2c_lock);
+
+ /* Set new slave IDs */
+ rc = i2c_set_slave_internal(ov, slave);
+ if (rc < 0)
+ goto out;
+
+ rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask);
+
+out:
+ /* Restore primary IDs */
+ if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0)
+ err("Couldn't restore primary I2C slave");
+
+ mutex_unlock(&ov->i2c_lock);
+ return rc;
+}
+
+/* Read from a specific I2C slave ID and register */
+static int
+i2c_r_slave(struct usb_ov511 *ov,
+ unsigned char slave,
+ unsigned char reg)
+{
+ int rc;
+
+ mutex_lock(&ov->i2c_lock);
+
+ /* Set new slave IDs */
+ rc = i2c_set_slave_internal(ov, slave);
+ if (rc < 0)
+ goto out;
+
+ if (ov->bclass == BCL_OV518)
+ rc = ov518_i2c_read_internal(ov, reg);
+ else
+ rc = ov511_i2c_read_internal(ov, reg);
+
+out:
+ /* Restore primary IDs */
+ if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0)
+ err("Couldn't restore primary I2C slave");
+
+ mutex_unlock(&ov->i2c_lock);
+ return rc;
+}
+
+/* Sets I2C read and write slave IDs. Returns <0 for error */
+static int
+ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid)
+{
+ int rc;
+
+ mutex_lock(&ov->i2c_lock);
+
+ rc = i2c_set_slave_internal(ov, sid);
+ if (rc < 0)
+ goto out;
+
+ // FIXME: Is this actually necessary?
+ rc = ov51x_reset(ov, OV511_RESET_NOREGS);
+out:
+ mutex_unlock(&ov->i2c_lock);
+ return rc;
+}
+
+static int
+write_regvals(struct usb_ov511 *ov, struct ov511_regvals * pRegvals)
+{
+ int rc;
+
+ while (pRegvals->bus != OV511_DONE_BUS) {
+ if (pRegvals->bus == OV511_REG_BUS) {
+ if ((rc = reg_w(ov, pRegvals->reg, pRegvals->val)) < 0)
+ return rc;
+ } else if (pRegvals->bus == OV511_I2C_BUS) {
+ if ((rc = i2c_w(ov, pRegvals->reg, pRegvals->val)) < 0)
+ return rc;
+ } else {
+ err("Bad regval array");
+ return -1;
+ }
+ pRegvals++;
+ }
+ return 0;
+}
+
+#ifdef OV511_DEBUG
+static void
+dump_i2c_range(struct usb_ov511 *ov, int reg1, int regn)
+{
+ int i, rc;
+
+ for (i = reg1; i <= regn; i++) {
+ rc = i2c_r(ov, i);
+ info("Sensor[0x%02X] = 0x%02X", i, rc);
+ }
+}
+
+static void
+dump_i2c_regs(struct usb_ov511 *ov)
+{
+ info("I2C REGS");
+ dump_i2c_range(ov, 0x00, 0x7C);
+}
+
+static void
+dump_reg_range(struct usb_ov511 *ov, int reg1, int regn)
+{
+ int i, rc;
+
+ for (i = reg1; i <= regn; i++) {
+ rc = reg_r(ov, i);
+ info("OV511[0x%02X] = 0x%02X", i, rc);
+ }
+}
+
+static void
+ov511_dump_regs(struct usb_ov511 *ov)
+{
+ info("CAMERA INTERFACE REGS");
+ dump_reg_range(ov, 0x10, 0x1f);
+ info("DRAM INTERFACE REGS");
+ dump_reg_range(ov, 0x20, 0x23);
+ info("ISO FIFO REGS");
+ dump_reg_range(ov, 0x30, 0x31);
+ info("PIO REGS");
+ dump_reg_range(ov, 0x38, 0x39);
+ dump_reg_range(ov, 0x3e, 0x3e);
+ info("I2C REGS");
+ dump_reg_range(ov, 0x40, 0x49);
+ info("SYSTEM CONTROL REGS");
+ dump_reg_range(ov, 0x50, 0x55);
+ dump_reg_range(ov, 0x5e, 0x5f);
+ info("OmniCE REGS");
+ dump_reg_range(ov, 0x70, 0x79);
+ /* NOTE: Quantization tables are not readable. You will get the value
+ * in reg. 0x79 for every table register */
+ dump_reg_range(ov, 0x80, 0x9f);
+ dump_reg_range(ov, 0xa0, 0xbf);
+
+}
+
+static void
+ov518_dump_regs(struct usb_ov511 *ov)
+{
+ info("VIDEO MODE REGS");
+ dump_reg_range(ov, 0x20, 0x2f);
+ info("DATA PUMP AND SNAPSHOT REGS");
+ dump_reg_range(ov, 0x30, 0x3f);
+ info("I2C REGS");
+ dump_reg_range(ov, 0x40, 0x4f);
+ info("SYSTEM CONTROL AND VENDOR REGS");
+ dump_reg_range(ov, 0x50, 0x5f);
+ info("60 - 6F");
+ dump_reg_range(ov, 0x60, 0x6f);
+ info("70 - 7F");
+ dump_reg_range(ov, 0x70, 0x7f);
+ info("Y QUANTIZATION TABLE");
+ dump_reg_range(ov, 0x80, 0x8f);
+ info("UV QUANTIZATION TABLE");
+ dump_reg_range(ov, 0x90, 0x9f);
+ info("A0 - BF");
+ dump_reg_range(ov, 0xa0, 0xbf);
+ info("CBR");
+ dump_reg_range(ov, 0xc0, 0xcf);
+}
+#endif
+
+/*****************************************************************************/
+
+/* Temporarily stops OV511 from functioning. Must do this before changing
+ * registers while the camera is streaming */
+static inline int
+ov51x_stop(struct usb_ov511 *ov)
+{
+ PDEBUG(4, "stopping");
+ ov->stopped = 1;
+ if (ov->bclass == BCL_OV518)
+ return (reg_w_mask(ov, R51x_SYS_RESET, 0x3a, 0x3a));
+ else
+ return (reg_w(ov, R51x_SYS_RESET, 0x3d));
+}
+
+/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not
+ * actually stopped (for performance). */
+static inline int
+ov51x_restart(struct usb_ov511 *ov)
+{
+ if (ov->stopped) {
+ PDEBUG(4, "restarting");
+ ov->stopped = 0;
+
+ /* Reinitialize the stream */
+ if (ov->bclass == BCL_OV518)
+ reg_w(ov, 0x2f, 0x80);
+
+ return (reg_w(ov, R51x_SYS_RESET, 0x00));
+ }
+
+ return 0;
+}
+
+/* Sleeps until no frames are active. Returns !0 if got signal */
+static int
+ov51x_wait_frames_inactive(struct usb_ov511 *ov)
+{
+ return wait_event_interruptible(ov->wq, ov->curframe < 0);
+}
+
+/* Resets the hardware snapshot button */
+static void
+ov51x_clear_snapshot(struct usb_ov511 *ov)
+{
+ if (ov->bclass == BCL_OV511) {
+ reg_w(ov, R51x_SYS_SNAP, 0x00);
+ reg_w(ov, R51x_SYS_SNAP, 0x02);
+ reg_w(ov, R51x_SYS_SNAP, 0x00);
+ } else if (ov->bclass == BCL_OV518) {
+ warn("snapshot reset not supported yet on OV518(+)");
+ } else {
+ err("clear snap: invalid bridge type");
+ }
+}
+
+#if 0
+/* Checks the status of the snapshot button. Returns 1 if it was pressed since
+ * it was last cleared, and zero in all other cases (including errors) */
+static int
+ov51x_check_snapshot(struct usb_ov511 *ov)
+{
+ int ret, status = 0;
+
+ if (ov->bclass == BCL_OV511) {
+ ret = reg_r(ov, R51x_SYS_SNAP);
+ if (ret < 0) {
+ err("Error checking snspshot status (%d)", ret);
+ } else if (ret & 0x08) {
+ status = 1;
+ }
+ } else if (ov->bclass == BCL_OV518) {
+ warn("snapshot check not supported yet on OV518(+)");
+ } else {
+ err("check snap: invalid bridge type");
+ }
+
+ return status;
+}
+#endif
+
+/* This does an initial reset of an OmniVision sensor and ensures that I2C
+ * is synchronized. Returns <0 for failure.
+ */
+static int
+init_ov_sensor(struct usb_ov511 *ov)
+{
+ int i, success;
+
+ /* Reset the sensor */
+ if (i2c_w(ov, 0x12, 0x80) < 0)
+ return -EIO;
+
+ /* Wait for it to initialize */
+ msleep(150);
+
+ for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
+ if ((i2c_r(ov, OV7610_REG_ID_HIGH) == 0x7F) &&
+ (i2c_r(ov, OV7610_REG_ID_LOW) == 0xA2)) {
+ success = 1;
+ continue;
+ }
+
+ /* Reset the sensor */
+ if (i2c_w(ov, 0x12, 0x80) < 0)
+ return -EIO;
+ /* Wait for it to initialize */
+ msleep(150);
+ /* Dummy read to sync I2C */
+ if (i2c_r(ov, 0x00) < 0)
+ return -EIO;
+ }
+
+ if (!success)
+ return -EIO;
+
+ PDEBUG(1, "I2C synced in %d attempt(s)", i);
+
+ return 0;
+}
+
+static int
+ov511_set_packet_size(struct usb_ov511 *ov, int size)
+{
+ int alt, mult;
+
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ mult = size >> 5;
+
+ if (ov->bridge == BRG_OV511) {
+ if (size == 0)
+ alt = OV511_ALT_SIZE_0;
+ else if (size == 257)
+ alt = OV511_ALT_SIZE_257;
+ else if (size == 513)
+ alt = OV511_ALT_SIZE_513;
+ else if (size == 769)
+ alt = OV511_ALT_SIZE_769;
+ else if (size == 993)
+ alt = OV511_ALT_SIZE_993;
+ else {
+ err("Set packet size: invalid size (%d)", size);
+ return -EINVAL;
+ }
+ } else if (ov->bridge == BRG_OV511PLUS) {
+ if (size == 0)
+ alt = OV511PLUS_ALT_SIZE_0;
+ else if (size == 33)
+ alt = OV511PLUS_ALT_SIZE_33;
+ else if (size == 129)
+ alt = OV511PLUS_ALT_SIZE_129;
+ else if (size == 257)
+ alt = OV511PLUS_ALT_SIZE_257;
+ else if (size == 385)
+ alt = OV511PLUS_ALT_SIZE_385;
+ else if (size == 513)
+ alt = OV511PLUS_ALT_SIZE_513;
+ else if (size == 769)
+ alt = OV511PLUS_ALT_SIZE_769;
+ else if (size == 961)
+ alt = OV511PLUS_ALT_SIZE_961;
+ else {
+ err("Set packet size: invalid size (%d)", size);
+ return -EINVAL;
+ }
+ } else {
+ err("Set packet size: Invalid bridge type");
+ return -EINVAL;
+ }
+
+ PDEBUG(3, "%d, mult=%d, alt=%d", size, mult, alt);
+
+ if (reg_w(ov, R51x_FIFO_PSIZE, mult) < 0)
+ return -EIO;
+
+ if (usb_set_interface(ov->dev, ov->iface, alt) < 0) {
+ err("Set packet size: set interface error");
+ return -EBUSY;
+ }
+
+ if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0)
+ return -EIO;
+
+ ov->packet_size = size;
+
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+/* Note: Unlike the OV511/OV511+, the size argument does NOT include the
+ * optional packet number byte. The actual size *is* stored in ov->packet_size,
+ * though. */
+static int
+ov518_set_packet_size(struct usb_ov511 *ov, int size)
+{
+ int alt;
+
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ if (ov->bclass == BCL_OV518) {
+ if (size == 0)
+ alt = OV518_ALT_SIZE_0;
+ else if (size == 128)
+ alt = OV518_ALT_SIZE_128;
+ else if (size == 256)
+ alt = OV518_ALT_SIZE_256;
+ else if (size == 384)
+ alt = OV518_ALT_SIZE_384;
+ else if (size == 512)
+ alt = OV518_ALT_SIZE_512;
+ else if (size == 640)
+ alt = OV518_ALT_SIZE_640;
+ else if (size == 768)
+ alt = OV518_ALT_SIZE_768;
+ else if (size == 896)
+ alt = OV518_ALT_SIZE_896;
+ else {
+ err("Set packet size: invalid size (%d)", size);
+ return -EINVAL;
+ }
+ } else {
+ err("Set packet size: Invalid bridge type");
+ return -EINVAL;
+ }
+
+ PDEBUG(3, "%d, alt=%d", size, alt);
+
+ ov->packet_size = size;
+ if (size > 0) {
+ /* Program ISO FIFO size reg (packet number isn't included) */
+ ov518_reg_w32(ov, 0x30, size, 2);
+
+ if (ov->packet_numbering)
+ ++ov->packet_size;
+ }
+
+ if (usb_set_interface(ov->dev, ov->iface, alt) < 0) {
+ err("Set packet size: set interface error");
+ return -EBUSY;
+ }
+
+ /* Initialize the stream */
+ if (reg_w(ov, 0x2f, 0x80) < 0)
+ return -EIO;
+
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+/* Upload compression params and quantization tables. Returns 0 for success. */
+static int
+ov511_init_compression(struct usb_ov511 *ov)
+{
+ int rc = 0;
+
+ if (!ov->compress_inited) {
+ reg_w(ov, 0x70, phy);
+ reg_w(ov, 0x71, phuv);
+ reg_w(ov, 0x72, pvy);
+ reg_w(ov, 0x73, pvuv);
+ reg_w(ov, 0x74, qhy);
+ reg_w(ov, 0x75, qhuv);
+ reg_w(ov, 0x76, qvy);
+ reg_w(ov, 0x77, qvuv);
+
+ if (ov511_upload_quan_tables(ov) < 0) {
+ err("Error uploading quantization tables");
+ rc = -EIO;
+ goto out;
+ }
+ }
+
+ ov->compress_inited = 1;
+out:
+ return rc;
+}
+
+/* Upload compression params and quantization tables. Returns 0 for success. */
+static int
+ov518_init_compression(struct usb_ov511 *ov)
+{
+ int rc = 0;
+
+ if (!ov->compress_inited) {
+ if (ov518_upload_quan_tables(ov) < 0) {
+ err("Error uploading quantization tables");
+ rc = -EIO;
+ goto out;
+ }
+ }
+
+ ov->compress_inited = 1;
+out:
+ return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's contrast setting to "val" */
+static int
+sensor_set_contrast(struct usb_ov511 *ov, unsigned short val)
+{
+ int rc;
+
+ PDEBUG(3, "%d", val);
+
+ if (ov->stop_during_set)
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV6620:
+ {
+ rc = i2c_w(ov, OV7610_REG_CNT, val >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ }
+ case SEN_OV6630:
+ {
+ rc = i2c_w_mask(ov, OV7610_REG_CNT, val >> 12, 0x0f);
+ if (rc < 0)
+ goto out;
+ break;
+ }
+ case SEN_OV7620:
+ {
+ unsigned char ctab[] = {
+ 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
+ 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
+ };
+
+ /* Use Y gamma control instead. Bit 0 enables it. */
+ rc = i2c_w(ov, 0x64, ctab[val>>12]);
+ if (rc < 0)
+ goto out;
+ break;
+ }
+ case SEN_SAA7111A:
+ {
+ rc = i2c_w(ov, 0x0b, val >> 9);
+ if (rc < 0)
+ goto out;
+ break;
+ }
+ default:
+ {
+ PDEBUG(3, "Unsupported with this sensor");
+ rc = -EPERM;
+ goto out;
+ }
+ }
+
+ rc = 0; /* Success */
+ ov->contrast = val;
+out:
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return rc;
+}
+
+/* Gets sensor's contrast setting */
+static int
+sensor_get_contrast(struct usb_ov511 *ov, unsigned short *val)
+{
+ int rc;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV6620:
+ rc = i2c_r(ov, OV7610_REG_CNT);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 8;
+ break;
+ case SEN_OV6630:
+ rc = i2c_r(ov, OV7610_REG_CNT);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 12;
+ break;
+ case SEN_OV7620:
+ /* Use Y gamma reg instead. Bit 0 is the enable bit. */
+ rc = i2c_r(ov, 0x64);
+ if (rc < 0)
+ return rc;
+ else
+ *val = (rc & 0xfe) << 8;
+ break;
+ case SEN_SAA7111A:
+ *val = ov->contrast;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ return -EPERM;
+ }
+
+ PDEBUG(3, "%d", *val);
+ ov->contrast = *val;
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's brightness setting to "val" */
+static int
+sensor_set_brightness(struct usb_ov511 *ov, unsigned short val)
+{
+ int rc;
+
+ PDEBUG(4, "%d", val);
+
+ if (ov->stop_during_set)
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = i2c_w(ov, OV7610_REG_BRT, val >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ case SEN_OV7620:
+ /* 7620 doesn't like manual changes when in auto mode */
+ if (!ov->auto_brt) {
+ rc = i2c_w(ov, OV7610_REG_BRT, val >> 8);
+ if (rc < 0)
+ goto out;
+ }
+ break;
+ case SEN_SAA7111A:
+ rc = i2c_w(ov, 0x0a, val >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ rc = -EPERM;
+ goto out;
+ }
+
+ rc = 0; /* Success */
+ ov->brightness = val;
+out:
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return rc;
+}
+
+/* Gets sensor's brightness setting */
+static int
+sensor_get_brightness(struct usb_ov511 *ov, unsigned short *val)
+{
+ int rc;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_OV7620:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = i2c_r(ov, OV7610_REG_BRT);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 8;
+ break;
+ case SEN_SAA7111A:
+ *val = ov->brightness;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ return -EPERM;
+ }
+
+ PDEBUG(3, "%d", *val);
+ ov->brightness = *val;
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's saturation (color intensity) setting to "val" */
+static int
+sensor_set_saturation(struct usb_ov511 *ov, unsigned short val)
+{
+ int rc;
+
+ PDEBUG(3, "%d", val);
+
+ if (ov->stop_during_set)
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = i2c_w(ov, OV7610_REG_SAT, val >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ case SEN_OV7620:
+// /* Use UV gamma control instead. Bits 0 & 7 are reserved. */
+// rc = ov_i2c_write(ov->dev, 0x62, (val >> 9) & 0x7e);
+// if (rc < 0)
+// goto out;
+ rc = i2c_w(ov, OV7610_REG_SAT, val >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ case SEN_SAA7111A:
+ rc = i2c_w(ov, 0x0c, val >> 9);
+ if (rc < 0)
+ goto out;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ rc = -EPERM;
+ goto out;
+ }
+
+ rc = 0; /* Success */
+ ov->colour = val;
+out:
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return rc;
+}
+
+/* Gets sensor's saturation (color intensity) setting */
+static int
+sensor_get_saturation(struct usb_ov511 *ov, unsigned short *val)
+{
+ int rc;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = i2c_r(ov, OV7610_REG_SAT);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 8;
+ break;
+ case SEN_OV7620:
+// /* Use UV gamma reg instead. Bits 0 & 7 are reserved. */
+// rc = i2c_r(ov, 0x62);
+// if (rc < 0)
+// return rc;
+// else
+// *val = (rc & 0x7e) << 9;
+ rc = i2c_r(ov, OV7610_REG_SAT);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 8;
+ break;
+ case SEN_SAA7111A:
+ *val = ov->colour;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ return -EPERM;
+ }
+
+ PDEBUG(3, "%d", *val);
+ ov->colour = *val;
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's hue (red/blue balance) setting to "val" */
+static int
+sensor_set_hue(struct usb_ov511 *ov, unsigned short val)
+{
+ int rc;
+
+ PDEBUG(3, "%d", val);
+
+ if (ov->stop_during_set)
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = i2c_w(ov, OV7610_REG_RED, 0xFF - (val >> 8));
+ if (rc < 0)
+ goto out;
+
+ rc = i2c_w(ov, OV7610_REG_BLUE, val >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ case SEN_OV7620:
+// Hue control is causing problems. I will enable it once it's fixed.
+#if 0
+ rc = i2c_w(ov, 0x7a, (unsigned char)(val >> 8) + 0xb);
+ if (rc < 0)
+ goto out;
+
+ rc = i2c_w(ov, 0x79, (unsigned char)(val >> 8) + 0xb);
+ if (rc < 0)
+ goto out;
+#endif
+ break;
+ case SEN_SAA7111A:
+ rc = i2c_w(ov, 0x0d, (val + 32768) >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ rc = -EPERM;
+ goto out;
+ }
+
+ rc = 0; /* Success */
+ ov->hue = val;
+out:
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return rc;
+}
+
+/* Gets sensor's hue (red/blue balance) setting */
+static int
+sensor_get_hue(struct usb_ov511 *ov, unsigned short *val)
+{
+ int rc;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = i2c_r(ov, OV7610_REG_BLUE);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 8;
+ break;
+ case SEN_OV7620:
+ rc = i2c_r(ov, 0x7a);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 8;
+ break;
+ case SEN_SAA7111A:
+ *val = ov->hue;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ return -EPERM;
+ }
+
+ PDEBUG(3, "%d", *val);
+ ov->hue = *val;
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int
+sensor_set_picture(struct usb_ov511 *ov, struct video_picture *p)
+{
+ int rc;
+
+ PDEBUG(4, "sensor_set_picture");
+
+ ov->whiteness = p->whiteness;
+
+ /* Don't return error if a setting is unsupported, or rest of settings
+ * will not be performed */
+
+ rc = sensor_set_contrast(ov, p->contrast);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_set_brightness(ov, p->brightness);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_set_saturation(ov, p->colour);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_set_hue(ov, p->hue);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ return 0;
+}
+
+static int
+sensor_get_picture(struct usb_ov511 *ov, struct video_picture *p)
+{
+ int rc;
+
+ PDEBUG(4, "sensor_get_picture");
+
+ /* Don't return error if a setting is unsupported, or rest of settings
+ * will not be performed */
+
+ rc = sensor_get_contrast(ov, &(p->contrast));
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_get_brightness(ov, &(p->brightness));
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_get_saturation(ov, &(p->colour));
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_get_hue(ov, &(p->hue));
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ p->whiteness = 105 << 8;
+
+ return 0;
+}
+
+#if 0
+// FIXME: Exposure range is only 0x00-0x7f in interlace mode
+/* Sets current exposure for sensor. This only has an effect if auto-exposure
+ * is off */
+static inline int
+sensor_set_exposure(struct usb_ov511 *ov, unsigned char val)
+{
+ int rc;
+
+ PDEBUG(3, "%d", val);
+
+ if (ov->stop_during_set)
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ switch (ov->sensor) {
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV7610:
+ case SEN_OV7620:
+ case SEN_OV76BE:
+ case SEN_OV8600:
+ rc = i2c_w(ov, 0x10, val);
+ if (rc < 0)
+ goto out;
+
+ break;
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ case SEN_SAA7111A:
+ PDEBUG(3, "Unsupported with this sensor");
+ return -EPERM;
+ default:
+ err("Sensor not supported for set_exposure");
+ return -EINVAL;
+ }
+
+ rc = 0; /* Success */
+ ov->exposure = val;
+out:
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return rc;
+}
+#endif
+
+/* Gets current exposure level from sensor, regardless of whether it is under
+ * manual control. */
+static int
+sensor_get_exposure(struct usb_ov511 *ov, unsigned char *val)
+{
+ int rc;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV7620:
+ case SEN_OV76BE:
+ case SEN_OV8600:
+ rc = i2c_r(ov, 0x10);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc;
+ break;
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ case SEN_SAA7111A:
+ val = NULL;
+ PDEBUG(3, "Unsupported with this sensor");
+ return -EPERM;
+ default:
+ err("Sensor not supported for get_exposure");
+ return -EINVAL;
+ }
+
+ PDEBUG(3, "%d", *val);
+ ov->exposure = *val;
+
+ return 0;
+}
+
+/* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */
+static void
+ov51x_led_control(struct usb_ov511 *ov, int enable)
+{
+ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+ if (ov->bridge == BRG_OV511PLUS)
+ reg_w(ov, R511_SYS_LED_CTL, enable ? 1 : 0);
+ else if (ov->bclass == BCL_OV518)
+ reg_w_mask(ov, R518_GPIO_OUT, enable ? 0x02 : 0x00, 0x02);
+
+ return;
+}
+
+/* Matches the sensor's internal frame rate to the lighting frequency.
+ * Valid frequencies are:
+ * 50 - 50Hz, for European and Asian lighting
+ * 60 - 60Hz, for American lighting
+ *
+ * Tested with: OV7610, OV7620, OV76BE, OV6620
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_light_freq(struct usb_ov511 *ov, int freq)
+{
+ int sixty;
+
+ PDEBUG(4, "%d Hz", freq);
+
+ if (freq == 60)
+ sixty = 1;
+ else if (freq == 50)
+ sixty = 0;
+ else {
+ err("Invalid light freq (%d Hz)", freq);
+ return -EINVAL;
+ }
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80);
+ i2c_w(ov, 0x2b, sixty?0x00:0xac);
+ i2c_w_mask(ov, 0x13, 0x10, 0x10);
+ i2c_w_mask(ov, 0x13, 0x00, 0x10);
+ break;
+ case SEN_OV7620:
+ case SEN_OV76BE:
+ case SEN_OV8600:
+ i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80);
+ i2c_w(ov, 0x2b, sixty?0x00:0xac);
+ i2c_w_mask(ov, 0x76, 0x01, 0x01);
+ break;
+ case SEN_OV6620:
+ case SEN_OV6630:
+ i2c_w(ov, 0x2b, sixty?0xa8:0x28);
+ i2c_w(ov, 0x2a, sixty?0x84:0xa4);
+ break;
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ case SEN_SAA7111A:
+ PDEBUG(5, "Unsupported with this sensor");
+ return -EPERM;
+ default:
+ err("Sensor not supported for set_light_freq");
+ return -EINVAL;
+ }
+
+ ov->lightfreq = freq;
+
+ return 0;
+}
+
+/* If enable is true, turn on the sensor's banding filter, otherwise turn it
+ * off. This filter tries to reduce the pattern of horizontal light/dark bands
+ * caused by some (usually fluorescent) lighting. The light frequency must be
+ * set either before or after enabling it with ov51x_set_light_freq().
+ *
+ * Tested with: OV7610, OV7620, OV76BE, OV6620.
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_banding_filter(struct usb_ov511 *ov, int enable)
+{
+ int rc;
+
+ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+ if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B
+ || ov->sensor == SEN_SAA7111A) {
+ PDEBUG(5, "Unsupported with this sensor");
+ return -EPERM;
+ }
+
+ rc = i2c_w_mask(ov, 0x2d, enable?0x04:0x00, 0x04);
+ if (rc < 0)
+ return rc;
+
+ ov->bandfilt = enable;
+
+ return 0;
+}
+
+/* If enable is true, turn on the sensor's auto brightness control, otherwise
+ * turn it off.
+ *
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_auto_brightness(struct usb_ov511 *ov, int enable)
+{
+ int rc;
+
+ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+ if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B
+ || ov->sensor == SEN_SAA7111A) {
+ PDEBUG(5, "Unsupported with this sensor");
+ return -EPERM;
+ }
+
+ rc = i2c_w_mask(ov, 0x2d, enable?0x10:0x00, 0x10);
+ if (rc < 0)
+ return rc;
+
+ ov->auto_brt = enable;
+
+ return 0;
+}
+
+/* If enable is true, turn on the sensor's auto exposure control, otherwise
+ * turn it off.
+ *
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_auto_exposure(struct usb_ov511 *ov, int enable)
+{
+ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ i2c_w_mask(ov, 0x29, enable?0x00:0x80, 0x80);
+ break;
+ case SEN_OV6620:
+ case SEN_OV7620:
+ case SEN_OV76BE:
+ case SEN_OV8600:
+ i2c_w_mask(ov, 0x13, enable?0x01:0x00, 0x01);
+ break;
+ case SEN_OV6630:
+ i2c_w_mask(ov, 0x28, enable?0x00:0x10, 0x10);
+ break;
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ case SEN_SAA7111A:
+ PDEBUG(5, "Unsupported with this sensor");
+ return -EPERM;
+ default:
+ err("Sensor not supported for set_auto_exposure");
+ return -EINVAL;
+ }
+
+ ov->auto_exp = enable;
+
+ return 0;
+}
+
+/* Modifies the sensor's exposure algorithm to allow proper exposure of objects
+ * that are illuminated from behind.
+ *
+ * Tested with: OV6620, OV7620
+ * Unsupported: OV7610, OV76BE, KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_backlight(struct usb_ov511 *ov, int enable)
+{
+ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+ switch (ov->sensor) {
+ case SEN_OV7620:
+ case SEN_OV8600:
+ i2c_w_mask(ov, 0x68, enable?0xe0:0xc0, 0xe0);
+ i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08);
+ i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02);
+ break;
+ case SEN_OV6620:
+ i2c_w_mask(ov, 0x4e, enable?0xe0:0xc0, 0xe0);
+ i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08);
+ i2c_w_mask(ov, 0x0e, enable?0x80:0x00, 0x80);
+ break;
+ case SEN_OV6630:
+ i2c_w_mask(ov, 0x4e, enable?0x80:0x60, 0xe0);
+ i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08);
+ i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02);
+ break;
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ case SEN_SAA7111A:
+ PDEBUG(5, "Unsupported with this sensor");
+ return -EPERM;
+ default:
+ err("Sensor not supported for set_backlight");
+ return -EINVAL;
+ }
+
+ ov->backlight = enable;
+
+ return 0;
+}
+
+static int
+sensor_set_mirror(struct usb_ov511 *ov, int enable)
+{
+ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+ switch (ov->sensor) {
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV7610:
+ case SEN_OV7620:
+ case SEN_OV76BE:
+ case SEN_OV8600:
+ i2c_w_mask(ov, 0x12, enable?0x40:0x00, 0x40);
+ break;
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ case SEN_SAA7111A:
+ PDEBUG(5, "Unsupported with this sensor");
+ return -EPERM;
+ default:
+ err("Sensor not supported for set_mirror");
+ return -EINVAL;
+ }
+
+ ov->mirror = enable;
+
+ return 0;
+}
+
+/* Returns number of bits per pixel (regardless of where they are located;
+ * planar or not), or zero for unsupported format.
+ */
+static inline int
+get_depth(int palette)
+{
+ switch (palette) {
+ case VIDEO_PALETTE_GREY: return 8;
+ case VIDEO_PALETTE_YUV420: return 12;
+ case VIDEO_PALETTE_YUV420P: return 12; /* Planar */
+ default: return 0; /* Invalid format */
+ }
+}
+
+/* Bytes per frame. Used by read(). Return of 0 indicates error */
+static inline long int
+get_frame_length(struct ov511_frame *frame)
+{
+ if (!frame)
+ return 0;
+ else
+ return ((frame->width * frame->height
+ * get_depth(frame->format)) >> 3);
+}
+
+static int
+mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height,
+ int mode, int sub_flag, int qvga)
+{
+ int clock;
+
+ /******** Mode (VGA/QVGA) and sensor specific regs ********/
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ i2c_w(ov, 0x14, qvga?0x24:0x04);
+// FIXME: Does this improve the image quality or frame rate?
+#if 0
+ i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20);
+ i2c_w(ov, 0x24, 0x10);
+ i2c_w(ov, 0x25, qvga?0x40:0x8a);
+ i2c_w(ov, 0x2f, qvga?0x30:0xb0);
+ i2c_w(ov, 0x35, qvga?0x1c:0x9c);
+#endif
+ break;
+ case SEN_OV7620:
+// i2c_w(ov, 0x2b, 0x00);
+ i2c_w(ov, 0x14, qvga?0xa4:0x84);
+ i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20);
+ i2c_w(ov, 0x24, qvga?0x20:0x3a);
+ i2c_w(ov, 0x25, qvga?0x30:0x60);
+ i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40);
+ i2c_w_mask(ov, 0x67, qvga?0xf0:0x90, 0xf0);
+ i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20);
+ break;
+ case SEN_OV76BE:
+// i2c_w(ov, 0x2b, 0x00);
+ i2c_w(ov, 0x14, qvga?0xa4:0x84);
+// FIXME: Enable this once 7620AE uses 7620 initial settings
+#if 0
+ i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20);
+ i2c_w(ov, 0x24, qvga?0x20:0x3a);
+ i2c_w(ov, 0x25, qvga?0x30:0x60);
+ i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40);
+ i2c_w_mask(ov, 0x67, qvga?0xb0:0x90, 0xf0);
+ i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20);
+#endif
+ break;
+ case SEN_OV6620:
+ i2c_w(ov, 0x14, qvga?0x24:0x04);
+ break;
+ case SEN_OV6630:
+ i2c_w(ov, 0x14, qvga?0xa0:0x80);
+ break;
+ default:
+ err("Invalid sensor");
+ return -EINVAL;
+ }
+
+ /******** Palette-specific regs ********/
+
+ if (mode == VIDEO_PALETTE_GREY) {
+ if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) {
+ /* these aren't valid on the OV6620/OV7620/6630? */
+ i2c_w_mask(ov, 0x0e, 0x40, 0x40);
+ }
+
+ if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518
+ && ov518_color) {
+ i2c_w_mask(ov, 0x12, 0x00, 0x10);
+ i2c_w_mask(ov, 0x13, 0x00, 0x20);
+ } else {
+ i2c_w_mask(ov, 0x13, 0x20, 0x20);
+ }
+ } else {
+ if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) {
+ /* not valid on the OV6620/OV7620/6630? */
+ i2c_w_mask(ov, 0x0e, 0x00, 0x40);
+ }
+
+ /* The OV518 needs special treatment. Although both the OV518
+ * and the OV6630 support a 16-bit video bus, only the 8 bit Y
+ * bus is actually used. The UV bus is tied to ground.
+ * Therefore, the OV6630 needs to be in 8-bit multiplexed
+ * output mode */
+
+ if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518
+ && ov518_color) {
+ i2c_w_mask(ov, 0x12, 0x10, 0x10);
+ i2c_w_mask(ov, 0x13, 0x20, 0x20);
+ } else {
+ i2c_w_mask(ov, 0x13, 0x00, 0x20);
+ }
+ }
+
+ /******** Clock programming ********/
+
+ /* The OV6620 needs special handling. This prevents the
+ * severe banding that normally occurs */
+ if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630)
+ {
+ /* Clock down */
+
+ i2c_w(ov, 0x2a, 0x04);
+
+ if (ov->compress) {
+// clock = 0; /* This ensures the highest frame rate */
+ clock = 3;
+ } else if (clockdiv == -1) { /* If user didn't override it */
+ clock = 3; /* Gives better exposure time */
+ } else {
+ clock = clockdiv;
+ }
+
+ PDEBUG(4, "Setting clock divisor to %d", clock);
+
+ i2c_w(ov, 0x11, clock);
+
+ i2c_w(ov, 0x2a, 0x84);
+ /* This next setting is critical. It seems to improve
+ * the gain or the contrast. The "reserved" bits seem
+ * to have some effect in this case. */
+ i2c_w(ov, 0x2d, 0x85);
+ }
+ else
+ {
+ if (ov->compress) {
+ clock = 1; /* This ensures the highest frame rate */
+ } else if (clockdiv == -1) { /* If user didn't override it */
+ /* Calculate and set the clock divisor */
+ clock = ((sub_flag ? ov->subw * ov->subh
+ : width * height)
+ * (mode == VIDEO_PALETTE_GREY ? 2 : 3) / 2)
+ / 66000;
+ } else {
+ clock = clockdiv;
+ }
+
+ PDEBUG(4, "Setting clock divisor to %d", clock);
+
+ i2c_w(ov, 0x11, clock);
+ }
+
+ /******** Special Features ********/
+
+ if (framedrop >= 0)
+ i2c_w(ov, 0x16, framedrop);
+
+ /* Test Pattern */
+ i2c_w_mask(ov, 0x12, (testpat?0x02:0x00), 0x02);
+
+ /* Enable auto white balance */
+ i2c_w_mask(ov, 0x12, 0x04, 0x04);
+
+ // This will go away as soon as ov51x_mode_init_sensor_regs()
+ // is fully tested.
+ /* 7620/6620/6630? don't have register 0x35, so play it safe */
+ if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) {
+ if (width == 640 && height == 480)
+ i2c_w(ov, 0x35, 0x9e);
+ else
+ i2c_w(ov, 0x35, 0x1e);
+ }
+
+ return 0;
+}
+
+static int
+set_ov_sensor_window(struct usb_ov511 *ov, int width, int height, int mode,
+ int sub_flag)
+{
+ int ret;
+ int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize;
+ int hoffset, voffset, hwscale = 0, vwscale = 0;
+
+ /* The different sensor ICs handle setting up of window differently.
+ * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ hwsbase = 0x38;
+ hwebase = 0x3a;
+ vwsbase = vwebase = 0x05;
+ break;
+ case SEN_OV6620:
+ case SEN_OV6630:
+ hwsbase = 0x38;
+ hwebase = 0x3a;
+ vwsbase = 0x05;
+ vwebase = 0x06;
+ break;
+ case SEN_OV7620:
+ hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */
+ hwebase = 0x2f;
+ vwsbase = vwebase = 0x05;
+ break;
+ default:
+ err("Invalid sensor");
+ return -EINVAL;
+ }
+
+ if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630) {
+ /* Note: OV518(+) does downsample on its own) */
+ if ((width > 176 && height > 144)
+ || ov->bclass == BCL_OV518) { /* CIF */
+ ret = mode_init_ov_sensor_regs(ov, width, height,
+ mode, sub_flag, 0);
+ if (ret < 0)
+ return ret;
+ hwscale = 1;
+ vwscale = 1; /* The datasheet says 0; it's wrong */
+ hwsize = 352;
+ vwsize = 288;
+ } else if (width > 176 || height > 144) {
+ err("Illegal dimensions");
+ return -EINVAL;
+ } else { /* QCIF */
+ ret = mode_init_ov_sensor_regs(ov, width, height,
+ mode, sub_flag, 1);
+ if (ret < 0)
+ return ret;
+ hwsize = 176;
+ vwsize = 144;
+ }
+ } else {
+ if (width > 320 && height > 240) { /* VGA */
+ ret = mode_init_ov_sensor_regs(ov, width, height,
+ mode, sub_flag, 0);
+ if (ret < 0)
+ return ret;
+ hwscale = 2;
+ vwscale = 1;
+ hwsize = 640;
+ vwsize = 480;
+ } else if (width > 320 || height > 240) {
+ err("Illegal dimensions");
+ return -EINVAL;
+ } else { /* QVGA */
+ ret = mode_init_ov_sensor_regs(ov, width, height,
+ mode, sub_flag, 1);
+ if (ret < 0)
+ return ret;
+ hwscale = 1;
+ hwsize = 320;
+ vwsize = 240;
+ }
+ }
+
+ /* Center the window */
+ hoffset = ((hwsize - width) / 2) >> hwscale;
+ voffset = ((vwsize - height) / 2) >> vwscale;
+
+ /* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
+ if (sub_flag) {
+ i2c_w(ov, 0x17, hwsbase+(ov->subx>>hwscale));
+ i2c_w(ov, 0x18, hwebase+((ov->subx+ov->subw)>>hwscale));
+ i2c_w(ov, 0x19, vwsbase+(ov->suby>>vwscale));
+ i2c_w(ov, 0x1a, vwebase+((ov->suby+ov->subh)>>vwscale));
+ } else {
+ i2c_w(ov, 0x17, hwsbase + hoffset);
+ i2c_w(ov, 0x18, hwebase + hoffset + (hwsize>>hwscale));
+ i2c_w(ov, 0x19, vwsbase + voffset);
+ i2c_w(ov, 0x1a, vwebase + voffset + (vwsize>>vwscale));
+ }
+
+#ifdef OV511_DEBUG
+ if (dump_sensor)
+ dump_i2c_regs(ov);
+#endif
+
+ return 0;
+}
+
+/* Set up the OV511/OV511+ with the given image parameters.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static int
+ov511_mode_init_regs(struct usb_ov511 *ov,
+ int width, int height, int mode, int sub_flag)
+{
+ int hsegs, vsegs;
+
+ if (sub_flag) {
+ width = ov->subw;
+ height = ov->subh;
+ }
+
+ PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
+ width, height, mode, sub_flag);
+
+ // FIXME: This should be moved to a 7111a-specific function once
+ // subcapture is dealt with properly
+ if (ov->sensor == SEN_SAA7111A) {
+ if (width == 320 && height == 240) {
+ /* No need to do anything special */
+ } else if (width == 640 && height == 480) {
+ /* Set the OV511 up as 320x480, but keep the
+ * V4L resolution as 640x480 */
+ width = 320;
+ } else {
+ err("SAA7111A only allows 320x240 or 640x480");
+ return -EINVAL;
+ }
+ }
+
+ /* Make sure width and height are a multiple of 8 */
+ if (width % 8 || height % 8) {
+ err("Invalid size (%d, %d) (mode = %d)", width, height, mode);
+ return -EINVAL;
+ }
+
+ if (width < ov->minwidth || height < ov->minheight) {
+ err("Requested dimensions are too small");
+ return -EINVAL;
+ }
+
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ if (mode == VIDEO_PALETTE_GREY) {
+ reg_w(ov, R511_CAM_UV_EN, 0x00);
+ reg_w(ov, R511_SNAP_UV_EN, 0x00);
+ reg_w(ov, R511_SNAP_OPTS, 0x01);
+ } else {
+ reg_w(ov, R511_CAM_UV_EN, 0x01);
+ reg_w(ov, R511_SNAP_UV_EN, 0x01);
+ reg_w(ov, R511_SNAP_OPTS, 0x03);
+ }
+
+ /* Here I'm assuming that snapshot size == image size.
+ * I hope that's always true. --claudio
+ */
+ hsegs = (width >> 3) - 1;
+ vsegs = (height >> 3) - 1;
+
+ reg_w(ov, R511_CAM_PXCNT, hsegs);
+ reg_w(ov, R511_CAM_LNCNT, vsegs);
+ reg_w(ov, R511_CAM_PXDIV, 0x00);
+ reg_w(ov, R511_CAM_LNDIV, 0x00);
+
+ /* YUV420, low pass filter on */
+ reg_w(ov, R511_CAM_OPTS, 0x03);
+
+ /* Snapshot additions */
+ reg_w(ov, R511_SNAP_PXCNT, hsegs);
+ reg_w(ov, R511_SNAP_LNCNT, vsegs);
+ reg_w(ov, R511_SNAP_PXDIV, 0x00);
+ reg_w(ov, R511_SNAP_LNDIV, 0x00);
+
+ if (ov->compress) {
+ /* Enable Y and UV quantization and compression */
+ reg_w(ov, R511_COMP_EN, 0x07);
+ reg_w(ov, R511_COMP_LUT_EN, 0x03);
+ ov51x_reset(ov, OV511_RESET_OMNICE);
+ }
+
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+/* Sets up the OV518/OV518+ with the given image parameters
+ *
+ * OV518 needs a completely different approach, until we can figure out what
+ * the individual registers do. Also, only 15 FPS is supported now.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static int
+ov518_mode_init_regs(struct usb_ov511 *ov,
+ int width, int height, int mode, int sub_flag)
+{
+ int hsegs, vsegs, hi_res;
+
+ if (sub_flag) {
+ width = ov->subw;
+ height = ov->subh;
+ }
+
+ PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
+ width, height, mode, sub_flag);
+
+ if (width % 16 || height % 8) {
+ err("Invalid size (%d, %d)", width, height);
+ return -EINVAL;
+ }
+
+ if (width < ov->minwidth || height < ov->minheight) {
+ err("Requested dimensions are too small");
+ return -EINVAL;
+ }
+
+ if (width >= 320 && height >= 240) {
+ hi_res = 1;
+ } else if (width >= 320 || height >= 240) {
+ err("Invalid width/height combination (%d, %d)", width, height);
+ return -EINVAL;
+ } else {
+ hi_res = 0;
+ }
+
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ /******** Set the mode ********/
+
+ reg_w(ov, 0x2b, 0);
+ reg_w(ov, 0x2c, 0);
+ reg_w(ov, 0x2d, 0);
+ reg_w(ov, 0x2e, 0);
+ reg_w(ov, 0x3b, 0);
+ reg_w(ov, 0x3c, 0);
+ reg_w(ov, 0x3d, 0);
+ reg_w(ov, 0x3e, 0);
+
+ if (ov->bridge == BRG_OV518 && ov518_color) {
+ /* OV518 needs U and V swapped */
+ i2c_w_mask(ov, 0x15, 0x00, 0x01);
+
+ if (mode == VIDEO_PALETTE_GREY) {
+ /* Set 16-bit input format (UV data are ignored) */
+ reg_w_mask(ov, 0x20, 0x00, 0x08);
+
+ /* Set 8-bit (4:0:0) output format */
+ reg_w_mask(ov, 0x28, 0x00, 0xf0);
+ reg_w_mask(ov, 0x38, 0x00, 0xf0);
+ } else {
+ /* Set 8-bit (YVYU) input format */
+ reg_w_mask(ov, 0x20, 0x08, 0x08);
+
+ /* Set 12-bit (4:2:0) output format */
+ reg_w_mask(ov, 0x28, 0x80, 0xf0);
+ reg_w_mask(ov, 0x38, 0x80, 0xf0);
+ }
+ } else {
+ reg_w(ov, 0x28, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
+ reg_w(ov, 0x38, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
+ }
+
+ hsegs = width / 16;
+ vsegs = height / 4;
+
+ reg_w(ov, 0x29, hsegs);
+ reg_w(ov, 0x2a, vsegs);
+
+ reg_w(ov, 0x39, hsegs);
+ reg_w(ov, 0x3a, vsegs);
+
+ /* Windows driver does this here; who knows why */
+ reg_w(ov, 0x2f, 0x80);
+
+ /******** Set the framerate (to 15 FPS) ********/
+
+ /* Mode independent, but framerate dependent, regs */
+ reg_w(ov, 0x51, 0x02); /* Clock divider; lower==faster */
+ reg_w(ov, 0x22, 0x18);
+ reg_w(ov, 0x23, 0xff);
+
+ if (ov->bridge == BRG_OV518PLUS)
+ reg_w(ov, 0x21, 0x19);
+ else
+ reg_w(ov, 0x71, 0x19); /* Compression-related? */
+
+ // FIXME: Sensor-specific
+ /* Bit 5 is what matters here. Of course, it is "reserved" */
+ i2c_w(ov, 0x54, 0x23);
+
+ reg_w(ov, 0x2f, 0x80);
+
+ if (ov->bridge == BRG_OV518PLUS) {
+ reg_w(ov, 0x24, 0x94);
+ reg_w(ov, 0x25, 0x90);
+ ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */
+ ov518_reg_w32(ov, 0xc6, 540, 2); /* 21ch */
+ ov518_reg_w32(ov, 0xc7, 540, 2); /* 21ch */
+ ov518_reg_w32(ov, 0xc8, 108, 2); /* 6ch */
+ ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */
+ ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */
+ ov518_reg_w32(ov, 0xcc, 2400, 2); /* 960h */
+ ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */
+ ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */
+ } else {
+ reg_w(ov, 0x24, 0x9f);
+ reg_w(ov, 0x25, 0x90);
+ ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */
+ ov518_reg_w32(ov, 0xc6, 500, 2); /* 1f4h */
+ ov518_reg_w32(ov, 0xc7, 500, 2); /* 1f4h */
+ ov518_reg_w32(ov, 0xc8, 142, 2); /* 8eh */
+ ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */
+ ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */
+ ov518_reg_w32(ov, 0xcc, 2000, 2); /* 7d0h */
+ ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */
+ ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */
+ }
+
+ reg_w(ov, 0x2f, 0x80);
+
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ /* Reset it just for good measure */
+ if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+/* This is a wrapper around the OV511, OV518, and sensor specific functions */
+static int
+mode_init_regs(struct usb_ov511 *ov,
+ int width, int height, int mode, int sub_flag)
+{
+ int rc = 0;
+
+ if (!ov || !ov->dev)
+ return -EFAULT;
+
+ if (ov->bclass == BCL_OV518) {
+ rc = ov518_mode_init_regs(ov, width, height, mode, sub_flag);
+ } else {
+ rc = ov511_mode_init_regs(ov, width, height, mode, sub_flag);
+ }
+
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV7620:
+ case SEN_OV76BE:
+ case SEN_OV8600:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = set_ov_sensor_window(ov, width, height, mode, sub_flag);
+ break;
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ err("KS0127-series decoders not supported yet");
+ rc = -EINVAL;
+ break;
+ case SEN_SAA7111A:
+// rc = mode_init_saa_sensor_regs(ov, width, height, mode,
+// sub_flag);
+
+ PDEBUG(1, "SAA status = 0x%02X", i2c_r(ov, 0x1f));
+ break;
+ default:
+ err("Unknown sensor");
+ rc = -EINVAL;
+ }
+
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ /* Sensor-independent settings */
+ rc = sensor_set_auto_brightness(ov, ov->auto_brt);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_set_auto_exposure(ov, ov->auto_exp);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_set_banding_filter(ov, bandingfilter);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ if (ov->lightfreq) {
+ rc = sensor_set_light_freq(ov, lightfreq);
+ if (FATAL_ERROR(rc))
+ return rc;
+ }
+
+ rc = sensor_set_backlight(ov, ov->backlight);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_set_mirror(ov, ov->mirror);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ return 0;
+}
+
+/* This sets the default image parameters. This is useful for apps that use
+ * read() and do not set these.
+ */
+static int
+ov51x_set_default_params(struct usb_ov511 *ov)
+{
+ int i;
+
+ /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
+ * (using read() instead). */
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].width = ov->maxwidth;
+ ov->frame[i].height = ov->maxheight;
+ ov->frame[i].bytes_read = 0;
+ if (force_palette)
+ ov->frame[i].format = force_palette;
+ else
+ ov->frame[i].format = VIDEO_PALETTE_YUV420;
+
+ ov->frame[i].depth = get_depth(ov->frame[i].format);
+ }
+
+ PDEBUG(3, "%dx%d, %s", ov->maxwidth, ov->maxheight,
+ symbolic(v4l1_plist, ov->frame[0].format));
+
+ /* Initialize to max width/height, YUV420 or RGB24 (if supported) */
+ if (mode_init_regs(ov, ov->maxwidth, ov->maxheight,
+ ov->frame[0].format, 0) < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**********************************************************************
+ *
+ * Video decoder stuff
+ *
+ **********************************************************************/
+
+/* Set analog input port of decoder */
+static int
+decoder_set_input(struct usb_ov511 *ov, int input)
+{
+ PDEBUG(4, "port %d", input);
+
+ switch (ov->sensor) {
+ case SEN_SAA7111A:
+ {
+ /* Select mode */
+ i2c_w_mask(ov, 0x02, input, 0x07);
+ /* Bypass chrominance trap for modes 4..7 */
+ i2c_w_mask(ov, 0x09, (input > 3) ? 0x80:0x00, 0x80);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Get ASCII name of video input */
+static int
+decoder_get_input_name(struct usb_ov511 *ov, int input, char *name)
+{
+ switch (ov->sensor) {
+ case SEN_SAA7111A:
+ {
+ if (input < 0 || input > 7)
+ return -EINVAL;
+ else if (input < 4)
+ sprintf(name, "CVBS-%d", input);
+ else // if (input < 8)
+ sprintf(name, "S-Video-%d", input - 4);
+ break;
+ }
+ default:
+ sprintf(name, "%s", "Camera");
+ }
+
+ return 0;
+}
+
+/* Set norm (NTSC, PAL, SECAM, AUTO) */
+static int
+decoder_set_norm(struct usb_ov511 *ov, int norm)
+{
+ PDEBUG(4, "%d", norm);
+
+ switch (ov->sensor) {
+ case SEN_SAA7111A:
+ {
+ int reg_8, reg_e;
+
+ if (norm == VIDEO_MODE_NTSC) {
+ reg_8 = 0x40; /* 60 Hz */
+ reg_e = 0x00; /* NTSC M / PAL BGHI */
+ } else if (norm == VIDEO_MODE_PAL) {
+ reg_8 = 0x00; /* 50 Hz */
+ reg_e = 0x00; /* NTSC M / PAL BGHI */
+ } else if (norm == VIDEO_MODE_AUTO) {
+ reg_8 = 0x80; /* Auto field detect */
+ reg_e = 0x00; /* NTSC M / PAL BGHI */
+ } else if (norm == VIDEO_MODE_SECAM) {
+ reg_8 = 0x00; /* 50 Hz */
+ reg_e = 0x50; /* SECAM / PAL 4.43 */
+ } else {
+ return -EINVAL;
+ }
+
+ i2c_w_mask(ov, 0x08, reg_8, 0xc0);
+ i2c_w_mask(ov, 0x0e, reg_e, 0x70);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**********************************************************************
+ *
+ * Raw data parsing
+ *
+ **********************************************************************/
+
+/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the
+ * image at pOut is specified by w.
+ */
+static inline void
+make_8x8(unsigned char *pIn, unsigned char *pOut, int w)
+{
+ unsigned char *pOut1 = pOut;
+ int x, y;
+
+ for (y = 0; y < 8; y++) {
+ pOut1 = pOut;
+ for (x = 0; x < 8; x++) {
+ *pOut1++ = *pIn++;
+ }
+ pOut += w;
+ }
+}
+
+/*
+ * For RAW BW (YUV 4:0:0) images, data show up in 256 byte segments.
+ * The segments represent 4 squares of 8x8 pixels as follows:
+ *
+ * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
+ * 8 9 ... 15 72 73 ... 79 200 201 ... 207
+ * ... ... ...
+ * 56 57 ... 63 120 121 ... 127 248 249 ... 255
+ *
+ */
+static void
+yuv400raw_to_yuv400p(struct ov511_frame *frame,
+ unsigned char *pIn0, unsigned char *pOut0)
+{
+ int x, y;
+ unsigned char *pIn, *pOut, *pOutLine;
+
+ /* Copy Y */
+ pIn = pIn0;
+ pOutLine = pOut0;
+ for (y = 0; y < frame->rawheight - 1; y += 8) {
+ pOut = pOutLine;
+ for (x = 0; x < frame->rawwidth - 1; x += 8) {
+ make_8x8(pIn, pOut, frame->rawwidth);
+ pIn += 64;
+ pOut += 8;
+ }
+ pOutLine += 8 * frame->rawwidth;
+ }
+}
+
+/*
+ * For YUV 4:2:0 images, the data show up in 384 byte segments.
+ * The first 64 bytes of each segment are U, the next 64 are V. The U and
+ * V are arranged as follows:
+ *
+ * 0 1 ... 7
+ * 8 9 ... 15
+ * ...
+ * 56 57 ... 63
+ *
+ * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
+ *
+ * The next 256 bytes are full resolution Y data and represent 4 squares
+ * of 8x8 pixels as follows:
+ *
+ * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
+ * 8 9 ... 15 72 73 ... 79 200 201 ... 207
+ * ... ... ...
+ * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255
+ *
+ * Note that the U and V data in one segment represent a 16 x 16 pixel
+ * area, but the Y data represent a 32 x 8 pixel area. If the width is not an
+ * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the
+ * next horizontal stripe.
+ *
+ * If dumppix module param is set, _parse_data just dumps the incoming segments,
+ * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
+ * this puts the data on the standard output and can be analyzed with the
+ * parseppm.c utility I wrote. That's a much faster way for figuring out how
+ * these data are scrambled.
+ */
+
+/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0.
+ *
+ * FIXME: Currently only handles width and height that are multiples of 16
+ */
+static void
+yuv420raw_to_yuv420p(struct ov511_frame *frame,
+ unsigned char *pIn0, unsigned char *pOut0)
+{
+ int k, x, y;
+ unsigned char *pIn, *pOut, *pOutLine;
+ const unsigned int a = frame->rawwidth * frame->rawheight;
+ const unsigned int w = frame->rawwidth / 2;
+
+ /* Copy U and V */
+ pIn = pIn0;
+ pOutLine = pOut0 + a;
+ for (y = 0; y < frame->rawheight - 1; y += 16) {
+ pOut = pOutLine;
+ for (x = 0; x < frame->rawwidth - 1; x += 16) {
+ make_8x8(pIn, pOut, w);
+ make_8x8(pIn + 64, pOut + a/4, w);
+ pIn += 384;
+ pOut += 8;
+ }
+ pOutLine += 8 * w;
+ }
+
+ /* Copy Y */
+ pIn = pIn0 + 128;
+ pOutLine = pOut0;
+ k = 0;
+ for (y = 0; y < frame->rawheight - 1; y += 8) {
+ pOut = pOutLine;
+ for (x = 0; x < frame->rawwidth - 1; x += 8) {
+ make_8x8(pIn, pOut, frame->rawwidth);
+ pIn += 64;
+ pOut += 8;
+ if ((++k) > 3) {
+ k = 0;
+ pIn += 128;
+ }
+ }
+ pOutLine += 8 * frame->rawwidth;
+ }
+}
+
+/**********************************************************************
+ *
+ * Decompression
+ *
+ **********************************************************************/
+
+static int
+request_decompressor(struct usb_ov511 *ov)
+{
+ if (ov->bclass == BCL_OV511 || ov->bclass == BCL_OV518) {
+ err("No decompressor available");
+ } else {
+ err("Unknown bridge");
+ }
+
+ return -ENOSYS;
+}
+
+static void
+decompress(struct usb_ov511 *ov, struct ov511_frame *frame,
+ unsigned char *pIn0, unsigned char *pOut0)
+{
+ if (!ov->decomp_ops)
+ if (request_decompressor(ov))
+ return;
+
+}
+
+/**********************************************************************
+ *
+ * Format conversion
+ *
+ **********************************************************************/
+
+/* Fuses even and odd fields together, and doubles width.
+ * INPUT: an odd field followed by an even field at pIn0, in YUV planar format
+ * OUTPUT: a normal YUV planar image, with correct aspect ratio
+ */
+static void
+deinterlace(struct ov511_frame *frame, int rawformat,
+ unsigned char *pIn0, unsigned char *pOut0)
+{
+ const int fieldheight = frame->rawheight / 2;
+ const int fieldpix = fieldheight * frame->rawwidth;
+ const int w = frame->width;
+ int x, y;
+ unsigned char *pInEven, *pInOdd, *pOut;
+
+ PDEBUG(5, "fieldheight=%d", fieldheight);
+
+ if (frame->rawheight != frame->height) {
+ err("invalid height");
+ return;
+ }
+
+ if ((frame->rawwidth * 2) != frame->width) {
+ err("invalid width");
+ return;
+ }
+
+ /* Y */
+ pInOdd = pIn0;
+ pInEven = pInOdd + fieldpix;
+ pOut = pOut0;
+ for (y = 0; y < fieldheight; y++) {
+ for (x = 0; x < frame->rawwidth; x++) {
+ *pOut = *pInEven;
+ *(pOut+1) = *pInEven++;
+ *(pOut+w) = *pInOdd;
+ *(pOut+w+1) = *pInOdd++;
+ pOut += 2;
+ }
+ pOut += w;
+ }
+
+ if (rawformat == RAWFMT_YUV420) {
+ /* U */
+ pInOdd = pIn0 + fieldpix * 2;
+ pInEven = pInOdd + fieldpix / 4;
+ for (y = 0; y < fieldheight / 2; y++) {
+ for (x = 0; x < frame->rawwidth / 2; x++) {
+ *pOut = *pInEven;
+ *(pOut+1) = *pInEven++;
+ *(pOut+w/2) = *pInOdd;
+ *(pOut+w/2+1) = *pInOdd++;
+ pOut += 2;
+ }
+ pOut += w/2;
+ }
+ /* V */
+ pInOdd = pIn0 + fieldpix * 2 + fieldpix / 2;
+ pInEven = pInOdd + fieldpix / 4;
+ for (y = 0; y < fieldheight / 2; y++) {
+ for (x = 0; x < frame->rawwidth / 2; x++) {
+ *pOut = *pInEven;
+ *(pOut+1) = *pInEven++;
+ *(pOut+w/2) = *pInOdd;
+ *(pOut+w/2+1) = *pInOdd++;
+ pOut += 2;
+ }
+ pOut += w/2;
+ }
+ }
+}
+
+static void
+ov51x_postprocess_grey(struct usb_ov511 *ov, struct ov511_frame *frame)
+{
+ /* Deinterlace frame, if necessary */
+ if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) {
+ if (frame->compressed)
+ decompress(ov, frame, frame->rawdata,
+ frame->tempdata);
+ else
+ yuv400raw_to_yuv400p(frame, frame->rawdata,
+ frame->tempdata);
+
+ deinterlace(frame, RAWFMT_YUV400, frame->tempdata,
+ frame->data);
+ } else {
+ if (frame->compressed)
+ decompress(ov, frame, frame->rawdata,
+ frame->data);
+ else
+ yuv400raw_to_yuv400p(frame, frame->rawdata,
+ frame->data);
+ }
+}
+
+/* Process raw YUV420 data into standard YUV420P */
+static void
+ov51x_postprocess_yuv420(struct usb_ov511 *ov, struct ov511_frame *frame)
+{
+ /* Deinterlace frame, if necessary */
+ if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) {
+ if (frame->compressed)
+ decompress(ov, frame, frame->rawdata, frame->tempdata);
+ else
+ yuv420raw_to_yuv420p(frame, frame->rawdata,
+ frame->tempdata);
+
+ deinterlace(frame, RAWFMT_YUV420, frame->tempdata,
+ frame->data);
+ } else {
+ if (frame->compressed)
+ decompress(ov, frame, frame->rawdata, frame->data);
+ else
+ yuv420raw_to_yuv420p(frame, frame->rawdata,
+ frame->data);
+ }
+}
+
+/* Post-processes the specified frame. This consists of:
+ * 1. Decompress frame, if necessary
+ * 2. Deinterlace frame and scale to proper size, if necessary
+ * 3. Convert from YUV planar to destination format, if necessary
+ * 4. Fix the RGB offset, if necessary
+ */
+static void
+ov51x_postprocess(struct usb_ov511 *ov, struct ov511_frame *frame)
+{
+ if (dumppix) {
+ memset(frame->data, 0,
+ MAX_DATA_SIZE(ov->maxwidth, ov->maxheight));
+ PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd);
+ memcpy(frame->data, frame->rawdata, frame->bytes_recvd);
+ } else {
+ switch (frame->format) {
+ case VIDEO_PALETTE_GREY:
+ ov51x_postprocess_grey(ov, frame);
+ break;
+ case VIDEO_PALETTE_YUV420:
+ case VIDEO_PALETTE_YUV420P:
+ ov51x_postprocess_yuv420(ov, frame);
+ break;
+ default:
+ err("Cannot convert data to %s",
+ symbolic(v4l1_plist, frame->format));
+ }
+ }
+}
+
+/**********************************************************************
+ *
+ * OV51x data transfer, IRQ handler
+ *
+ **********************************************************************/
+
+static inline void
+ov511_move_data(struct usb_ov511 *ov, unsigned char *in, int n)
+{
+ int num, offset;
+ int pnum = in[ov->packet_size - 1]; /* Get packet number */
+ int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight);
+ struct ov511_frame *frame = &ov->frame[ov->curframe];
+ struct timeval *ts;
+
+ /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
+ * byte non-zero. The EOF packet has image width/height in the
+ * 10th and 11th bytes. The 9th byte is given as follows:
+ *
+ * bit 7: EOF
+ * 6: compression enabled
+ * 5: 422/420/400 modes
+ * 4: 422/420/400 modes
+ * 3: 1
+ * 2: snapshot button on
+ * 1: snapshot frame
+ * 0: even/odd field
+ */
+
+ if (printph) {
+ info("ph(%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x",
+ pnum, in[0], in[1], in[2], in[3], in[4], in[5], in[6],
+ in[7], in[8], in[9], in[10], in[11]);
+ }
+
+ /* Check for SOF/EOF packet */
+ if ((in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) ||
+ (~in[8] & 0x08))
+ goto check_middle;
+
+ /* Frame end */
+ if (in[8] & 0x80) {
+ ts = (struct timeval *)(frame->data
+ + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight));
+ do_gettimeofday(ts);
+
+ /* Get the actual frame size from the EOF header */
+ frame->rawwidth = ((int)(in[9]) + 1) * 8;
+ frame->rawheight = ((int)(in[10]) + 1) * 8;
+
+ PDEBUG(4, "Frame end, frame=%d, pnum=%d, w=%d, h=%d, recvd=%d",
+ ov->curframe, pnum, frame->rawwidth, frame->rawheight,
+ frame->bytes_recvd);
+
+ /* Validate the header data */
+ RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth);
+ RESTRICT_TO_RANGE(frame->rawheight, ov->minheight,
+ ov->maxheight);
+
+ /* Don't allow byte count to exceed buffer size */
+ RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw);
+
+ if (frame->scanstate == STATE_LINES) {
+ int nextf;
+
+ frame->grabstate = FRAME_DONE;
+ wake_up_interruptible(&frame->wq);
+
+ /* If next frame is ready or grabbing,
+ * point to it */
+ nextf = (ov->curframe + 1) % OV511_NUMFRAMES;
+ if (ov->frame[nextf].grabstate == FRAME_READY
+ || ov->frame[nextf].grabstate == FRAME_GRABBING) {
+ ov->curframe = nextf;
+ ov->frame[nextf].scanstate = STATE_SCANNING;
+ } else {
+ if (frame->grabstate == FRAME_DONE) {
+ PDEBUG(4, "** Frame done **");
+ } else {
+ PDEBUG(4, "Frame not ready? state = %d",
+ ov->frame[nextf].grabstate);
+ }
+
+ ov->curframe = -1;
+ }
+ } else {
+ PDEBUG(5, "Frame done, but not scanning");
+ }
+ /* Image corruption caused by misplaced frame->segment = 0
+ * fixed by carlosf@conectiva.com.br
+ */
+ } else {
+ /* Frame start */
+ PDEBUG(4, "Frame start, framenum = %d", ov->curframe);
+
+ /* Check to see if it's a snapshot frame */
+ /* FIXME?? Should the snapshot reset go here? Performance? */
+ if (in[8] & 0x02) {
+ frame->snapshot = 1;
+ PDEBUG(3, "snapshot detected");
+ }
+
+ frame->scanstate = STATE_LINES;
+ frame->bytes_recvd = 0;
+ frame->compressed = in[8] & 0x40;
+ }
+
+check_middle:
+ /* Are we in a frame? */
+ if (frame->scanstate != STATE_LINES) {
+ PDEBUG(5, "Not in a frame; packet skipped");
+ return;
+ }
+
+ /* If frame start, skip header */
+ if (frame->bytes_recvd == 0)
+ offset = 9;
+ else
+ offset = 0;
+
+ num = n - offset - 1;
+
+ /* Dump all data exactly as received */
+ if (dumppix == 2) {
+ frame->bytes_recvd += n - 1;
+ if (frame->bytes_recvd <= max_raw)
+ memcpy(frame->rawdata + frame->bytes_recvd - (n - 1),
+ in, n - 1);
+ else
+ PDEBUG(3, "Raw data buffer overrun!! (%d)",
+ frame->bytes_recvd - max_raw);
+ } else if (!frame->compressed && !remove_zeros) {
+ frame->bytes_recvd += num;
+ if (frame->bytes_recvd <= max_raw)
+ memcpy(frame->rawdata + frame->bytes_recvd - num,
+ in + offset, num);
+ else
+ PDEBUG(3, "Raw data buffer overrun!! (%d)",
+ frame->bytes_recvd - max_raw);
+ } else { /* Remove all-zero FIFO lines (aligned 32-byte blocks) */
+ int b, read = 0, allzero, copied = 0;
+ if (offset) {
+ frame->bytes_recvd += 32 - offset; // Bytes out
+ memcpy(frame->rawdata, in + offset, 32 - offset);
+ read += 32;
+ }
+
+ while (read < n - 1) {
+ allzero = 1;
+ for (b = 0; b < 32; b++) {
+ if (in[read + b]) {
+ allzero = 0;
+ break;
+ }
+ }
+
+ if (allzero) {
+ /* Don't copy it */
+ } else {
+ if (frame->bytes_recvd + copied + 32 <= max_raw)
+ {
+ memcpy(frame->rawdata
+ + frame->bytes_recvd + copied,
+ in + read, 32);
+ copied += 32;
+ } else {
+ PDEBUG(3, "Raw data buffer overrun!!");
+ }
+ }
+ read += 32;
+ }
+
+ frame->bytes_recvd += copied;
+ }
+}
+
+static inline void
+ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n)
+{
+ int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight);
+ struct ov511_frame *frame = &ov->frame[ov->curframe];
+ struct timeval *ts;
+
+ /* Don't copy the packet number byte */
+ if (ov->packet_numbering)
+ --n;
+
+ /* A false positive here is likely, until OVT gives me
+ * the definitive SOF/EOF format */
+ if ((!(in[0] | in[1] | in[2] | in[3] | in[5])) && in[6]) {
+ if (printph) {
+ info("ph: %2x %2x %2x %2x %2x %2x %2x %2x", in[0],
+ in[1], in[2], in[3], in[4], in[5], in[6], in[7]);
+ }
+
+ if (frame->scanstate == STATE_LINES) {
+ PDEBUG(4, "Detected frame end/start");
+ goto eof;
+ } else { //scanstate == STATE_SCANNING
+ /* Frame start */
+ PDEBUG(4, "Frame start, framenum = %d", ov->curframe);
+ goto sof;
+ }
+ } else {
+ goto check_middle;
+ }
+
+eof:
+ ts = (struct timeval *)(frame->data
+ + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight));
+ do_gettimeofday(ts);
+
+ PDEBUG(4, "Frame end, curframe = %d, hw=%d, vw=%d, recvd=%d",
+ ov->curframe,
+ (int)(in[9]), (int)(in[10]), frame->bytes_recvd);
+
+ // FIXME: Since we don't know the header formats yet,
+ // there is no way to know what the actual image size is
+ frame->rawwidth = frame->width;
+ frame->rawheight = frame->height;
+
+ /* Validate the header data */
+ RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth);
+ RESTRICT_TO_RANGE(frame->rawheight, ov->minheight, ov->maxheight);
+
+ /* Don't allow byte count to exceed buffer size */
+ RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw);
+
+ if (frame->scanstate == STATE_LINES) {
+ int nextf;
+
+ frame->grabstate = FRAME_DONE;
+ wake_up_interruptible(&frame->wq);
+
+ /* If next frame is ready or grabbing,
+ * point to it */
+ nextf = (ov->curframe + 1) % OV511_NUMFRAMES;
+ if (ov->frame[nextf].grabstate == FRAME_READY
+ || ov->frame[nextf].grabstate == FRAME_GRABBING) {
+ ov->curframe = nextf;
+ ov->frame[nextf].scanstate = STATE_SCANNING;
+ frame = &ov->frame[nextf];
+ } else {
+ if (frame->grabstate == FRAME_DONE) {
+ PDEBUG(4, "** Frame done **");
+ } else {
+ PDEBUG(4, "Frame not ready? state = %d",
+ ov->frame[nextf].grabstate);
+ }
+
+ ov->curframe = -1;
+ PDEBUG(4, "SOF dropped (no active frame)");
+ return; /* Nowhere to store this frame */
+ }
+ }
+sof:
+ PDEBUG(4, "Starting capture on frame %d", frame->framenum);
+
+// Snapshot not reverse-engineered yet.
+#if 0
+ /* Check to see if it's a snapshot frame */
+ /* FIXME?? Should the snapshot reset go here? Performance? */
+ if (in[8] & 0x02) {
+ frame->snapshot = 1;
+ PDEBUG(3, "snapshot detected");
+ }
+#endif
+ frame->scanstate = STATE_LINES;
+ frame->bytes_recvd = 0;
+ frame->compressed = 1;
+
+check_middle:
+ /* Are we in a frame? */
+ if (frame->scanstate != STATE_LINES) {
+ PDEBUG(4, "scanstate: no SOF yet");
+ return;
+ }
+
+ /* Dump all data exactly as received */
+ if (dumppix == 2) {
+ frame->bytes_recvd += n;
+ if (frame->bytes_recvd <= max_raw)
+ memcpy(frame->rawdata + frame->bytes_recvd - n, in, n);
+ else
+ PDEBUG(3, "Raw data buffer overrun!! (%d)",
+ frame->bytes_recvd - max_raw);
+ } else {
+ /* All incoming data are divided into 8-byte segments. If the
+ * segment contains all zero bytes, it must be skipped. These
+ * zero-segments allow the OV518 to mainain a constant data rate
+ * regardless of the effectiveness of the compression. Segments
+ * are aligned relative to the beginning of each isochronous
+ * packet. The first segment in each image is a header (the
+ * decompressor skips it later).
+ */
+
+ int b, read = 0, allzero, copied = 0;
+
+ while (read < n) {
+ allzero = 1;
+ for (b = 0; b < 8; b++) {
+ if (in[read + b]) {
+ allzero = 0;
+ break;
+ }
+ }
+
+ if (allzero) {
+ /* Don't copy it */
+ } else {
+ if (frame->bytes_recvd + copied + 8 <= max_raw)
+ {
+ memcpy(frame->rawdata
+ + frame->bytes_recvd + copied,
+ in + read, 8);
+ copied += 8;
+ } else {
+ PDEBUG(3, "Raw data buffer overrun!!");
+ }
+ }
+ read += 8;
+ }
+ frame->bytes_recvd += copied;
+ }
+}
+
+static void
+ov51x_isoc_irq(struct urb *urb, struct pt_regs *regs)
+{
+ int i;
+ struct usb_ov511 *ov;
+ struct ov511_sbuf *sbuf;
+
+ if (!urb->context) {
+ PDEBUG(4, "no context");
+ return;
+ }
+
+ sbuf = urb->context;
+ ov = sbuf->ov;
+
+ if (!ov || !ov->dev || !ov->user) {
+ PDEBUG(4, "no device, or not open");
+ return;
+ }
+
+ if (!ov->streaming) {
+ PDEBUG(4, "hmmm... not streaming, but got interrupt");
+ return;
+ }
+
+ if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
+ PDEBUG(4, "URB unlinked");
+ return;
+ }
+
+ if (urb->status != -EINPROGRESS && urb->status != 0) {
+ err("ERROR: urb->status=%d: %s", urb->status,
+ symbolic(urb_errlist, urb->status));
+ }
+
+ /* Copy the data received into our frame buffer */
+ PDEBUG(5, "sbuf[%d]: Moving %d packets", sbuf->n,
+ urb->number_of_packets);
+ for (i = 0; i < urb->number_of_packets; i++) {
+ /* Warning: Don't call *_move_data() if no frame active! */
+ if (ov->curframe >= 0) {
+ int n = urb->iso_frame_desc[i].actual_length;
+ int st = urb->iso_frame_desc[i].status;
+ unsigned char *cdata;
+
+ urb->iso_frame_desc[i].actual_length = 0;
+ urb->iso_frame_desc[i].status = 0;
+
+ cdata = urb->transfer_buffer
+ + urb->iso_frame_desc[i].offset;
+
+ if (!n) {
+ PDEBUG(4, "Zero-length packet");
+ continue;
+ }
+
+ if (st)
+ PDEBUG(2, "data error: [%d] len=%d, status=%d",
+ i, n, st);
+
+ if (ov->bclass == BCL_OV511)
+ ov511_move_data(ov, cdata, n);
+ else if (ov->bclass == BCL_OV518)
+ ov518_move_data(ov, cdata, n);
+ else
+ err("Unknown bridge device (%d)", ov->bridge);
+
+ } else if (waitqueue_active(&ov->wq)) {
+ wake_up_interruptible(&ov->wq);
+ }
+ }
+
+ /* Resubmit this URB */
+ urb->dev = ov->dev;
+ if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0)
+ err("usb_submit_urb() ret %d", i);
+
+ return;
+}
+
+/****************************************************************************
+ *
+ * Stream initialization and termination
+ *
+ ***************************************************************************/
+
+static int
+ov51x_init_isoc(struct usb_ov511 *ov)
+{
+ struct urb *urb;
+ int fx, err, n, size;
+
+ PDEBUG(3, "*** Initializing capture ***");
+
+ ov->curframe = -1;
+
+ if (ov->bridge == BRG_OV511) {
+ if (cams == 1)
+ size = 993;
+ else if (cams == 2)
+ size = 513;
+ else if (cams == 3 || cams == 4)
+ size = 257;
+ else {
+ err("\"cams\" parameter too high!");
+ return -1;
+ }
+ } else if (ov->bridge == BRG_OV511PLUS) {
+ if (cams == 1)
+ size = 961;
+ else if (cams == 2)
+ size = 513;
+ else if (cams == 3 || cams == 4)
+ size = 257;
+ else if (cams >= 5 && cams <= 8)
+ size = 129;
+ else if (cams >= 9 && cams <= 31)
+ size = 33;
+ else {
+ err("\"cams\" parameter too high!");
+ return -1;
+ }
+ } else if (ov->bclass == BCL_OV518) {
+ if (cams == 1)
+ size = 896;
+ else if (cams == 2)
+ size = 512;
+ else if (cams == 3 || cams == 4)
+ size = 256;
+ else if (cams >= 5 && cams <= 8)
+ size = 128;
+ else {
+ err("\"cams\" parameter too high!");
+ return -1;
+ }
+ } else {
+ err("invalid bridge type");
+ return -1;
+ }
+
+ // FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now
+ if (ov->bclass == BCL_OV518) {
+ if (packetsize == -1) {
+ ov518_set_packet_size(ov, 640);
+ } else {
+ info("Forcing packet size to %d", packetsize);
+ ov518_set_packet_size(ov, packetsize);
+ }
+ } else {
+ if (packetsize == -1) {
+ ov511_set_packet_size(ov, size);
+ } else {
+ info("Forcing packet size to %d", packetsize);
+ ov511_set_packet_size(ov, packetsize);
+ }
+ }
+
+ for (n = 0; n < OV511_NUMSBUF; n++) {
+ urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
+ if (!urb) {
+ err("init isoc: usb_alloc_urb ret. NULL");
+ return -ENOMEM;
+ }
+ ov->sbuf[n].urb = urb;
+ urb->dev = ov->dev;
+ urb->context = &ov->sbuf[n];
+ urb->pipe = usb_rcvisocpipe(ov->dev, OV511_ENDPOINT_ADDRESS);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = ov->sbuf[n].data;
+ urb->complete = ov51x_isoc_irq;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = ov->packet_size * FRAMES_PER_DESC;
+ urb->interval = 1;
+ for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+ urb->iso_frame_desc[fx].offset = ov->packet_size * fx;
+ urb->iso_frame_desc[fx].length = ov->packet_size;
+ }
+ }
+
+ ov->streaming = 1;
+
+ for (n = 0; n < OV511_NUMSBUF; n++) {
+ ov->sbuf[n].urb->dev = ov->dev;
+ err = usb_submit_urb(ov->sbuf[n].urb, GFP_KERNEL);
+ if (err) {
+ err("init isoc: usb_submit_urb(%d) ret %d", n, err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void
+ov51x_unlink_isoc(struct usb_ov511 *ov)
+{
+ int n;
+
+ /* Unschedule all of the iso td's */
+ for (n = OV511_NUMSBUF - 1; n >= 0; n--) {
+ if (ov->sbuf[n].urb) {
+ usb_kill_urb(ov->sbuf[n].urb);
+ usb_free_urb(ov->sbuf[n].urb);
+ ov->sbuf[n].urb = NULL;
+ }
+ }
+}
+
+static void
+ov51x_stop_isoc(struct usb_ov511 *ov)
+{
+ if (!ov->streaming || !ov->dev)
+ return;
+
+ PDEBUG(3, "*** Stopping capture ***");
+
+ if (ov->bclass == BCL_OV518)
+ ov518_set_packet_size(ov, 0);
+ else
+ ov511_set_packet_size(ov, 0);
+
+ ov->streaming = 0;
+
+ ov51x_unlink_isoc(ov);
+}
+
+static int
+ov51x_new_frame(struct usb_ov511 *ov, int framenum)
+{
+ struct ov511_frame *frame;
+ int newnum;
+
+ PDEBUG(4, "ov->curframe = %d, framenum = %d", ov->curframe, framenum);
+
+ if (!ov->dev)
+ return -1;
+
+ /* If we're not grabbing a frame right now and the other frame is */
+ /* ready to be grabbed into, then use it instead */
+ if (ov->curframe == -1) {
+ newnum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES;
+ if (ov->frame[newnum].grabstate == FRAME_READY)
+ framenum = newnum;
+ } else
+ return 0;
+
+ frame = &ov->frame[framenum];
+
+ PDEBUG(4, "framenum = %d, width = %d, height = %d", framenum,
+ frame->width, frame->height);
+
+ frame->grabstate = FRAME_GRABBING;
+ frame->scanstate = STATE_SCANNING;
+ frame->snapshot = 0;
+
+ ov->curframe = framenum;
+
+ /* Make sure it's not too big */
+ if (frame->width > ov->maxwidth)
+ frame->width = ov->maxwidth;
+
+ frame->width &= ~7L; /* Multiple of 8 */
+
+ if (frame->height > ov->maxheight)
+ frame->height = ov->maxheight;
+
+ frame->height &= ~3L; /* Multiple of 4 */
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * Buffer management
+ *
+ ***************************************************************************/
+
+/*
+ * - You must acquire buf_lock before entering this function.
+ * - Because this code will free any non-null pointer, you must be sure to null
+ * them if you explicitly free them somewhere else!
+ */
+static void
+ov51x_do_dealloc(struct usb_ov511 *ov)
+{
+ int i;
+ PDEBUG(4, "entered");
+
+ if (ov->fbuf) {
+ rvfree(ov->fbuf, OV511_NUMFRAMES
+ * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight));
+ ov->fbuf = NULL;
+ }
+
+ vfree(ov->rawfbuf);
+ ov->rawfbuf = NULL;
+
+ vfree(ov->tempfbuf);
+ ov->tempfbuf = NULL;
+
+ for (i = 0; i < OV511_NUMSBUF; i++) {
+ kfree(ov->sbuf[i].data);
+ ov->sbuf[i].data = NULL;
+ }
+
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].data = NULL;
+ ov->frame[i].rawdata = NULL;
+ ov->frame[i].tempdata = NULL;
+ if (ov->frame[i].compbuf) {
+ free_page((unsigned long) ov->frame[i].compbuf);
+ ov->frame[i].compbuf = NULL;
+ }
+ }
+
+ PDEBUG(4, "buffer memory deallocated");
+ ov->buf_state = BUF_NOT_ALLOCATED;
+ PDEBUG(4, "leaving");
+}
+
+static int
+ov51x_alloc(struct usb_ov511 *ov)
+{
+ int i;
+ const int w = ov->maxwidth;
+ const int h = ov->maxheight;
+ const int data_bufsize = OV511_NUMFRAMES * MAX_DATA_SIZE(w, h);
+ const int raw_bufsize = OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h);
+
+ PDEBUG(4, "entered");
+ mutex_lock(&ov->buf_lock);
+
+ if (ov->buf_state == BUF_ALLOCATED)
+ goto out;
+
+ ov->fbuf = rvmalloc(data_bufsize);
+ if (!ov->fbuf)
+ goto error;
+
+ ov->rawfbuf = vmalloc(raw_bufsize);
+ if (!ov->rawfbuf)
+ goto error;
+
+ memset(ov->rawfbuf, 0, raw_bufsize);
+
+ ov->tempfbuf = vmalloc(raw_bufsize);
+ if (!ov->tempfbuf)
+ goto error;
+
+ memset(ov->tempfbuf, 0, raw_bufsize);
+
+ for (i = 0; i < OV511_NUMSBUF; i++) {
+ ov->sbuf[i].data = kmalloc(FRAMES_PER_DESC *
+ MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL);
+ if (!ov->sbuf[i].data)
+ goto error;
+
+ PDEBUG(4, "sbuf[%d] @ %p", i, ov->sbuf[i].data);
+ }
+
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].data = ov->fbuf + i * MAX_DATA_SIZE(w, h);
+ ov->frame[i].rawdata = ov->rawfbuf
+ + i * MAX_RAW_DATA_SIZE(w, h);
+ ov->frame[i].tempdata = ov->tempfbuf
+ + i * MAX_RAW_DATA_SIZE(w, h);
+
+ ov->frame[i].compbuf =
+ (unsigned char *) __get_free_page(GFP_KERNEL);
+ if (!ov->frame[i].compbuf)
+ goto error;
+
+ PDEBUG(4, "frame[%d] @ %p", i, ov->frame[i].data);
+ }
+
+ ov->buf_state = BUF_ALLOCATED;
+out:
+ mutex_unlock(&ov->buf_lock);
+ PDEBUG(4, "leaving");
+ return 0;
+error:
+ ov51x_do_dealloc(ov);
+ mutex_unlock(&ov->buf_lock);
+ PDEBUG(4, "errored");
+ return -ENOMEM;
+}
+
+static void
+ov51x_dealloc(struct usb_ov511 *ov)
+{
+ PDEBUG(4, "entered");
+ mutex_lock(&ov->buf_lock);
+ ov51x_do_dealloc(ov);
+ mutex_unlock(&ov->buf_lock);
+ PDEBUG(4, "leaving");
+}
+
+/****************************************************************************
+ *
+ * V4L 1 API
+ *
+ ***************************************************************************/
+
+static int
+ov51x_v4l1_open(struct inode *inode, struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct usb_ov511 *ov = video_get_drvdata(vdev);
+ int err, i;
+
+ PDEBUG(4, "opening");
+
+ mutex_lock(&ov->lock);
+
+ err = -EBUSY;
+ if (ov->user)
+ goto out;
+
+ ov->sub_flag = 0;
+
+ /* In case app doesn't set them... */
+ err = ov51x_set_default_params(ov);
+ if (err < 0)
+ goto out;
+
+ /* Make sure frames are reset */
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].grabstate = FRAME_UNUSED;
+ ov->frame[i].bytes_read = 0;
+ }
+
+ /* If compression is on, make sure now that a
+ * decompressor can be loaded */
+ if (ov->compress && !ov->decomp_ops) {
+ err = request_decompressor(ov);
+ if (err && !dumppix)
+ goto out;
+ }
+
+ err = ov51x_alloc(ov);
+ if (err < 0)
+ goto out;
+
+ err = ov51x_init_isoc(ov);
+ if (err) {
+ ov51x_dealloc(ov);
+ goto out;
+ }
+
+ ov->user++;
+ file->private_data = vdev;
+
+ if (ov->led_policy == LED_AUTO)
+ ov51x_led_control(ov, 1);
+
+out:
+ mutex_unlock(&ov->lock);
+ return err;
+}
+
+static int
+ov51x_v4l1_close(struct inode *inode, struct file *file)
+{
+ struct video_device *vdev = file->private_data;
+ struct usb_ov511 *ov = video_get_drvdata(vdev);
+
+ PDEBUG(4, "ov511_close");
+
+ mutex_lock(&ov->lock);
+
+ ov->user--;
+ ov51x_stop_isoc(ov);
+
+ if (ov->led_policy == LED_AUTO)
+ ov51x_led_control(ov, 0);
+
+ if (ov->dev)
+ ov51x_dealloc(ov);
+
+ mutex_unlock(&ov->lock);
+
+ /* Device unplugged while open. Only a minimum of unregistration is done
+ * here; the disconnect callback already did the rest. */
+ if (!ov->dev) {
+ mutex_lock(&ov->cbuf_lock);
+ kfree(ov->cbuf);
+ ov->cbuf = NULL;
+ mutex_unlock(&ov->cbuf_lock);
+
+ ov51x_dealloc(ov);
+ kfree(ov);
+ ov = NULL;
+ }
+
+ file->private_data = NULL;
+ return 0;
+}
+
+/* Do not call this function directly! */
+static int
+ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = file->private_data;
+ struct usb_ov511 *ov = video_get_drvdata(vdev);
+ PDEBUG(5, "IOCtl: 0x%X", cmd);
+
+ if (!ov->dev)
+ return -EIO;
+
+ switch (cmd) {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *b = arg;
+
+ PDEBUG(4, "VIDIOCGCAP");
+
+ memset(b, 0, sizeof(struct video_capability));
+ sprintf(b->name, "%s USB Camera",
+ symbolic(brglist, ov->bridge));
+ b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
+ b->channels = ov->num_inputs;
+ b->audios = 0;
+ b->maxwidth = ov->maxwidth;
+ b->maxheight = ov->maxheight;
+ b->minwidth = ov->minwidth;
+ b->minheight = ov->minheight;
+
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *v = arg;
+
+ PDEBUG(4, "VIDIOCGCHAN");
+
+ if ((unsigned)(v->channel) >= ov->num_inputs) {
+ err("Invalid channel (%d)", v->channel);
+ return -EINVAL;
+ }
+
+ v->norm = ov->norm;
+ v->type = VIDEO_TYPE_CAMERA;
+ v->flags = 0;
+// v->flags |= (ov->has_decoder) ? VIDEO_VC_NORM : 0;
+ v->tuners = 0;
+ decoder_get_input_name(ov, v->channel, v->name);
+
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ struct video_channel *v = arg;
+ int err;
+
+ PDEBUG(4, "VIDIOCSCHAN");
+
+ /* Make sure it's not a camera */
+ if (!ov->has_decoder) {
+ if (v->channel == 0)
+ return 0;
+ else
+ return -EINVAL;
+ }
+
+ if (v->norm != VIDEO_MODE_PAL &&
+ v->norm != VIDEO_MODE_NTSC &&
+ v->norm != VIDEO_MODE_SECAM &&
+ v->norm != VIDEO_MODE_AUTO) {
+ err("Invalid norm (%d)", v->norm);
+ return -EINVAL;
+ }
+
+ if ((unsigned)(v->channel) >= ov->num_inputs) {
+ err("Invalid channel (%d)", v->channel);
+ return -EINVAL;
+ }
+
+ err = decoder_set_input(ov, v->channel);
+ if (err)
+ return err;
+
+ err = decoder_set_norm(ov, v->norm);
+ if (err)
+ return err;
+
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture *p = arg;
+
+ PDEBUG(4, "VIDIOCGPICT");
+
+ memset(p, 0, sizeof(struct video_picture));
+ if (sensor_get_picture(ov, p))
+ return -EIO;
+
+ /* Can we get these from frame[0]? -claudio? */
+ p->depth = ov->frame[0].depth;
+ p->palette = ov->frame[0].format;
+
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture *p = arg;
+ int i, rc;
+
+ PDEBUG(4, "VIDIOCSPICT");
+
+ if (!get_depth(p->palette))
+ return -EINVAL;
+
+ if (sensor_set_picture(ov, p))
+ return -EIO;
+
+ if (force_palette && p->palette != force_palette) {
+ info("Palette rejected (%s)",
+ symbolic(v4l1_plist, p->palette));
+ return -EINVAL;
+ }
+
+ // FIXME: Format should be independent of frames
+ if (p->palette != ov->frame[0].format) {
+ PDEBUG(4, "Detected format change");
+
+ rc = ov51x_wait_frames_inactive(ov);
+ if (rc)
+ return rc;
+
+ mode_init_regs(ov, ov->frame[0].width,
+ ov->frame[0].height, p->palette, ov->sub_flag);
+ }
+
+ PDEBUG(4, "Setting depth=%d, palette=%s",
+ p->depth, symbolic(v4l1_plist, p->palette));
+
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].depth = p->depth;
+ ov->frame[i].format = p->palette;
+ }
+
+ return 0;
+ }
+ case VIDIOCGCAPTURE:
+ {
+ int *vf = arg;
+
+ PDEBUG(4, "VIDIOCGCAPTURE");
+
+ ov->sub_flag = *vf;
+ return 0;
+ }
+ case VIDIOCSCAPTURE:
+ {
+ struct video_capture *vc = arg;
+
+ PDEBUG(4, "VIDIOCSCAPTURE");
+
+ if (vc->flags)
+ return -EINVAL;
+ if (vc->decimation)
+ return -EINVAL;
+
+ vc->x &= ~3L;
+ vc->y &= ~1L;
+ vc->y &= ~31L;
+
+ if (vc->width == 0)
+ vc->width = 32;
+
+ vc->height /= 16;
+ vc->height *= 16;
+ if (vc->height == 0)
+ vc->height = 16;
+
+ ov->subx = vc->x;
+ ov->suby = vc->y;
+ ov->subw = vc->width;
+ ov->subh = vc->height;
+
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window *vw = arg;
+ int i, rc;
+
+ PDEBUG(4, "VIDIOCSWIN: %dx%d", vw->width, vw->height);
+
+#if 0
+ if (vw->flags)
+ return -EINVAL;
+ if (vw->clipcount)
+ return -EINVAL;
+ if (vw->height != ov->maxheight)
+ return -EINVAL;
+ if (vw->width != ov->maxwidth)
+ return -EINVAL;
+#endif
+
+ rc = ov51x_wait_frames_inactive(ov);
+ if (rc)
+ return rc;
+
+ rc = mode_init_regs(ov, vw->width, vw->height,
+ ov->frame[0].format, ov->sub_flag);
+ if (rc < 0)
+ return rc;
+
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].width = vw->width;
+ ov->frame[i].height = vw->height;
+ }
+
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window *vw = arg;
+
+ memset(vw, 0, sizeof(struct video_window));
+ vw->x = 0; /* FIXME */
+ vw->y = 0;
+ vw->width = ov->frame[0].width;
+ vw->height = ov->frame[0].height;
+ vw->flags = 30;
+
+ PDEBUG(4, "VIDIOCGWIN: %dx%d", vw->width, vw->height);
+
+ return 0;
+ }
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf *vm = arg;
+ int i;
+
+ PDEBUG(4, "VIDIOCGMBUF");
+
+ memset(vm, 0, sizeof(struct video_mbuf));
+ vm->size = OV511_NUMFRAMES
+ * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight);
+ vm->frames = OV511_NUMFRAMES;
+
+ vm->offsets[0] = 0;
+ for (i = 1; i < OV511_NUMFRAMES; i++) {
+ vm->offsets[i] = vm->offsets[i-1]
+ + MAX_DATA_SIZE(ov->maxwidth, ov->maxheight);
+ }
+
+ return 0;
+ }
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap *vm = arg;
+ int rc, depth;
+ unsigned int f = vm->frame;
+
+ PDEBUG(4, "VIDIOCMCAPTURE: frame: %d, %dx%d, %s", f, vm->width,
+ vm->height, symbolic(v4l1_plist, vm->format));
+
+ depth = get_depth(vm->format);
+ if (!depth) {
+ PDEBUG(2, "VIDIOCMCAPTURE: invalid format (%s)",
+ symbolic(v4l1_plist, vm->format));
+ return -EINVAL;
+ }
+
+ if (f >= OV511_NUMFRAMES) {
+ err("VIDIOCMCAPTURE: invalid frame (%d)", f);
+ return -EINVAL;
+ }
+
+ if (vm->width > ov->maxwidth
+ || vm->height > ov->maxheight) {
+ err("VIDIOCMCAPTURE: requested dimensions too big");
+ return -EINVAL;
+ }
+
+ if (ov->frame[f].grabstate == FRAME_GRABBING) {
+ PDEBUG(4, "VIDIOCMCAPTURE: already grabbing");
+ return -EBUSY;
+ }
+
+ if (force_palette && (vm->format != force_palette)) {
+ PDEBUG(2, "palette rejected (%s)",
+ symbolic(v4l1_plist, vm->format));
+ return -EINVAL;
+ }
+
+ if ((ov->frame[f].width != vm->width) ||
+ (ov->frame[f].height != vm->height) ||
+ (ov->frame[f].format != vm->format) ||
+ (ov->frame[f].sub_flag != ov->sub_flag) ||
+ (ov->frame[f].depth != depth)) {
+ PDEBUG(4, "VIDIOCMCAPTURE: change in image parameters");
+
+ rc = ov51x_wait_frames_inactive(ov);
+ if (rc)
+ return rc;
+
+ rc = mode_init_regs(ov, vm->width, vm->height,
+ vm->format, ov->sub_flag);
+#if 0
+ if (rc < 0) {
+ PDEBUG(1, "Got error while initializing regs ");
+ return ret;
+ }
+#endif
+ ov->frame[f].width = vm->width;
+ ov->frame[f].height = vm->height;
+ ov->frame[f].format = vm->format;
+ ov->frame[f].sub_flag = ov->sub_flag;
+ ov->frame[f].depth = depth;
+ }
+
+ /* Mark it as ready */
+ ov->frame[f].grabstate = FRAME_READY;
+
+ PDEBUG(4, "VIDIOCMCAPTURE: renewing frame %d", f);
+
+ return ov51x_new_frame(ov, f);
+ }
+ case VIDIOCSYNC:
+ {
+ unsigned int fnum = *((unsigned int *) arg);
+ struct ov511_frame *frame;
+ int rc;
+
+ if (fnum >= OV511_NUMFRAMES) {
+ err("VIDIOCSYNC: invalid frame (%d)", fnum);
+ return -EINVAL;
+ }
+
+ frame = &ov->frame[fnum];
+
+ PDEBUG(4, "syncing to frame %d, grabstate = %d", fnum,
+ frame->grabstate);
+
+ switch (frame->grabstate) {
+ case FRAME_UNUSED:
+ return -EINVAL;
+ case FRAME_READY:
+ case FRAME_GRABBING:
+ case FRAME_ERROR:
+redo:
+ if (!ov->dev)
+ return -EIO;
+
+ rc = wait_event_interruptible(frame->wq,
+ (frame->grabstate == FRAME_DONE)
+ || (frame->grabstate == FRAME_ERROR));
+
+ if (rc)
+ return rc;
+
+ if (frame->grabstate == FRAME_ERROR) {
+ if ((rc = ov51x_new_frame(ov, fnum)) < 0)
+ return rc;
+ goto redo;
+ }
+ /* Fall through */
+ case FRAME_DONE:
+ if (ov->snap_enabled && !frame->snapshot) {
+ if ((rc = ov51x_new_frame(ov, fnum)) < 0)
+ return rc;
+ goto redo;
+ }
+
+ frame->grabstate = FRAME_UNUSED;
+
+ /* Reset the hardware snapshot button */
+ /* FIXME - Is this the best place for this? */
+ if ((ov->snap_enabled) && (frame->snapshot)) {
+ frame->snapshot = 0;
+ ov51x_clear_snapshot(ov);
+ }
+
+ /* Decompression, format conversion, etc... */
+ ov51x_postprocess(ov, frame);
+
+ break;
+ } /* end switch */
+
+ return 0;
+ }
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer *vb = arg;
+
+ PDEBUG(4, "VIDIOCGFBUF");
+
+ memset(vb, 0, sizeof(struct video_buffer));
+
+ return 0;
+ }
+ case VIDIOCGUNIT:
+ {
+ struct video_unit *vu = arg;
+
+ PDEBUG(4, "VIDIOCGUNIT");
+
+ memset(vu, 0, sizeof(struct video_unit));
+
+ vu->video = ov->vdev->minor;
+ vu->vbi = VIDEO_NO_UNIT;
+ vu->radio = VIDEO_NO_UNIT;
+ vu->audio = VIDEO_NO_UNIT;
+ vu->teletext = VIDEO_NO_UNIT;
+
+ return 0;
+ }
+ case OV511IOC_WI2C:
+ {
+ struct ov511_i2c_struct *w = arg;
+
+ return i2c_w_slave(ov, w->slave, w->reg, w->value, w->mask);
+ }
+ case OV511IOC_RI2C:
+ {
+ struct ov511_i2c_struct *r = arg;
+ int rc;
+
+ rc = i2c_r_slave(ov, r->slave, r->reg);
+ if (rc < 0)
+ return rc;
+
+ r->value = rc;
+ return 0;
+ }
+ default:
+ PDEBUG(3, "Unsupported IOCtl: 0x%X", cmd);
+ return -ENOIOCTLCMD;
+ } /* end switch */
+
+ return 0;
+}
+
+static int
+ov51x_v4l1_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = file->private_data;
+ struct usb_ov511 *ov = video_get_drvdata(vdev);
+ int rc;
+
+ if (mutex_lock_interruptible(&ov->lock))
+ return -EINTR;
+
+ rc = video_usercopy(inode, file, cmd, arg, ov51x_v4l1_ioctl_internal);
+
+ mutex_unlock(&ov->lock);
+ return rc;
+}
+
+static ssize_t
+ov51x_v4l1_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos)
+{
+ struct video_device *vdev = file->private_data;
+ int noblock = file->f_flags&O_NONBLOCK;
+ unsigned long count = cnt;
+ struct usb_ov511 *ov = video_get_drvdata(vdev);
+ int i, rc = 0, frmx = -1;
+ struct ov511_frame *frame;
+
+ if (mutex_lock_interruptible(&ov->lock))
+ return -EINTR;
+
+ PDEBUG(4, "%ld bytes, noblock=%d", count, noblock);
+
+ if (!vdev || !buf) {
+ rc = -EFAULT;
+ goto error;
+ }
+
+ if (!ov->dev) {
+ rc = -EIO;
+ goto error;
+ }
+
+// FIXME: Only supports two frames
+ /* See if a frame is completed, then use it. */
+ if (ov->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */
+ frmx = 0;
+ else if (ov->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */
+ frmx = 1;
+
+ /* If nonblocking we return immediately */
+ if (noblock && (frmx == -1)) {
+ rc = -EAGAIN;
+ goto error;
+ }
+
+ /* If no FRAME_DONE, look for a FRAME_GRABBING state. */
+ /* See if a frame is in process (grabbing), then use it. */
+ if (frmx == -1) {
+ if (ov->frame[0].grabstate == FRAME_GRABBING)
+ frmx = 0;
+ else if (ov->frame[1].grabstate == FRAME_GRABBING)
+ frmx = 1;
+ }
+
+ /* If no frame is active, start one. */
+ if (frmx == -1) {
+ if ((rc = ov51x_new_frame(ov, frmx = 0))) {
+ err("read: ov51x_new_frame error");
+ goto error;
+ }
+ }
+
+ frame = &ov->frame[frmx];
+
+restart:
+ if (!ov->dev) {
+ rc = -EIO;
+ goto error;
+ }
+
+ /* Wait while we're grabbing the image */
+ PDEBUG(4, "Waiting image grabbing");
+ rc = wait_event_interruptible(frame->wq,
+ (frame->grabstate == FRAME_DONE)
+ || (frame->grabstate == FRAME_ERROR));
+
+ if (rc)
+ goto error;
+
+ PDEBUG(4, "Got image, frame->grabstate = %d", frame->grabstate);
+ PDEBUG(4, "bytes_recvd = %d", frame->bytes_recvd);
+
+ if (frame->grabstate == FRAME_ERROR) {
+ frame->bytes_read = 0;
+ err("** ick! ** Errored frame %d", ov->curframe);
+ if (ov51x_new_frame(ov, frmx)) {
+ err("read: ov51x_new_frame error");
+ goto error;
+ }
+ goto restart;
+ }
+
+
+ /* Repeat until we get a snapshot frame */
+ if (ov->snap_enabled)
+ PDEBUG(4, "Waiting snapshot frame");
+ if (ov->snap_enabled && !frame->snapshot) {
+ frame->bytes_read = 0;
+ if ((rc = ov51x_new_frame(ov, frmx))) {
+ err("read: ov51x_new_frame error");
+ goto error;
+ }
+ goto restart;
+ }
+
+ /* Clear the snapshot */
+ if (ov->snap_enabled && frame->snapshot) {
+ frame->snapshot = 0;
+ ov51x_clear_snapshot(ov);
+ }
+
+ /* Decompression, format conversion, etc... */
+ ov51x_postprocess(ov, frame);
+
+ PDEBUG(4, "frmx=%d, bytes_read=%ld, length=%ld", frmx,
+ frame->bytes_read,
+ get_frame_length(frame));
+
+ /* copy bytes to user space; we allow for partials reads */
+// if ((count + frame->bytes_read)
+// > get_frame_length((struct ov511_frame *)frame))
+// count = frame->scanlength - frame->bytes_read;
+
+ /* FIXME - count hardwired to be one frame... */
+ count = get_frame_length(frame);
+
+ PDEBUG(4, "Copy to user space: %ld bytes", count);
+ if ((i = copy_to_user(buf, frame->data + frame->bytes_read, count))) {
+ PDEBUG(4, "Copy failed! %d bytes not copied", i);
+ rc = -EFAULT;
+ goto error;
+ }
+
+ frame->bytes_read += count;
+ PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld",
+ count, frame->bytes_read);
+
+ /* If all data have been read... */
+ if (frame->bytes_read
+ >= get_frame_length(frame)) {
+ frame->bytes_read = 0;
+
+// FIXME: Only supports two frames
+ /* Mark it as available to be used again. */
+ ov->frame[frmx].grabstate = FRAME_UNUSED;
+ if ((rc = ov51x_new_frame(ov, !frmx))) {
+ err("ov51x_new_frame returned error");
+ goto error;
+ }
+ }
+
+ PDEBUG(4, "read finished, returning %ld (sweet)", count);
+
+ mutex_unlock(&ov->lock);
+ return count;
+
+error:
+ mutex_unlock(&ov->lock);
+ return rc;
+}
+
+static int
+ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = file->private_data;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ struct usb_ov511 *ov = video_get_drvdata(vdev);
+ unsigned long page, pos;
+
+ if (ov->dev == NULL)
+ return -EIO;
+
+ PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
+
+ if (size > (((OV511_NUMFRAMES
+ * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight)
+ + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))))
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&ov->lock))
+ return -EINTR;
+
+ pos = (unsigned long)ov->fbuf;
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
+ mutex_unlock(&ov->lock);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ mutex_unlock(&ov->lock);
+ return 0;
+}
+
+static struct file_operations ov511_fops = {
+ .owner = THIS_MODULE,
+ .open = ov51x_v4l1_open,
+ .release = ov51x_v4l1_close,
+ .read = ov51x_v4l1_read,
+ .mmap = ov51x_v4l1_mmap,
+ .ioctl = ov51x_v4l1_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device vdev_template = {
+ .owner = THIS_MODULE,
+ .name = "OV511 USB Camera",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_OV511,
+ .fops = &ov511_fops,
+ .release = video_device_release,
+ .minor = -1,
+};
+
+/****************************************************************************
+ *
+ * OV511 and sensor configuration
+ *
+ ***************************************************************************/
+
+/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses
+ * the same register settings as the OV7610, since they are very similar.
+ */
+static int
+ov7xx0_configure(struct usb_ov511 *ov)
+{
+ int i, success;
+ int rc;
+
+ /* Lawrence Glaister <lg@jfm.bc.ca> reports:
+ *
+ * Register 0x0f in the 7610 has the following effects:
+ *
+ * 0x85 (AEC method 1): Best overall, good contrast range
+ * 0x45 (AEC method 2): Very overexposed
+ * 0xa5 (spec sheet default): Ok, but the black level is
+ * shifted resulting in loss of contrast
+ * 0x05 (old driver setting): very overexposed, too much
+ * contrast
+ */
+ static struct ov511_regvals aRegvalsNorm7610[] = {
+ { OV511_I2C_BUS, 0x10, 0xff },
+ { OV511_I2C_BUS, 0x16, 0x06 },
+ { OV511_I2C_BUS, 0x28, 0x24 },
+ { OV511_I2C_BUS, 0x2b, 0xac },
+ { OV511_I2C_BUS, 0x12, 0x00 },
+ { OV511_I2C_BUS, 0x38, 0x81 },
+ { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */
+ { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */
+ { OV511_I2C_BUS, 0x15, 0x01 },
+ { OV511_I2C_BUS, 0x20, 0x1c },
+ { OV511_I2C_BUS, 0x23, 0x2a },
+ { OV511_I2C_BUS, 0x24, 0x10 },
+ { OV511_I2C_BUS, 0x25, 0x8a },
+ { OV511_I2C_BUS, 0x26, 0xa2 },
+ { OV511_I2C_BUS, 0x27, 0xc2 },
+ { OV511_I2C_BUS, 0x2a, 0x04 },
+ { OV511_I2C_BUS, 0x2c, 0xfe },
+ { OV511_I2C_BUS, 0x2d, 0x93 },
+ { OV511_I2C_BUS, 0x30, 0x71 },
+ { OV511_I2C_BUS, 0x31, 0x60 },
+ { OV511_I2C_BUS, 0x32, 0x26 },
+ { OV511_I2C_BUS, 0x33, 0x20 },
+ { OV511_I2C_BUS, 0x34, 0x48 },
+ { OV511_I2C_BUS, 0x12, 0x24 },
+ { OV511_I2C_BUS, 0x11, 0x01 },
+ { OV511_I2C_BUS, 0x0c, 0x24 },
+ { OV511_I2C_BUS, 0x0d, 0x24 },
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+ static struct ov511_regvals aRegvalsNorm7620[] = {
+ { OV511_I2C_BUS, 0x00, 0x00 },
+ { OV511_I2C_BUS, 0x01, 0x80 },
+ { OV511_I2C_BUS, 0x02, 0x80 },
+ { OV511_I2C_BUS, 0x03, 0xc0 },
+ { OV511_I2C_BUS, 0x06, 0x60 },
+ { OV511_I2C_BUS, 0x07, 0x00 },
+ { OV511_I2C_BUS, 0x0c, 0x24 },
+ { OV511_I2C_BUS, 0x0c, 0x24 },
+ { OV511_I2C_BUS, 0x0d, 0x24 },
+ { OV511_I2C_BUS, 0x11, 0x01 },
+ { OV511_I2C_BUS, 0x12, 0x24 },
+ { OV511_I2C_BUS, 0x13, 0x01 },
+ { OV511_I2C_BUS, 0x14, 0x84 },
+ { OV511_I2C_BUS, 0x15, 0x01 },
+ { OV511_I2C_BUS, 0x16, 0x03 },
+ { OV511_I2C_BUS, 0x17, 0x2f },
+ { OV511_I2C_BUS, 0x18, 0xcf },
+ { OV511_I2C_BUS, 0x19, 0x06 },
+ { OV511_I2C_BUS, 0x1a, 0xf5 },
+ { OV511_I2C_BUS, 0x1b, 0x00 },
+ { OV511_I2C_BUS, 0x20, 0x18 },
+ { OV511_I2C_BUS, 0x21, 0x80 },
+ { OV511_I2C_BUS, 0x22, 0x80 },
+ { OV511_I2C_BUS, 0x23, 0x00 },
+ { OV511_I2C_BUS, 0x26, 0xa2 },
+ { OV511_I2C_BUS, 0x27, 0xea },
+ { OV511_I2C_BUS, 0x28, 0x20 },
+ { OV511_I2C_BUS, 0x29, 0x00 },
+ { OV511_I2C_BUS, 0x2a, 0x10 },
+ { OV511_I2C_BUS, 0x2b, 0x00 },
+ { OV511_I2C_BUS, 0x2c, 0x88 },
+ { OV511_I2C_BUS, 0x2d, 0x91 },
+ { OV511_I2C_BUS, 0x2e, 0x80 },
+ { OV511_I2C_BUS, 0x2f, 0x44 },
+ { OV511_I2C_BUS, 0x60, 0x27 },
+ { OV511_I2C_BUS, 0x61, 0x02 },
+ { OV511_I2C_BUS, 0x62, 0x5f },
+ { OV511_I2C_BUS, 0x63, 0xd5 },
+ { OV511_I2C_BUS, 0x64, 0x57 },
+ { OV511_I2C_BUS, 0x65, 0x83 },
+ { OV511_I2C_BUS, 0x66, 0x55 },
+ { OV511_I2C_BUS, 0x67, 0x92 },
+ { OV511_I2C_BUS, 0x68, 0xcf },
+ { OV511_I2C_BUS, 0x69, 0x76 },
+ { OV511_I2C_BUS, 0x6a, 0x22 },
+ { OV511_I2C_BUS, 0x6b, 0x00 },
+ { OV511_I2C_BUS, 0x6c, 0x02 },
+ { OV511_I2C_BUS, 0x6d, 0x44 },
+ { OV511_I2C_BUS, 0x6e, 0x80 },
+ { OV511_I2C_BUS, 0x6f, 0x1d },
+ { OV511_I2C_BUS, 0x70, 0x8b },
+ { OV511_I2C_BUS, 0x71, 0x00 },
+ { OV511_I2C_BUS, 0x72, 0x14 },
+ { OV511_I2C_BUS, 0x73, 0x54 },
+ { OV511_I2C_BUS, 0x74, 0x00 },
+ { OV511_I2C_BUS, 0x75, 0x8e },
+ { OV511_I2C_BUS, 0x76, 0x00 },
+ { OV511_I2C_BUS, 0x77, 0xff },
+ { OV511_I2C_BUS, 0x78, 0x80 },
+ { OV511_I2C_BUS, 0x79, 0x80 },
+ { OV511_I2C_BUS, 0x7a, 0x80 },
+ { OV511_I2C_BUS, 0x7b, 0xe2 },
+ { OV511_I2C_BUS, 0x7c, 0x00 },
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+ PDEBUG(4, "starting configuration");
+
+ /* This looks redundant, but is necessary for WebCam 3 */
+ ov->primary_i2c_slave = OV7xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0)
+ return -1;
+
+ if (init_ov_sensor(ov) >= 0) {
+ PDEBUG(1, "OV7xx0 sensor initalized (method 1)");
+ } else {
+ /* Reset the 76xx */
+ if (i2c_w(ov, 0x12, 0x80) < 0)
+ return -1;
+
+ /* Wait for it to initialize */
+ msleep(150);
+
+ i = 0;
+ success = 0;
+ while (i <= i2c_detect_tries) {
+ if ((i2c_r(ov, OV7610_REG_ID_HIGH) == 0x7F) &&
+ (i2c_r(ov, OV7610_REG_ID_LOW) == 0xA2)) {
+ success = 1;
+ break;
+ } else {
+ i++;
+ }
+ }
+
+// Was (i == i2c_detect_tries) previously. This obviously used to always report
+// success. Whether anyone actually depended on that bug is unknown
+ if ((i >= i2c_detect_tries) && (success == 0)) {
+ err("Failed to read sensor ID. You might not have an");
+ err("OV7610/20, or it may be not responding. Report");
+ err("this to " EMAIL);
+ err("This is only a warning. You can attempt to use");
+ err("your camera anyway");
+// Only issue a warning for now
+// return -1;
+ } else {
+ PDEBUG(1, "OV7xx0 initialized (method 2, %dx)", i+1);
+ }
+ }
+
+ /* Detect sensor (sub)type */
+ rc = i2c_r(ov, OV7610_REG_COM_I);
+
+ if (rc < 0) {
+ err("Error detecting sensor type");
+ return -1;
+ } else if ((rc & 3) == 3) {
+ info("Sensor is an OV7610");
+ ov->sensor = SEN_OV7610;
+ } else if ((rc & 3) == 1) {
+ /* I don't know what's different about the 76BE yet. */
+ if (i2c_r(ov, 0x15) & 1)
+ info("Sensor is an OV7620AE");
+ else
+ info("Sensor is an OV76BE");
+
+ /* OV511+ will return all zero isoc data unless we
+ * configure the sensor as a 7620. Someone needs to
+ * find the exact reg. setting that causes this. */
+ if (ov->bridge == BRG_OV511PLUS) {
+ info("Enabling 511+/7620AE workaround");
+ ov->sensor = SEN_OV7620;
+ } else {
+ ov->sensor = SEN_OV76BE;
+ }
+ } else if ((rc & 3) == 0) {
+ info("Sensor is an OV7620");
+ ov->sensor = SEN_OV7620;
+ } else {
+ err("Unknown image sensor version: %d", rc & 3);
+ return -1;
+ }
+
+ if (ov->sensor == SEN_OV7620) {
+ PDEBUG(4, "Writing 7620 registers");
+ if (write_regvals(ov, aRegvalsNorm7620))
+ return -1;
+ } else {
+ PDEBUG(4, "Writing 7610 registers");
+ if (write_regvals(ov, aRegvalsNorm7610))
+ return -1;
+ }
+
+ /* Set sensor-specific vars */
+ ov->maxwidth = 640;
+ ov->maxheight = 480;
+ ov->minwidth = 64;
+ ov->minheight = 48;
+
+ // FIXME: These do not match the actual settings yet
+ ov->brightness = 0x80 << 8;
+ ov->contrast = 0x80 << 8;
+ ov->colour = 0x80 << 8;
+ ov->hue = 0x80 << 8;
+
+ return 0;
+}
+
+/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */
+static int
+ov6xx0_configure(struct usb_ov511 *ov)
+{
+ int rc;
+
+ static struct ov511_regvals aRegvalsNorm6x20[] = {
+ { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
+ { OV511_I2C_BUS, 0x11, 0x01 },
+ { OV511_I2C_BUS, 0x03, 0x60 },
+ { OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
+ { OV511_I2C_BUS, 0x07, 0xa8 },
+ /* The ratio of 0x0c and 0x0d controls the white point */
+ { OV511_I2C_BUS, 0x0c, 0x24 },
+ { OV511_I2C_BUS, 0x0d, 0x24 },
+ { OV511_I2C_BUS, 0x0f, 0x15 }, /* COMS */
+ { OV511_I2C_BUS, 0x10, 0x75 }, /* AEC Exposure time */
+ { OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */
+ { OV511_I2C_BUS, 0x14, 0x04 },
+ /* 0x16: 0x06 helps frame stability with moving objects */
+ { OV511_I2C_BUS, 0x16, 0x06 },
+// { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
+ { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
+ /* 0x28: 0x05 Selects RGB format if RGB on */
+ { OV511_I2C_BUS, 0x28, 0x05 },
+ { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
+// { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
+ { OV511_I2C_BUS, 0x2d, 0x99 },
+ { OV511_I2C_BUS, 0x33, 0xa0 }, /* Color Processing Parameter */
+ { OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */
+ { OV511_I2C_BUS, 0x38, 0x8b },
+ { OV511_I2C_BUS, 0x39, 0x40 },
+
+ { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
+ { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
+ { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
+
+ { OV511_I2C_BUS, 0x3d, 0x80 },
+ /* These next two registers (0x4a, 0x4b) are undocumented. They
+ * control the color balance */
+ { OV511_I2C_BUS, 0x4a, 0x80 },
+ { OV511_I2C_BUS, 0x4b, 0x80 },
+ { OV511_I2C_BUS, 0x4d, 0xd2 }, /* This reduces noise a bit */
+ { OV511_I2C_BUS, 0x4e, 0xc1 },
+ { OV511_I2C_BUS, 0x4f, 0x04 },
+// Do 50-53 have any effect?
+// Toggle 0x12[2] off and on here?
+ { OV511_DONE_BUS, 0x0, 0x00 }, /* END MARKER */
+ };
+
+ static struct ov511_regvals aRegvalsNorm6x30[] = {
+ /*OK*/ { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
+ { OV511_I2C_BUS, 0x11, 0x00 },
+ /*OK*/ { OV511_I2C_BUS, 0x03, 0x60 },
+ /*0A?*/ { OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
+ { OV511_I2C_BUS, 0x07, 0xa8 },
+ /* The ratio of 0x0c and 0x0d controls the white point */
+ /*OK*/ { OV511_I2C_BUS, 0x0c, 0x24 },
+ /*OK*/ { OV511_I2C_BUS, 0x0d, 0x24 },
+ /*A*/ { OV511_I2C_BUS, 0x0e, 0x20 },
+// /*04?*/ { OV511_I2C_BUS, 0x14, 0x80 },
+ { OV511_I2C_BUS, 0x16, 0x03 },
+// /*OK*/ { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
+ // 21 & 22? The suggested values look wrong. Go with default
+ /*A*/ { OV511_I2C_BUS, 0x23, 0xc0 },
+ /*A*/ { OV511_I2C_BUS, 0x25, 0x9a }, // Check this against default
+// /*OK*/ { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
+
+ /* 0x28: 0x05 Selects RGB format if RGB on */
+// /*04?*/ { OV511_I2C_BUS, 0x28, 0x05 },
+// /*04?*/ { OV511_I2C_BUS, 0x28, 0x45 }, // DEBUG: Tristate UV bus
+
+ /*OK*/ { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
+// /*OK*/ { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
+ { OV511_I2C_BUS, 0x2d, 0x99 },
+// /*A*/ { OV511_I2C_BUS, 0x33, 0x26 }, // Reserved bits on 6620
+// /*d2?*/ { OV511_I2C_BUS, 0x34, 0x03 }, /* Max A/D range */
+// /*8b?*/ { OV511_I2C_BUS, 0x38, 0x83 },
+// /*40?*/ { OV511_I2C_BUS, 0x39, 0xc0 }, // 6630 adds bit 7
+// { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
+// { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
+// { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
+ { OV511_I2C_BUS, 0x3d, 0x80 },
+// /*A*/ { OV511_I2C_BUS, 0x3f, 0x0e },
+
+ /* These next two registers (0x4a, 0x4b) are undocumented. They
+ * control the color balance */
+// /*OK?*/ { OV511_I2C_BUS, 0x4a, 0x80 }, // Check these
+// /*OK?*/ { OV511_I2C_BUS, 0x4b, 0x80 },
+ { OV511_I2C_BUS, 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */
+ /*c1?*/ { OV511_I2C_BUS, 0x4e, 0x40 },
+
+ /* UV average mode, color killer: strongest */
+ { OV511_I2C_BUS, 0x4f, 0x07 },
+
+ { OV511_I2C_BUS, 0x54, 0x23 }, /* Max AGC gain: 18dB */
+ { OV511_I2C_BUS, 0x57, 0x81 }, /* (default) */
+ { OV511_I2C_BUS, 0x59, 0x01 }, /* AGC dark current comp: +1 */
+ { OV511_I2C_BUS, 0x5a, 0x2c }, /* (undocumented) */
+ { OV511_I2C_BUS, 0x5b, 0x0f }, /* AWB chrominance levels */
+// { OV511_I2C_BUS, 0x5c, 0x10 },
+ { OV511_DONE_BUS, 0x0, 0x00 }, /* END MARKER */
+ };
+
+ PDEBUG(4, "starting sensor configuration");
+
+ if (init_ov_sensor(ov) < 0) {
+ err("Failed to read sensor ID. You might not have an OV6xx0,");
+ err("or it may be not responding. Report this to " EMAIL);
+ return -1;
+ } else {
+ PDEBUG(1, "OV6xx0 sensor detected");
+ }
+
+ /* Detect sensor (sub)type */
+ rc = i2c_r(ov, OV7610_REG_COM_I);
+
+ if (rc < 0) {
+ err("Error detecting sensor type");
+ return -1;
+ }
+
+ if ((rc & 3) == 0) {
+ ov->sensor = SEN_OV6630;
+ info("Sensor is an OV6630");
+ } else if ((rc & 3) == 1) {
+ ov->sensor = SEN_OV6620;
+ info("Sensor is an OV6620");
+ } else if ((rc & 3) == 2) {
+ ov->sensor = SEN_OV6630;
+ info("Sensor is an OV6630AE");
+ } else if ((rc & 3) == 3) {
+ ov->sensor = SEN_OV6630;
+ info("Sensor is an OV6630AF");
+ }
+
+ /* Set sensor-specific vars */
+ ov->maxwidth = 352;
+ ov->maxheight = 288;
+ ov->minwidth = 64;
+ ov->minheight = 48;
+
+ // FIXME: These do not match the actual settings yet
+ ov->brightness = 0x80 << 8;
+ ov->contrast = 0x80 << 8;
+ ov->colour = 0x80 << 8;
+ ov->hue = 0x80 << 8;
+
+ if (ov->sensor == SEN_OV6620) {
+ PDEBUG(4, "Writing 6x20 registers");
+ if (write_regvals(ov, aRegvalsNorm6x20))
+ return -1;
+ } else {
+ PDEBUG(4, "Writing 6x30 registers");
+ if (write_regvals(ov, aRegvalsNorm6x30))
+ return -1;
+ }
+
+ return 0;
+}
+
+/* This initializes the KS0127 and KS0127B video decoders. */
+static int
+ks0127_configure(struct usb_ov511 *ov)
+{
+ int rc;
+
+// FIXME: I don't know how to sync or reset it yet
+#if 0
+ if (ov51x_init_ks_sensor(ov) < 0) {
+ err("Failed to initialize the KS0127");
+ return -1;
+ } else {
+ PDEBUG(1, "KS012x(B) sensor detected");
+ }
+#endif
+
+ /* Detect decoder subtype */
+ rc = i2c_r(ov, 0x00);
+ if (rc < 0) {
+ err("Error detecting sensor type");
+ return -1;
+ } else if (rc & 0x08) {
+ rc = i2c_r(ov, 0x3d);
+ if (rc < 0) {
+ err("Error detecting sensor type");
+ return -1;
+ } else if ((rc & 0x0f) == 0) {
+ info("Sensor is a KS0127");
+ ov->sensor = SEN_KS0127;
+ } else if ((rc & 0x0f) == 9) {
+ info("Sensor is a KS0127B Rev. A");
+ ov->sensor = SEN_KS0127B;
+ }
+ } else {
+ err("Error: Sensor is an unsupported KS0122");
+ return -1;
+ }
+
+ /* Set sensor-specific vars */
+ ov->maxwidth = 640;
+ ov->maxheight = 480;
+ ov->minwidth = 64;
+ ov->minheight = 48;
+
+ // FIXME: These do not match the actual settings yet
+ ov->brightness = 0x80 << 8;
+ ov->contrast = 0x80 << 8;
+ ov->colour = 0x80 << 8;
+ ov->hue = 0x80 << 8;
+
+ /* This device is not supported yet. Bail out now... */
+ err("This sensor is not supported yet.");
+ return -1;
+
+ return 0;
+}
+
+/* This initializes the SAA7111A video decoder. */
+static int
+saa7111a_configure(struct usb_ov511 *ov)
+{
+ int rc;
+
+ /* Since there is no register reset command, all registers must be
+ * written, otherwise gives erratic results */
+ static struct ov511_regvals aRegvalsNormSAA7111A[] = {
+ { OV511_I2C_BUS, 0x06, 0xce },
+ { OV511_I2C_BUS, 0x07, 0x00 },
+ { OV511_I2C_BUS, 0x10, 0x44 }, /* YUV422, 240/286 lines */
+ { OV511_I2C_BUS, 0x0e, 0x01 }, /* NTSC M or PAL BGHI */
+ { OV511_I2C_BUS, 0x00, 0x00 },
+ { OV511_I2C_BUS, 0x01, 0x00 },
+ { OV511_I2C_BUS, 0x03, 0x23 },
+ { OV511_I2C_BUS, 0x04, 0x00 },
+ { OV511_I2C_BUS, 0x05, 0x00 },
+ { OV511_I2C_BUS, 0x08, 0xc8 }, /* Auto field freq */
+ { OV511_I2C_BUS, 0x09, 0x01 }, /* Chrom. trap off, APER=0.25 */
+ { OV511_I2C_BUS, 0x0a, 0x80 }, /* BRIG=128 */
+ { OV511_I2C_BUS, 0x0b, 0x40 }, /* CONT=1.0 */
+ { OV511_I2C_BUS, 0x0c, 0x40 }, /* SATN=1.0 */
+ { OV511_I2C_BUS, 0x0d, 0x00 }, /* HUE=0 */
+ { OV511_I2C_BUS, 0x0f, 0x00 },
+ { OV511_I2C_BUS, 0x11, 0x0c },
+ { OV511_I2C_BUS, 0x12, 0x00 },
+ { OV511_I2C_BUS, 0x13, 0x00 },
+ { OV511_I2C_BUS, 0x14, 0x00 },
+ { OV511_I2C_BUS, 0x15, 0x00 },
+ { OV511_I2C_BUS, 0x16, 0x00 },
+ { OV511_I2C_BUS, 0x17, 0x00 },
+ { OV511_I2C_BUS, 0x02, 0xc0 }, /* Composite input 0 */
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+// FIXME: I don't know how to sync or reset it yet
+#if 0
+ if (ov51x_init_saa_sensor(ov) < 0) {
+ err("Failed to initialize the SAA7111A");
+ return -1;
+ } else {
+ PDEBUG(1, "SAA7111A sensor detected");
+ }
+#endif
+
+ /* 640x480 not supported with PAL */
+ if (ov->pal) {
+ ov->maxwidth = 320;
+ ov->maxheight = 240; /* Even field only */
+ } else {
+ ov->maxwidth = 640;
+ ov->maxheight = 480; /* Even/Odd fields */
+ }
+
+ ov->minwidth = 320;
+ ov->minheight = 240; /* Even field only */
+
+ ov->has_decoder = 1;
+ ov->num_inputs = 8;
+ ov->norm = VIDEO_MODE_AUTO;
+ ov->stop_during_set = 0; /* Decoder guarantees stable image */
+
+ /* Decoder doesn't change these values, so we use these instead of
+ * acutally reading the registers (which doesn't work) */
+ ov->brightness = 0x80 << 8;
+ ov->contrast = 0x40 << 9;
+ ov->colour = 0x40 << 9;
+ ov->hue = 32768;
+
+ PDEBUG(4, "Writing SAA7111A registers");
+ if (write_regvals(ov, aRegvalsNormSAA7111A))
+ return -1;
+
+ /* Detect version of decoder. This must be done after writing the
+ * initial regs or the decoder will lock up. */
+ rc = i2c_r(ov, 0x00);
+
+ if (rc < 0) {
+ err("Error detecting sensor version");
+ return -1;
+ } else {
+ info("Sensor is an SAA7111A (version 0x%x)", rc);
+ ov->sensor = SEN_SAA7111A;
+ }
+
+ // FIXME: Fix this for OV518(+)
+ /* Latch to negative edge of clock. Otherwise, we get incorrect
+ * colors and jitter in the digital signal. */
+ if (ov->bclass == BCL_OV511)
+ reg_w(ov, 0x11, 0x00);
+ else
+ warn("SAA7111A not yet supported with OV518/OV518+");
+
+ return 0;
+}
+
+/* This initializes the OV511/OV511+ and the sensor */
+static int
+ov511_configure(struct usb_ov511 *ov)
+{
+ static struct ov511_regvals aRegvalsInit511[] = {
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x7f },
+ { OV511_REG_BUS, R51x_SYS_INIT, 0x01 },
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x7f },
+ { OV511_REG_BUS, R51x_SYS_INIT, 0x01 },
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x3f },
+ { OV511_REG_BUS, R51x_SYS_INIT, 0x01 },
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x3d },
+ { OV511_DONE_BUS, 0x0, 0x00},
+ };
+
+ static struct ov511_regvals aRegvalsNorm511[] = {
+ { OV511_REG_BUS, R511_DRAM_FLOW_CTL, 0x01 },
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 },
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 },
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 },
+ { OV511_REG_BUS, R511_FIFO_OPTS, 0x1f },
+ { OV511_REG_BUS, R511_COMP_EN, 0x00 },
+ { OV511_REG_BUS, R511_COMP_LUT_EN, 0x03 },
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+ static struct ov511_regvals aRegvalsNorm511Plus[] = {
+ { OV511_REG_BUS, R511_DRAM_FLOW_CTL, 0xff },
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 },
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 },
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 },
+ { OV511_REG_BUS, R511_FIFO_OPTS, 0xff },
+ { OV511_REG_BUS, R511_COMP_EN, 0x00 },
+ { OV511_REG_BUS, R511_COMP_LUT_EN, 0x03 },
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+ PDEBUG(4, "");
+
+ ov->customid = reg_r(ov, R511_SYS_CUST_ID);
+ if (ov->customid < 0) {
+ err("Unable to read camera bridge registers");
+ goto error;
+ }
+
+ PDEBUG (1, "CustomID = %d", ov->customid);
+ ov->desc = symbolic(camlist, ov->customid);
+ info("model: %s", ov->desc);
+
+ if (0 == strcmp(ov->desc, NOT_DEFINED_STR)) {
+ err("Camera type (%d) not recognized", ov->customid);
+ err("Please notify " EMAIL " of the name,");
+ err("manufacturer, model, and this number of your camera.");
+ err("Also include the output of the detection process.");
+ }
+
+ if (ov->customid == 70) /* USB Life TV (PAL/SECAM) */
+ ov->pal = 1;
+
+ if (write_regvals(ov, aRegvalsInit511))
+ goto error;
+
+ if (ov->led_policy == LED_OFF || ov->led_policy == LED_AUTO)
+ ov51x_led_control(ov, 0);
+
+ /* The OV511+ has undocumented bits in the flow control register.
+ * Setting it to 0xff fixes the corruption with moving objects. */
+ if (ov->bridge == BRG_OV511) {
+ if (write_regvals(ov, aRegvalsNorm511))
+ goto error;
+ } else if (ov->bridge == BRG_OV511PLUS) {
+ if (write_regvals(ov, aRegvalsNorm511Plus))
+ goto error;
+ } else {
+ err("Invalid bridge");
+ }
+
+ if (ov511_init_compression(ov))
+ goto error;
+
+ ov->packet_numbering = 1;
+ ov511_set_packet_size(ov, 0);
+
+ ov->snap_enabled = snapshot;
+
+ /* Test for 7xx0 */
+ PDEBUG(3, "Testing for 0V7xx0");
+ ov->primary_i2c_slave = OV7xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0)
+ goto error;
+
+ if (i2c_w(ov, 0x12, 0x80) < 0) {
+ /* Test for 6xx0 */
+ PDEBUG(3, "Testing for 0V6xx0");
+ ov->primary_i2c_slave = OV6xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0)
+ goto error;
+
+ if (i2c_w(ov, 0x12, 0x80) < 0) {
+ /* Test for 8xx0 */
+ PDEBUG(3, "Testing for 0V8xx0");
+ ov->primary_i2c_slave = OV8xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0)
+ goto error;
+
+ if (i2c_w(ov, 0x12, 0x80) < 0) {
+ /* Test for SAA7111A */
+ PDEBUG(3, "Testing for SAA7111A");
+ ov->primary_i2c_slave = SAA7111A_SID;
+ if (ov51x_set_slave_ids(ov, SAA7111A_SID) < 0)
+ goto error;
+
+ if (i2c_w(ov, 0x0d, 0x00) < 0) {
+ /* Test for KS0127 */
+ PDEBUG(3, "Testing for KS0127");
+ ov->primary_i2c_slave = KS0127_SID;
+ if (ov51x_set_slave_ids(ov, KS0127_SID) < 0)
+ goto error;
+
+ if (i2c_w(ov, 0x10, 0x00) < 0) {
+ err("Can't determine sensor slave IDs");
+ goto error;
+ } else {
+ if (ks0127_configure(ov) < 0) {
+ err("Failed to configure KS0127");
+ goto error;
+ }
+ }
+ } else {
+ if (saa7111a_configure(ov) < 0) {
+ err("Failed to configure SAA7111A");
+ goto error;
+ }
+ }
+ } else {
+ err("Detected unsupported OV8xx0 sensor");
+ goto error;
+ }
+ } else {
+ if (ov6xx0_configure(ov) < 0) {
+ err("Failed to configure OV6xx0");
+ goto error;
+ }
+ }
+ } else {
+ if (ov7xx0_configure(ov) < 0) {
+ err("Failed to configure OV7xx0");
+ goto error;
+ }
+ }
+
+ return 0;
+
+error:
+ err("OV511 Config failed");
+
+ return -EBUSY;
+}
+
+/* This initializes the OV518/OV518+ and the sensor */
+static int
+ov518_configure(struct usb_ov511 *ov)
+{
+ /* For 518 and 518+ */
+ static struct ov511_regvals aRegvalsInit518[] = {
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x40 },
+ { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 },
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x3e },
+ { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 },
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x00 },
+ { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 },
+ { OV511_REG_BUS, 0x46, 0x00 },
+ { OV511_REG_BUS, 0x5d, 0x03 },
+ { OV511_DONE_BUS, 0x0, 0x00},
+ };
+
+ static struct ov511_regvals aRegvalsNorm518[] = {
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */
+ { OV511_REG_BUS, 0x31, 0x0f },
+ { OV511_REG_BUS, 0x5d, 0x03 },
+ { OV511_REG_BUS, 0x24, 0x9f },
+ { OV511_REG_BUS, 0x25, 0x90 },
+ { OV511_REG_BUS, 0x20, 0x00 },
+ { OV511_REG_BUS, 0x51, 0x04 },
+ { OV511_REG_BUS, 0x71, 0x19 },
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+ static struct ov511_regvals aRegvalsNorm518Plus[] = {
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */
+ { OV511_REG_BUS, 0x31, 0x0f },
+ { OV511_REG_BUS, 0x5d, 0x03 },
+ { OV511_REG_BUS, 0x24, 0x9f },
+ { OV511_REG_BUS, 0x25, 0x90 },
+ { OV511_REG_BUS, 0x20, 0x60 },
+ { OV511_REG_BUS, 0x51, 0x02 },
+ { OV511_REG_BUS, 0x71, 0x19 },
+ { OV511_REG_BUS, 0x40, 0xff },
+ { OV511_REG_BUS, 0x41, 0x42 },
+ { OV511_REG_BUS, 0x46, 0x00 },
+ { OV511_REG_BUS, 0x33, 0x04 },
+ { OV511_REG_BUS, 0x21, 0x19 },
+ { OV511_REG_BUS, 0x3f, 0x10 },
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+ PDEBUG(4, "");
+
+ /* First 5 bits of custom ID reg are a revision ID on OV518 */
+ info("Device revision %d", 0x1F & reg_r(ov, R511_SYS_CUST_ID));
+
+ /* Give it the default description */
+ ov->desc = symbolic(camlist, 0);
+
+ if (write_regvals(ov, aRegvalsInit518))
+ goto error;
+
+ /* Set LED GPIO pin to output mode */
+ if (reg_w_mask(ov, 0x57, 0x00, 0x02) < 0)
+ goto error;
+
+ /* LED is off by default with OV518; have to explicitly turn it on */
+ if (ov->led_policy == LED_OFF || ov->led_policy == LED_AUTO)
+ ov51x_led_control(ov, 0);
+ else
+ ov51x_led_control(ov, 1);
+
+ /* Don't require compression if dumppix is enabled; otherwise it's
+ * required. OV518 has no uncompressed mode, to save RAM. */
+ if (!dumppix && !ov->compress) {
+ ov->compress = 1;
+ warn("Compression required with OV518...enabling");
+ }
+
+ if (ov->bridge == BRG_OV518) {
+ if (write_regvals(ov, aRegvalsNorm518))
+ goto error;
+ } else if (ov->bridge == BRG_OV518PLUS) {
+ if (write_regvals(ov, aRegvalsNorm518Plus))
+ goto error;
+ } else {
+ err("Invalid bridge");
+ }
+
+ if (reg_w(ov, 0x2f, 0x80) < 0)
+ goto error;
+
+ if (ov518_init_compression(ov))
+ goto error;
+
+ if (ov->bridge == BRG_OV518)
+ {
+ struct usb_interface *ifp;
+ struct usb_host_interface *alt;
+ __u16 mxps = 0;
+
+ ifp = usb_ifnum_to_if(ov->dev, 0);
+ if (ifp) {
+ alt = usb_altnum_to_altsetting(ifp, 7);
+ if (alt)
+ mxps = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+ }
+
+ /* Some OV518s have packet numbering by default, some don't */
+ if (mxps == 897)
+ ov->packet_numbering = 1;
+ else
+ ov->packet_numbering = 0;
+ } else {
+ /* OV518+ has packet numbering turned on by default */
+ ov->packet_numbering = 1;
+ }
+
+ ov518_set_packet_size(ov, 0);
+
+ ov->snap_enabled = snapshot;
+
+ /* Test for 76xx */
+ ov->primary_i2c_slave = OV7xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0)
+ goto error;
+
+ /* The OV518 must be more aggressive about sensor detection since
+ * I2C write will never fail if the sensor is not present. We have
+ * to try to initialize the sensor to detect its presence */
+
+ if (init_ov_sensor(ov) < 0) {
+ /* Test for 6xx0 */
+ ov->primary_i2c_slave = OV6xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0)
+ goto error;
+
+ if (init_ov_sensor(ov) < 0) {
+ /* Test for 8xx0 */
+ ov->primary_i2c_slave = OV8xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0)
+ goto error;
+
+ if (init_ov_sensor(ov) < 0) {
+ err("Can't determine sensor slave IDs");
+ goto error;
+ } else {
+ err("Detected unsupported OV8xx0 sensor");
+ goto error;
+ }
+ } else {
+ if (ov6xx0_configure(ov) < 0) {
+ err("Failed to configure OV6xx0");
+ goto error;
+ }
+ }
+ } else {
+ if (ov7xx0_configure(ov) < 0) {
+ err("Failed to configure OV7xx0");
+ goto error;
+ }
+ }
+
+ ov->maxwidth = 352;
+ ov->maxheight = 288;
+
+ // The OV518 cannot go as low as the sensor can
+ ov->minwidth = 160;
+ ov->minheight = 120;
+
+ return 0;
+
+error:
+ err("OV518 Config failed");
+
+ return -EBUSY;
+}
+
+/****************************************************************************
+ * sysfs
+ ***************************************************************************/
+
+static inline struct usb_ov511 *cd_to_ov(struct class_device *cd)
+{
+ struct video_device *vdev = to_video_device(cd);
+ return video_get_drvdata(vdev);
+}
+
+static ssize_t show_custom_id(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ return sprintf(buf, "%d\n", ov->customid);
+}
+static CLASS_DEVICE_ATTR(custom_id, S_IRUGO, show_custom_id, NULL);
+
+static ssize_t show_model(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ return sprintf(buf, "%s\n", ov->desc);
+}
+static CLASS_DEVICE_ATTR(model, S_IRUGO, show_model, NULL);
+
+static ssize_t show_bridge(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ return sprintf(buf, "%s\n", symbolic(brglist, ov->bridge));
+}
+static CLASS_DEVICE_ATTR(bridge, S_IRUGO, show_bridge, NULL);
+
+static ssize_t show_sensor(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ return sprintf(buf, "%s\n", symbolic(senlist, ov->sensor));
+}
+static CLASS_DEVICE_ATTR(sensor, S_IRUGO, show_sensor, NULL);
+
+static ssize_t show_brightness(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ unsigned short x;
+
+ if (!ov->dev)
+ return -ENODEV;
+ sensor_get_brightness(ov, &x);
+ return sprintf(buf, "%d\n", x >> 8);
+}
+static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL);
+
+static ssize_t show_saturation(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ unsigned short x;
+
+ if (!ov->dev)
+ return -ENODEV;
+ sensor_get_saturation(ov, &x);
+ return sprintf(buf, "%d\n", x >> 8);
+}
+static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL);
+
+static ssize_t show_contrast(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ unsigned short x;
+
+ if (!ov->dev)
+ return -ENODEV;
+ sensor_get_contrast(ov, &x);
+ return sprintf(buf, "%d\n", x >> 8);
+}
+static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL);
+
+static ssize_t show_hue(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ unsigned short x;
+
+ if (!ov->dev)
+ return -ENODEV;
+ sensor_get_hue(ov, &x);
+ return sprintf(buf, "%d\n", x >> 8);
+}
+static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL);
+
+static ssize_t show_exposure(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ unsigned char exp = 0;
+
+ if (!ov->dev)
+ return -ENODEV;
+ sensor_get_exposure(ov, &exp);
+ return sprintf(buf, "%d\n", exp >> 8);
+}
+static CLASS_DEVICE_ATTR(exposure, S_IRUGO, show_exposure, NULL);
+
+static void ov_create_sysfs(struct video_device *vdev)
+{
+ video_device_create_file(vdev, &class_device_attr_custom_id);
+ video_device_create_file(vdev, &class_device_attr_model);
+ video_device_create_file(vdev, &class_device_attr_bridge);
+ video_device_create_file(vdev, &class_device_attr_sensor);
+ video_device_create_file(vdev, &class_device_attr_brightness);
+ video_device_create_file(vdev, &class_device_attr_saturation);
+ video_device_create_file(vdev, &class_device_attr_contrast);
+ video_device_create_file(vdev, &class_device_attr_hue);
+ video_device_create_file(vdev, &class_device_attr_exposure);
+}
+
+/****************************************************************************
+ * USB routines
+ ***************************************************************************/
+
+static int
+ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_interface_descriptor *idesc;
+ struct usb_ov511 *ov;
+ int i;
+
+ PDEBUG(1, "probing for device...");
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -ENODEV;
+
+ idesc = &intf->cur_altsetting->desc;
+
+ if (idesc->bInterfaceClass != 0xFF)
+ return -ENODEV;
+ if (idesc->bInterfaceSubClass != 0x00)
+ return -ENODEV;
+
+ if ((ov = kzalloc(sizeof(*ov), GFP_KERNEL)) == NULL) {
+ err("couldn't kmalloc ov struct");
+ goto error_out;
+ }
+
+ ov->dev = dev;
+ ov->iface = idesc->bInterfaceNumber;
+ ov->led_policy = led;
+ ov->compress = compress;
+ ov->lightfreq = lightfreq;
+ ov->num_inputs = 1; /* Video decoder init functs. change this */
+ ov->stop_during_set = !fastset;
+ ov->backlight = backlight;
+ ov->mirror = mirror;
+ ov->auto_brt = autobright;
+ ov->auto_gain = autogain;
+ ov->auto_exp = autoexp;
+
+ switch (le16_to_cpu(dev->descriptor.idProduct)) {
+ case PROD_OV511:
+ ov->bridge = BRG_OV511;
+ ov->bclass = BCL_OV511;
+ break;
+ case PROD_OV511PLUS:
+ ov->bridge = BRG_OV511PLUS;
+ ov->bclass = BCL_OV511;
+ break;
+ case PROD_OV518:
+ ov->bridge = BRG_OV518;
+ ov->bclass = BCL_OV518;
+ break;
+ case PROD_OV518PLUS:
+ ov->bridge = BRG_OV518PLUS;
+ ov->bclass = BCL_OV518;
+ break;
+ case PROD_ME2CAM:
+ if (le16_to_cpu(dev->descriptor.idVendor) != VEND_MATTEL)
+ goto error;
+ ov->bridge = BRG_OV511PLUS;
+ ov->bclass = BCL_OV511;
+ break;
+ default:
+ err("Unknown product ID 0x%04x", le16_to_cpu(dev->descriptor.idProduct));
+ goto error;
+ }
+
+ info("USB %s video device found", symbolic(brglist, ov->bridge));
+
+ init_waitqueue_head(&ov->wq);
+
+ mutex_init(&ov->lock); /* to 1 == available */
+ mutex_init(&ov->buf_lock);
+ mutex_init(&ov->i2c_lock);
+ mutex_init(&ov->cbuf_lock);
+
+ ov->buf_state = BUF_NOT_ALLOCATED;
+
+ if (usb_make_path(dev, ov->usb_path, OV511_USB_PATH_LEN) < 0) {
+ err("usb_make_path error");
+ goto error;
+ }
+
+ /* Allocate control transfer buffer. */
+ /* Must be kmalloc()'ed, for DMA compatibility */
+ ov->cbuf = kmalloc(OV511_CBUF_SIZE, GFP_KERNEL);
+ if (!ov->cbuf)
+ goto error;
+
+ if (ov->bclass == BCL_OV518) {
+ if (ov518_configure(ov) < 0)
+ goto error;
+ } else {
+ if (ov511_configure(ov) < 0)
+ goto error;
+ }
+
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].framenum = i;
+ init_waitqueue_head(&ov->frame[i].wq);
+ }
+
+ for (i = 0; i < OV511_NUMSBUF; i++) {
+ ov->sbuf[i].ov = ov;
+ spin_lock_init(&ov->sbuf[i].lock);
+ ov->sbuf[i].n = i;
+ }
+
+ /* Unnecessary? (This is done on open(). Need to make sure variables
+ * are properly initialized without this before removing it, though). */
+ if (ov51x_set_default_params(ov) < 0)
+ goto error;
+
+#ifdef OV511_DEBUG
+ if (dump_bridge) {
+ if (ov->bclass == BCL_OV511)
+ ov511_dump_regs(ov);
+ else
+ ov518_dump_regs(ov);
+ }
+#endif
+
+ ov->vdev = video_device_alloc();
+ if (!ov->vdev)
+ goto error;
+
+ memcpy(ov->vdev, &vdev_template, sizeof(*ov->vdev));
+ ov->vdev->dev = &dev->dev;
+ video_set_drvdata(ov->vdev, ov);
+
+ for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) {
+ /* Minor 0 cannot be specified; assume user wants autodetect */
+ if (unit_video[i] == 0)
+ break;
+
+ if (video_register_device(ov->vdev, VFL_TYPE_GRABBER,
+ unit_video[i]) >= 0) {
+ break;
+ }
+ }
+
+ /* Use the next available one */
+ if ((ov->vdev->minor == -1) &&
+ video_register_device(ov->vdev, VFL_TYPE_GRABBER, -1) < 0) {
+ err("video_register_device failed");
+ goto error;
+ }
+
+ info("Device at %s registered to minor %d", ov->usb_path,
+ ov->vdev->minor);
+
+ usb_set_intfdata(intf, ov);
+ ov_create_sysfs(ov->vdev);
+ return 0;
+
+error:
+ if (ov->vdev) {
+ if (-1 == ov->vdev->minor)
+ video_device_release(ov->vdev);
+ else
+ video_unregister_device(ov->vdev);
+ ov->vdev = NULL;
+ }
+
+ if (ov->cbuf) {
+ mutex_lock(&ov->cbuf_lock);
+ kfree(ov->cbuf);
+ ov->cbuf = NULL;
+ mutex_unlock(&ov->cbuf_lock);
+ }
+
+ kfree(ov);
+ ov = NULL;
+
+error_out:
+ err("Camera initialization failed");
+ return -EIO;
+}
+
+static void
+ov51x_disconnect(struct usb_interface *intf)
+{
+ struct usb_ov511 *ov = usb_get_intfdata(intf);
+ int n;
+
+ PDEBUG(3, "");
+
+ usb_set_intfdata (intf, NULL);
+
+ if (!ov)
+ return;
+
+ if (ov->vdev)
+ video_unregister_device(ov->vdev);
+
+ for (n = 0; n < OV511_NUMFRAMES; n++)
+ ov->frame[n].grabstate = FRAME_ERROR;
+
+ ov->curframe = -1;
+
+ /* This will cause the process to request another frame */
+ for (n = 0; n < OV511_NUMFRAMES; n++)
+ wake_up_interruptible(&ov->frame[n].wq);
+
+ wake_up_interruptible(&ov->wq);
+
+ ov->streaming = 0;
+ ov51x_unlink_isoc(ov);
+
+ ov->dev = NULL;
+
+ /* Free the memory */
+ if (ov && !ov->user) {
+ mutex_lock(&ov->cbuf_lock);
+ kfree(ov->cbuf);
+ ov->cbuf = NULL;
+ mutex_unlock(&ov->cbuf_lock);
+
+ ov51x_dealloc(ov);
+ kfree(ov);
+ ov = NULL;
+ }
+
+ PDEBUG(3, "Disconnect complete");
+}
+
+static struct usb_driver ov511_driver = {
+ .name = "ov511",
+ .id_table = device_table,
+ .probe = ov51x_probe,
+ .disconnect = ov51x_disconnect
+};
+
+/****************************************************************************
+ *
+ * Module routines
+ *
+ ***************************************************************************/
+
+static int __init
+usb_ov511_init(void)
+{
+ int retval;
+
+ retval = usb_register(&ov511_driver);
+ if (retval)
+ goto out;
+
+ info(DRIVER_VERSION " : " DRIVER_DESC);
+
+out:
+ return retval;
+}
+
+static void __exit
+usb_ov511_exit(void)
+{
+ usb_deregister(&ov511_driver);
+ info("driver deregistered");
+
+}
+
+module_init(usb_ov511_init);
+module_exit(usb_ov511_exit);
+
diff --git a/drivers/media/video/ov511.h b/drivers/media/video/ov511.h
new file mode 100644
index 000000000000..bce9b3633889
--- /dev/null
+++ b/drivers/media/video/ov511.h
@@ -0,0 +1,568 @@
+#ifndef __LINUX_OV511_H
+#define __LINUX_OV511_H
+
+#include <asm/uaccess.h>
+#include <linux/videodev.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+
+#define OV511_DEBUG /* Turn on debug messages */
+
+#ifdef OV511_DEBUG
+ #define PDEBUG(level, fmt, args...) \
+ if (debug >= (level)) info("[%s:%d] " fmt, \
+ __FUNCTION__, __LINE__ , ## args)
+#else
+ #define PDEBUG(level, fmt, args...) do {} while(0)
+#endif
+
+/* This macro restricts an int variable to an inclusive range */
+#define RESTRICT_TO_RANGE(v,mi,ma) { \
+ if ((v) < (mi)) (v) = (mi); \
+ else if ((v) > (ma)) (v) = (ma); \
+}
+
+/* --------------------------------- */
+/* DEFINES FOR OV511 AND OTHER CHIPS */
+/* --------------------------------- */
+
+/* USB IDs */
+#define VEND_OMNIVISION 0x05A9
+#define PROD_OV511 0x0511
+#define PROD_OV511PLUS 0xA511
+#define PROD_OV518 0x0518
+#define PROD_OV518PLUS 0xA518
+
+#define VEND_MATTEL 0x0813
+#define PROD_ME2CAM 0x0002
+
+/* --------------------------------- */
+/* OV51x REGISTER MNEMONICS */
+/* --------------------------------- */
+
+/* Camera interface register numbers */
+#define R511_CAM_DELAY 0x10
+#define R511_CAM_EDGE 0x11
+#define R511_CAM_PXCNT 0x12
+#define R511_CAM_LNCNT 0x13
+#define R511_CAM_PXDIV 0x14
+#define R511_CAM_LNDIV 0x15
+#define R511_CAM_UV_EN 0x16
+#define R511_CAM_LINE_MODE 0x17
+#define R511_CAM_OPTS 0x18
+
+/* Snapshot mode camera interface register numbers */
+#define R511_SNAP_FRAME 0x19
+#define R511_SNAP_PXCNT 0x1A
+#define R511_SNAP_LNCNT 0x1B
+#define R511_SNAP_PXDIV 0x1C
+#define R511_SNAP_LNDIV 0x1D
+#define R511_SNAP_UV_EN 0x1E
+#define R511_SNAP_OPTS 0x1F
+
+/* DRAM register numbers */
+#define R511_DRAM_FLOW_CTL 0x20
+#define R511_DRAM_ARCP 0x21
+#define R511_DRAM_MRC 0x22
+#define R511_DRAM_RFC 0x23
+
+/* ISO FIFO register numbers */
+#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */
+#define R511_FIFO_OPTS 0x31
+
+/* Parallel IO register numbers */
+#define R511_PIO_OPTS 0x38
+#define R511_PIO_DATA 0x39
+#define R511_PIO_BIST 0x3E
+#define R518_GPIO_IN 0x55 /* OV518(+) only */
+#define R518_GPIO_OUT 0x56 /* OV518(+) only */
+#define R518_GPIO_CTL 0x57 /* OV518(+) only */
+#define R518_GPIO_PULSE_IN 0x58 /* OV518(+) only */
+#define R518_GPIO_PULSE_CLEAR 0x59 /* OV518(+) only */
+#define R518_GPIO_PULSE_POL 0x5a /* OV518(+) only */
+#define R518_GPIO_PULSE_EN 0x5b /* OV518(+) only */
+#define R518_GPIO_RESET 0x5c /* OV518(+) only */
+
+/* I2C registers */
+#define R511_I2C_CTL 0x40
+#define R518_I2C_CTL 0x47 /* OV518(+) only */
+#define R51x_I2C_W_SID 0x41
+#define R51x_I2C_SADDR_3 0x42
+#define R51x_I2C_SADDR_2 0x43
+#define R51x_I2C_R_SID 0x44
+#define R51x_I2C_DATA 0x45
+#define R51x_I2C_CLOCK 0x46
+#define R51x_I2C_TIMEOUT 0x47
+
+/* I2C snapshot registers */
+#define R511_SI2C_SADDR_3 0x48
+#define R511_SI2C_DATA 0x49
+
+/* System control registers */
+#define R51x_SYS_RESET 0x50
+ /* Reset type definitions */
+#define OV511_RESET_UDC 0x01
+#define OV511_RESET_I2C 0x02
+#define OV511_RESET_FIFO 0x04
+#define OV511_RESET_OMNICE 0x08
+#define OV511_RESET_DRAM 0x10
+#define OV511_RESET_CAM_INT 0x20
+#define OV511_RESET_OV511 0x40
+#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */
+#define OV511_RESET_ALL 0x7F
+
+#define R511_SYS_CLOCK_DIV 0x51
+#define R51x_SYS_SNAP 0x52
+#define R51x_SYS_INIT 0x53
+#define R511_SYS_PWR_CLK 0x54 /* OV511+/OV518(+) only */
+#define R511_SYS_LED_CTL 0x55 /* OV511+ only */
+#define R511_SYS_USER 0x5E
+#define R511_SYS_CUST_ID 0x5F
+
+/* OmniCE (compression) registers */
+#define R511_COMP_PHY 0x70
+#define R511_COMP_PHUV 0x71
+#define R511_COMP_PVY 0x72
+#define R511_COMP_PVUV 0x73
+#define R511_COMP_QHY 0x74
+#define R511_COMP_QHUV 0x75
+#define R511_COMP_QVY 0x76
+#define R511_COMP_QVUV 0x77
+#define R511_COMP_EN 0x78
+#define R511_COMP_LUT_EN 0x79
+#define R511_COMP_LUT_BEGIN 0x80
+
+/* --------------------------------- */
+/* ALTERNATE NUMBERS */
+/* --------------------------------- */
+
+/* Alternate numbers for various max packet sizes (OV511 only) */
+#define OV511_ALT_SIZE_992 0
+#define OV511_ALT_SIZE_993 1
+#define OV511_ALT_SIZE_768 2
+#define OV511_ALT_SIZE_769 3
+#define OV511_ALT_SIZE_512 4
+#define OV511_ALT_SIZE_513 5
+#define OV511_ALT_SIZE_257 6
+#define OV511_ALT_SIZE_0 7
+
+/* Alternate numbers for various max packet sizes (OV511+ only) */
+#define OV511PLUS_ALT_SIZE_0 0
+#define OV511PLUS_ALT_SIZE_33 1
+#define OV511PLUS_ALT_SIZE_129 2
+#define OV511PLUS_ALT_SIZE_257 3
+#define OV511PLUS_ALT_SIZE_385 4
+#define OV511PLUS_ALT_SIZE_513 5
+#define OV511PLUS_ALT_SIZE_769 6
+#define OV511PLUS_ALT_SIZE_961 7
+
+/* Alternate numbers for various max packet sizes (OV518(+) only) */
+#define OV518_ALT_SIZE_0 0
+#define OV518_ALT_SIZE_128 1
+#define OV518_ALT_SIZE_256 2
+#define OV518_ALT_SIZE_384 3
+#define OV518_ALT_SIZE_512 4
+#define OV518_ALT_SIZE_640 5
+#define OV518_ALT_SIZE_768 6
+#define OV518_ALT_SIZE_896 7
+
+/* --------------------------------- */
+/* OV7610 REGISTER MNEMONICS */
+/* --------------------------------- */
+
+/* OV7610 registers */
+#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */
+#define OV7610_REG_BLUE 0x01 /* blue channel balance */
+#define OV7610_REG_RED 0x02 /* red channel balance */
+#define OV7610_REG_SAT 0x03 /* saturation */
+ /* 04 reserved */
+#define OV7610_REG_CNT 0x05 /* Y contrast */
+#define OV7610_REG_BRT 0x06 /* Y brightness */
+ /* 08-0b reserved */
+#define OV7610_REG_BLUE_BIAS 0x0C /* blue channel bias (5:0) */
+#define OV7610_REG_RED_BIAS 0x0D /* read channel bias (5:0) */
+#define OV7610_REG_GAMMA_COEFF 0x0E /* gamma settings */
+#define OV7610_REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */
+#define OV7610_REG_EXP 0x10 /* manual exposure setting */
+#define OV7610_REG_CLOCK 0x11 /* polarity/clock prescaler */
+#define OV7610_REG_COM_A 0x12 /* misc common regs */
+#define OV7610_REG_COM_B 0x13 /* misc common regs */
+#define OV7610_REG_COM_C 0x14 /* misc common regs */
+#define OV7610_REG_COM_D 0x15 /* misc common regs */
+#define OV7610_REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */
+#define OV7610_REG_HWIN_START 0x17 /* horizontal window start */
+#define OV7610_REG_HWIN_END 0x18 /* horizontal window end */
+#define OV7610_REG_VWIN_START 0x19 /* vertical window start */
+#define OV7610_REG_VWIN_END 0x1A /* vertical window end */
+#define OV7610_REG_PIXEL_SHIFT 0x1B /* pixel shift */
+#define OV7610_REG_ID_HIGH 0x1C /* manufacturer ID MSB */
+#define OV7610_REG_ID_LOW 0x1D /* manufacturer ID LSB */
+ /* 0e-0f reserved */
+#define OV7610_REG_COM_E 0x20 /* misc common regs */
+#define OV7610_REG_YOFFSET 0x21 /* Y channel offset */
+#define OV7610_REG_UOFFSET 0x22 /* U channel offset */
+ /* 23 reserved */
+#define OV7610_REG_ECW 0x24 /* Exposure white level for AEC */
+#define OV7610_REG_ECB 0x25 /* Exposure black level for AEC */
+#define OV7610_REG_COM_F 0x26 /* misc settings */
+#define OV7610_REG_COM_G 0x27 /* misc settings */
+#define OV7610_REG_COM_H 0x28 /* misc settings */
+#define OV7610_REG_COM_I 0x29 /* misc settings */
+#define OV7610_REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */
+#define OV7610_REG_FRAMERATE_L 0x2B /* frame rate LSB */
+#define OV7610_REG_ALC 0x2C /* Auto Level Control settings */
+#define OV7610_REG_COM_J 0x2D /* misc settings */
+#define OV7610_REG_VOFFSET 0x2E /* V channel offset adjustment */
+#define OV7610_REG_ARRAY_BIAS 0x2F /* Array bias -- don't change */
+ /* 30-32 reserved */
+#define OV7610_REG_YGAMMA 0x33 /* misc gamma settings (7:6) */
+#define OV7610_REG_BIAS_ADJUST 0x34 /* misc bias settings */
+#define OV7610_REG_COM_L 0x35 /* misc settings */
+ /* 36-37 reserved */
+#define OV7610_REG_COM_K 0x38 /* misc registers */
+
+/* --------------------------------- */
+/* I2C ADDRESSES */
+/* --------------------------------- */
+
+#define OV7xx0_SID 0x42
+#define OV6xx0_SID 0xC0
+#define OV8xx0_SID 0xA0
+#define KS0127_SID 0xD8
+#define SAA7111A_SID 0x48
+
+/* --------------------------------- */
+/* MISCELLANEOUS DEFINES */
+/* --------------------------------- */
+
+#define I2C_CLOCK_PRESCALER 0x03
+
+#define FRAMES_PER_DESC 10 /* FIXME - What should this be? */
+#define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */
+#define PIXELS_PER_SEG 256 /* Pixels per segment */
+
+#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */
+
+#define OV511_NUMFRAMES 2
+#if OV511_NUMFRAMES > VIDEO_MAX_FRAME
+ #error "OV511_NUMFRAMES is too high"
+#endif
+
+#define OV511_NUMSBUF 2
+
+/* Control transfers use up to 4 bytes */
+#define OV511_CBUF_SIZE 4
+
+/* Size of usb_make_path() buffer */
+#define OV511_USB_PATH_LEN 64
+
+/* Bridge types */
+enum {
+ BRG_UNKNOWN,
+ BRG_OV511,
+ BRG_OV511PLUS,
+ BRG_OV518,
+ BRG_OV518PLUS,
+};
+
+/* Bridge classes */
+enum {
+ BCL_UNKNOWN,
+ BCL_OV511,
+ BCL_OV518,
+};
+
+/* Sensor types */
+enum {
+ SEN_UNKNOWN,
+ SEN_OV76BE,
+ SEN_OV7610,
+ SEN_OV7620,
+ SEN_OV7620AE,
+ SEN_OV6620,
+ SEN_OV6630,
+ SEN_OV6630AE,
+ SEN_OV6630AF,
+ SEN_OV8600,
+ SEN_KS0127,
+ SEN_KS0127B,
+ SEN_SAA7111A,
+};
+
+enum {
+ STATE_SCANNING, /* Scanning for start */
+ STATE_HEADER, /* Parsing header */
+ STATE_LINES, /* Parsing lines */
+};
+
+/* Buffer states */
+enum {
+ BUF_NOT_ALLOCATED,
+ BUF_ALLOCATED,
+};
+
+/* --------- Definition of ioctl interface --------- */
+
+#define OV511_INTERFACE_VER 101
+
+/* LED options */
+enum {
+ LED_OFF,
+ LED_ON,
+ LED_AUTO,
+};
+
+/* Raw frame formats */
+enum {
+ RAWFMT_INVALID,
+ RAWFMT_YUV400,
+ RAWFMT_YUV420,
+ RAWFMT_YUV422,
+ RAWFMT_GBR422,
+};
+
+struct ov511_i2c_struct {
+ unsigned char slave; /* Write slave ID (read ID - 1) */
+ unsigned char reg; /* Index of register */
+ unsigned char value; /* User sets this w/ write, driver does w/ read */
+ unsigned char mask; /* Bits to be changed. Not used with read ops */
+};
+
+/* ioctls */
+#define OV511IOC_WI2C _IOW('v', BASE_VIDIOCPRIVATE + 5, \
+ struct ov511_i2c_struct)
+#define OV511IOC_RI2C _IOWR('v', BASE_VIDIOCPRIVATE + 6, \
+ struct ov511_i2c_struct)
+/* ------------- End IOCTL interface -------------- */
+
+struct usb_ov511; /* Forward declaration */
+
+struct ov511_sbuf {
+ struct usb_ov511 *ov;
+ unsigned char *data;
+ struct urb *urb;
+ spinlock_t lock;
+ int n;
+};
+
+enum {
+ FRAME_UNUSED, /* Unused (no MCAPTURE) */
+ FRAME_READY, /* Ready to start grabbing */
+ FRAME_GRABBING, /* In the process of being grabbed into */
+ FRAME_DONE, /* Finished grabbing, but not been synced yet */
+ FRAME_ERROR, /* Something bad happened while processing */
+};
+
+struct ov511_regvals {
+ enum {
+ OV511_DONE_BUS,
+ OV511_REG_BUS,
+ OV511_I2C_BUS,
+ } bus;
+ unsigned char reg;
+ unsigned char val;
+};
+
+struct ov511_frame {
+ int framenum; /* Index of this frame */
+ unsigned char *data; /* Frame buffer */
+ unsigned char *tempdata; /* Temp buffer for multi-stage conversions */
+ unsigned char *rawdata; /* Raw camera data buffer */
+ unsigned char *compbuf; /* Temp buffer for decompressor */
+
+ int depth; /* Bytes per pixel */
+ int width; /* Width application is expecting */
+ int height; /* Height application is expecting */
+
+ int rawwidth; /* Actual width of frame sent from camera */
+ int rawheight; /* Actual height of frame sent from camera */
+
+ int sub_flag; /* Sub-capture mode for this frame? */
+ unsigned int format; /* Format for this frame */
+ int compressed; /* Is frame compressed? */
+
+ volatile int grabstate; /* State of grabbing */
+ int scanstate; /* State of scanning */
+
+ int bytes_recvd; /* Number of image bytes received from camera */
+
+ long bytes_read; /* Amount that has been read() */
+
+ wait_queue_head_t wq; /* Processes waiting */
+
+ int snapshot; /* True if frame was a snapshot */
+};
+
+#define DECOMP_INTERFACE_VER 4
+
+/* Compression module operations */
+struct ov51x_decomp_ops {
+ int (*decomp_400)(unsigned char *, unsigned char *, unsigned char *,
+ int, int, int);
+ int (*decomp_420)(unsigned char *, unsigned char *, unsigned char *,
+ int, int, int);
+ int (*decomp_422)(unsigned char *, unsigned char *, unsigned char *,
+ int, int, int);
+ struct module *owner;
+};
+
+struct usb_ov511 {
+ struct video_device *vdev;
+ struct usb_device *dev;
+
+ int customid;
+ char *desc;
+ unsigned char iface;
+ char usb_path[OV511_USB_PATH_LEN];
+
+ /* Determined by sensor type */
+ int maxwidth;
+ int maxheight;
+ int minwidth;
+ int minheight;
+
+ int brightness;
+ int colour;
+ int contrast;
+ int hue;
+ int whiteness;
+ int exposure;
+ int auto_brt; /* Auto brightness enabled flag */
+ int auto_gain; /* Auto gain control enabled flag */
+ int auto_exp; /* Auto exposure enabled flag */
+ int backlight; /* Backlight exposure algorithm flag */
+ int mirror; /* Image is reversed horizontally */
+
+ int led_policy; /* LED: off|on|auto; OV511+ only */
+
+ struct mutex lock; /* Serializes user-accessible operations */
+ int user; /* user count for exclusive use */
+
+ int streaming; /* Are we streaming Isochronous? */
+ int grabbing; /* Are we grabbing? */
+
+ int compress; /* Should the next frame be compressed? */
+ int compress_inited; /* Are compression params uploaded? */
+
+ int lightfreq; /* Power (lighting) frequency */
+ int bandfilt; /* Banding filter enabled flag */
+
+ unsigned char *fbuf; /* Videodev buffer area */
+ unsigned char *tempfbuf; /* Temporary (intermediate) buffer area */
+ unsigned char *rawfbuf; /* Raw camera data buffer area */
+
+ int sub_flag; /* Pix Array subcapture on flag */
+ int subx; /* Pix Array subcapture x offset */
+ int suby; /* Pix Array subcapture y offset */
+ int subw; /* Pix Array subcapture width */
+ int subh; /* Pix Array subcapture height */
+
+ int curframe; /* Current receiving sbuf */
+ struct ov511_frame frame[OV511_NUMFRAMES];
+
+ struct ov511_sbuf sbuf[OV511_NUMSBUF];
+
+ wait_queue_head_t wq; /* Processes waiting */
+
+ int snap_enabled; /* Snapshot mode enabled */
+
+ int bridge; /* Type of bridge (BRG_*) */
+ int bclass; /* Class of bridge (BCL_*) */
+ int sensor; /* Type of image sensor chip (SEN_*) */
+
+ int packet_size; /* Frame size per isoc desc */
+ int packet_numbering; /* Is ISO frame numbering enabled? */
+
+ /* Framebuffer/sbuf management */
+ int buf_state;
+ struct mutex buf_lock;
+
+ struct ov51x_decomp_ops *decomp_ops;
+
+ /* Stop streaming while changing picture settings */
+ int stop_during_set;
+
+ int stopped; /* Streaming is temporarily paused */
+
+ /* Video decoder stuff */
+ int input; /* Composite, S-VIDEO, etc... */
+ int num_inputs; /* Number of inputs */
+ int norm; /* NTSC / PAL / SECAM */
+ int has_decoder; /* Device has a video decoder */
+ int pal; /* Device is designed for PAL resolution */
+
+ /* I2C interface */
+ struct mutex i2c_lock; /* Protect I2C controller regs */
+ unsigned char primary_i2c_slave; /* I2C write id of sensor */
+
+ /* Control transaction stuff */
+ unsigned char *cbuf; /* Buffer for payload */
+ struct mutex cbuf_lock;
+};
+
+/* Used to represent a list of values and their respective symbolic names */
+struct symbolic_list {
+ int num;
+ char *name;
+};
+
+#define NOT_DEFINED_STR "Unknown"
+
+/* Returns the name of the matching element in the symbolic_list array. The
+ * end of the list must be marked with an element that has a NULL name.
+ */
+static inline char *
+symbolic(struct symbolic_list list[], int num)
+{
+ int i;
+
+ for (i = 0; list[i].name != NULL; i++)
+ if (list[i].num == num)
+ return (list[i].name);
+
+ return (NOT_DEFINED_STR);
+}
+
+/* Compression stuff */
+
+#define OV511_QUANTABLESIZE 64
+#define OV518_QUANTABLESIZE 32
+
+#define OV511_YQUANTABLE { \
+ 0, 1, 1, 2, 2, 3, 3, 4, \
+ 1, 1, 1, 2, 2, 3, 4, 4, \
+ 1, 1, 2, 2, 3, 4, 4, 4, \
+ 2, 2, 2, 3, 4, 4, 4, 4, \
+ 2, 2, 3, 4, 4, 5, 5, 5, \
+ 3, 3, 4, 4, 5, 5, 5, 5, \
+ 3, 4, 4, 4, 5, 5, 5, 5, \
+ 4, 4, 4, 4, 5, 5, 5, 5 \
+}
+
+#define OV511_UVQUANTABLE { \
+ 0, 2, 2, 3, 4, 4, 4, 4, \
+ 2, 2, 2, 4, 4, 4, 4, 4, \
+ 2, 2, 3, 4, 4, 4, 4, 4, \
+ 3, 4, 4, 4, 4, 4, 4, 4, \
+ 4, 4, 4, 4, 4, 4, 4, 4, \
+ 4, 4, 4, 4, 4, 4, 4, 4, \
+ 4, 4, 4, 4, 4, 4, 4, 4, \
+ 4, 4, 4, 4, 4, 4, 4, 4 \
+}
+
+#define OV518_YQUANTABLE { \
+ 5, 4, 5, 6, 6, 7, 7, 7, \
+ 5, 5, 5, 5, 6, 7, 7, 7, \
+ 6, 6, 6, 6, 7, 7, 7, 8, \
+ 7, 7, 6, 7, 7, 7, 8, 8 \
+}
+
+#define OV518_UVQUANTABLE { \
+ 6, 6, 6, 7, 7, 7, 7, 7, \
+ 6, 6, 6, 7, 7, 7, 7, 7, \
+ 6, 6, 6, 7, 7, 7, 7, 8, \
+ 7, 7, 7, 7, 7, 7, 8, 8 \
+}
+
+#endif
diff --git a/drivers/media/video/pwc/Makefile b/drivers/media/video/pwc/Makefile
new file mode 100644
index 000000000000..2d93a775011a
--- /dev/null
+++ b/drivers/media/video/pwc/Makefile
@@ -0,0 +1,20 @@
+ifneq ($(KERNELRELEASE),)
+
+pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o pwc-timon.o pwc-kiara.o
+
+obj-$(CONFIG_USB_PWC) += pwc.o
+
+else
+
+KDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+
+default:
+ $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
+
+endif
+
+clean:
+ rm -f *.[oas] .*.flags *.ko .*.cmd .*.d .*.tmp *.mod.c
+ rm -rf .tmp_versions
+
diff --git a/drivers/media/video/pwc/philips.txt b/drivers/media/video/pwc/philips.txt
new file mode 100644
index 000000000000..04a640d723ed
--- /dev/null
+++ b/drivers/media/video/pwc/philips.txt
@@ -0,0 +1,236 @@
+This file contains some additional information for the Philips and OEM webcams.
+E-mail: webcam@smcc.demon.nl Last updated: 2004-01-19
+Site: http://www.smcc.demon.nl/webcam/
+
+As of this moment, the following cameras are supported:
+ * Philips PCA645
+ * Philips PCA646
+ * Philips PCVC675
+ * Philips PCVC680
+ * Philips PCVC690
+ * Philips PCVC720/40
+ * Philips PCVC730
+ * Philips PCVC740
+ * Philips PCVC750
+ * Askey VC010
+ * Creative Labs Webcam 5
+ * Creative Labs Webcam Pro Ex
+ * Logitech QuickCam 3000 Pro
+ * Logitech QuickCam 4000 Pro
+ * Logitech QuickCam Notebook Pro
+ * Logitech QuickCam Zoom
+ * Logitech QuickCam Orbit
+ * Logitech QuickCam Sphere
+ * Samsung MPC-C10
+ * Samsung MPC-C30
+ * Sotec Afina Eye
+ * AME CU-001
+ * Visionite VCS-UM100
+ * Visionite VCS-UC300
+
+The main webpage for the Philips driver is at the address above. It contains
+a lot of extra information, a FAQ, and the binary plugin 'PWCX'. This plugin
+contains decompression routines that allow you to use higher image sizes and
+framerates; in addition the webcam uses less bandwidth on the USB bus (handy
+if you want to run more than 1 camera simultaneously). These routines fall
+under a NDA, and may therefor not be distributed as source; however, its use
+is completely optional.
+
+You can build this code either into your kernel, or as a module. I recommend
+the latter, since it makes troubleshooting a lot easier. The built-in
+microphone is supported through the USB Audio class.
+
+When you load the module you can set some default settings for the
+camera; some programs depend on a particular image-size or -format and
+don't know how to set it properly in the driver. The options are:
+
+size
+ Can be one of 'sqcif', 'qsif', 'qcif', 'sif', 'cif' or
+ 'vga', for an image size of resp. 128x96, 160x120, 176x144,
+ 320x240, 352x288 and 640x480 (of course, only for those cameras that
+ support these resolutions).
+
+fps
+ Specifies the desired framerate. Is an integer in the range of 4-30.
+
+fbufs
+ This paramter specifies the number of internal buffers to use for storing
+ frames from the cam. This will help if the process that reads images from
+ the cam is a bit slow or momentarely busy. However, on slow machines it
+ only introduces lag, so choose carefully. The default is 3, which is
+ reasonable. You can set it between 2 and 5.
+
+mbufs
+ This is an integer between 1 and 10. It will tell the module the number of
+ buffers to reserve for mmap(), VIDIOCCGMBUF, VIDIOCMCAPTURE and friends.
+ The default is 2, which is adequate for most applications (double
+ buffering).
+
+ Should you experience a lot of 'Dumping frame...' messages during
+ grabbing with a tool that uses mmap(), you might want to increase if.
+ However, it doesn't really buffer images, it just gives you a bit more
+ slack when your program is behind. But you need a multi-threaded or
+ forked program to really take advantage of these buffers.
+
+ The absolute maximum is 10, but don't set it too high! Every buffer takes
+ up 460 KB of RAM, so unless you have a lot of memory setting this to
+ something more than 4 is an absolute waste. This memory is only
+ allocated during open(), so nothing is wasted when the camera is not in
+ use.
+
+power_save
+ When power_save is enabled (set to 1), the module will try to shut down
+ the cam on close() and re-activate on open(). This will save power and
+ turn off the LED. Not all cameras support this though (the 645 and 646
+ don't have power saving at all), and some models don't work either (they
+ will shut down, but never wake up). Consider this experimental. By
+ default this option is disabled.
+
+compression (only useful with the plugin)
+ With this option you can control the compression factor that the camera
+ uses to squeeze the image through the USB bus. You can set the
+ parameter between 0 and 3:
+ 0 = prefer uncompressed images; if the requested mode is not available
+ in an uncompressed format, the driver will silently switch to low
+ compression.
+ 1 = low compression.
+ 2 = medium compression.
+ 3 = high compression.
+
+ High compression takes less bandwidth of course, but it could also
+ introduce some unwanted artefacts. The default is 2, medium compression.
+ See the FAQ on the website for an overview of which modes require
+ compression.
+
+ The compression parameter does not apply to the 645 and 646 cameras
+ and OEM models derived from those (only a few). Most cams honour this
+ parameter.
+
+leds
+ This settings takes 2 integers, that define the on/off time for the LED
+ (in milliseconds). One of the interesting things that you can do with
+ this is let the LED blink while the camera is in use. This:
+
+ leds=500,500
+
+ will blink the LED once every second. But with:
+
+ leds=0,0
+
+ the LED never goes on, making it suitable for silent surveillance.
+
+ By default the camera's LED is on solid while in use, and turned off
+ when the camera is not used anymore.
+
+ This parameter works only with the ToUCam range of cameras (720, 730, 740,
+ 750) and OEMs. For other cameras this command is silently ignored, and
+ the LED cannot be controlled.
+
+ Finally: this parameters does not take effect UNTIL the first time you
+ open the camera device. Until then, the LED remains on.
+
+dev_hint
+ A long standing problem with USB devices is their dynamic nature: you
+ never know what device a camera gets assigned; it depends on module load
+ order, the hub configuration, the order in which devices are plugged in,
+ and the phase of the moon (i.e. it can be random). With this option you
+ can give the driver a hint as to what video device node (/dev/videoX) it
+ should use with a specific camera. This is also handy if you have two
+ cameras of the same model.
+
+ A camera is specified by its type (the number from the camera model,
+ like PCA645, PCVC750VC, etc) and optionally the serial number (visible
+ in /proc/bus/usb/devices). A hint consists of a string with the following
+ format:
+
+ [type[.serialnumber]:]node
+
+ The square brackets mean that both the type and the serialnumber are
+ optional, but a serialnumber cannot be specified without a type (which
+ would be rather pointless). The serialnumber is separated from the type
+ by a '.'; the node number by a ':'.
+
+ This somewhat cryptic syntax is best explained by a few examples:
+
+ dev_hint=3,5 The first detected cam gets assigned
+ /dev/video3, the second /dev/video5. Any
+ other cameras will get the first free
+ available slot (see below).
+
+ dev_hint=645:1,680:2 The PCA645 camera will get /dev/video1,
+ and a PCVC680 /dev/video2.
+
+ dev_hint=645.0123:3,645.4567:0 The PCA645 camera with serialnumber
+ 0123 goes to /dev/video3, the same
+ camera model with the 4567 serial
+ gets /dev/video0.
+
+ dev_hint=750:1,4,5,6 The PCVC750 camera will get /dev/video1, the
+ next 3 Philips cams will use /dev/video4
+ through /dev/video6.
+
+ Some points worth knowing:
+ - Serialnumbers are case sensitive and must be written full, including
+ leading zeroes (it's treated as a string).
+ - If a device node is already occupied, registration will fail and
+ the webcam is not available.
+ - You can have up to 64 video devices; be sure to make enough device
+ nodes in /dev if you want to spread the numbers (this does not apply
+ to devfs). After /dev/video9 comes /dev/video10 (not /dev/videoA).
+ - If a camera does not match any dev_hint, it will simply get assigned
+ the first available device node, just as it used to be.
+
+trace
+ In order to better detect problems, it is now possible to turn on a
+ 'trace' of some of the calls the module makes; it logs all items in your
+ kernel log at debug level.
+
+ The trace variable is a bitmask; each bit represents a certain feature.
+ If you want to trace something, look up the bit value(s) in the table
+ below, add the values together and supply that to the trace variable.
+
+ Value Value Description Default
+ (dec) (hex)
+ 1 0x1 Module initialization; this will log messages On
+ while loading and unloading the module
+
+ 2 0x2 probe() and disconnect() traces On
+
+ 4 0x4 Trace open() and close() calls Off
+
+ 8 0x8 read(), mmap() and associated ioctl() calls Off
+
+ 16 0x10 Memory allocation of buffers, etc. Off
+
+ 32 0x20 Showing underflow, overflow and Dumping frame On
+ messages
+
+ 64 0x40 Show viewport and image sizes Off
+
+ 128 0x80 PWCX debugging Off
+
+ For example, to trace the open() & read() fuctions, sum 8 + 4 = 12,
+ so you would supply trace=12 during insmod or modprobe. If
+ you want to turn the initialization and probing tracing off, set trace=0.
+ The default value for trace is 35 (0x23).
+
+
+
+Example:
+
+ # modprobe pwc size=cif fps=15 power_save=1
+
+The fbufs, mbufs and trace parameters are global and apply to all connected
+cameras. Each camera has its own set of buffers.
+
+size and fps only specify defaults when you open() the device; this is to
+accommodate some tools that don't set the size. You can change these
+settings after open() with the Video4Linux ioctl() calls. The default of
+defaults is QCIF size at 10 fps.
+
+The compression parameter is semiglobal; it sets the initial compression
+preference for all camera's, but this parameter can be set per camera with
+the VIDIOCPWCSCQUAL ioctl() call.
+
+All parameters are optional.
+
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c
new file mode 100644
index 000000000000..0398b812e0ce
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-ctrl.c
@@ -0,0 +1,1541 @@
+/* Driver for Philips webcam
+ Functions that send various control messages to the webcam, including
+ video modes.
+ (C) 1999-2003 Nemosoft Unv.
+ (C) 2004 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ Changes
+ 2001/08/03 Alvarado Added methods for changing white balance and
+ red/green gains
+ */
+
+/* Control functions for the cam; brightness, contrast, video mode, etc. */
+
+#ifdef __KERNEL__
+#include <asm/uaccess.h>
+#endif
+#include <asm/errno.h>
+
+#include "pwc.h"
+#include "pwc-ioctl.h"
+#include "pwc-uncompress.h"
+#include "pwc-kiara.h"
+#include "pwc-timon.h"
+
+/* Request types: video */
+#define SET_LUM_CTL 0x01
+#define GET_LUM_CTL 0x02
+#define SET_CHROM_CTL 0x03
+#define GET_CHROM_CTL 0x04
+#define SET_STATUS_CTL 0x05
+#define GET_STATUS_CTL 0x06
+#define SET_EP_STREAM_CTL 0x07
+#define GET_EP_STREAM_CTL 0x08
+#define SET_MPT_CTL 0x0D
+#define GET_MPT_CTL 0x0E
+
+/* Selectors for the Luminance controls [GS]ET_LUM_CTL */
+#define AGC_MODE_FORMATTER 0x2000
+#define PRESET_AGC_FORMATTER 0x2100
+#define SHUTTER_MODE_FORMATTER 0x2200
+#define PRESET_SHUTTER_FORMATTER 0x2300
+#define PRESET_CONTOUR_FORMATTER 0x2400
+#define AUTO_CONTOUR_FORMATTER 0x2500
+#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600
+#define CONTRAST_FORMATTER 0x2700
+#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800
+#define FLICKERLESS_MODE_FORMATTER 0x2900
+#define AE_CONTROL_SPEED 0x2A00
+#define BRIGHTNESS_FORMATTER 0x2B00
+#define GAMMA_FORMATTER 0x2C00
+
+/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */
+#define WB_MODE_FORMATTER 0x1000
+#define AWB_CONTROL_SPEED_FORMATTER 0x1100
+#define AWB_CONTROL_DELAY_FORMATTER 0x1200
+#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300
+#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400
+#define COLOUR_MODE_FORMATTER 0x1500
+#define SATURATION_MODE_FORMATTER1 0x1600
+#define SATURATION_MODE_FORMATTER2 0x1700
+
+/* Selectors for the Status controls [GS]ET_STATUS_CTL */
+#define SAVE_USER_DEFAULTS_FORMATTER 0x0200
+#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300
+#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400
+#define READ_AGC_FORMATTER 0x0500
+#define READ_SHUTTER_FORMATTER 0x0600
+#define READ_RED_GAIN_FORMATTER 0x0700
+#define READ_BLUE_GAIN_FORMATTER 0x0800
+#define SENSOR_TYPE_FORMATTER1 0x0C00
+#define READ_RAW_Y_MEAN_FORMATTER 0x3100
+#define SET_POWER_SAVE_MODE_FORMATTER 0x3200
+#define MIRROR_IMAGE_FORMATTER 0x3300
+#define LED_FORMATTER 0x3400
+#define SENSOR_TYPE_FORMATTER2 0x3700
+
+/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */
+#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100
+
+/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */
+#define PT_RELATIVE_CONTROL_FORMATTER 0x01
+#define PT_RESET_CONTROL_FORMATTER 0x02
+#define PT_STATUS_FORMATTER 0x03
+
+static const char *size2name[PSZ_MAX] =
+{
+ "subQCIF",
+ "QSIF",
+ "QCIF",
+ "SIF",
+ "CIF",
+ "VGA",
+};
+
+/********/
+
+/* Entries for the Nala (645/646) camera; the Nala doesn't have compression
+ preferences, so you either get compressed or non-compressed streams.
+
+ An alternate value of 0 means this mode is not available at all.
+ */
+
+struct Nala_table_entry {
+ char alternate; /* USB alternate setting */
+ int compressed; /* Compressed yes/no */
+
+ unsigned char mode[3]; /* precomputed mode table */
+};
+
+static struct Nala_table_entry Nala_table[PSZ_MAX][8] =
+{
+#include "pwc-nala.h"
+};
+
+
+/****************************************************************************/
+
+
+#define SendControlMsg(request, value, buflen) \
+ usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), \
+ request, \
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \
+ value, \
+ pdev->vcinterface, \
+ &buf, buflen, 500)
+
+#define RecvControlMsg(request, value, buflen) \
+ usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), \
+ request, \
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \
+ value, \
+ pdev->vcinterface, \
+ &buf, buflen, 500)
+
+
+#if PWC_DEBUG
+void pwc_hexdump(void *p, int len)
+{
+ int i;
+ unsigned char *s;
+ char buf[100], *d;
+
+ s = (unsigned char *)p;
+ d = buf;
+ *d = '\0';
+ Debug("Doing hexdump @ %p, %d bytes.\n", p, len);
+ for (i = 0; i < len; i++) {
+ d += sprintf(d, "%02X ", *s++);
+ if ((i & 0xF) == 0xF) {
+ Debug("%s\n", buf);
+ d = buf;
+ *d = '\0';
+ }
+ }
+ if ((i & 0xF) != 0)
+ Debug("%s\n", buf);
+}
+#endif
+
+static inline int send_video_command(struct usb_device *udev, int index, void *buf, int buflen)
+{
+ return usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ SET_EP_STREAM_CTL,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ VIDEO_OUTPUT_CONTROL_FORMATTER,
+ index,
+ buf, buflen, 1000);
+}
+
+
+
+static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames)
+{
+ unsigned char buf[3];
+ int ret, fps;
+ struct Nala_table_entry *pEntry;
+ int frames2frames[31] =
+ { /* closest match of framerate */
+ 0, 0, 0, 0, 4, /* 0-4 */
+ 5, 5, 7, 7, 10, /* 5-9 */
+ 10, 10, 12, 12, 15, /* 10-14 */
+ 15, 15, 15, 20, 20, /* 15-19 */
+ 20, 20, 20, 24, 24, /* 20-24 */
+ 24, 24, 24, 24, 24, /* 25-29 */
+ 24 /* 30 */
+ };
+ int frames2table[31] =
+ { 0, 0, 0, 0, 0, /* 0-4 */
+ 1, 1, 1, 2, 2, /* 5-9 */
+ 3, 3, 4, 4, 4, /* 10-14 */
+ 5, 5, 5, 5, 5, /* 15-19 */
+ 6, 6, 6, 6, 7, /* 20-24 */
+ 7, 7, 7, 7, 7, /* 25-29 */
+ 7 /* 30 */
+ };
+
+ if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25)
+ return -EINVAL;
+ frames = frames2frames[frames];
+ fps = frames2table[frames];
+ pEntry = &Nala_table[size][fps];
+ if (pEntry->alternate == 0)
+ return -EINVAL;
+
+ if (pEntry->compressed)
+ return -ENOENT; /* Not supported. */
+
+ memcpy(buf, pEntry->mode, 3);
+ ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3);
+ if (ret < 0) {
+ Debug("Failed to send video command... %d\n", ret);
+ return ret;
+ }
+ if (pEntry->compressed && pdev->vpalette != VIDEO_PALETTE_RAW)
+ {
+ switch(pdev->type) {
+ case 645:
+ case 646:
+/* pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data); */
+ break;
+
+ case 675:
+ case 680:
+ case 690:
+ case 720:
+ case 730:
+ case 740:
+ case 750:
+/* pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */
+ break;
+ }
+ }
+
+ pdev->cmd_len = 3;
+ memcpy(pdev->cmd_buf, buf, 3);
+
+ /* Set various parameters */
+ pdev->vframes = frames;
+ pdev->vsize = size;
+ pdev->valternate = pEntry->alternate;
+ pdev->image = pwc_image_sizes[size];
+ pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2;
+ if (pEntry->compressed) {
+ if (pdev->release < 5) { /* 4 fold compression */
+ pdev->vbandlength = 528;
+ pdev->frame_size /= 4;
+ }
+ else {
+ pdev->vbandlength = 704;
+ pdev->frame_size /= 3;
+ }
+ }
+ else
+ pdev->vbandlength = 0;
+ return 0;
+}
+
+
+static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot)
+{
+ unsigned char buf[13];
+ const struct Timon_table_entry *pChoose;
+ int ret, fps;
+
+ if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3)
+ return -EINVAL;
+ if (size == PSZ_VGA && frames > 15)
+ return -EINVAL;
+ fps = (frames / 5) - 1;
+
+ /* Find a supported framerate with progressively higher compression ratios
+ if the preferred ratio is not available.
+ */
+ pChoose = NULL;
+ while (compression <= 3) {
+ pChoose = &Timon_table[size][fps][compression];
+ if (pChoose->alternate != 0)
+ break;
+ compression++;
+ }
+ if (pChoose == NULL || pChoose->alternate == 0)
+ return -ENOENT; /* Not supported. */
+
+ memcpy(buf, pChoose->mode, 13);
+ if (snapshot)
+ buf[0] |= 0x80;
+ ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 13);
+ if (ret < 0)
+ return ret;
+
+/* if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW)
+ pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */
+
+ pdev->cmd_len = 13;
+ memcpy(pdev->cmd_buf, buf, 13);
+
+ /* Set various parameters */
+ pdev->vframes = frames;
+ pdev->vsize = size;
+ pdev->vsnapshot = snapshot;
+ pdev->valternate = pChoose->alternate;
+ pdev->image = pwc_image_sizes[size];
+ pdev->vbandlength = pChoose->bandlength;
+ if (pChoose->bandlength > 0)
+ pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4;
+ else
+ pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8;
+ return 0;
+}
+
+
+static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot)
+{
+ const struct Kiara_table_entry *pChoose = NULL;
+ int fps, ret;
+ unsigned char buf[12];
+ struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}};
+
+ if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3)
+ return -EINVAL;
+ if (size == PSZ_VGA && frames > 15)
+ return -EINVAL;
+ fps = (frames / 5) - 1;
+
+ /* special case: VGA @ 5 fps and snapshot is raw bayer mode */
+ if (size == PSZ_VGA && frames == 5 && snapshot)
+ {
+ /* Only available in case the raw palette is selected or
+ we have the decompressor available. This mode is
+ only available in compressed form
+ */
+ if (pdev->vpalette == VIDEO_PALETTE_RAW)
+ {
+ Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette);
+ pChoose = &RawEntry;
+ }
+ else
+ {
+ Info("VGA/5 BAYER mode _must_ have a decompressor available, or use RAW palette.\n");
+ }
+ }
+ else
+ {
+ /* Find a supported framerate with progressively higher compression ratios
+ if the preferred ratio is not available.
+ Skip this step when using RAW modes.
+ */
+ while (compression <= 3) {
+ pChoose = &Kiara_table[size][fps][compression];
+ if (pChoose->alternate != 0)
+ break;
+ compression++;
+ }
+ }
+ if (pChoose == NULL || pChoose->alternate == 0)
+ return -ENOENT; /* Not supported. */
+
+ Debug("Using alternate setting %d.\n", pChoose->alternate);
+
+ /* usb_control_msg won't take staticly allocated arrays as argument?? */
+ memcpy(buf, pChoose->mode, 12);
+ if (snapshot)
+ buf[0] |= 0x80;
+
+ /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */
+ ret = send_video_command(pdev->udev, 4 /* pdev->vendpoint */, buf, 12);
+ if (ret < 0)
+ return ret;
+
+/* if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW)
+ pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */
+
+ pdev->cmd_len = 12;
+ memcpy(pdev->cmd_buf, buf, 12);
+ /* All set and go */
+ pdev->vframes = frames;
+ pdev->vsize = size;
+ pdev->vsnapshot = snapshot;
+ pdev->valternate = pChoose->alternate;
+ pdev->image = pwc_image_sizes[size];
+ pdev->vbandlength = pChoose->bandlength;
+ if (pdev->vbandlength > 0)
+ pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4;
+ else
+ pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8;
+ return 0;
+}
+
+
+
+static void pwc_set_image_buffer_size(struct pwc_device *pdev)
+{
+ int i, factor = 0, filler = 0;
+
+ /* for PALETTE_YUV420P */
+ switch(pdev->vpalette)
+ {
+ case VIDEO_PALETTE_YUV420P:
+ factor = 6;
+ filler = 128;
+ break;
+ case VIDEO_PALETTE_RAW:
+ factor = 6; /* can be uncompressed YUV420P */
+ filler = 0;
+ break;
+ }
+
+ /* Set sizes in bytes */
+ pdev->image.size = pdev->image.x * pdev->image.y * factor / 4;
+ pdev->view.size = pdev->view.x * pdev->view.y * factor / 4;
+
+ /* Align offset, or you'll get some very weird results in
+ YUV420 mode... x must be multiple of 4 (to get the Y's in
+ place), and y even (or you'll mixup U & V). This is less of a
+ problem for YUV420P.
+ */
+ pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC;
+ pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE;
+
+ /* Fill buffers with gray or black */
+ for (i = 0; i < MAX_IMAGES; i++) {
+ if (pdev->image_ptr[i] != NULL)
+ memset(pdev->image_ptr[i], filler, pdev->view.size);
+ }
+}
+
+
+
+/**
+ @pdev: device structure
+ @width: viewport width
+ @height: viewport height
+ @frame: framerate, in fps
+ @compression: preferred compression ratio
+ @snapshot: snapshot mode or streaming
+ */
+int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot)
+{
+ int ret, size;
+
+ Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette);
+ size = pwc_decode_size(pdev, width, height);
+ if (size < 0) {
+ Debug("Could not find suitable size.\n");
+ return -ERANGE;
+ }
+ Debug("decode_size = %d.\n", size);
+
+ ret = -EINVAL;
+ switch(pdev->type) {
+ case 645:
+ case 646:
+ ret = set_video_mode_Nala(pdev, size, frames);
+ break;
+
+ case 675:
+ case 680:
+ case 690:
+ ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot);
+ break;
+
+ case 720:
+ case 730:
+ case 740:
+ case 750:
+ ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot);
+ break;
+ }
+ if (ret < 0) {
+ if (ret == -ENOENT)
+ Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames);
+ else {
+ Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret);
+ }
+ return ret;
+ }
+ pdev->view.x = width;
+ pdev->view.y = height;
+ pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size;
+ pwc_set_image_buffer_size(pdev);
+ Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y);
+ return 0;
+}
+
+
+/* BRIGHTNESS */
+
+int pwc_get_brightness(struct pwc_device *pdev)
+{
+ char buf;
+ int ret;
+
+ ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ return buf << 9;
+}
+
+int pwc_set_brightness(struct pwc_device *pdev, int value)
+{
+ char buf;
+
+ if (value < 0)
+ value = 0;
+ if (value > 0xffff)
+ value = 0xffff;
+ buf = (value >> 9) & 0x7f;
+ return SendControlMsg(SET_LUM_CTL, BRIGHTNESS_FORMATTER, 1);
+}
+
+/* CONTRAST */
+
+int pwc_get_contrast(struct pwc_device *pdev)
+{
+ char buf;
+ int ret;
+
+ ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ return buf << 10;
+}
+
+int pwc_set_contrast(struct pwc_device *pdev, int value)
+{
+ char buf;
+
+ if (value < 0)
+ value = 0;
+ if (value > 0xffff)
+ value = 0xffff;
+ buf = (value >> 10) & 0x3f;
+ return SendControlMsg(SET_LUM_CTL, CONTRAST_FORMATTER, 1);
+}
+
+/* GAMMA */
+
+int pwc_get_gamma(struct pwc_device *pdev)
+{
+ char buf;
+ int ret;
+
+ ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ return buf << 11;
+}
+
+int pwc_set_gamma(struct pwc_device *pdev, int value)
+{
+ char buf;
+
+ if (value < 0)
+ value = 0;
+ if (value > 0xffff)
+ value = 0xffff;
+ buf = (value >> 11) & 0x1f;
+ return SendControlMsg(SET_LUM_CTL, GAMMA_FORMATTER, 1);
+}
+
+
+/* SATURATION */
+
+int pwc_get_saturation(struct pwc_device *pdev)
+{
+ char buf;
+ int ret;
+
+ if (pdev->type < 675)
+ return -1;
+ ret = RecvControlMsg(GET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1);
+ if (ret < 0)
+ return ret;
+ return 32768 + buf * 327;
+}
+
+int pwc_set_saturation(struct pwc_device *pdev, int value)
+{
+ char buf;
+
+ if (pdev->type < 675)
+ return -EINVAL;
+ if (value < 0)
+ value = 0;
+ if (value > 0xffff)
+ value = 0xffff;
+ /* saturation ranges from -100 to +100 */
+ buf = (value - 32768) / 327;
+ return SendControlMsg(SET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1);
+}
+
+/* AGC */
+
+static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value)
+{
+ char buf;
+ int ret;
+
+ if (mode)
+ buf = 0x0; /* auto */
+ else
+ buf = 0xff; /* fixed */
+
+ ret = SendControlMsg(SET_LUM_CTL, AGC_MODE_FORMATTER, 1);
+
+ if (!mode && ret >= 0) {
+ if (value < 0)
+ value = 0;
+ if (value > 0xffff)
+ value = 0xffff;
+ buf = (value >> 10) & 0x3F;
+ ret = SendControlMsg(SET_LUM_CTL, PRESET_AGC_FORMATTER, 1);
+ }
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static inline int pwc_get_agc(struct pwc_device *pdev, int *value)
+{
+ unsigned char buf;
+ int ret;
+
+ ret = RecvControlMsg(GET_LUM_CTL, AGC_MODE_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+
+ if (buf != 0) { /* fixed */
+ ret = RecvControlMsg(GET_LUM_CTL, PRESET_AGC_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ if (buf > 0x3F)
+ buf = 0x3F;
+ *value = (buf << 10);
+ }
+ else { /* auto */
+ ret = RecvControlMsg(GET_STATUS_CTL, READ_AGC_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ /* Gah... this value ranges from 0x00 ... 0x9F */
+ if (buf > 0x9F)
+ buf = 0x9F;
+ *value = -(48 + buf * 409);
+ }
+
+ return 0;
+}
+
+static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value)
+{
+ char buf[2];
+ int speed, ret;
+
+
+ if (mode)
+ buf[0] = 0x0; /* auto */
+ else
+ buf[0] = 0xff; /* fixed */
+
+ ret = SendControlMsg(SET_LUM_CTL, SHUTTER_MODE_FORMATTER, 1);
+
+ if (!mode && ret >= 0) {
+ if (value < 0)
+ value = 0;
+ if (value > 0xffff)
+ value = 0xffff;
+ switch(pdev->type) {
+ case 675:
+ case 680:
+ case 690:
+ /* speed ranges from 0x0 to 0x290 (656) */
+ speed = (value / 100);
+ buf[1] = speed >> 8;
+ buf[0] = speed & 0xff;
+ break;
+ case 720:
+ case 730:
+ case 740:
+ case 750:
+ /* speed seems to range from 0x0 to 0xff */
+ buf[1] = 0;
+ buf[0] = value >> 8;
+ break;
+ }
+
+ ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2);
+ }
+ return ret;
+}
+
+
+/* POWER */
+
+int pwc_camera_power(struct pwc_device *pdev, int power)
+{
+ char buf;
+
+ if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6))
+ return 0; /* Not supported by Nala or Timon < release 6 */
+
+ if (power)
+ buf = 0x00; /* active */
+ else
+ buf = 0xFF; /* power save */
+ return SendControlMsg(SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, 1);
+}
+
+
+
+/* private calls */
+
+static inline int pwc_restore_user(struct pwc_device *pdev)
+{
+ char buf; /* dummy */
+ return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0);
+}
+
+static inline int pwc_save_user(struct pwc_device *pdev)
+{
+ char buf; /* dummy */
+ return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0);
+}
+
+static inline int pwc_restore_factory(struct pwc_device *pdev)
+{
+ char buf; /* dummy */
+ return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0);
+}
+
+ /* ************************************************* */
+ /* Patch by Alvarado: (not in the original version */
+
+ /*
+ * the camera recognizes modes from 0 to 4:
+ *
+ * 00: indoor (incandescant lighting)
+ * 01: outdoor (sunlight)
+ * 02: fluorescent lighting
+ * 03: manual
+ * 04: auto
+ */
+static inline int pwc_set_awb(struct pwc_device *pdev, int mode)
+{
+ char buf;
+ int ret;
+
+ if (mode < 0)
+ mode = 0;
+
+ if (mode > 4)
+ mode = 4;
+
+ buf = mode & 0x07; /* just the lowest three bits */
+
+ ret = SendControlMsg(SET_CHROM_CTL, WB_MODE_FORMATTER, 1);
+
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static inline int pwc_get_awb(struct pwc_device *pdev)
+{
+ unsigned char buf;
+ int ret;
+
+ ret = RecvControlMsg(GET_CHROM_CTL, WB_MODE_FORMATTER, 1);
+
+ if (ret < 0)
+ return ret;
+ return buf;
+}
+
+static inline int pwc_set_red_gain(struct pwc_device *pdev, int value)
+{
+ unsigned char buf;
+
+ if (value < 0)
+ value = 0;
+ if (value > 0xffff)
+ value = 0xffff;
+ /* only the msb is considered */
+ buf = value >> 8;
+ return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1);
+}
+
+static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value)
+{
+ unsigned char buf;
+ int ret;
+
+ ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ *value = buf << 8;
+ return 0;
+}
+
+
+static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value)
+{
+ unsigned char buf;
+
+ if (value < 0)
+ value = 0;
+ if (value > 0xffff)
+ value = 0xffff;
+ /* only the msb is considered */
+ buf = value >> 8;
+ return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1);
+}
+
+static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value)
+{
+ unsigned char buf;
+ int ret;
+
+ ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ *value = buf << 8;
+ return 0;
+}
+
+
+/* The following two functions are different, since they only read the
+ internal red/blue gains, which may be different from the manual
+ gains set or read above.
+ */
+static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value)
+{
+ unsigned char buf;
+ int ret;
+
+ ret = RecvControlMsg(GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ *value = buf << 8;
+ return 0;
+}
+
+static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value)
+{
+ unsigned char buf;
+ int ret;
+
+ ret = RecvControlMsg(GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ *value = buf << 8;
+ return 0;
+}
+
+
+static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed)
+{
+ unsigned char buf;
+
+ /* useful range is 0x01..0x20 */
+ buf = speed / 0x7f0;
+ return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1);
+}
+
+static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value)
+{
+ unsigned char buf;
+ int ret;
+
+ ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ *value = buf * 0x7f0;
+ return 0;
+}
+
+
+static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay)
+{
+ unsigned char buf;
+
+ /* useful range is 0x01..0x3F */
+ buf = (delay >> 10);
+ return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1);
+}
+
+static inline int pwc_get_wb_delay(struct pwc_device *pdev, int *value)
+{
+ unsigned char buf;
+ int ret;
+
+ ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ *value = buf << 10;
+ return 0;
+}
+
+
+int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)
+{
+ unsigned char buf[2];
+
+ if (pdev->type < 730)
+ return 0;
+ on_value /= 100;
+ off_value /= 100;
+ if (on_value < 0)
+ on_value = 0;
+ if (on_value > 0xff)
+ on_value = 0xff;
+ if (off_value < 0)
+ off_value = 0;
+ if (off_value > 0xff)
+ off_value = 0xff;
+
+ buf[0] = on_value;
+ buf[1] = off_value;
+
+ return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2);
+}
+
+static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value)
+{
+ unsigned char buf[2];
+ int ret;
+
+ if (pdev->type < 730) {
+ *on_value = -1;
+ *off_value = -1;
+ return 0;
+ }
+
+ ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2);
+ if (ret < 0)
+ return ret;
+ *on_value = buf[0] * 100;
+ *off_value = buf[1] * 100;
+ return 0;
+}
+
+static inline int pwc_set_contour(struct pwc_device *pdev, int contour)
+{
+ unsigned char buf;
+ int ret;
+
+ if (contour < 0)
+ buf = 0xff; /* auto contour on */
+ else
+ buf = 0x0; /* auto contour off */
+ ret = SendControlMsg(SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+
+ if (contour < 0)
+ return 0;
+ if (contour > 0xffff)
+ contour = 0xffff;
+
+ buf = (contour >> 10); /* contour preset is [0..3f] */
+ ret = SendControlMsg(SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static inline int pwc_get_contour(struct pwc_device *pdev, int *contour)
+{
+ unsigned char buf;
+ int ret;
+
+ ret = RecvControlMsg(GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+
+ if (buf == 0) {
+ /* auto mode off, query current preset value */
+ ret = RecvControlMsg(GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ *contour = buf << 10;
+ }
+ else
+ *contour = -1;
+ return 0;
+}
+
+
+static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight)
+{
+ unsigned char buf;
+
+ if (backlight)
+ buf = 0xff;
+ else
+ buf = 0x0;
+ return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1);
+}
+
+static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight)
+{
+ int ret;
+ unsigned char buf;
+
+ ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ *backlight = buf;
+ return 0;
+}
+
+
+static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker)
+{
+ unsigned char buf;
+
+ if (flicker)
+ buf = 0xff;
+ else
+ buf = 0x0;
+ return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1);
+}
+
+static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker)
+{
+ int ret;
+ unsigned char buf;
+
+ ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ *flicker = buf;
+ return 0;
+}
+
+
+static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise)
+{
+ unsigned char buf;
+
+ if (noise < 0)
+ noise = 0;
+ if (noise > 3)
+ noise = 3;
+ buf = noise;
+ return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
+}
+
+static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise)
+{
+ int ret;
+ unsigned char buf;
+
+ ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ *noise = buf;
+ return 0;
+}
+
+static int pwc_mpt_reset(struct pwc_device *pdev, int flags)
+{
+ unsigned char buf;
+
+ buf = flags & 0x03; // only lower two bits are currently used
+ return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1);
+}
+
+static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt)
+{
+ unsigned char buf[4];
+
+ /* set new relative angle; angles are expressed in degrees * 100,
+ but cam as .5 degree resolution, hence divide by 200. Also
+ the angle must be multiplied by 64 before it's send to
+ the cam (??)
+ */
+ pan = 64 * pan / 100;
+ tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */
+ buf[0] = pan & 0xFF;
+ buf[1] = (pan >> 8) & 0xFF;
+ buf[2] = tilt & 0xFF;
+ buf[3] = (tilt >> 8) & 0xFF;
+ return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4);
+}
+
+static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status)
+{
+ int ret;
+ unsigned char buf[5];
+
+ ret = RecvControlMsg(GET_MPT_CTL, PT_STATUS_FORMATTER, 5);
+ if (ret < 0)
+ return ret;
+ status->status = buf[0] & 0x7; // 3 bits are used for reporting
+ status->time_pan = (buf[1] << 8) + buf[2];
+ status->time_tilt = (buf[3] << 8) + buf[4];
+ return 0;
+}
+
+
+int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
+{
+ unsigned char buf;
+ int ret = -1, request;
+
+ if (pdev->type < 675)
+ request = SENSOR_TYPE_FORMATTER1;
+ else if (pdev->type < 730)
+ return -1; /* The Vesta series doesn't have this call */
+ else
+ request = SENSOR_TYPE_FORMATTER2;
+
+ ret = RecvControlMsg(GET_STATUS_CTL, request, 1);
+ if (ret < 0)
+ return ret;
+ if (pdev->type < 675)
+ *sensor = buf | 0x100;
+ else
+ *sensor = buf;
+ return 0;
+}
+
+
+ /* End of Add-Ons */
+ /* ************************************************* */
+
+
+int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
+{
+ int ret = 0;
+
+ switch(cmd) {
+ case VIDIOCPWCRUSER:
+ {
+ if (pwc_restore_user(pdev))
+ ret = -EINVAL;
+ break;
+ }
+
+ case VIDIOCPWCSUSER:
+ {
+ if (pwc_save_user(pdev))
+ ret = -EINVAL;
+ break;
+ }
+
+ case VIDIOCPWCFACTORY:
+ {
+ if (pwc_restore_factory(pdev))
+ ret = -EINVAL;
+ break;
+ }
+
+ case VIDIOCPWCSCQUAL:
+ {
+ int *qual = arg;
+
+ if (*qual < 0 || *qual > 3)
+ ret = -EINVAL;
+ else
+ ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, *qual, pdev->vsnapshot);
+ if (ret >= 0)
+ pdev->vcompression = *qual;
+ break;
+ }
+
+ case VIDIOCPWCGCQUAL:
+ {
+ int *qual = arg;
+ *qual = pdev->vcompression;
+ break;
+ }
+
+ case VIDIOCPWCPROBE:
+ {
+ struct pwc_probe *probe = arg;
+ strcpy(probe->name, pdev->vdev->name);
+ probe->type = pdev->type;
+ break;
+ }
+
+ case VIDIOCPWCGSERIAL:
+ {
+ struct pwc_serial *serial = arg;
+ strcpy(serial->serial, pdev->serial);
+ break;
+ }
+
+ case VIDIOCPWCSAGC:
+ {
+ int *agc = arg;
+ if (pwc_set_agc(pdev, *agc < 0 ? 1 : 0, *agc))
+ ret = -EINVAL;
+ break;
+ }
+
+ case VIDIOCPWCGAGC:
+ {
+ int *agc = arg;
+
+ if (pwc_get_agc(pdev, agc))
+ ret = -EINVAL;
+ break;
+ }
+
+ case VIDIOCPWCSSHUTTER:
+ {
+ int *shutter_speed = arg;
+ ret = pwc_set_shutter_speed(pdev, *shutter_speed < 0 ? 1 : 0, *shutter_speed);
+ break;
+ }
+
+ case VIDIOCPWCSAWB:
+ {
+ struct pwc_whitebalance *wb = arg;
+
+ ret = pwc_set_awb(pdev, wb->mode);
+ if (ret >= 0 && wb->mode == PWC_WB_MANUAL) {
+ pwc_set_red_gain(pdev, wb->manual_red);
+ pwc_set_blue_gain(pdev, wb->manual_blue);
+ }
+ break;
+ }
+
+ case VIDIOCPWCGAWB:
+ {
+ struct pwc_whitebalance *wb = arg;
+
+ memset(wb, 0, sizeof(struct pwc_whitebalance));
+ wb->mode = pwc_get_awb(pdev);
+ if (wb->mode < 0)
+ ret = -EINVAL;
+ else {
+ if (wb->mode == PWC_WB_MANUAL) {
+ ret = pwc_get_red_gain(pdev, &wb->manual_red);
+ if (ret < 0)
+ break;
+ ret = pwc_get_blue_gain(pdev, &wb->manual_blue);
+ if (ret < 0)
+ break;
+ }
+ if (wb->mode == PWC_WB_AUTO) {
+ ret = pwc_read_red_gain(pdev, &wb->read_red);
+ if (ret < 0)
+ break;
+ ret = pwc_read_blue_gain(pdev, &wb->read_blue);
+ if (ret < 0)
+ break;
+ }
+ }
+ break;
+ }
+
+ case VIDIOCPWCSAWBSPEED:
+ {
+ struct pwc_wb_speed *wbs = arg;
+
+ if (wbs->control_speed > 0) {
+ ret = pwc_set_wb_speed(pdev, wbs->control_speed);
+ }
+ if (wbs->control_delay > 0) {
+ ret = pwc_set_wb_delay(pdev, wbs->control_delay);
+ }
+ break;
+ }
+
+ case VIDIOCPWCGAWBSPEED:
+ {
+ struct pwc_wb_speed *wbs = arg;
+
+ ret = pwc_get_wb_speed(pdev, &wbs->control_speed);
+ if (ret < 0)
+ break;
+ ret = pwc_get_wb_delay(pdev, &wbs->control_delay);
+ if (ret < 0)
+ break;
+ break;
+ }
+
+ case VIDIOCPWCSLED:
+ {
+ struct pwc_leds *leds = arg;
+ ret = pwc_set_leds(pdev, leds->led_on, leds->led_off);
+ break;
+ }
+
+
+ case VIDIOCPWCGLED:
+ {
+ struct pwc_leds *leds = arg;
+ ret = pwc_get_leds(pdev, &leds->led_on, &leds->led_off);
+ break;
+ }
+
+ case VIDIOCPWCSCONTOUR:
+ {
+ int *contour = arg;
+ ret = pwc_set_contour(pdev, *contour);
+ break;
+ }
+
+ case VIDIOCPWCGCONTOUR:
+ {
+ int *contour = arg;
+ ret = pwc_get_contour(pdev, contour);
+ break;
+ }
+
+ case VIDIOCPWCSBACKLIGHT:
+ {
+ int *backlight = arg;
+ ret = pwc_set_backlight(pdev, *backlight);
+ break;
+ }
+
+ case VIDIOCPWCGBACKLIGHT:
+ {
+ int *backlight = arg;
+ ret = pwc_get_backlight(pdev, backlight);
+ break;
+ }
+
+ case VIDIOCPWCSFLICKER:
+ {
+ int *flicker = arg;
+ ret = pwc_set_flicker(pdev, *flicker);
+ break;
+ }
+
+ case VIDIOCPWCGFLICKER:
+ {
+ int *flicker = arg;
+ ret = pwc_get_flicker(pdev, flicker);
+ break;
+ }
+
+ case VIDIOCPWCSDYNNOISE:
+ {
+ int *dynnoise = arg;
+ ret = pwc_set_dynamic_noise(pdev, *dynnoise);
+ break;
+ }
+
+ case VIDIOCPWCGDYNNOISE:
+ {
+ int *dynnoise = arg;
+ ret = pwc_get_dynamic_noise(pdev, dynnoise);
+ break;
+ }
+
+ case VIDIOCPWCGREALSIZE:
+ {
+ struct pwc_imagesize *size = arg;
+ size->width = pdev->image.x;
+ size->height = pdev->image.y;
+ break;
+ }
+
+ case VIDIOCPWCMPTRESET:
+ {
+ if (pdev->features & FEATURE_MOTOR_PANTILT)
+ {
+ int *flags = arg;
+
+ ret = pwc_mpt_reset(pdev, *flags);
+ if (ret >= 0)
+ {
+ pdev->pan_angle = 0;
+ pdev->tilt_angle = 0;
+ }
+ }
+ else
+ {
+ ret = -ENXIO;
+ }
+ break;
+ }
+
+ case VIDIOCPWCMPTGRANGE:
+ {
+ if (pdev->features & FEATURE_MOTOR_PANTILT)
+ {
+ struct pwc_mpt_range *range = arg;
+ *range = pdev->angle_range;
+ }
+ else
+ {
+ ret = -ENXIO;
+ }
+ break;
+ }
+
+ case VIDIOCPWCMPTSANGLE:
+ {
+ int new_pan, new_tilt;
+
+ if (pdev->features & FEATURE_MOTOR_PANTILT)
+ {
+ struct pwc_mpt_angles *angles = arg;
+ /* The camera can only set relative angles, so
+ do some calculations when getting an absolute angle .
+ */
+ if (angles->absolute)
+ {
+ new_pan = angles->pan;
+ new_tilt = angles->tilt;
+ }
+ else
+ {
+ new_pan = pdev->pan_angle + angles->pan;
+ new_tilt = pdev->tilt_angle + angles->tilt;
+ }
+ /* check absolute ranges */
+ if (new_pan < pdev->angle_range.pan_min ||
+ new_pan > pdev->angle_range.pan_max ||
+ new_tilt < pdev->angle_range.tilt_min ||
+ new_tilt > pdev->angle_range.tilt_max)
+ {
+ ret = -ERANGE;
+ }
+ else
+ {
+ /* go to relative range, check again */
+ new_pan -= pdev->pan_angle;
+ new_tilt -= pdev->tilt_angle;
+ /* angles are specified in degrees * 100, thus the limit = 36000 */
+ if (new_pan < -36000 || new_pan > 36000 || new_tilt < -36000 || new_tilt > 36000)
+ ret = -ERANGE;
+ }
+ if (ret == 0) /* no errors so far */
+ {
+ ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt);
+ if (ret >= 0)
+ {
+ pdev->pan_angle += new_pan;
+ pdev->tilt_angle += new_tilt;
+ }
+ if (ret == -EPIPE) /* stall -> out of range */
+ ret = -ERANGE;
+ }
+ }
+ else
+ {
+ ret = -ENXIO;
+ }
+ break;
+ }
+
+ case VIDIOCPWCMPTGANGLE:
+ {
+
+ if (pdev->features & FEATURE_MOTOR_PANTILT)
+ {
+ struct pwc_mpt_angles *angles = arg;
+
+ angles->absolute = 1;
+ angles->pan = pdev->pan_angle;
+ angles->tilt = pdev->tilt_angle;
+ }
+ else
+ {
+ ret = -ENXIO;
+ }
+ break;
+ }
+
+ case VIDIOCPWCMPTSTATUS:
+ {
+ if (pdev->features & FEATURE_MOTOR_PANTILT)
+ {
+ struct pwc_mpt_status *status = arg;
+ ret = pwc_mpt_get_status(pdev, status);
+ }
+ else
+ {
+ ret = -ENXIO;
+ }
+ break;
+ }
+
+ case VIDIOCPWCGVIDCMD:
+ {
+ struct pwc_video_command *cmd = arg;
+
+ cmd->type = pdev->type;
+ cmd->release = pdev->release;
+ cmd->command_len = pdev->cmd_len;
+ memcpy(&cmd->command_buf, pdev->cmd_buf, pdev->cmd_len);
+ cmd->bandlength = pdev->vbandlength;
+ cmd->frame_size = pdev->frame_size;
+ break;
+ }
+ /*
+ case VIDIOCPWCGVIDTABLE:
+ {
+ struct pwc_table_init_buffer *table = arg;
+ table->len = pdev->cmd_len;
+ memcpy(&table->buffer, pdev->decompress_data, pdev->decompressor->table_size);
+ break;
+ }
+ */
+
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+
+ if (ret > 0)
+ return 0;
+ return ret;
+}
+
+
+
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
new file mode 100644
index 000000000000..90eb26042817
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -0,0 +1,2205 @@
+/* Linux driver for Philips webcam
+ USB and Video4Linux interface part.
+ (C) 1999-2004 Nemosoft Unv.
+ (C) 2004 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+/*
+ This code forms the interface between the USB layers and the Philips
+ specific stuff. Some adanved stuff of the driver falls under an
+ NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and
+ is thus not distributed in source form. The binary pwcx.o module
+ contains the code that falls under the NDA.
+
+ In case you're wondering: 'pwc' stands for "Philips WebCam", but
+ I really didn't want to type 'philips_web_cam' every time (I'm lazy as
+ any Linux kernel hacker, but I don't like uncomprehensible abbreviations
+ without explanation).
+
+ Oh yes, convention: to disctinguish between all the various pointers to
+ device-structures, I use these names for the pointer variables:
+ udev: struct usb_device *
+ vdev: struct video_device *
+ pdev: struct pwc_devive *
+*/
+
+/* Contributors:
+ - Alvarado: adding whitebalance code
+ - Alistar Moire: QuickCam 3000 Pro device/product ID
+ - Tony Hoyle: Creative Labs Webcam 5 device/product ID
+ - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
+ - Jk Fang: Sotec Afina Eye ID
+ - Xavier Roche: QuickCam Pro 4000 ID
+ - Jens Knudsen: QuickCam Zoom ID
+ - J. Debert: QuickCam for Notebooks ID
+*/
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <asm/io.h>
+
+#include "pwc.h"
+#include "pwc-ioctl.h"
+#include "pwc-kiara.h"
+#include "pwc-timon.h"
+#include "pwc-uncompress.h"
+
+/* Function prototypes and driver templates */
+
+/* hotplug device table support */
+static struct usb_device_id pwc_device_table [] = {
+ { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */
+ { USB_DEVICE(0x0471, 0x0303) },
+ { USB_DEVICE(0x0471, 0x0304) },
+ { USB_DEVICE(0x0471, 0x0307) },
+ { USB_DEVICE(0x0471, 0x0308) },
+ { USB_DEVICE(0x0471, 0x030C) },
+ { USB_DEVICE(0x0471, 0x0310) },
+ { USB_DEVICE(0x0471, 0x0311) },
+ { USB_DEVICE(0x0471, 0x0312) },
+ { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */
+ { USB_DEVICE(0x069A, 0x0001) }, /* Askey */
+ { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */
+ { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
+ { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */
+ { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */
+ { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */
+ { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */
+ { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */
+ { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */
+ { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */
+ { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */
+ { USB_DEVICE(0x055D, 0x9001) },
+ { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
+ { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
+ { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
+ { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */
+ { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */
+ { USB_DEVICE(0x0d81, 0x1900) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, pwc_device_table);
+
+static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id);
+static void usb_pwc_disconnect(struct usb_interface *intf);
+
+static struct usb_driver pwc_driver = {
+ .name = "Philips webcam", /* name */
+ .id_table = pwc_device_table,
+ .probe = usb_pwc_probe, /* probe() */
+ .disconnect = usb_pwc_disconnect, /* disconnect() */
+};
+
+#define MAX_DEV_HINTS 20
+#define MAX_ISOC_ERRORS 20
+
+static int default_size = PSZ_QCIF;
+static int default_fps = 10;
+static int default_fbufs = 3; /* Default number of frame buffers */
+static int default_mbufs = 2; /* Default number of mmap() buffers */
+ int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX;
+static int power_save = 0;
+static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */
+static int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */
+static struct {
+ int type;
+ char serial_number[30];
+ int device_node;
+ struct pwc_device *pdev;
+} device_hint[MAX_DEV_HINTS];
+
+/***/
+
+static int pwc_video_open(struct inode *inode, struct file *file);
+static int pwc_video_close(struct inode *inode, struct file *file);
+static ssize_t pwc_video_read(struct file *file, char __user * buf,
+ size_t count, loff_t *ppos);
+static unsigned int pwc_video_poll(struct file *file, poll_table *wait);
+static int pwc_video_ioctl(struct inode *inode, struct file *file,
+ unsigned int ioctlnr, unsigned long arg);
+static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma);
+
+static struct file_operations pwc_fops = {
+ .owner = THIS_MODULE,
+ .open = pwc_video_open,
+ .release = pwc_video_close,
+ .read = pwc_video_read,
+ .poll = pwc_video_poll,
+ .mmap = pwc_video_mmap,
+ .ioctl = pwc_video_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+static struct video_device pwc_template = {
+ .owner = THIS_MODULE,
+ .name = "Philips Webcam", /* Filled in later */
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_PWC,
+ .release = video_device_release,
+ .fops = &pwc_fops,
+ .minor = -1,
+};
+
+/***************************************************************************/
+
+/* Okay, this is some magic that I worked out and the reasoning behind it...
+
+ The biggest problem with any USB device is of course: "what to do
+ when the user unplugs the device while it is in use by an application?"
+ We have several options:
+ 1) Curse them with the 7 plagues when they do (requires divine intervention)
+ 2) Tell them not to (won't work: they'll do it anyway)
+ 3) Oops the kernel (this will have a negative effect on a user's uptime)
+ 4) Do something sensible.
+
+ Of course, we go for option 4.
+
+ It happens that this device will be linked to two times, once from
+ usb_device and once from the video_device in their respective 'private'
+ pointers. This is done when the device is probed() and all initialization
+ succeeded. The pwc_device struct links back to both structures.
+
+ When a device is unplugged while in use it will be removed from the
+ list of known USB devices; I also de-register it as a V4L device, but
+ unfortunately I can't free the memory since the struct is still in use
+ by the file descriptor. This free-ing is then deferend until the first
+ opportunity. Crude, but it works.
+
+ A small 'advantage' is that if a user unplugs the cam and plugs it back
+ in, it should get assigned the same video device minor, but unfortunately
+ it's non-trivial to re-link the cam back to the video device... (that
+ would surely be magic! :))
+*/
+
+/***************************************************************************/
+/* Private functions */
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the area.
+ */
+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)
+ {
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr=(unsigned long) mem;
+ while (size > 0)
+ {
+ SetPageReserved(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)
+ {
+ adr=(unsigned long) mem;
+ while ((long) size > 0)
+ {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr+=PAGE_SIZE;
+ size-=PAGE_SIZE;
+ }
+ vfree(mem);
+ }
+}
+
+
+
+
+static int pwc_allocate_buffers(struct pwc_device *pdev)
+{
+ int i;
+ void *kbuf;
+
+ Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev);
+
+ if (pdev == NULL)
+ return -ENXIO;
+
+#ifdef PWC_MAGIC
+ if (pdev->magic != PWC_MAGIC) {
+ Err("allocate_buffers(): magic failed.\n");
+ return -ENXIO;
+ }
+#endif
+ /* Allocate Isochronous pipe buffers */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ if (pdev->sbuf[i].data == NULL) {
+ kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
+ if (kbuf == NULL) {
+ Err("Failed to allocate iso buffer %d.\n", i);
+ return -ENOMEM;
+ }
+ Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf);
+ pdev->sbuf[i].data = kbuf;
+ memset(kbuf, 0, ISO_BUFFER_SIZE);
+ }
+ }
+
+ /* Allocate frame buffer structure */
+ if (pdev->fbuf == NULL) {
+ kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL);
+ if (kbuf == NULL) {
+ Err("Failed to allocate frame buffer structure.\n");
+ return -ENOMEM;
+ }
+ Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf);
+ pdev->fbuf = kbuf;
+ memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf));
+ }
+ /* create frame buffers, and make circular ring */
+ for (i = 0; i < default_fbufs; i++) {
+ if (pdev->fbuf[i].data == NULL) {
+ kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */
+ if (kbuf == NULL) {
+ Err("Failed to allocate frame buffer %d.\n", i);
+ return -ENOMEM;
+ }
+ Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i, kbuf);
+ pdev->fbuf[i].data = kbuf;
+ memset(kbuf, 128, PWC_FRAME_SIZE);
+ }
+ }
+
+ /* Allocate decompressor table space */
+ kbuf = NULL;
+ switch (pdev->type)
+ {
+ case 675:
+ case 680:
+ case 690:
+ case 720:
+ case 730:
+ case 740:
+ case 750:
+#if 0
+ Trace(TRACE_MEMORY,"private_data(%zu)\n",sizeof(struct pwc_dec23_private));
+ kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); /* Timon & Kiara */
+ break;
+ case 645:
+ case 646:
+ /* TODO & FIXME */
+ kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL);
+ break;
+#endif
+ ;
+ }
+ pdev->decompress_data = kbuf;
+
+ /* Allocate image buffer; double buffer for mmap() */
+ kbuf = rvmalloc(default_mbufs * pdev->len_per_image);
+ if (kbuf == NULL) {
+ Err("Failed to allocate image buffer(s). needed (%d)\n",default_mbufs * pdev->len_per_image);
+ return -ENOMEM;
+ }
+ Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf);
+ pdev->image_data = kbuf;
+ for (i = 0; i < default_mbufs; i++)
+ pdev->image_ptr[i] = kbuf + i * pdev->len_per_image;
+ for (; i < MAX_IMAGES; i++)
+ pdev->image_ptr[i] = NULL;
+
+ kbuf = NULL;
+
+ Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n");
+ return 0;
+}
+
+static void pwc_free_buffers(struct pwc_device *pdev)
+{
+ int i;
+
+ Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev);
+
+ if (pdev == NULL)
+ return;
+#ifdef PWC_MAGIC
+ if (pdev->magic != PWC_MAGIC) {
+ Err("free_buffers(): magic failed.\n");
+ return;
+ }
+#endif
+
+ /* Release Iso-pipe buffers */
+ for (i = 0; i < MAX_ISO_BUFS; i++)
+ if (pdev->sbuf[i].data != NULL) {
+ Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev->sbuf[i].data);
+ kfree(pdev->sbuf[i].data);
+ pdev->sbuf[i].data = NULL;
+ }
+
+ /* The same for frame buffers */
+ if (pdev->fbuf != NULL) {
+ for (i = 0; i < default_fbufs; i++) {
+ if (pdev->fbuf[i].data != NULL) {
+ Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data);
+ vfree(pdev->fbuf[i].data);
+ pdev->fbuf[i].data = NULL;
+ }
+ }
+ kfree(pdev->fbuf);
+ pdev->fbuf = NULL;
+ }
+
+ /* Intermediate decompression buffer & tables */
+ if (pdev->decompress_data != NULL) {
+ Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev->decompress_data);
+ kfree(pdev->decompress_data);
+ pdev->decompress_data = NULL;
+ }
+ pdev->decompressor = NULL;
+
+ /* Release image buffers */
+ if (pdev->image_data != NULL) {
+ Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data);
+ rvfree(pdev->image_data, default_mbufs * pdev->len_per_image);
+ }
+ pdev->image_data = NULL;
+
+ Trace(TRACE_MEMORY, "Leaving free_buffers().\n");
+}
+
+/* The frame & image buffer mess.
+
+ Yes, this is a mess. Well, it used to be simple, but alas... In this
+ module, 3 buffers schemes are used to get the data from the USB bus to
+ the user program. The first scheme involves the ISO buffers (called thus
+ since they transport ISO data from the USB controller), and not really
+ interesting. Suffices to say the data from this buffer is quickly
+ gathered in an interrupt handler (pwc_isoc_handler) and placed into the
+ frame buffer.
+
+ The frame buffer is the second scheme, and is the central element here.
+ It collects the data from a single frame from the camera (hence, the
+ name). Frames are delimited by the USB camera with a short USB packet,
+ so that's easy to detect. The frame buffers form a list that is filled
+ by the camera+USB controller and drained by the user process through
+ either read() or mmap().
+
+ The image buffer is the third scheme, in which frames are decompressed
+ and converted into planar format. For mmap() there is more than
+ one image buffer available.
+
+ The frame buffers provide the image buffering. In case the user process
+ is a bit slow, this introduces lag and some undesired side-effects.
+ The problem arises when the frame buffer is full. I used to drop the last
+ frame, which makes the data in the queue stale very quickly. But dropping
+ the frame at the head of the queue proved to be a litte bit more difficult.
+ I tried a circular linked scheme, but this introduced more problems than
+ it solved.
+
+ Because filling and draining are completely asynchronous processes, this
+ requires some fiddling with pointers and mutexes.
+
+ Eventually, I came up with a system with 2 lists: an 'empty' frame list
+ and a 'full' frame list:
+ * Initially, all frame buffers but one are on the 'empty' list; the one
+ remaining buffer is our initial fill frame.
+ * If a frame is needed for filling, we try to take it from the 'empty'
+ list, unless that list is empty, in which case we take the buffer at
+ the head of the 'full' list.
+ * When our fill buffer has been filled, it is appended to the 'full'
+ list.
+ * If a frame is needed by read() or mmap(), it is taken from the head of
+ the 'full' list, handled, and then appended to the 'empty' list. If no
+ buffer is present on the 'full' list, we wait.
+ The advantage is that the buffer that is currently being decompressed/
+ converted, is on neither list, and thus not in our way (any other scheme
+ I tried had the problem of old data lingering in the queue).
+
+ Whatever strategy you choose, it always remains a tradeoff: with more
+ frame buffers the chances of a missed frame are reduced. On the other
+ hand, on slower machines it introduces lag because the queue will
+ always be full.
+ */
+
+/**
+ \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first.
+ */
+static inline int pwc_next_fill_frame(struct pwc_device *pdev)
+{
+ int ret;
+ unsigned long flags;
+
+ ret = 0;
+ spin_lock_irqsave(&pdev->ptrlock, flags);
+ if (pdev->fill_frame != NULL) {
+ /* append to 'full' list */
+ if (pdev->full_frames == NULL) {
+ pdev->full_frames = pdev->fill_frame;
+ pdev->full_frames_tail = pdev->full_frames;
+ }
+ else {
+ pdev->full_frames_tail->next = pdev->fill_frame;
+ pdev->full_frames_tail = pdev->fill_frame;
+ }
+ }
+ if (pdev->empty_frames != NULL) {
+ /* We have empty frames available. That's easy */
+ pdev->fill_frame = pdev->empty_frames;
+ pdev->empty_frames = pdev->empty_frames->next;
+ }
+ else {
+ /* Hmm. Take it from the full list */
+#if PWC_DEBUG
+ /* sanity check */
+ if (pdev->full_frames == NULL) {
+ Err("Neither empty or full frames available!\n");
+ spin_unlock_irqrestore(&pdev->ptrlock, flags);
+ return -EINVAL;
+ }
+#endif
+ pdev->fill_frame = pdev->full_frames;
+ pdev->full_frames = pdev->full_frames->next;
+ ret = 1;
+ }
+ pdev->fill_frame->next = NULL;
+#if PWC_DEBUG
+ Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence);
+ pdev->fill_frame->sequence = pdev->sequence++;
+#endif
+ spin_unlock_irqrestore(&pdev->ptrlock, flags);
+ return ret;
+}
+
+
+/**
+ \brief Reset all buffers, pointers and lists, except for the image_used[] buffer.
+
+ If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble.
+ */
+static void pwc_reset_buffers(struct pwc_device *pdev)
+{
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdev->ptrlock, flags);
+ pdev->full_frames = NULL;
+ pdev->full_frames_tail = NULL;
+ for (i = 0; i < default_fbufs; i++) {
+ pdev->fbuf[i].filled = 0;
+ if (i > 0)
+ pdev->fbuf[i].next = &pdev->fbuf[i - 1];
+ else
+ pdev->fbuf->next = NULL;
+ }
+ pdev->empty_frames = &pdev->fbuf[default_fbufs - 1];
+ pdev->empty_frames_tail = pdev->fbuf;
+ pdev->read_frame = NULL;
+ pdev->fill_frame = pdev->empty_frames;
+ pdev->empty_frames = pdev->empty_frames->next;
+
+ pdev->image_read_pos = 0;
+ pdev->fill_image = 0;
+ spin_unlock_irqrestore(&pdev->ptrlock, flags);
+}
+
+
+/**
+ \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers.
+ */
+static int pwc_handle_frame(struct pwc_device *pdev)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdev->ptrlock, flags);
+ /* First grab our read_frame; this is removed from all lists, so
+ we can release the lock after this without problems */
+ if (pdev->read_frame != NULL) {
+ /* This can't theoretically happen */
+ Err("Huh? Read frame still in use?\n");
+ }
+ else {
+ if (pdev->full_frames == NULL) {
+ Err("Woops. No frames ready.\n");
+ }
+ else {
+ pdev->read_frame = pdev->full_frames;
+ pdev->full_frames = pdev->full_frames->next;
+ pdev->read_frame->next = NULL;
+ }
+
+ if (pdev->read_frame != NULL) {
+#if PWC_DEBUG
+ Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence);
+#endif
+ /* Decompression is a lenghty process, so it's outside of the lock.
+ This gives the isoc_handler the opportunity to fill more frames
+ in the mean time.
+ */
+ spin_unlock_irqrestore(&pdev->ptrlock, flags);
+ ret = pwc_decompress(pdev);
+ spin_lock_irqsave(&pdev->ptrlock, flags);
+
+ /* We're done with read_buffer, tack it to the end of the empty buffer list */
+ if (pdev->empty_frames == NULL) {
+ pdev->empty_frames = pdev->read_frame;
+ pdev->empty_frames_tail = pdev->empty_frames;
+ }
+ else {
+ pdev->empty_frames_tail->next = pdev->read_frame;
+ pdev->empty_frames_tail = pdev->read_frame;
+ }
+ pdev->read_frame = NULL;
+ }
+ }
+ spin_unlock_irqrestore(&pdev->ptrlock, flags);
+ return ret;
+}
+
+/**
+ \brief Advance pointers of image buffer (after each user request)
+*/
+static inline void pwc_next_image(struct pwc_device *pdev)
+{
+ pdev->image_used[pdev->fill_image] = 0;
+ pdev->fill_image = (pdev->fill_image + 1) % default_mbufs;
+}
+
+
+/* This gets called for the Isochronous pipe (video). This is done in
+ * interrupt time, so it has to be fast, not crash, and not stall. Neat.
+ */
+static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
+{
+ struct pwc_device *pdev;
+ int i, fst, flen;
+ int awake;
+ struct pwc_frame_buf *fbuf;
+ unsigned char *fillptr = NULL, *iso_buf = NULL;
+
+ awake = 0;
+ pdev = (struct pwc_device *)urb->context;
+ if (pdev == NULL) {
+ Err("isoc_handler() called with NULL device?!\n");
+ return;
+ }
+#ifdef PWC_MAGIC
+ if (pdev->magic != PWC_MAGIC) {
+ Err("isoc_handler() called with bad magic!\n");
+ return;
+ }
+#endif
+ if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
+ Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
+ return;
+ }
+ if (urb->status != -EINPROGRESS && urb->status != 0) {
+ const char *errmsg;
+
+ errmsg = "Unknown";
+ switch(urb->status) {
+ case -ENOSR: errmsg = "Buffer error (overrun)"; break;
+ case -EPIPE: errmsg = "Stalled (device not responding)"; break;
+ case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break;
+ case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break;
+ case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break;
+ case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break;
+ }
+ Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
+ /* Give up after a number of contiguous errors on the USB bus.
+ Appearantly something is wrong so we simulate an unplug event.
+ */
+ if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
+ {
+ Info("Too many ISOC errors, bailing out.\n");
+ pdev->error_status = EIO;
+ awake = 1;
+ wake_up_interruptible(&pdev->frameq);
+ }
+ goto handler_end; // ugly, but practical
+ }
+
+ fbuf = pdev->fill_frame;
+ if (fbuf == NULL) {
+ Err("pwc_isoc_handler without valid fill frame.\n");
+ awake = 1;
+ goto handler_end;
+ }
+ else {
+ fillptr = fbuf->data + fbuf->filled;
+ }
+
+ /* Reset ISOC error counter. We did get here, after all. */
+ pdev->visoc_errors = 0;
+
+ /* vsync: 0 = don't copy data
+ 1 = sync-hunt
+ 2 = synched
+ */
+ /* Compact data */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ fst = urb->iso_frame_desc[i].status;
+ flen = urb->iso_frame_desc[i].actual_length;
+ iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ if (fst == 0) {
+ if (flen > 0) { /* if valid data... */
+ if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */
+ pdev->vsync = 2;
+
+ /* ...copy data to frame buffer, if possible */
+ if (flen + fbuf->filled > pdev->frame_total_size) {
+ Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size);
+ pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */
+ pdev->vframes_error++;
+ }
+ else {
+ memmove(fillptr, iso_buf, flen);
+ fillptr += flen;
+ }
+ }
+ fbuf->filled += flen;
+ } /* ..flen > 0 */
+
+ if (flen < pdev->vlast_packet_size) {
+ /* Shorter packet... We probably have the end of an image-frame;
+ wake up read() process and let select()/poll() do something.
+ Decompression is done in user time over there.
+ */
+ if (pdev->vsync == 2) {
+ /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus
+ frames on the USB wire after an exposure change. This conditition is
+ however detected in the cam and a bit is set in the header.
+ */
+ if (pdev->type == 730) {
+ unsigned char *ptr = (unsigned char *)fbuf->data;
+
+ if (ptr[1] == 1 && ptr[0] & 0x10) {
+#if PWC_DEBUG
+ Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence);
+#endif
+ pdev->drop_frames += 2;
+ pdev->vframes_error++;
+ }
+ if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+ if (ptr[0] & 0x01)
+ Info("Snapshot button pressed.\n");
+ else
+ Info("Snapshot button released.\n");
+ }
+ if ((ptr[0] ^ pdev->vmirror) & 0x02) {
+ if (ptr[0] & 0x02)
+ Info("Image is mirrored.\n");
+ else
+ Info("Image is normal.\n");
+ }
+ pdev->vmirror = ptr[0] & 0x03;
+ /* Sometimes the trailer of the 730 is still sent as a 4 byte packet
+ after a short frame; this condition is filtered out specifically. A 4 byte
+ frame doesn't make sense anyway.
+ So we get either this sequence:
+ drop_bit set -> 4 byte frame -> short frame -> good frame
+ Or this one:
+ drop_bit set -> short frame -> good frame
+ So we drop either 3 or 2 frames in all!
+ */
+ if (fbuf->filled == 4)
+ pdev->drop_frames++;
+ }
+
+ /* In case we were instructed to drop the frame, do so silently.
+ The buffer pointers are not updated either (but the counters are reset below).
+ */
+ if (pdev->drop_frames > 0)
+ pdev->drop_frames--;
+ else {
+ /* Check for underflow first */
+ if (fbuf->filled < pdev->frame_total_size) {
+ Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled);
+ pdev->vframes_error++;
+ }
+ else {
+ /* Send only once per EOF */
+ awake = 1; /* delay wake_ups */
+
+ /* Find our next frame to fill. This will always succeed, since we
+ * nick a frame from either empty or full list, but if we had to
+ * take it from the full list, it means a frame got dropped.
+ */
+ if (pwc_next_fill_frame(pdev)) {
+ pdev->vframes_dumped++;
+ if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) {
+ if (pdev->vframes_dumped < 20)
+ Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count);
+ if (pdev->vframes_dumped == 20)
+ Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count);
+ }
+ }
+ fbuf = pdev->fill_frame;
+ }
+ } /* !drop_frames */
+ pdev->vframe_count++;
+ }
+ fbuf->filled = 0;
+ fillptr = fbuf->data;
+ pdev->vsync = 1;
+ } /* .. flen < last_packet_size */
+ pdev->vlast_packet_size = flen;
+ } /* ..status == 0 */
+#if PWC_DEBUG
+ /* This is normally not interesting to the user, unless you are really debugging something */
+ else {
+ static int iso_error = 0;
+ iso_error++;
+ if (iso_error < 20)
+ Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst);
+ }
+#endif
+ }
+
+handler_end:
+ if (awake)
+ wake_up_interruptible(&pdev->frameq);
+
+ urb->dev = pdev->udev;
+ i = usb_submit_urb(urb, GFP_ATOMIC);
+ if (i != 0)
+ Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
+}
+
+
+static int pwc_isoc_init(struct pwc_device *pdev)
+{
+ struct usb_device *udev;
+ struct urb *urb;
+ int i, j, ret;
+
+ struct usb_interface *intf;
+ struct usb_host_interface *idesc = NULL;
+
+ if (pdev == NULL)
+ return -EFAULT;
+ if (pdev->iso_init)
+ return 0;
+ pdev->vsync = 0;
+ udev = pdev->udev;
+
+ /* Get the current alternate interface, adjust packet size */
+ if (!udev->actconfig)
+ return -EFAULT;
+
+ intf = usb_ifnum_to_if(udev, 0);
+ if (intf)
+ idesc = usb_altnum_to_altsetting(intf, pdev->valternate);
+
+ if (!idesc)
+ return -EFAULT;
+
+ /* Search video endpoint */
+ pdev->vmax_packet_size = -1;
+ for (i = 0; i < idesc->desc.bNumEndpoints; i++)
+ if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) {
+ pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize);
+ break;
+ }
+
+ if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
+ Err("Failed to find packet size for video endpoint in current alternate setting.\n");
+ return -ENFILE; /* Odd error, that should be noticeable */
+ }
+
+ /* Set alternate interface */
+ ret = 0;
+ Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate);
+ ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
+ if (urb == NULL) {
+ Err("Failed to allocate urb %d\n", i);
+ ret = -ENOMEM;
+ break;
+ }
+ pdev->sbuf[i].urb = urb;
+ Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb);
+ }
+ if (ret) {
+ /* De-allocate in reverse order */
+ while (i >= 0) {
+ if (pdev->sbuf[i].urb != NULL)
+ usb_free_urb(pdev->sbuf[i].urb);
+ pdev->sbuf[i].urb = NULL;
+ i--;
+ }
+ return ret;
+ }
+
+ /* init URB structure */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ urb = pdev->sbuf[i].urb;
+
+ urb->interval = 1; // devik
+ urb->dev = udev;
+ urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = pdev->sbuf[i].data;
+ urb->transfer_buffer_length = ISO_BUFFER_SIZE;
+ urb->complete = pwc_isoc_handler;
+ urb->context = pdev;
+ urb->start_frame = 0;
+ urb->number_of_packets = ISO_FRAMES_PER_DESC;
+ for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
+ urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
+ urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
+ }
+ }
+
+ /* link */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL);
+ if (ret)
+ Err("isoc_init() submit_urb %d failed with error %d\n", i, ret);
+ else
+ Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb);
+ }
+
+ /* All is done... */
+ pdev->iso_init = 1;
+ Trace(TRACE_OPEN, "<< pwc_isoc_init()\n");
+ return 0;
+}
+
+static void pwc_isoc_cleanup(struct pwc_device *pdev)
+{
+ int i;
+
+ Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n");
+ if (pdev == NULL)
+ return;
+
+ /* Unlinking ISOC buffers one by one */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ struct urb *urb;
+
+ urb = pdev->sbuf[i].urb;
+ if (urb != 0) {
+ if (pdev->iso_init) {
+ Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb);
+ usb_kill_urb(urb);
+ }
+ Trace(TRACE_MEMORY, "Freeing URB\n");
+ usb_free_urb(urb);
+ pdev->sbuf[i].urb = NULL;
+ }
+ }
+
+ /* Stop camera, but only if we are sure the camera is still there (unplug
+ is signalled by EPIPE)
+ */
+ if (pdev->error_status && pdev->error_status != EPIPE) {
+ Trace(TRACE_OPEN, "Setting alternate interface 0.\n");
+ usb_set_interface(pdev->udev, 0, 0);
+ }
+
+ pdev->iso_init = 0;
+ Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n");
+}
+
+int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot)
+{
+ int ret, start;
+
+ /* Stop isoc stuff */
+ pwc_isoc_cleanup(pdev);
+ /* Reset parameters */
+ pwc_reset_buffers(pdev);
+ /* Try to set video mode... */
+ start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot);
+ if (ret) {
+ Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n");
+ /* That failed... restore old mode (we know that worked) */
+ start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
+ if (start) {
+ Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n");
+ }
+ }
+ if (start == 0)
+ {
+ if (pwc_isoc_init(pdev) < 0)
+ {
+ Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n");
+ ret = -EAGAIN; /* let's try again, who knows if it works a second time */
+ }
+ }
+ pdev->drop_frames++; /* try to avoid garbage during switch */
+ return ret; /* Return original error code */
+}
+
+
+/***************************************************************************/
+/* Video4Linux functions */
+
+static int pwc_video_open(struct inode *inode, struct file *file)
+{
+ int i;
+ struct video_device *vdev = video_devdata(file);
+ struct pwc_device *pdev;
+
+ Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev);
+
+ pdev = (struct pwc_device *)vdev->priv;
+ if (pdev == NULL)
+ BUG();
+ if (pdev->vopen)
+ return -EBUSY;
+
+ down(&pdev->modlock);
+ if (!pdev->usb_init) {
+ Trace(TRACE_OPEN, "Doing first time initialization.\n");
+ pdev->usb_init = 1;
+
+ if (pwc_trace & TRACE_OPEN)
+ {
+ /* Query sensor type */
+ const char *sensor_type = NULL;
+ int ret;
+
+ ret = pwc_get_cmos_sensor(pdev, &i);
+ if (ret >= 0)
+ {
+ switch(i) {
+ case 0x00: sensor_type = "Hyundai CMOS sensor"; break;
+ case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break;
+ case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break;
+ case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break;
+ case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break;
+ case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break;
+ case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break;
+ case 0x40: sensor_type = "UPA 1021 sensor"; break;
+ case 0x100: sensor_type = "VGA sensor"; break;
+ case 0x101: sensor_type = "PAL MR sensor"; break;
+ default: sensor_type = "unknown type of sensor"; break;
+ }
+ }
+ if (sensor_type != NULL)
+ Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i);
+ }
+ }
+
+ /* Turn on camera */
+ if (power_save) {
+ i = pwc_camera_power(pdev, 1);
+ if (i < 0)
+ Info("Failed to restore power to the camera! (%d)\n", i);
+ }
+ /* Set LED on/off time */
+ if (pwc_set_leds(pdev, led_on, led_off) < 0)
+ Info("Failed to set LED on/off time.\n");
+
+ pwc_construct(pdev); /* set min/max sizes correct */
+
+ /* So far, so good. Allocate memory. */
+ i = pwc_allocate_buffers(pdev);
+ if (i < 0) {
+ Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n");
+ up(&pdev->modlock);
+ return i;
+ }
+
+ /* Reset buffers & parameters */
+ pwc_reset_buffers(pdev);
+ for (i = 0; i < default_mbufs; i++)
+ pdev->image_used[i] = 0;
+ pdev->vframe_count = 0;
+ pdev->vframes_dumped = 0;
+ pdev->vframes_error = 0;
+ pdev->visoc_errors = 0;
+ pdev->error_status = 0;
+#if PWC_DEBUG
+ pdev->sequence = 0;
+#endif
+ pwc_construct(pdev); /* set min/max sizes correct */
+
+ /* Set some defaults */
+ pdev->vsnapshot = 0;
+
+ /* Start iso pipe for video; first try the last used video size
+ (or the default one); if that fails try QCIF/10 or QSIF/10;
+ it that fails too, give up.
+ */
+ i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0);
+ if (i) {
+ Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n");
+ if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750)
+ i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0);
+ else
+ i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0);
+ }
+ if (i) {
+ Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n");
+ up(&pdev->modlock);
+ return i;
+ }
+
+ i = pwc_isoc_init(pdev);
+ if (i) {
+ Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i);
+ up(&pdev->modlock);
+ return i;
+ }
+
+ pdev->vopen++;
+ file->private_data = vdev;
+ up(&pdev->modlock);
+ Trace(TRACE_OPEN, "<< video_open() returns 0.\n");
+ return 0;
+}
+
+/* Note that all cleanup is done in the reverse order as in _open */
+static int pwc_video_close(struct inode *inode, struct file *file)
+{
+ struct video_device *vdev = file->private_data;
+ struct pwc_device *pdev;
+ int i;
+
+ Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev);
+
+ pdev = (struct pwc_device *)vdev->priv;
+ if (pdev->vopen == 0)
+ Info("video_close() called on closed device?\n");
+
+ /* Dump statistics, but only if a reasonable amount of frames were
+ processed (to prevent endless log-entries in case of snap-shot
+ programs)
+ */
+ if (pdev->vframe_count > 20)
+ Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error);
+
+ switch (pdev->type)
+ {
+ case 675:
+ case 680:
+ case 690:
+ case 720:
+ case 730:
+ case 740:
+ case 750:
+/* pwc_dec23_exit(); *//* Timon & Kiara */
+ break;
+ case 645:
+ case 646:
+/* pwc_dec1_exit(); */
+ break;
+ }
+
+ pwc_isoc_cleanup(pdev);
+ pwc_free_buffers(pdev);
+
+ /* Turn off LEDS and power down camera, but only when not unplugged */
+ if (pdev->error_status != EPIPE) {
+ /* Turn LEDs off */
+ if (pwc_set_leds(pdev, 0, 0) < 0)
+ Info("Failed to set LED on/off time.\n");
+ if (power_save) {
+ i = pwc_camera_power(pdev, 0);
+ if (i < 0)
+ Err("Failed to power down camera (%d)\n", i);
+ }
+ }
+ pdev->vopen = 0;
+ Trace(TRACE_OPEN, "<< video_close()\n");
+ return 0;
+}
+
+/*
+ * FIXME: what about two parallel reads ????
+ * ANSWER: Not supported. You can't open the device more than once,
+ despite what the V4L1 interface says. First, I don't see
+ the need, second there's no mechanism of alerting the
+ 2nd/3rd/... process of events like changing image size.
+ And I don't see the point of blocking that for the
+ 2nd/3rd/... process.
+ In multi-threaded environments reading parallel from any
+ device is tricky anyhow.
+ */
+
+static ssize_t pwc_video_read(struct file *file, char __user * buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = file->private_data;
+ struct pwc_device *pdev;
+ int noblock = file->f_flags & O_NONBLOCK;
+ DECLARE_WAITQUEUE(wait, current);
+ int bytes_to_read;
+
+ Trace(TRACE_READ, "video_read(0x%p, %p, %zu) called.\n", vdev, buf, count);
+ if (vdev == NULL)
+ return -EFAULT;
+ pdev = vdev->priv;
+ if (pdev == NULL)
+ return -EFAULT;
+ if (pdev->error_status)
+ return -pdev->error_status; /* Something happened, report what. */
+
+ /* In case we're doing partial reads, we don't have to wait for a frame */
+ if (pdev->image_read_pos == 0) {
+ /* Do wait queueing according to the (doc)book */
+ add_wait_queue(&pdev->frameq, &wait);
+ while (pdev->full_frames == NULL) {
+ /* Check for unplugged/etc. here */
+ if (pdev->error_status) {
+ remove_wait_queue(&pdev->frameq, &wait);
+ set_current_state(TASK_RUNNING);
+ return -pdev->error_status ;
+ }
+ if (noblock) {
+ remove_wait_queue(&pdev->frameq, &wait);
+ set_current_state(TASK_RUNNING);
+ return -EWOULDBLOCK;
+ }
+ if (signal_pending(current)) {
+ remove_wait_queue(&pdev->frameq, &wait);
+ set_current_state(TASK_RUNNING);
+ return -ERESTARTSYS;
+ }
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ remove_wait_queue(&pdev->frameq, &wait);
+ set_current_state(TASK_RUNNING);
+
+ /* Decompress and release frame */
+ if (pwc_handle_frame(pdev))
+ return -EFAULT;
+ }
+
+ Trace(TRACE_READ, "Copying data to user space.\n");
+ if (pdev->vpalette == VIDEO_PALETTE_RAW)
+ bytes_to_read = pdev->frame_size;
+ else
+ bytes_to_read = pdev->view.size;
+
+ /* copy bytes to user space; we allow for partial reads */
+ if (count + pdev->image_read_pos > bytes_to_read)
+ count = bytes_to_read - pdev->image_read_pos;
+ if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count))
+ return -EFAULT;
+ pdev->image_read_pos += count;
+ if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */
+ pdev->image_read_pos = 0;
+ pwc_next_image(pdev);
+ }
+ return count;
+}
+
+static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
+{
+ struct video_device *vdev = file->private_data;
+ struct pwc_device *pdev;
+
+ if (vdev == NULL)
+ return -EFAULT;
+ pdev = vdev->priv;
+ if (pdev == NULL)
+ return -EFAULT;
+
+ poll_wait(file, &pdev->frameq, wait);
+ if (pdev->error_status)
+ return POLLERR;
+ if (pdev->full_frames != NULL) /* we have frames waiting */
+ return (POLLIN | POLLRDNORM);
+
+ return 0;
+}
+
+static int pwc_video_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = file->private_data;
+ struct pwc_device *pdev;
+ DECLARE_WAITQUEUE(wait, current);
+
+ if (vdev == NULL)
+ return -EFAULT;
+ pdev = vdev->priv;
+ if (pdev == NULL)
+ return -EFAULT;
+
+ switch (cmd) {
+ /* Query cabapilities */
+ case VIDIOCGCAP:
+ {
+ struct video_capability *caps = arg;
+
+ strcpy(caps->name, vdev->name);
+ caps->type = VID_TYPE_CAPTURE;
+ caps->channels = 1;
+ caps->audios = 1;
+ caps->minwidth = pdev->view_min.x;
+ caps->minheight = pdev->view_min.y;
+ caps->maxwidth = pdev->view_max.x;
+ caps->maxheight = pdev->view_max.y;
+ break;
+ }
+
+ /* Channel functions (simulate 1 channel) */
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *v = arg;
+
+ if (v->channel != 0)
+ return -EINVAL;
+ v->flags = 0;
+ v->tuners = 0;
+ v->type = VIDEO_TYPE_CAMERA;
+ strcpy(v->name, "Webcam");
+ return 0;
+ }
+
+ case VIDIOCSCHAN:
+ {
+ /* The spec says the argument is an integer, but
+ the bttv driver uses a video_channel arg, which
+ makes sense becasue it also has the norm flag.
+ */
+ struct video_channel *v = arg;
+ if (v->channel != 0)
+ return -EINVAL;
+ return 0;
+ }
+
+
+ /* Picture functions; contrast etc. */
+ case VIDIOCGPICT:
+ {
+ struct video_picture *p = arg;
+ int val;
+
+ val = pwc_get_brightness(pdev);
+ if (val >= 0)
+ p->brightness = val;
+ else
+ p->brightness = 0xffff;
+ val = pwc_get_contrast(pdev);
+ if (val >= 0)
+ p->contrast = val;
+ else
+ p->contrast = 0xffff;
+ /* Gamma, Whiteness, what's the difference? :) */
+ val = pwc_get_gamma(pdev);
+ if (val >= 0)
+ p->whiteness = val;
+ else
+ p->whiteness = 0xffff;
+ val = pwc_get_saturation(pdev);
+ if (val >= 0)
+ p->colour = val;
+ else
+ p->colour = 0xffff;
+ p->depth = 24;
+ p->palette = pdev->vpalette;
+ p->hue = 0xFFFF; /* N/A */
+ break;
+ }
+
+ case VIDIOCSPICT:
+ {
+ struct video_picture *p = arg;
+ /*
+ * FIXME: Suppose we are mid read
+ ANSWER: No problem: the firmware of the camera
+ can handle brightness/contrast/etc
+ changes at _any_ time, and the palette
+ is used exactly once in the uncompress
+ routine.
+ */
+ pwc_set_brightness(pdev, p->brightness);
+ pwc_set_contrast(pdev, p->contrast);
+ pwc_set_gamma(pdev, p->whiteness);
+ pwc_set_saturation(pdev, p->colour);
+ if (p->palette && p->palette != pdev->vpalette) {
+ switch (p->palette) {
+ case VIDEO_PALETTE_YUV420P:
+ case VIDEO_PALETTE_RAW:
+ pdev->vpalette = p->palette;
+ return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ }
+ break;
+ }
+
+ /* Window/size parameters */
+ case VIDIOCGWIN:
+ {
+ struct video_window *vw = arg;
+
+ vw->x = 0;
+ vw->y = 0;
+ vw->width = pdev->view.x;
+ vw->height = pdev->view.y;
+ vw->chromakey = 0;
+ vw->flags = (pdev->vframes << PWC_FPS_SHIFT) |
+ (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0);
+ break;
+ }
+
+ case VIDIOCSWIN:
+ {
+ struct video_window *vw = arg;
+ int fps, snapshot, ret;
+
+ fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT;
+ snapshot = vw->flags & PWC_FPS_SNAPSHOT;
+ if (fps == 0)
+ fps = pdev->vframes;
+ if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot)
+ return 0;
+ ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ /* We don't have overlay support (yet) */
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer *vb = arg;
+
+ memset(vb,0,sizeof(*vb));
+ break;
+ }
+
+ /* mmap() functions */
+ case VIDIOCGMBUF:
+ {
+ /* Tell the user program how much memory is needed for a mmap() */
+ struct video_mbuf *vm = arg;
+ int i;
+
+ memset(vm, 0, sizeof(*vm));
+ vm->size = default_mbufs * pdev->len_per_image;
+ vm->frames = default_mbufs; /* double buffering should be enough for most applications */
+ for (i = 0; i < default_mbufs; i++)
+ vm->offsets[i] = i * pdev->len_per_image;
+ break;
+ }
+
+ case VIDIOCMCAPTURE:
+ {
+ /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */
+ struct video_mmap *vm = arg;
+
+ Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format);
+ if (vm->frame < 0 || vm->frame >= default_mbufs)
+ return -EINVAL;
+
+ /* xawtv is nasty. It probes the available palettes
+ by setting a very small image size and trying
+ various palettes... The driver doesn't support
+ such small images, so I'm working around it.
+ */
+ if (vm->format)
+ {
+ switch (vm->format)
+ {
+ case VIDEO_PALETTE_YUV420P:
+ case VIDEO_PALETTE_RAW:
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ }
+
+ if ((vm->width != pdev->view.x || vm->height != pdev->view.y) &&
+ (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) {
+ int ret;
+
+ Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n");
+ ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
+ if (ret)
+ return ret;
+ } /* ... size mismatch */
+
+ /* FIXME: should we lock here? */
+ if (pdev->image_used[vm->frame])
+ return -EBUSY; /* buffer wasn't available. Bummer */
+ pdev->image_used[vm->frame] = 1;
+
+ /* Okay, we're done here. In the SYNC call we wait until a
+ frame comes available, then expand image into the given
+ buffer.
+ In contrast to the CPiA cam the Philips cams deliver a
+ constant stream, almost like a grabber card. Also,
+ we have separate buffers for the rawdata and the image,
+ meaning we can nearly always expand into the requested buffer.
+ */
+ Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n");
+ break;
+ }
+
+ case VIDIOCSYNC:
+ {
+ /* The doc says: "Whenever a buffer is used it should
+ call VIDIOCSYNC to free this frame up and continue."
+
+ The only odd thing about this whole procedure is
+ that MCAPTURE flags the buffer as "in use", and
+ SYNC immediately unmarks it, while it isn't
+ after SYNC that you know that the buffer actually
+ got filled! So you better not start a CAPTURE in
+ the same frame immediately (use double buffering).
+ This is not a problem for this cam, since it has
+ extra intermediate buffers, but a hardware
+ grabber card will then overwrite the buffer
+ you're working on.
+ */
+ int *mbuf = arg;
+ int ret;
+
+ Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf);
+
+ /* bounds check */
+ if (*mbuf < 0 || *mbuf >= default_mbufs)
+ return -EINVAL;
+ /* check if this buffer was requested anyway */
+ if (pdev->image_used[*mbuf] == 0)
+ return -EINVAL;
+
+ /* Add ourselves to the frame wait-queue.
+
+ FIXME: needs auditing for safety.
+ QUESTION: In what respect? I think that using the
+ frameq is safe now.
+ */
+ add_wait_queue(&pdev->frameq, &wait);
+ while (pdev->full_frames == NULL) {
+ if (pdev->error_status) {
+ remove_wait_queue(&pdev->frameq, &wait);
+ set_current_state(TASK_RUNNING);
+ return -pdev->error_status;
+ }
+
+ if (signal_pending(current)) {
+ remove_wait_queue(&pdev->frameq, &wait);
+ set_current_state(TASK_RUNNING);
+ return -ERESTARTSYS;
+ }
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ remove_wait_queue(&pdev->frameq, &wait);
+ set_current_state(TASK_RUNNING);
+
+ /* The frame is ready. Expand in the image buffer
+ requested by the user. I don't care if you
+ mmap() 5 buffers and request data in this order:
+ buffer 4 2 3 0 1 2 3 0 4 3 1 . . .
+ Grabber hardware may not be so forgiving.
+ */
+ Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n");
+ pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */
+ /* Decompress, etc */
+ ret = pwc_handle_frame(pdev);
+ pdev->image_used[*mbuf] = 0;
+ if (ret)
+ return -EFAULT;
+ break;
+ }
+
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+
+ strcpy(v->name, "Microphone");
+ v->audio = -1; /* unknown audio minor */
+ v->flags = 0;
+ v->mode = VIDEO_SOUND_MONO;
+ v->volume = 0;
+ v->bass = 0;
+ v->treble = 0;
+ v->balance = 0x8000;
+ v->step = 1;
+ break;
+ }
+
+ case VIDIOCSAUDIO:
+ {
+ /* Dummy: nothing can be set */
+ break;
+ }
+
+ case VIDIOCGUNIT:
+ {
+ struct video_unit *vu = arg;
+
+ vu->video = pdev->vdev->minor & 0x3F;
+ vu->audio = -1; /* not known yet */
+ vu->vbi = -1;
+ vu->radio = -1;
+ vu->teletext = -1;
+ break;
+ }
+ default:
+ return pwc_ioctl(pdev, cmd, arg);
+ } /* ..switch */
+ return 0;
+}
+
+static int pwc_video_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl);
+}
+
+
+static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = file->private_data;
+ struct pwc_device *pdev;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end-vma->vm_start;
+ unsigned long page, pos;
+
+ Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size);
+ pdev = vdev->priv;
+
+ vma->vm_flags |= VM_IO;
+
+ pos = (unsigned long)pdev->image_data;
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ return 0;
+}
+
+/***************************************************************************/
+/* USB functions */
+
+/* This function gets called when a new device is plugged in or the usb core
+ * is loaded.
+ */
+
+static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct pwc_device *pdev = NULL;
+ int vendor_id, product_id, type_id;
+ int i, hint;
+ int features = 0;
+ int video_nr = -1; /* default: use next available device */
+ char serial_number[30], *name;
+
+ /* Check if we can handle this device */
+ Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ intf->altsetting->desc.bInterfaceNumber);
+
+ /* the interfaces are probed one by one. We are only interested in the
+ video interface (0) now.
+ Interface 1 is the Audio Control, and interface 2 Audio itself.
+ */
+ if (intf->altsetting->desc.bInterfaceNumber > 0)
+ return -ENODEV;
+
+ vendor_id = le16_to_cpu(udev->descriptor.idVendor);
+ product_id = le16_to_cpu(udev->descriptor.idProduct);
+
+ if (vendor_id == 0x0471) {
+ switch (product_id) {
+ case 0x0302:
+ Info("Philips PCA645VC USB webcam detected.\n");
+ name = "Philips 645 webcam";
+ type_id = 645;
+ break;
+ case 0x0303:
+ Info("Philips PCA646VC USB webcam detected.\n");
+ name = "Philips 646 webcam";
+ type_id = 646;
+ break;
+ case 0x0304:
+ Info("Askey VC010 type 2 USB webcam detected.\n");
+ name = "Askey VC010 webcam";
+ type_id = 646;
+ break;
+ case 0x0307:
+ Info("Philips PCVC675K (Vesta) USB webcam detected.\n");
+ name = "Philips 675 webcam";
+ type_id = 675;
+ break;
+ case 0x0308:
+ Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
+ name = "Philips 680 webcam";
+ type_id = 680;
+ break;
+ case 0x030C:
+ Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
+ name = "Philips 690 webcam";
+ type_id = 690;
+ break;
+ case 0x0310:
+ Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
+ name = "Philips 730 webcam";
+ type_id = 730;
+ break;
+ case 0x0311:
+ Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
+ name = "Philips 740 webcam";
+ type_id = 740;
+ break;
+ case 0x0312:
+ Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
+ name = "Philips 750 webcam";
+ type_id = 750;
+ break;
+ case 0x0313:
+ Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
+ name = "Philips 720K/40 webcam";
+ type_id = 720;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x069A) {
+ switch(product_id) {
+ case 0x0001:
+ Info("Askey VC010 type 1 USB webcam detected.\n");
+ name = "Askey VC010 webcam";
+ type_id = 645;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x046d) {
+ switch(product_id) {
+ case 0x08b0:
+ Info("Logitech QuickCam Pro 3000 USB webcam detected.\n");
+ name = "Logitech QuickCam Pro 3000";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b1:
+ Info("Logitech QuickCam Notebook Pro USB webcam detected.\n");
+ name = "Logitech QuickCam Notebook Pro";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b2:
+ Info("Logitech QuickCam 4000 Pro USB webcam detected.\n");
+ name = "Logitech QuickCam Pro 4000";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b3:
+ Info("Logitech QuickCam Zoom USB webcam detected.\n");
+ name = "Logitech QuickCam Zoom";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08B4:
+ Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
+ name = "Logitech QuickCam Zoom";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b5:
+ Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
+ name = "Logitech QuickCam Orbit";
+ type_id = 740; /* CCD sensor */
+ features |= FEATURE_MOTOR_PANTILT;
+ break;
+ case 0x08b6:
+ case 0x08b7:
+ case 0x08b8:
+ Info("Logitech QuickCam detected (reserved ID).\n");
+ name = "Logitech QuickCam (res.)";
+ type_id = 730; /* Assuming CMOS */
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x055d) {
+ /* I don't know the difference between the C10 and the C30;
+ I suppose the difference is the sensor, but both cameras
+ work equally well with a type_id of 675
+ */
+ switch(product_id) {
+ case 0x9000:
+ Info("Samsung MPC-C10 USB webcam detected.\n");
+ name = "Samsung MPC-C10";
+ type_id = 675;
+ break;
+ case 0x9001:
+ Info("Samsung MPC-C30 USB webcam detected.\n");
+ name = "Samsung MPC-C30";
+ type_id = 675;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x041e) {
+ switch(product_id) {
+ case 0x400c:
+ Info("Creative Labs Webcam 5 detected.\n");
+ name = "Creative Labs Webcam 5";
+ type_id = 730;
+ break;
+ case 0x4011:
+ Info("Creative Labs Webcam Pro Ex detected.\n");
+ name = "Creative Labs Webcam Pro Ex";
+ type_id = 740;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x04cc) {
+ switch(product_id) {
+ case 0x8116:
+ Info("Sotec Afina Eye USB webcam detected.\n");
+ name = "Sotec Afina Eye";
+ type_id = 730;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x06be) {
+ switch(product_id) {
+ case 0x8116:
+ /* This is essentially the same cam as the Sotec Afina Eye */
+ Info("AME Co. Afina Eye USB webcam detected.\n");
+ name = "AME Co. Afina Eye";
+ type_id = 750;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+
+ }
+ else if (vendor_id == 0x0d81) {
+ switch(product_id) {
+ case 0x1900:
+ Info("Visionite VCS-UC300 USB webcam detected.\n");
+ name = "Visionite VCS-UC300";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x1910:
+ Info("Visionite VCS-UM100 USB webcam detected.\n");
+ name = "Visionite VCS-UM100";
+ type_id = 730; /* CMOS sensor */
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else
+ return -ENODEV; /* Not any of the know types; but the list keeps growing. */
+
+ memset(serial_number, 0, 30);
+ usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
+ Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number);
+
+ if (udev->descriptor.bNumConfigurations > 1)
+ Info("Warning: more than 1 configuration available.\n");
+
+ /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
+ pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL);
+ if (pdev == NULL) {
+ Err("Oops, could not allocate memory for pwc_device.\n");
+ return -ENOMEM;
+ }
+ pdev->type = type_id;
+ pdev->vsize = default_size;
+ pdev->vframes = default_fps;
+ strcpy(pdev->serial, serial_number);
+ pdev->features = features;
+ if (vendor_id == 0x046D && product_id == 0x08B5)
+ {
+ /* Logitech QuickCam Orbit
+ The ranges have been determined experimentally; they may differ from cam to cam.
+ Also, the exact ranges left-right and up-down are different for my cam
+ */
+ pdev->angle_range.pan_min = -7000;
+ pdev->angle_range.pan_max = 7000;
+ pdev->angle_range.tilt_min = -3000;
+ pdev->angle_range.tilt_max = 2500;
+ }
+
+ init_MUTEX(&pdev->modlock);
+ spin_lock_init(&pdev->ptrlock);
+
+ pdev->udev = udev;
+ init_waitqueue_head(&pdev->frameq);
+ pdev->vcompression = pwc_preferred_compression;
+
+ /* Allocate video_device structure */
+ pdev->vdev = video_device_alloc();
+ if (pdev->vdev == 0)
+ {
+ Err("Err, cannot allocate video_device struture. Failing probe.");
+ kfree(pdev);
+ return -ENOMEM;
+ }
+ memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template));
+ strcpy(pdev->vdev->name, name);
+ pdev->vdev->owner = THIS_MODULE;
+ video_set_drvdata(pdev->vdev, pdev);
+
+ pdev->release = le16_to_cpu(udev->descriptor.bcdDevice);
+ Trace(TRACE_PROBE, "Release: %04x\n", pdev->release);
+
+ /* Now search device_hint[] table for a match, so we can hint a node number. */
+ for (hint = 0; hint < MAX_DEV_HINTS; hint++) {
+ if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) &&
+ (device_hint[hint].pdev == NULL)) {
+ /* so far, so good... try serial number */
+ if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) {
+ /* match! */
+ video_nr = device_hint[hint].device_node;
+ Trace(TRACE_PROBE, "Found hint, will try to register as /dev/video%d\n", video_nr);
+ break;
+ }
+ }
+ }
+
+ pdev->vdev->release = video_device_release;
+ i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr);
+ if (i < 0) {
+ Err("Failed to register as video device (%d).\n", i);
+ video_device_release(pdev->vdev); /* Drip... drip... drip... */
+ kfree(pdev); /* Oops, no memory leaks please */
+ return -EIO;
+ }
+ else {
+ Info("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F);
+ }
+
+ /* occupy slot */
+ if (hint < MAX_DEV_HINTS)
+ device_hint[hint].pdev = pdev;
+
+ Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev);
+ usb_set_intfdata (intf, pdev);
+ return 0;
+}
+
+/* The user janked out the cable... */
+static void usb_pwc_disconnect(struct usb_interface *intf)
+{
+ struct pwc_device *pdev;
+ int hint;
+
+ lock_kernel();
+ pdev = usb_get_intfdata (intf);
+ usb_set_intfdata (intf, NULL);
+ if (pdev == NULL) {
+ Err("pwc_disconnect() Called without private pointer.\n");
+ goto disconnect_out;
+ }
+ if (pdev->udev == NULL) {
+ Err("pwc_disconnect() already called for %p\n", pdev);
+ goto disconnect_out;
+ }
+ if (pdev->udev != interface_to_usbdev(intf)) {
+ Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
+ goto disconnect_out;
+ }
+#ifdef PWC_MAGIC
+ if (pdev->magic != PWC_MAGIC) {
+ Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n");
+ goto disconnect_out;
+ }
+#endif
+
+ /* We got unplugged; this is signalled by an EPIPE error code */
+ if (pdev->vopen) {
+ Info("Disconnected while webcam is in use!\n");
+ pdev->error_status = EPIPE;
+ }
+
+ /* Alert waiting processes */
+ wake_up_interruptible(&pdev->frameq);
+ /* Wait until device is closed */
+ while (pdev->vopen)
+ schedule();
+ /* Device is now closed, so we can safely unregister it */
+ Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n");
+ video_unregister_device(pdev->vdev);
+
+ /* Free memory (don't set pdev to 0 just yet) */
+ kfree(pdev);
+
+disconnect_out:
+ /* search device_hint[] table if we occupy a slot, by any chance */
+ for (hint = 0; hint < MAX_DEV_HINTS; hint++)
+ if (device_hint[hint].pdev == pdev)
+ device_hint[hint].pdev = NULL;
+
+ unlock_kernel();
+}
+
+
+/* *grunt* We have to do atoi ourselves :-( */
+static int pwc_atoi(const char *s)
+{
+ int k = 0;
+
+ k = 0;
+ while (*s != '\0' && *s >= '0' && *s <= '9') {
+ k = 10 * k + (*s - '0');
+ s++;
+ }
+ return k;
+}
+
+
+/*
+ * Initialization code & module stuff
+ */
+
+static char size[10];
+static int fps = 0;
+static int fbufs = 0;
+static int mbufs = 0;
+static int trace = -1;
+static int compression = -1;
+static int leds[2] = { -1, -1 };
+static char *dev_hint[MAX_DEV_HINTS] = { };
+
+module_param_string(size, size, sizeof(size), 0);
+MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga");
+module_param(fps, int, 0000);
+MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30");
+module_param(fbufs, int, 0000);
+MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve");
+module_param(mbufs, int, 0000);
+MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers");
+module_param(trace, int, 0000);
+MODULE_PARM_DESC(trace, "For debugging purposes");
+module_param(power_save, bool, 0000);
+MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off");
+module_param(compression, int, 0000);
+MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)");
+module_param_array(leds, int, NULL, 0000);
+MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
+module_param_array(dev_hint, charp, NULL, 0000);
+MODULE_PARM_DESC(dev_hint, "Device node hints");
+
+MODULE_DESCRIPTION("Philips & OEM USB webcam driver");
+MODULE_AUTHOR("Luc Saillard <luc@saillard.org>");
+MODULE_LICENSE("GPL");
+
+static int __init usb_pwc_init(void)
+{
+ int i, sz;
+ char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
+
+ Info("Philips webcam module version " PWC_VERSION " loaded.\n");
+ Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n");
+ Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n");
+ Info("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n");
+
+ if (fps) {
+ if (fps < 4 || fps > 30) {
+ Err("Framerate out of bounds (4-30).\n");
+ return -EINVAL;
+ }
+ default_fps = fps;
+ Info("Default framerate set to %d.\n", default_fps);
+ }
+
+ if (size[0]) {
+ /* string; try matching with array */
+ for (sz = 0; sz < PSZ_MAX; sz++) {
+ if (!strcmp(sizenames[sz], size)) { /* Found! */
+ default_size = sz;
+ break;
+ }
+ }
+ if (sz == PSZ_MAX) {
+ Err("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n");
+ return -EINVAL;
+ }
+ Info("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y);
+ }
+ if (mbufs) {
+ if (mbufs < 1 || mbufs > MAX_IMAGES) {
+ Err("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES);
+ return -EINVAL;
+ }
+ default_mbufs = mbufs;
+ Info("Number of image buffers set to %d.\n", default_mbufs);
+ }
+ if (fbufs) {
+ if (fbufs < 2 || fbufs > MAX_FRAMES) {
+ Err("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES);
+ return -EINVAL;
+ }
+ default_fbufs = fbufs;
+ Info("Number of frame buffers set to %d.\n", default_fbufs);
+ }
+ if (trace >= 0) {
+ Info("Trace options: 0x%04x\n", trace);
+ pwc_trace = trace;
+ }
+ if (compression >= 0) {
+ if (compression > 3) {
+ Err("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n");
+ return -EINVAL;
+ }
+ pwc_preferred_compression = compression;
+ Info("Preferred compression set to %d.\n", pwc_preferred_compression);
+ }
+ if (power_save)
+ Info("Enabling power save on open/close.\n");
+ if (leds[0] >= 0)
+ led_on = leds[0];
+ if (leds[1] >= 0)
+ led_off = leds[1];
+
+ /* Big device node whoopla. Basically, it allows you to assign a
+ device node (/dev/videoX) to a camera, based on its type
+ & serial number. The format is [type[.serialnumber]:]node.
+
+ Any camera that isn't matched by these rules gets the next
+ available free device node.
+ */
+ for (i = 0; i < MAX_DEV_HINTS; i++) {
+ char *s, *colon, *dot;
+
+ /* This loop also initializes the array */
+ device_hint[i].pdev = NULL;
+ s = dev_hint[i];
+ if (s != NULL && *s != '\0') {
+ device_hint[i].type = -1; /* wildcard */
+ strcpy(device_hint[i].serial_number, "*");
+
+ /* parse string: chop at ':' & '/' */
+ colon = dot = s;
+ while (*colon != '\0' && *colon != ':')
+ colon++;
+ while (*dot != '\0' && *dot != '.')
+ dot++;
+ /* Few sanity checks */
+ if (*dot != '\0' && dot > colon) {
+ Err("Malformed camera hint: the colon must be after the dot.\n");
+ return -EINVAL;
+ }
+
+ if (*colon == '\0') {
+ /* No colon */
+ if (*dot != '\0') {
+ Err("Malformed camera hint: no colon + device node given.\n");
+ return -EINVAL;
+ }
+ else {
+ /* No type or serial number specified, just a number. */
+ device_hint[i].device_node = pwc_atoi(s);
+ }
+ }
+ else {
+ /* There's a colon, so we have at least a type and a device node */
+ device_hint[i].type = pwc_atoi(s);
+ device_hint[i].device_node = pwc_atoi(colon + 1);
+ if (*dot != '\0') {
+ /* There's a serial number as well */
+ int k;
+
+ dot++;
+ k = 0;
+ while (*dot != ':' && k < 29) {
+ device_hint[i].serial_number[k++] = *dot;
+ dot++;
+ }
+ device_hint[i].serial_number[k] = '\0';
+ }
+ }
+#if PWC_DEBUG
+ Debug("device_hint[%d]:\n", i);
+ Debug(" type : %d\n", device_hint[i].type);
+ Debug(" serial# : %s\n", device_hint[i].serial_number);
+ Debug(" node : %d\n", device_hint[i].device_node);
+#endif
+ }
+ else
+ device_hint[i].type = 0; /* not filled */
+ } /* ..for MAX_DEV_HINTS */
+
+ Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver);
+ return usb_register(&pwc_driver);
+}
+
+static void __exit usb_pwc_exit(void)
+{
+ Trace(TRACE_MODULE, "Deregistering driver.\n");
+ usb_deregister(&pwc_driver);
+ Info("Philips webcam module removed.\n");
+}
+
+module_init(usb_pwc_init);
+module_exit(usb_pwc_exit);
+
diff --git a/drivers/media/video/pwc/pwc-ioctl.h b/drivers/media/video/pwc/pwc-ioctl.h
new file mode 100644
index 000000000000..5f9cb08bc02e
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-ioctl.h
@@ -0,0 +1,292 @@
+#ifndef PWC_IOCTL_H
+#define PWC_IOCTL_H
+
+/* (C) 2001-2004 Nemosoft Unv.
+ (C) 2004 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* This is pwc-ioctl.h belonging to PWC 8.12.1
+ It contains structures and defines to communicate from user space
+ directly to the driver.
+ */
+
+/*
+ Changes
+ 2001/08/03 Alvarado Added ioctl constants to access methods for
+ changing white balance and red/blue gains
+ 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE
+ 2003/12/13 Nemosft Unv. Some modifications to make interfacing to
+ PWCX easier
+ */
+
+/* These are private ioctl() commands, specific for the Philips webcams.
+ They contain functions not found in other webcams, and settings not
+ specified in the Video4Linux API.
+
+ The #define names are built up like follows:
+ VIDIOC VIDeo IOCtl prefix
+ PWC Philps WebCam
+ G optional: Get
+ S optional: Set
+ ... the function
+ */
+
+
+ /* Enumeration of image sizes */
+#define PSZ_SQCIF 0x00
+#define PSZ_QSIF 0x01
+#define PSZ_QCIF 0x02
+#define PSZ_SIF 0x03
+#define PSZ_CIF 0x04
+#define PSZ_VGA 0x05
+#define PSZ_MAX 6
+
+
+/* The frame rate is encoded in the video_window.flags parameter using
+ the upper 16 bits, since some flags are defined nowadays. The following
+ defines provide a mask and shift to filter out this value.
+
+ In 'Snapshot' mode the camera freezes its automatic exposure and colour
+ balance controls.
+ */
+#define PWC_FPS_SHIFT 16
+#define PWC_FPS_MASK 0x00FF0000
+#define PWC_FPS_FRMASK 0x003F0000
+#define PWC_FPS_SNAPSHOT 0x00400000
+
+
+/* structure for transferring x & y coordinates */
+struct pwc_coord
+{
+ int x, y; /* guess what */
+ int size; /* size, or offset */
+};
+
+
+/* Used with VIDIOCPWCPROBE */
+struct pwc_probe
+{
+ char name[32];
+ int type;
+};
+
+struct pwc_serial
+{
+ char serial[30]; /* String with serial number. Contains terminating 0 */
+};
+
+/* pwc_whitebalance.mode values */
+#define PWC_WB_INDOOR 0
+#define PWC_WB_OUTDOOR 1
+#define PWC_WB_FL 2
+#define PWC_WB_MANUAL 3
+#define PWC_WB_AUTO 4
+
+/* Used with VIDIOCPWC[SG]AWB (Auto White Balance).
+ Set mode to one of the PWC_WB_* values above.
+ *red and *blue are the respective gains of these colour components inside
+ the camera; range 0..65535
+ When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read;
+ otherwise undefined.
+ 'read_red' and 'read_blue' are read-only.
+*/
+struct pwc_whitebalance
+{
+ int mode;
+ int manual_red, manual_blue; /* R/W */
+ int read_red, read_blue; /* R/O */
+};
+
+/*
+ 'control_speed' and 'control_delay' are used in automatic whitebalance mode,
+ and tell the camera how fast it should react to changes in lighting, and
+ with how much delay. Valid values are 0..65535.
+*/
+struct pwc_wb_speed
+{
+ int control_speed;
+ int control_delay;
+
+};
+
+/* Used with VIDIOCPWC[SG]LED */
+struct pwc_leds
+{
+ int led_on; /* Led on-time; range = 0..25000 */
+ int led_off; /* Led off-time; range = 0..25000 */
+};
+
+/* Image size (used with GREALSIZE) */
+struct pwc_imagesize
+{
+ int width;
+ int height;
+};
+
+/* Defines and structures for Motorized Pan & Tilt */
+#define PWC_MPT_PAN 0x01
+#define PWC_MPT_TILT 0x02
+#define PWC_MPT_TIMEOUT 0x04 /* for status */
+
+/* Set angles; when absolute != 0, the angle is absolute and the
+ driver calculates the relative offset for you. This can only
+ be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns
+ absolute angles.
+ */
+struct pwc_mpt_angles
+{
+ int absolute; /* write-only */
+ int pan; /* degrees * 100 */
+ int tilt; /* degress * 100 */
+};
+
+/* Range of angles of the camera, both horizontally and vertically.
+ */
+struct pwc_mpt_range
+{
+ int pan_min, pan_max; /* degrees * 100 */
+ int tilt_min, tilt_max;
+};
+
+struct pwc_mpt_status
+{
+ int status;
+ int time_pan;
+ int time_tilt;
+};
+
+
+/* This is used for out-of-kernel decompression. With it, you can get
+ all the necessary information to initialize and use the decompressor
+ routines in standalone applications.
+ */
+struct pwc_video_command
+{
+ int type; /* camera type (645, 675, 730, etc.) */
+ int release; /* release number */
+
+ int size; /* one of PSZ_* */
+ int alternate;
+ int command_len; /* length of USB video command */
+ unsigned char command_buf[13]; /* Actual USB video command */
+ int bandlength; /* >0 = compressed */
+ int frame_size; /* Size of one (un)compressed frame */
+};
+
+/* Flags for PWCX subroutines. Not all modules honour all flags. */
+#define PWCX_FLAG_PLANAR 0x0001
+#define PWCX_FLAG_BAYER 0x0008
+
+
+/* IOCTL definitions */
+
+ /* Restore user settings */
+#define VIDIOCPWCRUSER _IO('v', 192)
+ /* Save user settings */
+#define VIDIOCPWCSUSER _IO('v', 193)
+ /* Restore factory settings */
+#define VIDIOCPWCFACTORY _IO('v', 194)
+
+ /* You can manipulate the compression factor. A compression preference of 0
+ means use uncompressed modes when available; 1 is low compression, 2 is
+ medium and 3 is high compression preferred. Of course, the higher the
+ compression, the lower the bandwidth used but more chance of artefacts
+ in the image. The driver automatically chooses a higher compression when
+ the preferred mode is not available.
+ */
+ /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */
+#define VIDIOCPWCSCQUAL _IOW('v', 195, int)
+ /* Get preferred compression quality */
+#define VIDIOCPWCGCQUAL _IOR('v', 195, int)
+
+
+/* Retrieve serial number of camera */
+#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial)
+
+ /* This is a probe function; since so many devices are supported, it
+ becomes difficult to include all the names in programs that want to
+ check for the enhanced Philips stuff. So in stead, try this PROBE;
+ it returns a structure with the original name, and the corresponding
+ Philips type.
+ To use, fill the structure with zeroes, call PROBE and if that succeeds,
+ compare the name with that returned from VIDIOCGCAP; they should be the
+ same. If so, you can be assured it is a Philips (OEM) cam and the type
+ is valid.
+ */
+#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe)
+
+ /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */
+#define VIDIOCPWCSAGC _IOW('v', 200, int)
+ /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */
+#define VIDIOCPWCGAGC _IOR('v', 200, int)
+ /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */
+#define VIDIOCPWCSSHUTTER _IOW('v', 201, int)
+
+ /* Color compensation (Auto White Balance) */
+#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance)
+#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance)
+
+ /* Auto WB speed */
+#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed)
+#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed)
+
+ /* LEDs on/off/blink; int range 0..65535 */
+#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds)
+#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds)
+
+ /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */
+#define VIDIOCPWCSCONTOUR _IOW('v', 206, int)
+#define VIDIOCPWCGCONTOUR _IOR('v', 206, int)
+
+ /* Backlight compensation; 0 = off, otherwise on */
+#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int)
+#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int)
+
+ /* Flickerless mode; = 0 off, otherwise on */
+#define VIDIOCPWCSFLICKER _IOW('v', 208, int)
+#define VIDIOCPWCGFLICKER _IOR('v', 208, int)
+
+ /* Dynamic noise reduction; 0 off, 3 = high noise reduction */
+#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int)
+#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int)
+
+ /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */
+#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize)
+
+ /* Motorized pan & tilt functions */
+#define VIDIOCPWCMPTRESET _IOW('v', 211, int)
+#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range)
+#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles)
+#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles)
+#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status)
+
+ /* Get the USB set-video command; needed for initializing libpwcx */
+#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command)
+struct pwc_table_init_buffer {
+ int len;
+ char *buffer;
+
+};
+#define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer)
+
+#endif
diff --git a/drivers/media/video/pwc/pwc-kiara.c b/drivers/media/video/pwc/pwc-kiara.c
new file mode 100644
index 000000000000..c498c68bace1
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-kiara.c
@@ -0,0 +1,318 @@
+/* Linux driver for Philips webcam
+ (C) 2004 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+/* This tables contains entries for the 730/740/750 (Kiara) camera, with
+ 4 different qualities (no compression, low, medium, high).
+ It lists the bandwidth requirements for said mode by its alternate interface
+ number. An alternate of 0 means that the mode is unavailable.
+
+ There are 6 * 4 * 4 entries:
+ 6 different resolutions subqcif, qsif, qcif, sif, cif, vga
+ 6 framerates: 5, 10, 15, 20, 25, 30
+ 4 compression modi: none, low, medium, high
+
+ When an uncompressed mode is not available, the next available compressed mode
+ will be chosen (unless the decompressor is absent). Sometimes there are only
+ 1 or 2 compressed modes available; in that case entries are duplicated.
+*/
+
+
+#include "pwc-kiara.h"
+#include "pwc-uncompress.h"
+
+const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] =
+{
+ /* SQCIF */
+ {
+ /* 5 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ },
+ /* QSIF */
+ {
+ /* 5 fps */
+ {
+ {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
+ {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
+ {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
+ {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
+ },
+ /* 10 fps */
+ {
+ {2, 291, 0, {0x1C, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0x01, 0x80}},
+ {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}},
+ {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}},
+ {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}},
+ },
+ /* 15 fps */
+ {
+ {3, 437, 0, {0x1B, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x01, 0x80}},
+ {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}},
+ {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}},
+ {1, 192, 420, {0x13, 0xF4, 0x30, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x18, 0xC0, 0x00, 0x80}},
+ },
+ /* 20 fps */
+ {
+ {4, 589, 0, {0x1A, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4D, 0x02, 0x80}},
+ {3, 448, 730, {0x12, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xC0, 0x01, 0x80}},
+ {2, 292, 476, {0x12, 0xF4, 0x30, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0x01, 0x80}},
+ {1, 192, 312, {0x12, 0xF4, 0x50, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0x00, 0x80}},
+ },
+ /* 25 fps */
+ {
+ {5, 703, 0, {0x19, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x02, 0x80}},
+ {3, 447, 610, {0x11, 0xF4, 0x30, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x28, 0xBF, 0x01, 0x80}},
+ {2, 292, 398, {0x11, 0xF4, 0x50, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x28, 0x24, 0x01, 0x80}},
+ {1, 193, 262, {0x11, 0xF4, 0x50, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x28, 0xC1, 0x00, 0x80}},
+ },
+ /* 30 fps */
+ {
+ {8, 874, 0, {0x18, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x6A, 0x03, 0x80}},
+ {5, 704, 730, {0x10, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x28, 0xC0, 0x02, 0x80}},
+ {3, 448, 492, {0x10, 0xF4, 0x30, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x28, 0xC0, 0x01, 0x80}},
+ {2, 292, 320, {0x10, 0xF4, 0x50, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x28, 0x24, 0x01, 0x80}},
+ },
+ },
+ /* QCIF */
+ {
+ /* 5 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ },
+ /* SIF */
+ {
+ /* 5 fps */
+ {
+ {4, 582, 0, {0x0D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x02, 0x80}},
+ {3, 387, 1276, {0x05, 0xF4, 0x30, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x01, 0x80}},
+ {2, 291, 960, {0x05, 0xF4, 0x30, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0x01, 0x80}},
+ {1, 191, 630, {0x05, 0xF4, 0x50, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x18, 0xBF, 0x00, 0x80}},
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {6, 775, 1278, {0x04, 0xF4, 0x30, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x03, 0x80}},
+ {3, 447, 736, {0x04, 0xF4, 0x30, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x28, 0xBF, 0x01, 0x80}},
+ {2, 292, 480, {0x04, 0xF4, 0x70, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x28, 0x24, 0x01, 0x80}},
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {9, 955, 1050, {0x03, 0xF4, 0x30, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x03, 0x80}},
+ {4, 592, 650, {0x03, 0xF4, 0x30, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x50, 0x02, 0x80}},
+ {3, 448, 492, {0x03, 0xF4, 0x50, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x38, 0xC0, 0x01, 0x80}},
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {9, 958, 782, {0x02, 0xF4, 0x30, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x03, 0x80}},
+ {5, 703, 574, {0x02, 0xF4, 0x50, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x02, 0x80}},
+ {3, 446, 364, {0x02, 0xF4, 0x90, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x38, 0xBE, 0x01, 0x80}},
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {9, 958, 654, {0x01, 0xF4, 0x30, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x03, 0x80}},
+ {6, 776, 530, {0x01, 0xF4, 0x50, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x03, 0x80}},
+ {4, 592, 404, {0x01, 0xF4, 0x70, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x48, 0x50, 0x02, 0x80}},
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {9, 957, 526, {0x00, 0xF4, 0x50, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x03, 0x80}},
+ {6, 775, 426, {0x00, 0xF4, 0x70, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x03, 0x80}},
+ {4, 590, 324, {0x00, 0x7A, 0x88, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x50, 0x4E, 0x02, 0x80}},
+ },
+ },
+ /* CIF */
+ {
+ /* 5 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ },
+ /* VGA */
+ {
+ /* 5 fps */
+ {
+ {0, },
+ {6, 773, 1272, {0x25, 0xF4, 0x30, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}},
+ {4, 592, 976, {0x25, 0xF4, 0x50, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x02, 0x80}},
+ {3, 448, 738, {0x25, 0xF4, 0x90, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x01, 0x80}},
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {9, 956, 788, {0x24, 0xF4, 0x70, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x03, 0x80}},
+ {6, 776, 640, {0x24, 0xF4, 0xB0, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x03, 0x80}},
+ {4, 592, 488, {0x24, 0x7A, 0xE8, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x02, 0x80}},
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}},
+ {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}},
+ {8, 895, 492, {0x23, 0x7A, 0xE8, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x03, 0x80}},
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ },
+};
+
diff --git a/drivers/media/video/pwc/pwc-kiara.h b/drivers/media/video/pwc/pwc-kiara.h
new file mode 100644
index 000000000000..12929abbb1f0
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-kiara.h
@@ -0,0 +1,45 @@
+/* Linux driver for Philips webcam
+ (C) 2004 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* Entries for the Kiara (730/740/750) camera */
+
+#ifndef PWC_KIARA_H
+#define PWC_KIARA_H
+
+#include "pwc-ioctl.h"
+
+struct Kiara_table_entry
+{
+ char alternate; /* USB alternate interface */
+ unsigned short packetsize; /* Normal packet size */
+ unsigned short bandlength; /* Bandlength when decompressing */
+ unsigned char mode[12]; /* precomputed mode settings for cam */
+};
+
+const extern struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4];
+const extern unsigned int KiaraRomTable[8][2][16][8];
+
+#endif
+
+
diff --git a/drivers/media/video/pwc/pwc-misc.c b/drivers/media/video/pwc/pwc-misc.c
new file mode 100644
index 000000000000..b7a4bd3524c7
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-misc.c
@@ -0,0 +1,140 @@
+/* Linux driver for Philips webcam
+ Various miscellaneous functions and tables.
+ (C) 1999-2003 Nemosoft Unv.
+ (C) 2004 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/slab.h>
+
+#include "pwc.h"
+
+struct pwc_coord pwc_image_sizes[PSZ_MAX] =
+{
+ { 128, 96, 0 },
+ { 160, 120, 0 },
+ { 176, 144, 0 },
+ { 320, 240, 0 },
+ { 352, 288, 0 },
+ { 640, 480, 0 },
+};
+
+/* x,y -> PSZ_ */
+int pwc_decode_size(struct pwc_device *pdev, int width, int height)
+{
+ int i, find;
+
+ /* Make sure we don't go beyond our max size.
+ NB: we have different limits for RAW and normal modes. In case
+ you don't have the decompressor loaded or use RAW mode,
+ the maximum viewable size is smaller.
+ */
+ if (pdev->vpalette == VIDEO_PALETTE_RAW)
+ {
+ if (width > pdev->abs_max.x || height > pdev->abs_max.y)
+ {
+ Debug("VIDEO_PALETTE_RAW: going beyond abs_max.\n");
+ return -1;
+ }
+ }
+ else
+ {
+ if (width > pdev->view_max.x || height > pdev->view_max.y)
+ {
+ Debug("VIDEO_PALETTE_ not RAW: going beyond view_max.\n");
+ return -1;
+ }
+ }
+
+ /* Find the largest size supported by the camera that fits into the
+ requested size.
+ */
+ find = -1;
+ for (i = 0; i < PSZ_MAX; i++) {
+ if (pdev->image_mask & (1 << i)) {
+ if (pwc_image_sizes[i].x <= width && pwc_image_sizes[i].y <= height)
+ find = i;
+ }
+ }
+ return find;
+}
+
+/* initialize variables depending on type and decompressor*/
+void pwc_construct(struct pwc_device *pdev)
+{
+ switch(pdev->type) {
+ case 645:
+ case 646:
+ pdev->view_min.x = 128;
+ pdev->view_min.y = 96;
+ pdev->view_max.x = 352;
+ pdev->view_max.y = 288;
+ pdev->abs_max.x = 352;
+ pdev->abs_max.y = 288;
+ pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF;
+ pdev->vcinterface = 2;
+ pdev->vendpoint = 4;
+ pdev->frame_header_size = 0;
+ pdev->frame_trailer_size = 0;
+ break;
+ case 675:
+ case 680:
+ case 690:
+ pdev->view_min.x = 128;
+ pdev->view_min.y = 96;
+ /* Anthill bug #38: PWC always reports max size, even without PWCX */
+ pdev->view_max.x = 640;
+ pdev->view_max.y = 480;
+ pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA;
+ pdev->abs_max.x = 640;
+ pdev->abs_max.y = 480;
+ pdev->vcinterface = 3;
+ pdev->vendpoint = 4;
+ pdev->frame_header_size = 0;
+ pdev->frame_trailer_size = 0;
+ break;
+ case 720:
+ case 730:
+ case 740:
+ case 750:
+ pdev->view_min.x = 160;
+ pdev->view_min.y = 120;
+ pdev->view_max.x = 640;
+ pdev->view_max.y = 480;
+ pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA;
+ pdev->abs_max.x = 640;
+ pdev->abs_max.y = 480;
+ pdev->vcinterface = 3;
+ pdev->vendpoint = 5;
+ pdev->frame_header_size = TOUCAM_HEADER_SIZE;
+ pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE;
+ break;
+ }
+ Debug("type = %d\n",pdev->type);
+ pdev->vpalette = VIDEO_PALETTE_YUV420P; /* default */
+ pdev->view_min.size = pdev->view_min.x * pdev->view_min.y;
+ pdev->view_max.size = pdev->view_max.x * pdev->view_max.y;
+ /* length of image, in YUV format; always allocate enough memory. */
+ pdev->len_per_image = (pdev->abs_max.x * pdev->abs_max.y * 3) / 2;
+}
+
+
diff --git a/drivers/media/video/pwc/pwc-nala.h b/drivers/media/video/pwc/pwc-nala.h
new file mode 100644
index 000000000000..e6c5cb69d03b
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-nala.h
@@ -0,0 +1,66 @@
+ /* SQCIF */
+ {
+ {0, 0, {0x04, 0x01, 0x03}},
+ {8, 0, {0x05, 0x01, 0x03}},
+ {7, 0, {0x08, 0x01, 0x03}},
+ {7, 0, {0x0A, 0x01, 0x03}},
+ {6, 0, {0x0C, 0x01, 0x03}},
+ {5, 0, {0x0F, 0x01, 0x03}},
+ {4, 0, {0x14, 0x01, 0x03}},
+ {3, 0, {0x18, 0x01, 0x03}},
+ },
+ /* QSIF */
+ {
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ },
+ /* QCIF */
+ {
+ {0, 0, {0x04, 0x01, 0x02}},
+ {8, 0, {0x05, 0x01, 0x02}},
+ {7, 0, {0x08, 0x01, 0x02}},
+ {6, 0, {0x0A, 0x01, 0x02}},
+ {5, 0, {0x0C, 0x01, 0x02}},
+ {4, 0, {0x0F, 0x01, 0x02}},
+ {1, 0, {0x14, 0x01, 0x02}},
+ {1, 0, {0x18, 0x01, 0x02}},
+ },
+ /* SIF */
+ {
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ },
+ /* CIF */
+ {
+ {4, 0, {0x04, 0x01, 0x01}},
+ {7, 1, {0x05, 0x03, 0x01}},
+ {6, 1, {0x08, 0x03, 0x01}},
+ {4, 1, {0x0A, 0x03, 0x01}},
+ {3, 1, {0x0C, 0x03, 0x01}},
+ {2, 1, {0x0F, 0x03, 0x01}},
+ {0},
+ {0},
+ },
+ /* VGA */
+ {
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ },
diff --git a/drivers/media/video/pwc/pwc-timon.c b/drivers/media/video/pwc/pwc-timon.c
new file mode 100644
index 000000000000..dee967173d6c
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-timon.c
@@ -0,0 +1,316 @@
+/* Linux driver for Philips webcam
+ (C) 2004 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+/* This tables contains entries for the 675/680/690 (Timon) camera, with
+ 4 different qualities (no compression, low, medium, high).
+ It lists the bandwidth requirements for said mode by its alternate interface
+ number. An alternate of 0 means that the mode is unavailable.
+
+ There are 6 * 4 * 4 entries:
+ 6 different resolutions subqcif, qsif, qcif, sif, cif, vga
+ 6 framerates: 5, 10, 15, 20, 25, 30
+ 4 compression modi: none, low, medium, high
+
+ When an uncompressed mode is not available, the next available compressed mode
+ will be chosen (unless the decompressor is absent). Sometimes there are only
+ 1 or 2 compressed modes available; in that case entries are duplicated.
+*/
+
+#include "pwc-timon.h"
+
+const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] =
+{
+ /* SQCIF */
+ {
+ /* 5 fps */
+ {
+ {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
+ {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
+ {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
+ {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
+ },
+ /* 10 fps */
+ {
+ {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
+ {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
+ {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
+ {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
+ },
+ /* 15 fps */
+ {
+ {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
+ {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
+ {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
+ {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
+ },
+ /* 20 fps */
+ {
+ {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
+ {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
+ {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
+ {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
+ },
+ /* 25 fps */
+ {
+ {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
+ {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
+ {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
+ {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
+ },
+ /* 30 fps */
+ {
+ {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
+ {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
+ {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
+ {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
+ },
+ },
+ /* QSIF */
+ {
+ /* 5 fps */
+ {
+ {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
+ {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
+ {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
+ {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
+ },
+ /* 10 fps */
+ {
+ {2, 291, 0, {0x2C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0xA1, 0xC0, 0x02}},
+ {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
+ {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
+ {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
+ },
+ /* 15 fps */
+ {
+ {3, 437, 0, {0x2B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x6D, 0xC0, 0x02}},
+ {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
+ {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
+ {1, 191, 420, {0x2B, 0xF4, 0x0D, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
+ },
+ /* 20 fps */
+ {
+ {4, 588, 0, {0x2A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4C, 0x52, 0xC0, 0x02}},
+ {3, 447, 730, {0x2A, 0xF4, 0x05, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
+ {2, 292, 476, {0x2A, 0xF4, 0x0D, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0xA1, 0xC0, 0x02}},
+ {1, 192, 312, {0x2A, 0xF4, 0x1D, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}},
+ },
+ /* 25 fps */
+ {
+ {5, 703, 0, {0x29, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x42, 0xC0, 0x02}},
+ {3, 447, 610, {0x29, 0xF4, 0x05, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
+ {2, 292, 398, {0x29, 0xF4, 0x0D, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}},
+ {1, 192, 262, {0x29, 0xF4, 0x25, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}},
+ },
+ /* 30 fps */
+ {
+ {8, 873, 0, {0x28, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x69, 0x37, 0xC0, 0x02}},
+ {5, 704, 774, {0x28, 0xF4, 0x05, 0x18, 0x21, 0x17, 0x59, 0x0F, 0x18, 0xC0, 0x42, 0xC0, 0x02}},
+ {3, 448, 492, {0x28, 0xF4, 0x05, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x18, 0xC0, 0x69, 0xC0, 0x02}},
+ {2, 291, 320, {0x28, 0xF4, 0x1D, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}},
+ },
+ },
+ /* QCIF */
+ {
+ /* 5 fps */
+ {
+ {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
+ {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
+ {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
+ {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
+ },
+ /* 10 fps */
+ {
+ {3, 385, 0, {0x0C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x81, 0x79, 0xC0, 0x02}},
+ {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
+ {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
+ {1, 194, 532, {0x0C, 0xF4, 0x05, 0x10, 0x9A, 0x0F, 0xBE, 0x1B, 0x08, 0xC2, 0xF0, 0xC0, 0x02}},
+ },
+ /* 15 fps */
+ {
+ {4, 577, 0, {0x0B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x52, 0xC0, 0x02}},
+ {3, 447, 818, {0x0B, 0xF4, 0x05, 0x19, 0x89, 0x18, 0xAD, 0x0F, 0x10, 0xBF, 0x69, 0xC0, 0x02}},
+ {2, 292, 534, {0x0B, 0xF4, 0x05, 0x10, 0xA3, 0x0F, 0xC7, 0x19, 0x10, 0x24, 0xA1, 0xC0, 0x02}},
+ {1, 195, 356, {0x0B, 0xF4, 0x15, 0x0B, 0x11, 0x0A, 0x35, 0x1E, 0x10, 0xC3, 0xF0, 0xC0, 0x02}},
+ },
+ /* 20 fps */
+ {
+ {6, 776, 0, {0x0A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x08, 0x3F, 0xC0, 0x02}},
+ {4, 591, 804, {0x0A, 0xF4, 0x05, 0x19, 0x1E, 0x18, 0x42, 0x0F, 0x18, 0x4F, 0x4E, 0xC0, 0x02}},
+ {3, 447, 608, {0x0A, 0xF4, 0x05, 0x12, 0xFD, 0x12, 0x21, 0x15, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
+ {2, 291, 396, {0x0A, 0xF4, 0x15, 0x0C, 0x5E, 0x0B, 0x82, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}},
+ },
+ /* 25 fps */
+ {
+ {9, 928, 0, {0x09, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA0, 0x33, 0xC0, 0x02}},
+ {5, 703, 800, {0x09, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x10, 0x18, 0xBF, 0x42, 0xC0, 0x02}},
+ {3, 447, 508, {0x09, 0xF4, 0x0D, 0x0F, 0xD2, 0x0E, 0xF6, 0x1B, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
+ {2, 292, 332, {0x09, 0xF4, 0x1D, 0x0A, 0x5A, 0x09, 0x7E, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}},
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {9, 956, 876, {0x08, 0xF4, 0x05, 0x1B, 0x58, 0x1A, 0x7C, 0x0E, 0x20, 0xBC, 0x33, 0x10, 0x02}},
+ {4, 592, 542, {0x08, 0xF4, 0x05, 0x10, 0xE4, 0x10, 0x08, 0x17, 0x20, 0x50, 0x4E, 0x10, 0x02}},
+ {2, 291, 266, {0x08, 0xF4, 0x25, 0x08, 0x48, 0x07, 0x6C, 0x1E, 0x20, 0x23, 0xA1, 0x10, 0x02}},
+ },
+ },
+ /* SIF */
+ {
+ /* 5 fps */
+ {
+ {4, 582, 0, {0x35, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x52, 0x60, 0x02}},
+ {3, 387, 1276, {0x35, 0xF4, 0x05, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x79, 0x60, 0x02}},
+ {2, 291, 960, {0x35, 0xF4, 0x0D, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0xA1, 0x60, 0x02}},
+ {1, 191, 630, {0x35, 0xF4, 0x1D, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x08, 0xBF, 0xF4, 0x60, 0x02}},
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {6, 775, 1278, {0x34, 0xF4, 0x05, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x3F, 0x10, 0x02}},
+ {3, 447, 736, {0x34, 0xF4, 0x15, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x18, 0xBF, 0x69, 0x10, 0x02}},
+ {2, 291, 480, {0x34, 0xF4, 0x2D, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x18, 0x23, 0xA1, 0x10, 0x02}},
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {9, 955, 1050, {0x33, 0xF4, 0x05, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x33, 0x10, 0x02}},
+ {4, 591, 650, {0x33, 0xF4, 0x15, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x4F, 0x4E, 0x10, 0x02}},
+ {3, 448, 492, {0x33, 0xF4, 0x25, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x28, 0xC0, 0x69, 0x10, 0x02}},
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {9, 958, 782, {0x32, 0xF4, 0x0D, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x33, 0xD0, 0x02}},
+ {5, 703, 574, {0x32, 0xF4, 0x1D, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x42, 0xD0, 0x02}},
+ {3, 446, 364, {0x32, 0xF4, 0x3D, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x30, 0xBE, 0x69, 0xD0, 0x02}},
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {9, 958, 654, {0x31, 0xF4, 0x15, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x33, 0x90, 0x02}},
+ {6, 776, 530, {0x31, 0xF4, 0x25, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x3F, 0x90, 0x02}},
+ {4, 592, 404, {0x31, 0xF4, 0x35, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x38, 0x50, 0x4E, 0x90, 0x02}},
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {9, 957, 526, {0x30, 0xF4, 0x25, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x33, 0x60, 0x02}},
+ {6, 775, 426, {0x30, 0xF4, 0x35, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x3F, 0x60, 0x02}},
+ {4, 590, 324, {0x30, 0x7A, 0x4B, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x40, 0x4E, 0x52, 0x60, 0x02}},
+ },
+ },
+ /* CIF */
+ {
+ /* 5 fps */
+ {
+ {6, 771, 0, {0x15, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x3F, 0x80, 0x02}},
+ {4, 465, 1278, {0x15, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x03, 0x18, 0xD1, 0x65, 0x80, 0x02}},
+ {2, 291, 800, {0x15, 0xF4, 0x15, 0x18, 0xF4, 0x17, 0x3C, 0x05, 0x18, 0x23, 0xA1, 0x80, 0x02}},
+ {1, 193, 528, {0x15, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x18, 0xC1, 0xF4, 0x80, 0x02}},
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {9, 932, 1278, {0x14, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x04, 0x30, 0xA4, 0x33, 0x10, 0x02}},
+ {4, 591, 812, {0x14, 0xF4, 0x15, 0x19, 0x56, 0x17, 0x9E, 0x06, 0x28, 0x4F, 0x4E, 0x10, 0x02}},
+ {2, 291, 400, {0x14, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x28, 0x23, 0xA1, 0x10, 0x02}},
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {9, 956, 876, {0x13, 0xF4, 0x0D, 0x1B, 0x58, 0x19, 0xA0, 0x05, 0x38, 0xBC, 0x33, 0x60, 0x02}},
+ {5, 703, 644, {0x13, 0xF4, 0x1D, 0x14, 0x1C, 0x12, 0x64, 0x08, 0x38, 0xBF, 0x42, 0x60, 0x02}},
+ {3, 448, 410, {0x13, 0xF4, 0x3D, 0x0C, 0xC4, 0x0B, 0x0C, 0x0E, 0x38, 0xC0, 0x69, 0x60, 0x02}},
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {9, 956, 650, {0x12, 0xF4, 0x1D, 0x14, 0x4A, 0x12, 0x92, 0x09, 0x48, 0xBC, 0x33, 0x10, 0x03}},
+ {6, 776, 528, {0x12, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x40, 0x08, 0x3F, 0x10, 0x03}},
+ {4, 591, 402, {0x12, 0xF4, 0x3D, 0x0C, 0x8F, 0x0A, 0xD7, 0x0E, 0x40, 0x4F, 0x4E, 0x10, 0x03}},
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {9, 956, 544, {0x11, 0xF4, 0x25, 0x10, 0xF4, 0x0F, 0x3C, 0x0A, 0x48, 0xBC, 0x33, 0xC0, 0x02}},
+ {7, 840, 478, {0x11, 0xF4, 0x2D, 0x0E, 0xEB, 0x0D, 0x33, 0x0B, 0x48, 0x48, 0x3B, 0xC0, 0x02}},
+ {5, 703, 400, {0x11, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x48, 0xBF, 0x42, 0xC0, 0x02}},
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {9, 956, 438, {0x10, 0xF4, 0x35, 0x0D, 0xAC, 0x0B, 0xF4, 0x0D, 0x50, 0xBC, 0x33, 0x10, 0x02}},
+ {7, 838, 384, {0x10, 0xF4, 0x45, 0x0B, 0xFD, 0x0A, 0x45, 0x0F, 0x50, 0x46, 0x3B, 0x10, 0x02}},
+ {6, 773, 354, {0x10, 0x7A, 0x4B, 0x0B, 0x0C, 0x09, 0x80, 0x10, 0x50, 0x05, 0x3F, 0x10, 0x02}},
+ },
+ },
+ /* VGA */
+ {
+ /* 5 fps */
+ {
+ {0, },
+ {6, 773, 1272, {0x1D, 0xF4, 0x15, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x3F, 0x10, 0x02}},
+ {4, 592, 976, {0x1D, 0xF4, 0x25, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x4E, 0x10, 0x02}},
+ {3, 448, 738, {0x1D, 0xF4, 0x3D, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x69, 0x10, 0x02}},
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {9, 956, 788, {0x1C, 0xF4, 0x35, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x33, 0x10, 0x02}},
+ {6, 776, 640, {0x1C, 0x7A, 0x53, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x3F, 0x10, 0x02}},
+ {4, 592, 488, {0x1C, 0x7A, 0x6B, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x4E, 0x10, 0x02}},
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}},
+ {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}},
+ {8, 895, 492, {0x1B, 0x7A, 0x6B, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x37, 0x80, 0x02}},
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ },
+};
+
diff --git a/drivers/media/video/pwc/pwc-timon.h b/drivers/media/video/pwc/pwc-timon.h
new file mode 100644
index 000000000000..a86b3782a081
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-timon.h
@@ -0,0 +1,61 @@
+/* Linux driver for Philips webcam
+ (C) 2004 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+
+/* This tables contains entries for the 675/680/690 (Timon) camera, with
+ 4 different qualities (no compression, low, medium, high).
+ It lists the bandwidth requirements for said mode by its alternate interface
+ number. An alternate of 0 means that the mode is unavailable.
+
+ There are 6 * 4 * 4 entries:
+ 6 different resolutions subqcif, qsif, qcif, sif, cif, vga
+ 6 framerates: 5, 10, 15, 20, 25, 30
+ 4 compression modi: none, low, medium, high
+
+ When an uncompressed mode is not available, the next available compressed mode
+ will be chosen (unless the decompressor is absent). Sometimes there are only
+ 1 or 2 compressed modes available; in that case entries are duplicated.
+*/
+
+#ifndef PWC_TIMON_H
+#define PWC_TIMON_H
+
+#include "pwc-ioctl.h"
+
+struct Timon_table_entry
+{
+ char alternate; /* USB alternate interface */
+ unsigned short packetsize; /* Normal packet size */
+ unsigned short bandlength; /* Bandlength when decompressing */
+ unsigned char mode[13]; /* precomputed mode settings for cam */
+};
+
+const extern struct Timon_table_entry Timon_table[PSZ_MAX][6][4];
+const extern unsigned int TimonRomTable [16][2][16][8];
+
+
+#endif
+
+
diff --git a/drivers/media/video/pwc/pwc-uncompress.c b/drivers/media/video/pwc/pwc-uncompress.c
new file mode 100644
index 000000000000..ef4204eab6c4
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-uncompress.c
@@ -0,0 +1,146 @@
+/* Linux driver for Philips webcam
+ Decompression frontend.
+ (C) 1999-2003 Nemosoft Unv.
+ (C) 2004 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <asm/current.h>
+#include <asm/types.h>
+
+#include "pwc.h"
+#include "pwc-uncompress.h"
+
+int pwc_decompress(struct pwc_device *pdev)
+{
+ struct pwc_frame_buf *fbuf;
+ int n, line, col, stride;
+ void *yuv, *image;
+ u16 *src;
+ u16 *dsty, *dstu, *dstv;
+
+ if (pdev == NULL)
+ return -EFAULT;
+#if defined(__KERNEL__) && defined(PWC_MAGIC)
+ if (pdev->magic != PWC_MAGIC) {
+ Err("pwc_decompress(): magic failed.\n");
+ return -EFAULT;
+ }
+#endif
+
+ fbuf = pdev->read_frame;
+ if (fbuf == NULL)
+ return -EFAULT;
+ image = pdev->image_ptr[pdev->fill_image];
+ if (!image)
+ return -EFAULT;
+
+ yuv = fbuf->data + pdev->frame_header_size; /* Skip header */
+
+ /* Raw format; that's easy... */
+ if (pdev->vpalette == VIDEO_PALETTE_RAW)
+ {
+ memcpy(image, yuv, pdev->frame_size);
+ return 0;
+ }
+
+ if (pdev->vbandlength == 0) {
+ /* Uncompressed mode. We copy the data into the output buffer,
+ using the viewport size (which may be larger than the image
+ size). Unfortunately we have to do a bit of byte stuffing
+ to get the desired output format/size.
+ */
+ /*
+ * We do some byte shuffling here to go from the
+ * native format to YUV420P.
+ */
+ src = (u16 *)yuv;
+ n = pdev->view.x * pdev->view.y;
+
+ /* offset in Y plane */
+ stride = pdev->view.x * pdev->offset.y + pdev->offset.x;
+ dsty = (u16 *)(image + stride);
+
+ /* offsets in U/V planes */
+ stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2;
+ dstu = (u16 *)(image + n + stride);
+ dstv = (u16 *)(image + n + n / 4 + stride);
+
+ /* increment after each line */
+ stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */
+
+ for (line = 0; line < pdev->image.y; line++) {
+ for (col = 0; col < pdev->image.x; col += 4) {
+ *dsty++ = *src++;
+ *dsty++ = *src++;
+ if (line & 1)
+ *dstv++ = *src++;
+ else
+ *dstu++ = *src++;
+ }
+ dsty += stride;
+ if (line & 1)
+ dstv += (stride >> 1);
+ else
+ dstu += (stride >> 1);
+ }
+ }
+ else {
+ /* Compressed; the decompressor routines will write the data
+ in planar format immediately.
+ */
+ int flags;
+
+ flags = PWCX_FLAG_PLANAR;
+ if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot)
+ {
+ printk(KERN_ERR "pwc: Mode Bayer is not supported for now\n");
+ flags |= PWCX_FLAG_BAYER;
+ return -ENXIO; /* No such device or address: missing decompressor */
+ }
+
+#if 0
+ switch (pdev->type)
+ {
+ case 675:
+ case 680:
+ case 690:
+ case 720:
+ case 730:
+ case 740:
+ case 750:
+ pwc_dec23_decompress(&pdev->image, &pdev->view,
+ &pdev->offset, yuv, image, flags,
+ pdev->decompress_data, pdev->vbandlength);
+ break;
+ case 645:
+ case 646:
+ /* TODO & FIXME */
+ return -ENXIO; /* Missing decompressor */
+ break;
+ }
+#endif
+ }
+ return 0;
+}
+
+
diff --git a/drivers/media/video/pwc/pwc-uncompress.h b/drivers/media/video/pwc/pwc-uncompress.h
new file mode 100644
index 000000000000..d3b9250e4ed3
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-uncompress.h
@@ -0,0 +1,41 @@
+/* (C) 1999-2003 Nemosoft Unv.
+ (C) 2004 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* This file is the bridge between the kernel module and the plugin; it
+ describes the structures and datatypes used in both modules. Any
+ significant change should be reflected by increasing the
+ pwc_decompressor_version major number.
+ */
+#ifndef PWC_UNCOMPRESS_H
+#define PWC_UNCOMPRESS_H
+
+#include <linux/config.h>
+
+#include "pwc-ioctl.h"
+
+/* from pwc-dec.h */
+#define PWCX_FLAG_PLANAR 0x0001
+/* */
+
+#endif
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
new file mode 100644
index 000000000000..6dd76bb3dff1
--- /dev/null
+++ b/drivers/media/video/pwc/pwc.h
@@ -0,0 +1,272 @@
+/* (C) 1999-2003 Nemosoft Unv.
+ (C) 2004 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef PWC_H
+#define PWC_H
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/spinlock.h>
+#include <linux/videodev.h>
+#include <linux/wait.h>
+#include <linux/smp_lock.h>
+#include <asm/semaphore.h>
+#include <asm/errno.h>
+
+#include "pwc-uncompress.h"
+#include "pwc-ioctl.h"
+
+/* Defines and structures for the Philips webcam */
+/* Used for checking memory corruption/pointer validation */
+#define PWC_MAGIC 0x89DC10ABUL
+#undef PWC_MAGIC
+
+/* Turn some debugging options on/off */
+#define PWC_DEBUG 0
+
+/* Trace certain actions in the driver */
+#define TRACE_MODULE 0x0001
+#define TRACE_PROBE 0x0002
+#define TRACE_OPEN 0x0004
+#define TRACE_READ 0x0008
+#define TRACE_MEMORY 0x0010
+#define TRACE_FLOW 0x0020
+#define TRACE_SIZE 0x0040
+#define TRACE_PWCX 0x0080
+#define TRACE_SEQUENCE 0x1000
+
+#define Trace(R, A...) if (pwc_trace & R) printk(KERN_DEBUG PWC_NAME " " A)
+#define Debug(A...) printk(KERN_DEBUG PWC_NAME " " A)
+#define Info(A...) printk(KERN_INFO PWC_NAME " " A)
+#define Err(A...) printk(KERN_ERR PWC_NAME " " A)
+
+
+/* Defines for ToUCam cameras */
+#define TOUCAM_HEADER_SIZE 8
+#define TOUCAM_TRAILER_SIZE 4
+
+#define FEATURE_MOTOR_PANTILT 0x0001
+
+/* Version block */
+#define PWC_MAJOR 9
+#define PWC_MINOR 0
+#define PWC_VERSION "9.0.2-unofficial"
+#define PWC_NAME "pwc"
+
+/* Turn certain features on/off */
+#define PWC_INT_PIPE 0
+
+/* Ignore errors in the first N frames, to allow for startup delays */
+#define FRAME_LOWMARK 5
+
+/* Size and number of buffers for the ISO pipe. */
+#define MAX_ISO_BUFS 2
+#define ISO_FRAMES_PER_DESC 10
+#define ISO_MAX_FRAME_SIZE 960
+#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE)
+
+/* Frame buffers: contains compressed or uncompressed video data. */
+#define MAX_FRAMES 5
+/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */
+#define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE)
+
+/* Absolute maximum number of buffers available for mmap() */
+#define MAX_IMAGES 10
+
+/* The following structures were based on cpia.h. Why reinvent the wheel? :-) */
+struct pwc_iso_buf
+{
+ void *data;
+ int length;
+ int read;
+ struct urb *urb;
+};
+
+/* intermediate buffers with raw data from the USB cam */
+struct pwc_frame_buf
+{
+ void *data;
+ volatile int filled; /* number of bytes filled */
+ struct pwc_frame_buf *next; /* list */
+#if PWC_DEBUG
+ int sequence; /* Sequence number */
+#endif
+};
+
+struct pwc_device
+{
+ struct video_device *vdev;
+#ifdef PWC_MAGIC
+ int magic;
+#endif
+ /* Pointer to our usb_device */
+ struct usb_device *udev;
+
+ int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
+ int release; /* release number */
+ int features; /* feature bits */
+ char serial[30]; /* serial number (string) */
+ int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */
+ int usb_init; /* set when the cam has been initialized over USB */
+
+ /*** Video data ***/
+ int vopen; /* flag */
+ int vendpoint; /* video isoc endpoint */
+ int vcinterface; /* video control interface */
+ int valternate; /* alternate interface needed */
+ int vframes, vsize; /* frames-per-second & size (see PSZ_*) */
+ int vpalette; /* palette: 420P, RAW or RGBBAYER */
+ int vframe_count; /* received frames */
+ int vframes_dumped; /* counter for dumped frames */
+ int vframes_error; /* frames received in error */
+ int vmax_packet_size; /* USB maxpacket size */
+ int vlast_packet_size; /* for frame synchronisation */
+ int visoc_errors; /* number of contiguous ISOC errors */
+ int vcompression; /* desired compression factor */
+ int vbandlength; /* compressed band length; 0 is uncompressed */
+ char vsnapshot; /* snapshot mode */
+ char vsync; /* used by isoc handler */
+ char vmirror; /* for ToUCaM series */
+
+ int cmd_len;
+ unsigned char cmd_buf[13];
+
+ /* The image acquisition requires 3 to 4 steps:
+ 1. data is gathered in short packets from the USB controller
+ 2. data is synchronized and packed into a frame buffer
+ 3a. in case data is compressed, decompress it directly into image buffer
+ 3b. in case data is uncompressed, copy into image buffer with viewport
+ 4. data is transferred to the user process
+
+ Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES....
+ We have in effect a back-to-back-double-buffer system.
+ */
+ /* 1: isoc */
+ struct pwc_iso_buf sbuf[MAX_ISO_BUFS];
+ char iso_init;
+
+ /* 2: frame */
+ struct pwc_frame_buf *fbuf; /* all frames */
+ struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */
+ struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */
+ struct pwc_frame_buf *fill_frame; /* frame currently being filled */
+ struct pwc_frame_buf *read_frame; /* frame currently read by user process */
+ int frame_header_size, frame_trailer_size;
+ int frame_size;
+ int frame_total_size; /* including header & trailer */
+ int drop_frames;
+#if PWC_DEBUG
+ int sequence; /* Debugging aid */
+#endif
+
+ /* 3: decompression */
+ struct pwc_decompressor *decompressor; /* function block with decompression routines */
+ void *decompress_data; /* private data for decompression engine */
+
+ /* 4: image */
+ /* We have an 'image' and a 'view', where 'image' is the fixed-size image
+ as delivered by the camera, and 'view' is the size requested by the
+ program. The camera image is centered in this viewport, laced with
+ a gray or black border. view_min <= image <= view <= view_max;
+ */
+ int image_mask; /* bitmask of supported sizes */
+ struct pwc_coord view_min, view_max; /* minimum and maximum viewable sizes */
+ struct pwc_coord abs_max; /* maximum supported size with compression */
+ struct pwc_coord image, view; /* image and viewport size */
+ struct pwc_coord offset; /* offset within the viewport */
+
+ void *image_data; /* total buffer, which is subdivided into ... */
+ void *image_ptr[MAX_IMAGES]; /* ...several images... */
+ int fill_image; /* ...which are rotated. */
+ int len_per_image; /* length per image */
+ int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */
+ int image_used[MAX_IMAGES]; /* For MCAPTURE and SYNC */
+
+ struct semaphore modlock; /* to prevent races in video_open(), etc */
+ spinlock_t ptrlock; /* for manipulating the buffer pointers */
+
+ /*** motorized pan/tilt feature */
+ struct pwc_mpt_range angle_range;
+ int pan_angle; /* in degrees * 100 */
+ int tilt_angle; /* absolute angle; 0,0 is home position */
+
+ /*** Misc. data ***/
+ wait_queue_head_t frameq; /* When waiting for a frame to finish... */
+#if PWC_INT_PIPE
+ void *usb_int_handler; /* for the interrupt endpoint */
+#endif
+};
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Global variable */
+extern int pwc_trace;
+
+/** functions in pwc-if.c */
+int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot);
+
+/** Functions in pwc-misc.c */
+/* sizes in pixels */
+extern struct pwc_coord pwc_image_sizes[PSZ_MAX];
+
+int pwc_decode_size(struct pwc_device *pdev, int width, int height);
+void pwc_construct(struct pwc_device *pdev);
+
+/** Functions in pwc-ctrl.c */
+/* Request a certain video mode. Returns < 0 if not possible */
+extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot);
+
+/* Various controls; should be obvious. Value 0..65535, or < 0 on error */
+extern int pwc_get_brightness(struct pwc_device *pdev);
+extern int pwc_set_brightness(struct pwc_device *pdev, int value);
+extern int pwc_get_contrast(struct pwc_device *pdev);
+extern int pwc_set_contrast(struct pwc_device *pdev, int value);
+extern int pwc_get_gamma(struct pwc_device *pdev);
+extern int pwc_set_gamma(struct pwc_device *pdev, int value);
+extern int pwc_get_saturation(struct pwc_device *pdev);
+extern int pwc_set_saturation(struct pwc_device *pdev, int value);
+extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value);
+extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor);
+
+/* Power down or up the camera; not supported by all models */
+extern int pwc_camera_power(struct pwc_device *pdev, int power);
+
+/* Private ioctl()s; see pwc-ioctl.h */
+extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg);
+
+
+/** pwc-uncompress.c */
+/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */
+extern int pwc_decompress(struct pwc_device *pdev);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c
new file mode 100644
index 000000000000..f03ea7f89596
--- /dev/null
+++ b/drivers/media/video/se401.c
@@ -0,0 +1,1435 @@
+/*
+ * Endpoints (formerly known as AOX) se401 USB Camera Driver
+ *
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
+ *
+ * Still somewhat based on the Linux ov511 driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Thanks to Endpoints Inc. (www.endpoints.com) for making documentation on
+ * their chipset available and supporting me while writing this driver.
+ * - Jeroen Vreeken
+ */
+
+static const char version[] = "0.24";
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/usb.h>
+#include "se401.h"
+
+static int flickerless=0;
+static int video_nr = -1;
+
+static struct usb_device_id device_table [] = {
+ { USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */
+ { USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */
+ { USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */
+ { USB_DEVICE(0x047d, 0x5002) },/* Kensington 6701(5/7) */
+ { USB_DEVICE(0x047d, 0x5003) },/* Kensington 67016 */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");
+MODULE_DESCRIPTION("SE401 USB Camera Driver");
+MODULE_LICENSE("GPL");
+module_param(flickerless, int, 0);
+MODULE_PARM_DESC(flickerless, "Net frequency to adjust exposure time to (0/50/60)");
+module_param(video_nr, int, 0);
+
+static struct usb_driver se401_driver;
+
+
+/**********************************************************************
+ *
+ * Memory management
+ *
+ **********************************************************************/
+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 (size > 0) {
+ SetPageReserved(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;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
+
+
+/****************************************************************************
+ *
+ * se401 register read/write functions
+ *
+ ***************************************************************************/
+
+static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req,
+ unsigned short value, unsigned char *cp, int size)
+{
+ return usb_control_msg (
+ se401->dev,
+ set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0),
+ req,
+ (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ 0,
+ cp,
+ size,
+ 1000
+ );
+}
+
+static int se401_set_feature(struct usb_se401 *se401, unsigned short selector,
+ unsigned short param)
+{
+ /* specs say that the selector (address) should go in the value field
+ and the param in index, but in the logs of the windows driver they do
+ this the other way around...
+ */
+ return usb_control_msg (
+ se401->dev,
+ usb_sndctrlpipe(se401->dev, 0),
+ SE401_REQ_SET_EXT_FEATURE,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ param,
+ selector,
+ NULL,
+ 0,
+ 1000
+ );
+}
+
+static unsigned short se401_get_feature(struct usb_se401 *se401,
+ unsigned short selector)
+{
+ /* For 'set' the selecetor should be in index, not sure if the spec is
+ wrong here to....
+ */
+ unsigned char cp[2];
+ usb_control_msg (
+ se401->dev,
+ usb_rcvctrlpipe(se401->dev, 0),
+ SE401_REQ_GET_EXT_FEATURE,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0,
+ selector,
+ cp,
+ 2,
+ 1000
+ );
+ return cp[0]+cp[1]*256;
+}
+
+/****************************************************************************
+ *
+ * Camera control
+ *
+ ***************************************************************************/
+
+
+static int se401_send_pict(struct usb_se401 *se401)
+{
+ se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l);/* integration time low */
+ se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m);/* integration time mid */
+ se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h);/* integration time mid */
+ se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);/* reset level value */
+ se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain);/* red color gain */
+ se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain);/* green color gain */
+ se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain);/* blue color gain */
+
+ return 0;
+}
+
+static void se401_set_exposure(struct usb_se401 *se401, int brightness)
+{
+ int integration=brightness<<5;
+
+ if (flickerless==50) {
+ integration=integration-integration%106667;
+ }
+ if (flickerless==60) {
+ integration=integration-integration%88889;
+ }
+ se401->brightness=integration>>5;
+ se401->expose_h=(integration>>16)&0xff;
+ se401->expose_m=(integration>>8)&0xff;
+ se401->expose_l=integration&0xff;
+}
+
+static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p)
+{
+ p->brightness=se401->brightness;
+ if (se401->enhance) {
+ p->whiteness=32768;
+ } else {
+ p->whiteness=0;
+ }
+ p->colour=65535;
+ p->contrast=65535;
+ p->hue=se401->rgain<<10;
+ p->palette=se401->palette;
+ p->depth=3; /* rgb24 */
+ return 0;
+}
+
+
+static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p)
+{
+ if (p->palette != VIDEO_PALETTE_RGB24)
+ return 1;
+ se401->palette=p->palette;
+ if (p->hue!=se401->hue) {
+ se401->rgain= p->hue>>10;
+ se401->bgain= 0x40-(p->hue>>10);
+ se401->hue=p->hue;
+ }
+ if (p->brightness!=se401->brightness) {
+ se401_set_exposure(se401, p->brightness);
+ }
+ if (p->whiteness>=32768) {
+ se401->enhance=1;
+ } else {
+ se401->enhance=0;
+ }
+ se401_send_pict(se401);
+ se401_send_pict(se401);
+ return 0;
+}
+
+/*
+ Hyundai have some really nice docs about this and other sensor related
+ stuff on their homepage: www.hei.co.kr
+*/
+static void se401_auto_resetlevel(struct usb_se401 *se401)
+{
+ unsigned int ahrc, alrc;
+ int oldreset=se401->resetlevel;
+
+ /* For some reason this normally read-only register doesn't get reset
+ to zero after reading them just once...
+ */
+ se401_get_feature(se401, HV7131_REG_HIREFNOH);
+ se401_get_feature(se401, HV7131_REG_HIREFNOL);
+ se401_get_feature(se401, HV7131_REG_LOREFNOH);
+ se401_get_feature(se401, HV7131_REG_LOREFNOL);
+ ahrc=256*se401_get_feature(se401, HV7131_REG_HIREFNOH) +
+ se401_get_feature(se401, HV7131_REG_HIREFNOL);
+ alrc=256*se401_get_feature(se401, HV7131_REG_LOREFNOH) +
+ se401_get_feature(se401, HV7131_REG_LOREFNOL);
+
+ /* Not an exact science, but it seems to work pretty well... */
+ if (alrc > 10) {
+ while (alrc>=10 && se401->resetlevel < 63) {
+ se401->resetlevel++;
+ alrc /=2;
+ }
+ } else if (ahrc > 20) {
+ while (ahrc>=20 && se401->resetlevel > 0) {
+ se401->resetlevel--;
+ ahrc /=2;
+ }
+ }
+ if (se401->resetlevel!=oldreset)
+ se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);
+
+ return;
+}
+
+/* irq handler for snapshot button */
+static void se401_button_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_se401 *se401 = urb->context;
+ int status;
+
+ if (!se401->dev) {
+ info("ohoh: device vapourished");
+ return;
+ }
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ if (urb->actual_length >=2) {
+ if (se401->button)
+ se401->buttonpressed=1;
+ }
+exit:
+ status = usb_submit_urb (urb, GFP_ATOMIC);
+ if (status)
+ err ("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, status);
+}
+
+static void se401_video_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_se401 *se401 = urb->context;
+ int length = urb->actual_length;
+
+ /* ohoh... */
+ if (!se401->streaming)
+ return;
+
+ if (!se401->dev) {
+ info ("ohoh: device vapourished");
+ return;
+ }
+
+ /* 0 sized packets happen if we are to fast, but sometimes the camera
+ keeps sending them forever...
+ */
+ if (length && !urb->status) {
+ se401->nullpackets=0;
+ switch(se401->scratch[se401->scratch_next].state) {
+ case BUFFER_READY:
+ case BUFFER_BUSY: {
+ se401->dropped++;
+ break;
+ }
+ case BUFFER_UNUSED: {
+ memcpy(se401->scratch[se401->scratch_next].data, (unsigned char *)urb->transfer_buffer, length);
+ se401->scratch[se401->scratch_next].state=BUFFER_READY;
+ se401->scratch[se401->scratch_next].offset=se401->bayeroffset;
+ se401->scratch[se401->scratch_next].length=length;
+ if (waitqueue_active(&se401->wq)) {
+ wake_up_interruptible(&se401->wq);
+ }
+ se401->scratch_overflow=0;
+ se401->scratch_next++;
+ if (se401->scratch_next>=SE401_NUMSCRATCH)
+ se401->scratch_next=0;
+ break;
+ }
+ }
+ se401->bayeroffset+=length;
+ if (se401->bayeroffset>=se401->cheight*se401->cwidth) {
+ se401->bayeroffset=0;
+ }
+ } else {
+ se401->nullpackets++;
+ if (se401->nullpackets > SE401_MAX_NULLPACKETS) {
+ if (waitqueue_active(&se401->wq)) {
+ wake_up_interruptible(&se401->wq);
+ }
+ }
+ }
+
+ /* Resubmit urb for new data */
+ urb->status=0;
+ urb->dev=se401->dev;
+ if(usb_submit_urb(urb, GFP_KERNEL))
+ info("urb burned down");
+ return;
+}
+
+static void se401_send_size(struct usb_se401 *se401, int width, int height)
+{
+ int i=0;
+ int mode=0x03; /* No compression */
+ int sendheight=height;
+ int sendwidth=width;
+
+ /* JangGu compression can only be used with the camera supported sizes,
+ but bayer seems to work with any size that fits on the sensor.
+ We check if we can use compression with the current size with either
+ 4 or 16 times subcapturing, if not we use uncompressed bayer data
+ but this will result in cutouts of the maximum size....
+ */
+ while (i<se401->sizes && !(se401->width[i]==width && se401->height[i]==height))
+ i++;
+ while (i<se401->sizes) {
+ if (se401->width[i]==width*2 && se401->height[i]==height*2) {
+ sendheight=se401->height[i];
+ sendwidth=se401->width[i];
+ mode=0x40;
+ }
+ if (se401->width[i]==width*4 && se401->height[i]==height*4) {
+ sendheight=se401->height[i];
+ sendwidth=se401->width[i];
+ mode=0x42;
+ }
+ i++;
+ }
+
+ se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0);
+ se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0);
+ se401_set_feature(se401, SE401_OPERATINGMODE, mode);
+
+ if (mode==0x03) {
+ se401->format=FMT_BAYER;
+ } else {
+ se401->format=FMT_JANGGU;
+ }
+
+ return;
+}
+
+/*
+ In this function se401_send_pict is called several times,
+ for some reason (depending on the state of the sensor and the phase of
+ the moon :) doing this only in either place doesn't always work...
+*/
+static int se401_start_stream(struct usb_se401 *se401)
+{
+ struct urb *urb;
+ int err=0, i;
+ se401->streaming=1;
+
+ se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
+ se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
+
+ /* Set picture settings */
+ se401_set_feature(se401, HV7131_REG_MODE_B, 0x05);/*windowed + pix intg */
+ se401_send_pict(se401);
+
+ se401_send_size(se401, se401->cwidth, se401->cheight);
+
+ se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, NULL, 0);
+
+ /* Do some memory allocation */
+ for (i=0; i<SE401_NUMFRAMES; i++) {
+ se401->frame[i].data=se401->fbuf + i * se401->maxframesize;
+ se401->frame[i].curpix=0;
+ }
+ for (i=0; i<SE401_NUMSBUF; i++) {
+ se401->sbuf[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
+ }
+
+ se401->bayeroffset=0;
+ se401->scratch_next=0;
+ se401->scratch_use=0;
+ se401->scratch_overflow=0;
+ for (i=0; i<SE401_NUMSCRATCH; i++) {
+ se401->scratch[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
+ se401->scratch[i].state=BUFFER_UNUSED;
+ }
+
+ for (i=0; i<SE401_NUMSBUF; i++) {
+ urb=usb_alloc_urb(0, GFP_KERNEL);
+ if(!urb)
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(urb, se401->dev,
+ usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT),
+ se401->sbuf[i].data, SE401_PACKETSIZE,
+ se401_video_irq,
+ se401);
+
+ se401->urb[i]=urb;
+
+ err=usb_submit_urb(se401->urb[i], GFP_KERNEL);
+ if(err)
+ err("urb burned down");
+ }
+
+ se401->framecount=0;
+
+ return 0;
+}
+
+static int se401_stop_stream(struct usb_se401 *se401)
+{
+ int i;
+
+ if (!se401->streaming || !se401->dev)
+ return 1;
+
+ se401->streaming=0;
+
+ se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0);
+
+ se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0);
+ se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
+
+ for (i=0; i<SE401_NUMSBUF; i++) if (se401->urb[i]) {
+ usb_kill_urb(se401->urb[i]);
+ usb_free_urb(se401->urb[i]);
+ se401->urb[i]=NULL;
+ kfree(se401->sbuf[i].data);
+ }
+ for (i=0; i<SE401_NUMSCRATCH; i++) {
+ kfree(se401->scratch[i].data);
+ se401->scratch[i].data=NULL;
+ }
+
+ return 0;
+}
+
+static int se401_set_size(struct usb_se401 *se401, int width, int height)
+{
+ int wasstreaming=se401->streaming;
+ /* Check to see if we need to change */
+ if (se401->cwidth==width && se401->cheight==height)
+ return 0;
+
+ /* Check for a valid mode */
+ if (!width || !height)
+ return 1;
+ if ((width & 1) || (height & 1))
+ return 1;
+ if (width>se401->width[se401->sizes-1])
+ return 1;
+ if (height>se401->height[se401->sizes-1])
+ return 1;
+
+ /* Stop a current stream and start it again at the new size */
+ if (wasstreaming)
+ se401_stop_stream(se401);
+ se401->cwidth=width;
+ se401->cheight=height;
+ if (wasstreaming)
+ se401_start_stream(se401);
+ return 0;
+}
+
+
+/****************************************************************************
+ *
+ * Video Decoding
+ *
+ ***************************************************************************/
+
+/*
+ This shouldn't really be done in a v4l driver....
+ But it does make the image look a lot more usable.
+ Basically it lifts the dark pixels more than the light pixels.
+*/
+static inline void enhance_picture(unsigned char *frame, int len)
+{
+ while (len--) {
+ *frame=(((*frame^255)*(*frame^255))/255)^255;
+ frame++;
+ }
+}
+
+static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data)
+{
+ struct se401_frame *frame=&se401->frame[se401->curframe];
+ int linelength=se401->cwidth*3;
+
+ if (frame->curlinepix >= linelength) {
+ frame->curlinepix=0;
+ frame->curline+=linelength;
+ }
+
+ /* First three are absolute, all others relative.
+ * Format is rgb from right to left (mirrorred image),
+ * we flip it to get bgr from left to right. */
+ if (frame->curlinepix < 3) {
+ *(frame->curline-frame->curlinepix)=1+data*4;
+ } else {
+ *(frame->curline-frame->curlinepix)=
+ *(frame->curline-frame->curlinepix+3)+data*4;
+ }
+ frame->curlinepix++;
+}
+
+static inline void decode_JangGu_vlc (struct usb_se401 *se401, unsigned char *data, int bit_exp, int packetlength)
+{
+ int pos=0;
+ int vlc_cod=0;
+ int vlc_size=0;
+ int vlc_data=0;
+ int bit_cur;
+ int bit;
+ data+=4;
+ while (pos < packetlength) {
+ bit_cur=8;
+ while (bit_cur && bit_exp) {
+ bit=((*data)>>(bit_cur-1))&1;
+ if (!vlc_cod) {
+ if (bit) {
+ vlc_size++;
+ } else {
+ if (!vlc_size) {
+ decode_JangGu_integrate(se401, 0);
+ } else {
+ vlc_cod=2;
+ vlc_data=0;
+ }
+ }
+ } else {
+ if (vlc_cod==2) {
+ if (!bit)
+ vlc_data = -(1<<vlc_size) + 1;
+ vlc_cod--;
+ }
+ vlc_size--;
+ vlc_data+=bit<<vlc_size;
+ if (!vlc_size) {
+ decode_JangGu_integrate(se401, vlc_data);
+ vlc_cod=0;
+ }
+ }
+ bit_cur--;
+ bit_exp--;
+ }
+ pos++;
+ data++;
+ }
+}
+
+static inline void decode_JangGu (struct usb_se401 *se401, struct se401_scratch *buffer)
+{
+ unsigned char *data=buffer->data;
+ int len=buffer->length;
+ int bit_exp=0, pix_exp=0, frameinfo=0, packetlength=0, size;
+ int datapos=0;
+
+ /* New image? */
+ if (!se401->frame[se401->curframe].curpix) {
+ se401->frame[se401->curframe].curlinepix=0;
+ se401->frame[se401->curframe].curline=
+ se401->frame[se401->curframe].data+
+ se401->cwidth*3-1;
+ if (se401->frame[se401->curframe].grabstate==FRAME_READY)
+ se401->frame[se401->curframe].grabstate=FRAME_GRABBING;
+ se401->vlcdatapos=0;
+ }
+ while (datapos < len) {
+ size=1024-se401->vlcdatapos;
+ if (size+datapos > len)
+ size=len-datapos;
+ memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size);
+ se401->vlcdatapos+=size;
+ packetlength=0;
+ if (se401->vlcdatapos >= 4) {
+ bit_exp=se401->vlcdata[3]+(se401->vlcdata[2]<<8);
+ pix_exp=se401->vlcdata[1]+((se401->vlcdata[0]&0x3f)<<8);
+ frameinfo=se401->vlcdata[0]&0xc0;
+ packetlength=((bit_exp+47)>>4)<<1;
+ if (packetlength > 1024) {
+ se401->vlcdatapos=0;
+ datapos=len;
+ packetlength=0;
+ se401->error++;
+ se401->frame[se401->curframe].curpix=0;
+ }
+ }
+ if (packetlength && se401->vlcdatapos >= packetlength) {
+ decode_JangGu_vlc(se401, se401->vlcdata, bit_exp, packetlength);
+ se401->frame[se401->curframe].curpix+=pix_exp*3;
+ datapos+=size-(se401->vlcdatapos-packetlength);
+ se401->vlcdatapos=0;
+ if (se401->frame[se401->curframe].curpix>=se401->cwidth*se401->cheight*3) {
+ if (se401->frame[se401->curframe].curpix==se401->cwidth*se401->cheight*3) {
+ if (se401->frame[se401->curframe].grabstate==FRAME_GRABBING) {
+ se401->frame[se401->curframe].grabstate=FRAME_DONE;
+ se401->framecount++;
+ se401->readcount++;
+ }
+ if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) {
+ se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1);
+ }
+ } else {
+ se401->error++;
+ }
+ se401->frame[se401->curframe].curpix=0;
+ datapos=len;
+ }
+ } else {
+ datapos+=size;
+ }
+ }
+}
+
+static inline void decode_bayer (struct usb_se401 *se401, struct se401_scratch *buffer)
+{
+ unsigned char *data=buffer->data;
+ int len=buffer->length;
+ int offset=buffer->offset;
+ int datasize=se401->cwidth*se401->cheight;
+ struct se401_frame *frame=&se401->frame[se401->curframe];
+
+ unsigned char *framedata=frame->data, *curline, *nextline;
+ int width=se401->cwidth;
+ int blineoffset=0, bline;
+ int linelength=width*3, i;
+
+
+ if (frame->curpix==0) {
+ if (frame->grabstate==FRAME_READY) {
+ frame->grabstate=FRAME_GRABBING;
+ }
+ frame->curline=framedata+linelength;
+ frame->curlinepix=0;
+ }
+
+ if (offset!=frame->curpix) {
+ /* Regard frame as lost :( */
+ frame->curpix=0;
+ se401->error++;
+ return;
+ }
+
+ /* Check if we have to much data */
+ if (frame->curpix+len > datasize) {
+ len=datasize-frame->curpix;
+ }
+ if (se401->cheight%4)
+ blineoffset=1;
+ bline=frame->curpix/se401->cwidth+blineoffset;
+
+ curline=frame->curline;
+ nextline=curline+linelength;
+ if (nextline >= framedata+datasize*3)
+ nextline=curline;
+ while (len) {
+ if (frame->curlinepix>=width) {
+ frame->curlinepix-=width;
+ bline=frame->curpix/width+blineoffset;
+ curline+=linelength*2;
+ nextline+=linelength*2;
+ if (curline >= framedata+datasize*3) {
+ frame->curlinepix++;
+ curline-=3;
+ nextline-=3;
+ len--;
+ data++;
+ frame->curpix++;
+ }
+ if (nextline >= framedata+datasize*3)
+ nextline=curline;
+ }
+ if ((bline&1)) {
+ if ((frame->curlinepix&1)) {
+ *(curline+2)=*data;
+ *(curline-1)=*data;
+ *(nextline+2)=*data;
+ *(nextline-1)=*data;
+ } else {
+ *(curline+1)=
+ (*(curline+1)+*data)/2;
+ *(curline-2)=
+ (*(curline-2)+*data)/2;
+ *(nextline+1)=*data;
+ *(nextline-2)=*data;
+ }
+ } else {
+ if ((frame->curlinepix&1)) {
+ *(curline+1)=
+ (*(curline+1)+*data)/2;
+ *(curline-2)=
+ (*(curline-2)+*data)/2;
+ *(nextline+1)=*data;
+ *(nextline-2)=*data;
+ } else {
+ *curline=*data;
+ *(curline-3)=*data;
+ *nextline=*data;
+ *(nextline-3)=*data;
+ }
+ }
+ frame->curlinepix++;
+ curline-=3;
+ nextline-=3;
+ len--;
+ data++;
+ frame->curpix++;
+ }
+ frame->curline=curline;
+
+ if (frame->curpix>=datasize) {
+ /* Fix the top line */
+ framedata+=linelength;
+ for (i=0; i<linelength; i++) {
+ framedata--;
+ *framedata=*(framedata+linelength);
+ }
+ /* Fix the left side (green is already present) */
+ for (i=0; i<se401->cheight; i++) {
+ *framedata=*(framedata+3);
+ *(framedata+1)=*(framedata+4);
+ *(framedata+2)=*(framedata+5);
+ framedata+=linelength;
+ }
+ frame->curpix=0;
+ frame->grabstate=FRAME_DONE;
+ se401->framecount++;
+ se401->readcount++;
+ if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) {
+ se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1);
+ }
+ }
+}
+
+static int se401_newframe(struct usb_se401 *se401, int framenr)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int errors=0;
+
+ while (se401->streaming &&
+ (se401->frame[framenr].grabstate==FRAME_READY ||
+ se401->frame[framenr].grabstate==FRAME_GRABBING) ) {
+ if(!se401->frame[framenr].curpix) {
+ errors++;
+ }
+ wait_interruptible(
+ se401->scratch[se401->scratch_use].state!=BUFFER_READY,
+ &se401->wq,
+ &wait
+ );
+ if (se401->nullpackets > SE401_MAX_NULLPACKETS) {
+ se401->nullpackets=0;
+ info("to many null length packets, restarting capture");
+ se401_stop_stream(se401);
+ se401_start_stream(se401);
+ } else {
+ if (se401->scratch[se401->scratch_use].state!=BUFFER_READY) {
+ se401->frame[framenr].grabstate=FRAME_ERROR;
+ return -EIO;
+ }
+ se401->scratch[se401->scratch_use].state=BUFFER_BUSY;
+ if (se401->format==FMT_JANGGU) {
+ decode_JangGu(se401, &se401->scratch[se401->scratch_use]);
+ } else {
+ decode_bayer(se401, &se401->scratch[se401->scratch_use]);
+ }
+ se401->scratch[se401->scratch_use].state=BUFFER_UNUSED;
+ se401->scratch_use++;
+ if (se401->scratch_use>=SE401_NUMSCRATCH)
+ se401->scratch_use=0;
+ if (errors > SE401_MAX_ERRORS) {
+ errors=0;
+ info("to much errors, restarting capture");
+ se401_stop_stream(se401);
+ se401_start_stream(se401);
+ }
+ }
+ }
+
+ if (se401->frame[framenr].grabstate==FRAME_DONE)
+ if (se401->enhance)
+ enhance_picture(se401->frame[framenr].data, se401->cheight*se401->cwidth*3);
+ return 0;
+}
+
+static void usb_se401_remove_disconnected (struct usb_se401 *se401)
+{
+ int i;
+
+ se401->dev = NULL;
+
+ for (i=0; i<SE401_NUMSBUF; i++)
+ if (se401->urb[i]) {
+ usb_kill_urb(se401->urb[i]);
+ usb_free_urb(se401->urb[i]);
+ se401->urb[i] = NULL;
+ kfree(se401->sbuf[i].data);
+ }
+ for (i=0; i<SE401_NUMSCRATCH; i++) {
+ kfree(se401->scratch[i].data);
+ }
+ if (se401->inturb) {
+ usb_kill_urb(se401->inturb);
+ usb_free_urb(se401->inturb);
+ }
+ info("%s disconnected", se401->camera_name);
+
+ /* Free the memory */
+ kfree(se401->width);
+ kfree(se401->height);
+ kfree(se401);
+}
+
+
+
+/****************************************************************************
+ *
+ * Video4Linux
+ *
+ ***************************************************************************/
+
+
+static int se401_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct usb_se401 *se401 = (struct usb_se401 *)dev;
+ int err = 0;
+
+ if (se401->user)
+ return -EBUSY;
+ se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES);
+ if (se401->fbuf)
+ file->private_data = dev;
+ else
+ err = -ENOMEM;
+ se401->user = !err;
+
+ return err;
+}
+
+static int se401_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = file->private_data;
+ struct usb_se401 *se401 = (struct usb_se401 *)dev;
+ int i;
+
+ rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES);
+ if (se401->removed) {
+ usb_se401_remove_disconnected(se401);
+ info("device unregistered");
+ } else {
+ for (i=0; i<SE401_NUMFRAMES; i++)
+ se401->frame[i].grabstate=FRAME_UNUSED;
+ if (se401->streaming)
+ se401_stop_stream(se401);
+ se401->user=0;
+ }
+ file->private_data = NULL;
+ return 0;
+}
+
+static int se401_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = file->private_data;
+ struct usb_se401 *se401 = (struct usb_se401 *)vdev;
+
+ if (!se401->dev)
+ return -EIO;
+
+ switch (cmd) {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *b = arg;
+ strcpy(b->name, se401->camera_name);
+ b->type = VID_TYPE_CAPTURE;
+ b->channels = 1;
+ b->audios = 0;
+ b->maxwidth = se401->width[se401->sizes-1];
+ b->maxheight = se401->height[se401->sizes-1];
+ b->minwidth = se401->width[0];
+ b->minheight = se401->height[0];
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *v = arg;
+
+ if (v->channel != 0)
+ return -EINVAL;
+ v->flags = 0;
+ v->tuners = 0;
+ v->type = VIDEO_TYPE_CAMERA;
+ strcpy(v->name, "Camera");
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ struct video_channel *v = arg;
+
+ if (v->channel != 0)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture *p = arg;
+
+ se401_get_pict(se401, p);
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture *p = arg;
+
+ if (se401_set_pict(se401, p))
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window *vw = arg;
+
+ if (vw->flags)
+ return -EINVAL;
+ if (vw->clipcount)
+ return -EINVAL;
+ if (se401_set_size(se401, vw->width, vw->height))
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window *vw = arg;
+
+ vw->x = 0; /* FIXME */
+ vw->y = 0;
+ vw->chromakey = 0;
+ vw->flags = 0;
+ vw->clipcount = 0;
+ vw->width = se401->cwidth;
+ vw->height = se401->cheight;
+ return 0;
+ }
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf *vm = arg;
+ int i;
+
+ memset(vm, 0, sizeof(*vm));
+ vm->size = SE401_NUMFRAMES * se401->maxframesize;
+ vm->frames = SE401_NUMFRAMES;
+ for (i=0; i<SE401_NUMFRAMES; i++)
+ vm->offsets[i] = se401->maxframesize * i;
+ return 0;
+ }
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap *vm = arg;
+
+ if (vm->format != VIDEO_PALETTE_RGB24)
+ return -EINVAL;
+ if (vm->frame >= SE401_NUMFRAMES)
+ return -EINVAL;
+ if (se401->frame[vm->frame].grabstate != FRAME_UNUSED)
+ return -EBUSY;
+
+ /* Is this according to the v4l spec??? */
+ if (se401_set_size(se401, vm->width, vm->height))
+ return -EINVAL;
+ se401->frame[vm->frame].grabstate=FRAME_READY;
+
+ if (!se401->streaming)
+ se401_start_stream(se401);
+
+ /* Set the picture properties */
+ if (se401->framecount==0)
+ se401_send_pict(se401);
+ /* Calibrate the reset level after a few frames. */
+ if (se401->framecount%20==1)
+ se401_auto_resetlevel(se401);
+
+ return 0;
+ }
+ case VIDIOCSYNC:
+ {
+ int *frame = arg;
+ int ret=0;
+
+ if(*frame <0 || *frame >= SE401_NUMFRAMES)
+ return -EINVAL;
+
+ ret=se401_newframe(se401, *frame);
+ se401->frame[*frame].grabstate=FRAME_UNUSED;
+ return ret;
+ }
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer *vb = arg;
+
+ memset(vb, 0, sizeof(*vb));
+ return 0;
+ }
+ case VIDIOCKEY:
+ return 0;
+ case VIDIOCCAPTURE:
+ return -EINVAL;
+ case VIDIOCSFBUF:
+ return -EINVAL;
+ case VIDIOCGTUNER:
+ case VIDIOCSTUNER:
+ return -EINVAL;
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ return -EINVAL;
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ } /* end switch */
+
+ return 0;
+}
+
+static int se401_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, se401_do_ioctl);
+}
+
+static ssize_t se401_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int realcount=count, ret=0;
+ struct video_device *dev = file->private_data;
+ struct usb_se401 *se401 = (struct usb_se401 *)dev;
+
+
+ if (se401->dev == NULL)
+ return -EIO;
+ if (realcount > se401->cwidth*se401->cheight*3)
+ realcount=se401->cwidth*se401->cheight*3;
+
+ /* Shouldn't happen: */
+ if (se401->frame[0].grabstate==FRAME_GRABBING)
+ return -EBUSY;
+ se401->frame[0].grabstate=FRAME_READY;
+ se401->frame[1].grabstate=FRAME_UNUSED;
+ se401->curframe=0;
+
+ if (!se401->streaming)
+ se401_start_stream(se401);
+
+ /* Set the picture properties */
+ if (se401->framecount==0)
+ se401_send_pict(se401);
+ /* Calibrate the reset level after a few frames. */
+ if (se401->framecount%20==1)
+ se401_auto_resetlevel(se401);
+
+ ret=se401_newframe(se401, 0);
+
+ se401->frame[0].grabstate=FRAME_UNUSED;
+ if (ret)
+ return ret;
+ if (copy_to_user(buf, se401->frame[0].data, realcount))
+ return -EFAULT;
+
+ return realcount;
+}
+
+static int se401_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = file->private_data;
+ struct usb_se401 *se401 = (struct usb_se401 *)dev;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end-vma->vm_start;
+ unsigned long page, pos;
+
+ mutex_lock(&se401->lock);
+
+ if (se401->dev == NULL) {
+ mutex_unlock(&se401->lock);
+ return -EIO;
+ }
+ if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) {
+ mutex_unlock(&se401->lock);
+ return -EINVAL;
+ }
+ pos = (unsigned long)se401->fbuf;
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
+ mutex_unlock(&se401->lock);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+ mutex_unlock(&se401->lock);
+
+ return 0;
+}
+
+static struct file_operations se401_fops = {
+ .owner = THIS_MODULE,
+ .open = se401_open,
+ .release = se401_close,
+ .read = se401_read,
+ .mmap = se401_mmap,
+ .ioctl = se401_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+static struct video_device se401_template = {
+ .owner = THIS_MODULE,
+ .name = "se401 USB camera",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_SE401,
+ .fops = &se401_fops,
+};
+
+
+
+/***************************/
+static int se401_init(struct usb_se401 *se401, int button)
+{
+ int i=0, rc;
+ unsigned char cp[0x40];
+ char temp[200];
+
+ /* led on */
+ se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
+
+ /* get camera descriptor */
+ rc=se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0, cp, sizeof(cp));
+ if (cp[1]!=0x41) {
+ err("Wrong descriptor type");
+ return 1;
+ }
+ sprintf (temp, "ExtraFeatures: %d", cp[3]);
+
+ se401->sizes=cp[4]+cp[5]*256;
+ se401->width=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL);
+ if (!se401->width)
+ return 1;
+ se401->height=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL);
+ if (!se401->height) {
+ kfree(se401->width);
+ return 1;
+ }
+ for (i=0; i<se401->sizes; i++) {
+ se401->width[i]=cp[6+i*4+0]+cp[6+i*4+1]*256;
+ se401->height[i]=cp[6+i*4+2]+cp[6+i*4+3]*256;
+ }
+ sprintf (temp, "%s Sizes:", temp);
+ for (i=0; i<se401->sizes; i++) {
+ sprintf(temp, "%s %dx%d", temp, se401->width[i], se401->height[i]);
+ }
+ info("%s", temp);
+ se401->maxframesize=se401->width[se401->sizes-1]*se401->height[se401->sizes-1]*3;
+
+ rc=se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp));
+ se401->cwidth=cp[0]+cp[1]*256;
+ rc=se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp));
+ se401->cheight=cp[0]+cp[1]*256;
+
+ if (!cp[2] && SE401_FORMAT_BAYER) {
+ err("Bayer format not supported!");
+ return 1;
+ }
+ /* set output mode (BAYER) */
+ se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE, SE401_FORMAT_BAYER, NULL, 0);
+
+ rc=se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp));
+ se401->brightness=cp[0]+cp[1]*256;
+ /* some default values */
+ se401->resetlevel=0x2d;
+ se401->rgain=0x20;
+ se401->ggain=0x20;
+ se401->bgain=0x20;
+ se401_set_exposure(se401, 20000);
+ se401->palette=VIDEO_PALETTE_RGB24;
+ se401->enhance=1;
+ se401->dropped=0;
+ se401->error=0;
+ se401->framecount=0;
+ se401->readcount=0;
+
+ /* Start interrupt transfers for snapshot button */
+ if (button) {
+ se401->inturb=usb_alloc_urb(0, GFP_KERNEL);
+ if (!se401->inturb) {
+ info("Allocation of inturb failed");
+ return 1;
+ }
+ usb_fill_int_urb(se401->inturb, se401->dev,
+ usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT),
+ &se401->button, sizeof(se401->button),
+ se401_button_irq,
+ se401,
+ 8
+ );
+ if (usb_submit_urb(se401->inturb, GFP_KERNEL)) {
+ info("int urb burned down");
+ return 1;
+ }
+ } else
+ se401->inturb=NULL;
+
+ /* Flash the led */
+ se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
+ se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
+ se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
+ se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0);
+
+ return 0;
+}
+
+static int se401_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_interface_descriptor *interface;
+ struct usb_se401 *se401;
+ char *camera_name=NULL;
+ int button=1;
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -ENODEV;
+
+ interface = &intf->cur_altsetting->desc;
+
+ /* Is it an se401? */
+ if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 &&
+ le16_to_cpu(dev->descriptor.idProduct) == 0x0004) {
+ camera_name="Endpoints/Aox SE401";
+ } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 &&
+ le16_to_cpu(dev->descriptor.idProduct) == 0x030b) {
+ camera_name="Philips PCVC665K";
+ } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
+ le16_to_cpu(dev->descriptor.idProduct) == 0x5001) {
+ camera_name="Kensington VideoCAM 67014";
+ } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
+ le16_to_cpu(dev->descriptor.idProduct) == 0x5002) {
+ camera_name="Kensington VideoCAM 6701(5/7)";
+ } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
+ le16_to_cpu(dev->descriptor.idProduct) == 0x5003) {
+ camera_name="Kensington VideoCAM 67016";
+ button=0;
+ } else
+ return -ENODEV;
+
+ /* Checking vendor/product should be enough, but what the hell */
+ if (interface->bInterfaceClass != 0x00)
+ return -ENODEV;
+ if (interface->bInterfaceSubClass != 0x00)
+ return -ENODEV;
+
+ /* We found one */
+ info("SE401 camera found: %s", camera_name);
+
+ if ((se401 = kzalloc(sizeof(*se401), GFP_KERNEL)) == NULL) {
+ err("couldn't kmalloc se401 struct");
+ return -ENOMEM;
+ }
+
+ se401->dev = dev;
+ se401->iface = interface->bInterfaceNumber;
+ se401->camera_name = camera_name;
+
+ info("firmware version: %02x", le16_to_cpu(dev->descriptor.bcdDevice) & 255);
+
+ if (se401_init(se401, button)) {
+ kfree(se401);
+ return -EIO;
+ }
+
+ memcpy(&se401->vdev, &se401_template, sizeof(se401_template));
+ memcpy(se401->vdev.name, se401->camera_name, strlen(se401->camera_name));
+ init_waitqueue_head(&se401->wq);
+ mutex_init(&se401->lock);
+ wmb();
+
+ if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
+ kfree(se401);
+ err("video_register_device failed");
+ return -EIO;
+ }
+ info("registered new video device: video%d", se401->vdev.minor);
+
+ usb_set_intfdata (intf, se401);
+ return 0;
+}
+
+static void se401_disconnect(struct usb_interface *intf)
+{
+ struct usb_se401 *se401 = usb_get_intfdata (intf);
+
+ usb_set_intfdata (intf, NULL);
+ if (se401) {
+ video_unregister_device(&se401->vdev);
+ if (!se401->user){
+ usb_se401_remove_disconnected(se401);
+ } else {
+ se401->frame[0].grabstate = FRAME_ERROR;
+ se401->frame[0].grabstate = FRAME_ERROR;
+
+ se401->streaming = 0;
+
+ wake_up_interruptible(&se401->wq);
+ se401->removed = 1;
+ }
+ }
+}
+
+static struct usb_driver se401_driver = {
+ .name = "se401",
+ .id_table = device_table,
+ .probe = se401_probe,
+ .disconnect = se401_disconnect,
+};
+
+
+
+/****************************************************************************
+ *
+ * Module routines
+ *
+ ***************************************************************************/
+
+static int __init usb_se401_init(void)
+{
+ info("SE401 usb camera driver version %s registering", version);
+ if (flickerless)
+ if (flickerless!=50 && flickerless!=60) {
+ info("Invallid flickerless value, use 0, 50 or 60.");
+ return -1;
+ }
+ return usb_register(&se401_driver);
+}
+
+static void __exit usb_se401_exit(void)
+{
+ usb_deregister(&se401_driver);
+ info("SE401 driver deregistered");
+}
+
+module_init(usb_se401_init);
+module_exit(usb_se401_exit);
diff --git a/drivers/media/video/se401.h b/drivers/media/video/se401.h
new file mode 100644
index 000000000000..e88a40d4c86a
--- /dev/null
+++ b/drivers/media/video/se401.h
@@ -0,0 +1,234 @@
+
+#ifndef __LINUX_se401_H
+#define __LINUX_se401_H
+
+#include <asm/uaccess.h>
+#include <linux/videodev.h>
+#include <linux/smp_lock.h>
+#include <linux/mutex.h>
+
+#define se401_DEBUG /* Turn on debug messages */
+
+#ifdef se401_DEBUG
+# define PDEBUG(level, fmt, args...) \
+if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args)
+#else
+# define PDEBUG(level, fmt, args...) do {} while(0)
+#endif
+
+/* An almost drop-in replacement for sleep_on_interruptible */
+#define wait_interruptible(test, queue, wait) \
+{ \
+ add_wait_queue(queue, wait); \
+ set_current_state(TASK_INTERRUPTIBLE); \
+ if (test) \
+ schedule(); \
+ remove_wait_queue(queue, wait); \
+ set_current_state(TASK_RUNNING); \
+ if (signal_pending(current)) \
+ break; \
+}
+
+#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06
+#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41
+#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42
+#define SE401_REQ_CAPTURE_FRAME 0x43
+#define SE401_REQ_GET_BRT 0x44
+#define SE401_REQ_SET_BRT 0x45
+#define SE401_REQ_GET_WIDTH 0x4c
+#define SE401_REQ_SET_WIDTH 0x4d
+#define SE401_REQ_GET_HEIGHT 0x4e
+#define SE401_REQ_SET_HEIGHT 0x4f
+#define SE401_REQ_GET_OUTPUT_MODE 0x50
+#define SE401_REQ_SET_OUTPUT_MODE 0x51
+#define SE401_REQ_GET_EXT_FEATURE 0x52
+#define SE401_REQ_SET_EXT_FEATURE 0x53
+#define SE401_REQ_CAMERA_POWER 0x56
+#define SE401_REQ_LED_CONTROL 0x57
+#define SE401_REQ_BIOS 0xff
+
+#define SE401_BIOS_READ 0x07
+
+#define SE401_FORMAT_BAYER 0x40
+
+/* Hyundai hv7131b registers
+ 7121 and 7141 should be the same (haven't really checked...) */
+/* Mode registers: */
+#define HV7131_REG_MODE_A 0x00
+#define HV7131_REG_MODE_B 0x01
+#define HV7131_REG_MODE_C 0x02
+/* Frame registers: */
+#define HV7131_REG_FRSU 0x10
+#define HV7131_REG_FRSL 0x11
+#define HV7131_REG_FCSU 0x12
+#define HV7131_REG_FCSL 0x13
+#define HV7131_REG_FWHU 0x14
+#define HV7131_REG_FWHL 0x15
+#define HV7131_REG_FWWU 0x16
+#define HV7131_REG_FWWL 0x17
+/* Timing registers: */
+#define HV7131_REG_THBU 0x20
+#define HV7131_REG_THBL 0x21
+#define HV7131_REG_TVBU 0x22
+#define HV7131_REG_TVBL 0x23
+#define HV7131_REG_TITU 0x25
+#define HV7131_REG_TITM 0x26
+#define HV7131_REG_TITL 0x27
+#define HV7131_REG_TMCD 0x28
+/* Adjust Registers: */
+#define HV7131_REG_ARLV 0x30
+#define HV7131_REG_ARCG 0x31
+#define HV7131_REG_AGCG 0x32
+#define HV7131_REG_ABCG 0x33
+#define HV7131_REG_APBV 0x34
+#define HV7131_REG_ASLP 0x54
+/* Offset Registers: */
+#define HV7131_REG_OFSR 0x50
+#define HV7131_REG_OFSG 0x51
+#define HV7131_REG_OFSB 0x52
+/* REset level statistics registers: */
+#define HV7131_REG_LOREFNOH 0x57
+#define HV7131_REG_LOREFNOL 0x58
+#define HV7131_REG_HIREFNOH 0x59
+#define HV7131_REG_HIREFNOL 0x5a
+
+/* se401 registers */
+#define SE401_OPERATINGMODE 0x2000
+
+
+/* size of usb transfers */
+#define SE401_PACKETSIZE 4096
+/* number of queued bulk transfers to use, should be about 8 */
+#define SE401_NUMSBUF 1
+/* read the usb specs for this one :) */
+#define SE401_VIDEO_ENDPOINT 1
+#define SE401_BUTTON_ENDPOINT 2
+/* number of frames supported by the v4l part */
+#define SE401_NUMFRAMES 2
+/* scratch buffers for passing data to the decoders */
+#define SE401_NUMSCRATCH 32
+/* maximum amount of data in a JangGu packet */
+#define SE401_VLCDATALEN 1024
+/* number of nul sized packets to receive before kicking the camera */
+#define SE401_MAX_NULLPACKETS 4000
+/* number of decoding errors before kicking the camera */
+#define SE401_MAX_ERRORS 200
+
+struct usb_device;
+
+struct se401_sbuf {
+ unsigned char *data;
+};
+
+enum {
+ FRAME_UNUSED, /* Unused (no MCAPTURE) */
+ FRAME_READY, /* Ready to start grabbing */
+ FRAME_GRABBING, /* In the process of being grabbed into */
+ FRAME_DONE, /* Finished grabbing, but not been synced yet */
+ FRAME_ERROR, /* Something bad happened while processing */
+};
+
+enum {
+ FMT_BAYER,
+ FMT_JANGGU,
+};
+
+enum {
+ BUFFER_UNUSED,
+ BUFFER_READY,
+ BUFFER_BUSY,
+ BUFFER_DONE,
+};
+
+struct se401_scratch {
+ unsigned char *data;
+ volatile int state;
+ int offset;
+ int length;
+};
+
+struct se401_frame {
+ unsigned char *data; /* Frame buffer */
+
+ volatile int grabstate; /* State of grabbing */
+
+ unsigned char *curline;
+ int curlinepix;
+ int curpix;
+};
+
+struct usb_se401 {
+ struct video_device vdev;
+
+ /* Device structure */
+ struct usb_device *dev;
+
+ unsigned char iface;
+
+ char *camera_name;
+
+ int change;
+ int brightness;
+ int hue;
+ int rgain;
+ int ggain;
+ int bgain;
+ int expose_h;
+ int expose_m;
+ int expose_l;
+ int resetlevel;
+
+ int enhance;
+
+ int format;
+ int sizes;
+ int *width;
+ int *height;
+ int cwidth; /* current width */
+ int cheight; /* current height */
+ int palette;
+ int maxframesize;
+ int cframesize; /* current framesize */
+
+ struct mutex lock;
+ int user; /* user count for exclusive use */
+ int removed; /* device disconnected */
+
+ int streaming; /* Are we streaming video? */
+
+ char *fbuf; /* Videodev buffer area */
+
+ struct urb *urb[SE401_NUMSBUF];
+ struct urb *inturb;
+
+ int button;
+ int buttonpressed;
+
+ int curframe; /* Current receiving frame */
+ struct se401_frame frame[SE401_NUMFRAMES];
+ int readcount;
+ int framecount;
+ int error;
+ int dropped;
+
+ int scratch_next;
+ int scratch_use;
+ int scratch_overflow;
+ struct se401_scratch scratch[SE401_NUMSCRATCH];
+
+ /* Decoder specific data: */
+ unsigned char vlcdata[SE401_VLCDATALEN];
+ int vlcdatapos;
+ int bayeroffset;
+
+ struct se401_sbuf sbuf[SE401_NUMSBUF];
+
+ wait_queue_head_t wq; /* Processes waiting */
+
+ int nullpackets;
+};
+
+
+
+#endif
+
diff --git a/drivers/media/video/sn9c102/Makefile b/drivers/media/video/sn9c102/Makefile
new file mode 100644
index 000000000000..8bcb0f71d69f
--- /dev/null
+++ b/drivers/media/video/sn9c102/Makefile
@@ -0,0 +1,7 @@
+sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o \
+ sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bca.o \
+ sn9c102_pas202bcb.o sn9c102_tas5110c1b.o \
+ sn9c102_tas5130d1b.o
+
+obj-$(CONFIG_USB_SN9C102) += sn9c102.o
+
diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h
new file mode 100644
index 000000000000..1d70a62b9f23
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102.h
@@ -0,0 +1,218 @@
+/***************************************************************************
+ * V4L2 driver for SN9C10x PC Camera Controllers *
+ * *
+ * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _SN9C102_H_
+#define _SN9C102_H_
+
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/rwsem.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/stddef.h>
+
+#include "sn9c102_sensor.h"
+
+/*****************************************************************************/
+
+#define SN9C102_DEBUG
+#define SN9C102_DEBUG_LEVEL 2
+#define SN9C102_MAX_DEVICES 64
+#define SN9C102_PRESERVE_IMGSCALE 0
+#define SN9C102_FORCE_MUNMAP 0
+#define SN9C102_MAX_FRAMES 32
+#define SN9C102_URBS 2
+#define SN9C102_ISO_PACKETS 7
+#define SN9C102_ALTERNATE_SETTING 8
+#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS)
+#define SN9C102_CTRL_TIMEOUT 300
+#define SN9C102_FRAME_TIMEOUT 2
+
+/*****************************************************************************/
+
+enum sn9c102_bridge {
+ BRIDGE_SN9C101 = 0x01,
+ BRIDGE_SN9C102 = 0x02,
+ BRIDGE_SN9C103 = 0x04,
+};
+
+SN9C102_ID_TABLE
+SN9C102_SENSOR_TABLE
+
+enum sn9c102_frame_state {
+ F_UNUSED,
+ F_QUEUED,
+ F_GRABBING,
+ F_DONE,
+ F_ERROR,
+};
+
+struct sn9c102_frame_t {
+ void* bufmem;
+ struct v4l2_buffer buf;
+ enum sn9c102_frame_state state;
+ struct list_head frame;
+ unsigned long vma_use_count;
+};
+
+enum sn9c102_dev_state {
+ DEV_INITIALIZED = 0x01,
+ DEV_DISCONNECTED = 0x02,
+ DEV_MISCONFIGURED = 0x04,
+};
+
+enum sn9c102_io_method {
+ IO_NONE,
+ IO_READ,
+ IO_MMAP,
+};
+
+enum sn9c102_stream_state {
+ STREAM_OFF,
+ STREAM_INTERRUPT,
+ STREAM_ON,
+};
+
+typedef char sn9c103_sof_header_t[18];
+typedef char sn9c102_sof_header_t[12];
+typedef char sn9c102_eof_header_t[4];
+
+struct sn9c102_sysfs_attr {
+ u8 reg, i2c_reg;
+ sn9c103_sof_header_t frame_header;
+};
+
+struct sn9c102_module_param {
+ u8 force_munmap;
+ u16 frame_timeout;
+};
+
+static DEFINE_MUTEX(sn9c102_sysfs_lock);
+static DECLARE_RWSEM(sn9c102_disconnect);
+
+struct sn9c102_device {
+ struct video_device* v4ldev;
+
+ enum sn9c102_bridge bridge;
+ struct sn9c102_sensor sensor;
+
+ struct usb_device* usbdev;
+ struct urb* urb[SN9C102_URBS];
+ void* transfer_buffer[SN9C102_URBS];
+ u8* control_buffer;
+
+ struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES];
+ struct list_head inqueue, outqueue;
+ u32 frame_count, nbuffers, nreadbuffers;
+
+ enum sn9c102_io_method io;
+ enum sn9c102_stream_state stream;
+
+ struct v4l2_jpegcompression compression;
+
+ struct sn9c102_sysfs_attr sysfs;
+ sn9c103_sof_header_t sof_header;
+ u16 reg[63];
+
+ struct sn9c102_module_param module_param;
+
+ enum sn9c102_dev_state state;
+ u8 users;
+
+ struct mutex dev_mutex, fileop_mutex;
+ spinlock_t queue_lock;
+ wait_queue_head_t open, wait_frame, wait_stream;
+};
+
+/*****************************************************************************/
+
+struct sn9c102_device*
+sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id)
+{
+ if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id))
+ return cam;
+
+ return NULL;
+}
+
+
+void
+sn9c102_attach_sensor(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor)
+{
+ memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor));
+}
+
+/*****************************************************************************/
+
+#undef DBG
+#undef KDBG
+#ifdef SN9C102_DEBUG
+# define DBG(level, fmt, args...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1) \
+ dev_err(&cam->usbdev->dev, fmt "\n", ## args); \
+ else if ((level) == 2) \
+ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \
+ else if ((level) >= 3) \
+ dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args); \
+ } \
+} while (0)
+# define V4LDBG(level, name, cmd) \
+do { \
+ if (debug >= (level)) \
+ v4l_print_ioctl(name, cmd); \
+} while (0)
+# define KDBG(level, fmt, args...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1 || (level) == 2) \
+ pr_info("sn9c102: " fmt "\n", ## args); \
+ else if ((level) == 3) \
+ pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__, \
+ __LINE__ , ## args); \
+ } \
+} while (0)
+#else
+# define DBG(level, fmt, args...) do {;} while(0)
+# define V4LDBG(level, name, cmd) do {;} while(0)
+# define KDBG(level, fmt, args...) do {;} while(0)
+#endif
+
+#undef PDBG
+#define PDBG(fmt, args...) \
+dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args)
+
+#undef PDBGG
+#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
+
+#endif /* _SN9C102_H_ */
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
new file mode 100644
index 000000000000..4c6cc6395723
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -0,0 +1,2919 @@
+/***************************************************************************
+ * V4L2 driver for SN9C10x PC Camera Controllers *
+ * *
+ * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/compiler.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/page-flags.h>
+#include <linux/byteorder/generic.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "sn9c102.h"
+
+/*****************************************************************************/
+
+#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers"
+#define SN9C102_MODULE_AUTHOR "(C) 2004-2006 Luca Risolia"
+#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
+#define SN9C102_MODULE_LICENSE "GPL"
+#define SN9C102_MODULE_VERSION "1:1.27"
+#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 27)
+
+/*****************************************************************************/
+
+MODULE_DEVICE_TABLE(usb, sn9c102_id_table);
+
+MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL);
+MODULE_DESCRIPTION(SN9C102_MODULE_NAME);
+MODULE_VERSION(SN9C102_MODULE_VERSION);
+MODULE_LICENSE(SN9C102_MODULE_LICENSE);
+
+static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1};
+module_param_array(video_nr, short, NULL, 0444);
+MODULE_PARM_DESC(video_nr,
+ "\n<-1|n[,...]> Specify V4L2 minor mode number."
+ "\n -1 = use next available (default)"
+ "\n n = use minor number n (integer >= 0)"
+ "\nYou can specify up to "__MODULE_STRING(SN9C102_MAX_DEVICES)
+ " cameras this way."
+ "\nFor example:"
+ "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+ "\nthe second camera and use auto for the first"
+ "\none and for every other camera."
+ "\n");
+
+static short force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] =
+ SN9C102_FORCE_MUNMAP};
+module_param_array(force_munmap, bool, NULL, 0444);
+MODULE_PARM_DESC(force_munmap,
+ "\n<0|1[,...]> Force the application to unmap previously"
+ "\nmapped buffer memory before calling any VIDIOC_S_CROP or"
+ "\nVIDIOC_S_FMT ioctl's. Not all the applications support"
+ "\nthis feature. This parameter is specific for each"
+ "\ndetected camera."
+ "\n 0 = do not force memory unmapping"
+ "\n 1 = force memory unmapping (save memory)"
+ "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
+ "\n");
+
+static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] =
+ SN9C102_FRAME_TIMEOUT};
+module_param_array(frame_timeout, uint, NULL, 0644);
+MODULE_PARM_DESC(frame_timeout,
+ "\n<n[,...]> Timeout for a video frame in seconds."
+ "\nThis parameter is specific for each detected camera."
+ "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"."
+ "\n");
+
+#ifdef SN9C102_DEBUG
+static unsigned short debug = SN9C102_DEBUG_LEVEL;
+module_param(debug, ushort, 0644);
+MODULE_PARM_DESC(debug,
+ "\n<n> Debugging information level, from 0 to 3:"
+ "\n0 = none (use carefully)"
+ "\n1 = critical errors"
+ "\n2 = significant informations"
+ "\n3 = more verbose messages"
+ "\nLevel 3 is useful for testing only, when only "
+ "one device is used."
+ "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"."
+ "\n");
+#endif
+
+/*****************************************************************************/
+
+static sn9c102_sof_header_t sn9c102_sof_header[] = {
+ {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x00},
+ {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01},
+};
+
+static sn9c103_sof_header_t sn9c103_sof_header[] = {
+ {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x20},
+};
+
+static sn9c102_eof_header_t sn9c102_eof_header[] = {
+ {0x00, 0x00, 0x00, 0x00},
+ {0x40, 0x00, 0x00, 0x00},
+ {0x80, 0x00, 0x00, 0x00},
+ {0xc0, 0x00, 0x00, 0x00},
+};
+
+/*****************************************************************************/
+
+static u32
+sn9c102_request_buffers(struct sn9c102_device* cam, u32 count,
+ enum sn9c102_io_method io)
+{
+ struct v4l2_pix_format* p = &(cam->sensor.pix_format);
+ struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
+ const size_t imagesize = cam->module_param.force_munmap ||
+ io == IO_READ ?
+ (p->width * p->height * p->priv) / 8 :
+ (r->width * r->height * p->priv) / 8;
+ void* buff = NULL;
+ u32 i;
+
+ if (count > SN9C102_MAX_FRAMES)
+ count = SN9C102_MAX_FRAMES;
+
+ cam->nbuffers = count;
+ while (cam->nbuffers > 0) {
+ if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize))))
+ break;
+ cam->nbuffers--;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
+ cam->frame[i].buf.index = i;
+ cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
+ cam->frame[i].buf.length = imagesize;
+ cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->frame[i].buf.sequence = 0;
+ cam->frame[i].buf.field = V4L2_FIELD_NONE;
+ cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
+ cam->frame[i].buf.flags = 0;
+ }
+
+ return cam->nbuffers;
+}
+
+
+static void sn9c102_release_buffers(struct sn9c102_device* cam)
+{
+ if (cam->nbuffers) {
+ vfree(cam->frame[0].bufmem);
+ cam->nbuffers = 0;
+ }
+ cam->frame_current = NULL;
+}
+
+
+static void sn9c102_empty_framequeues(struct sn9c102_device* cam)
+{
+ u32 i;
+
+ INIT_LIST_HEAD(&cam->inqueue);
+ INIT_LIST_HEAD(&cam->outqueue);
+
+ for (i = 0; i < SN9C102_MAX_FRAMES; i++) {
+ cam->frame[i].state = F_UNUSED;
+ cam->frame[i].buf.bytesused = 0;
+ }
+}
+
+
+static void sn9c102_requeue_outqueue(struct sn9c102_device* cam)
+{
+ struct sn9c102_frame_t *i;
+
+ list_for_each_entry(i, &cam->outqueue, frame) {
+ i->state = F_QUEUED;
+ list_add(&i->frame, &cam->inqueue);
+ }
+
+ INIT_LIST_HEAD(&cam->outqueue);
+}
+
+
+static void sn9c102_queue_unusedframes(struct sn9c102_device* cam)
+{
+ unsigned long lock_flags;
+ u32 i;
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].state == F_UNUSED) {
+ cam->frame[i].state = F_QUEUED;
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[i].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ }
+}
+
+/*****************************************************************************/
+
+int sn9c102_write_regs(struct sn9c102_device* cam, u8* buff, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ int i, res;
+
+ if (index + sizeof(buff) >= ARRAY_SIZE(cam->reg))
+ return -1;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ index, 0, buff, sizeof(buff),
+ SN9C102_CTRL_TIMEOUT*sizeof(buff));
+ if (res < 0) {
+ DBG(3, "Failed to write registers (index 0x%02X, error %d)",
+ index, res);
+ return -1;
+ }
+
+ for (i = 0; i < sizeof(buff); i++)
+ cam->reg[index+i] = buff[i];
+
+ return 0;
+}
+
+
+int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int res;
+
+ if (index >= ARRAY_SIZE(cam->reg))
+ return -1;
+
+ *buff = value;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
+ if (res < 0) {
+ DBG(3, "Failed to write a register (value 0x%02X, index "
+ "0x%02X, error %d)", value, index, res);
+ return -1;
+ }
+
+ cam->reg[index] = value;
+
+ return 0;
+}
+
+
+/* NOTE: reading some registers always returns 0 */
+static int sn9c102_read_reg(struct sn9c102_device* cam, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+ index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ DBG(3, "Failed to read a register (index 0x%02X, error %d)",
+ index, res);
+
+ return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index)
+{
+ if (index >= ARRAY_SIZE(cam->reg))
+ return -1;
+
+ return cam->reg[index];
+}
+
+
+static int
+sn9c102_i2c_wait(struct sn9c102_device* cam, struct sn9c102_sensor* sensor)
+{
+ int i, r;
+
+ for (i = 1; i <= 5; i++) {
+ r = sn9c102_read_reg(cam, 0x08);
+ if (r < 0)
+ return -EIO;
+ if (r & 0x04)
+ return 0;
+ if (sensor->frequency & SN9C102_I2C_400KHZ)
+ udelay(5*16);
+ else
+ udelay(16*16);
+ }
+ return -EBUSY;
+}
+
+
+static int
+sn9c102_i2c_detect_read_error(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor)
+{
+ int r;
+ r = sn9c102_read_reg(cam, 0x08);
+ return (r < 0 || (r >= 0 && !(r & 0x08))) ? -EIO : 0;
+}
+
+
+static int
+sn9c102_i2c_detect_write_error(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor)
+{
+ int r;
+ r = sn9c102_read_reg(cam, 0x08);
+ return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0;
+}
+
+
+int
+sn9c102_i2c_try_raw_read(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor, u8 data0, u8 data1,
+ u8 n, u8 buffer[])
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ /* Write cycle */
+ data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
+ ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10;
+ data[1] = data0; /* I2C slave id */
+ data[2] = data1; /* address */
+ data[7] = 0x10;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += sn9c102_i2c_wait(cam, sensor);
+
+ /* Read cycle - n bytes */
+ data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
+ ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) |
+ (n << 4) | 0x02;
+ data[1] = data0;
+ data[7] = 0x10;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += sn9c102_i2c_wait(cam, sensor);
+
+ /* The first read byte will be placed in data[4] */
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+ 0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += sn9c102_i2c_detect_read_error(cam, sensor);
+
+ PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1,
+ data[4]);
+
+ if (err) {
+ DBG(3, "I2C read failed for %s image sensor", sensor->name);
+ return -1;
+ }
+
+ if (buffer)
+ memcpy(buffer, data, sizeof(buffer));
+
+ return (int)data[4];
+}
+
+
+int
+sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor, u8 n, u8 data0,
+ u8 data1, u8 data2, u8 data3, u8 data4, u8 data5)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ /* Write cycle. It usually is address + value */
+ data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
+ ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0)
+ | ((n - 1) << 4);
+ data[1] = data0;
+ data[2] = data1;
+ data[3] = data2;
+ data[4] = data3;
+ data[5] = data4;
+ data[6] = data5;
+ data[7] = 0x14;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += sn9c102_i2c_wait(cam, sensor);
+ err += sn9c102_i2c_detect_write_error(cam, sensor);
+
+ if (err)
+ DBG(3, "I2C write failed for %s image sensor", sensor->name);
+
+ PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
+ "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X",
+ n, data0, data1, data2, data3, data4, data5);
+
+ return err ? -1 : 0;
+}
+
+
+int
+sn9c102_i2c_try_read(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor, u8 address)
+{
+ return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id,
+ address, 1, NULL);
+}
+
+
+int
+sn9c102_i2c_try_write(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor, u8 address, u8 value)
+{
+ return sn9c102_i2c_try_raw_write(cam, sensor, 3,
+ sensor->i2c_slave_id, address,
+ value, 0, 0, 0);
+}
+
+
+int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address)
+{
+ return sn9c102_i2c_try_read(cam, &cam->sensor, address);
+}
+
+
+int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value)
+{
+ return sn9c102_i2c_try_write(cam, &cam->sensor, address, value);
+}
+
+/*****************************************************************************/
+
+static void*
+sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
+{
+ size_t soflen = 0, i;
+ u8 j, n = 0;
+
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ soflen = sizeof(sn9c102_sof_header_t);
+ n = sizeof(sn9c102_sof_header) / soflen;
+ break;
+ case BRIDGE_SN9C103:
+ soflen = sizeof(sn9c103_sof_header_t);
+ n = sizeof(sn9c103_sof_header) / soflen;
+ }
+
+ for (i = 0; (len >= soflen) && (i <= len - soflen); i++)
+ for (j = 0; j < n; j++)
+ /* The invariable part of the header is 6 bytes long */
+ if ((cam->bridge != BRIDGE_SN9C103 &&
+ !memcmp(mem + i, sn9c102_sof_header[j], 6)) ||
+ (cam->bridge == BRIDGE_SN9C103 &&
+ !memcmp(mem + i, sn9c103_sof_header[j], 6))) {
+ memcpy(cam->sof_header, mem + i, soflen);
+ /* Skip the header */
+ return mem + i + soflen;
+ }
+
+ return NULL;
+}
+
+
+static void*
+sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
+{
+ size_t eoflen = sizeof(sn9c102_eof_header_t), i;
+ unsigned j, n = sizeof(sn9c102_eof_header) / eoflen;
+
+ if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
+ return NULL; /* EOF header does not exist in compressed data */
+
+ for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++)
+ for (j = 0; j < n; j++)
+ if (!memcmp(mem + i, sn9c102_eof_header[j], eoflen))
+ return mem + i;
+
+ return NULL;
+}
+
+
+static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
+{
+ struct sn9c102_device* cam = urb->context;
+ struct sn9c102_frame_t** f;
+ size_t imagesize, soflen;
+ u8 i;
+ int err = 0;
+
+ if (urb->status == -ENOENT)
+ return;
+
+ f = &cam->frame_current;
+
+ if (cam->stream == STREAM_INTERRUPT) {
+ cam->stream = STREAM_OFF;
+ if ((*f))
+ (*f)->state = F_QUEUED;
+ DBG(3, "Stream interrupted");
+ wake_up(&cam->wait_stream);
+ }
+
+ if (cam->state & DEV_DISCONNECTED)
+ return;
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ wake_up_interruptible(&cam->wait_frame);
+ return;
+ }
+
+ if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
+ goto resubmit_urb;
+
+ if (!(*f))
+ (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t,
+ frame);
+
+ imagesize = (cam->sensor.pix_format.width *
+ cam->sensor.pix_format.height *
+ cam->sensor.pix_format.priv) / 8;
+
+ soflen = (cam->bridge) == BRIDGE_SN9C103 ?
+ sizeof(sn9c103_sof_header_t) :
+ sizeof(sn9c102_sof_header_t);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int img, len, status;
+ void *pos, *sof, *eof;
+
+ len = urb->iso_frame_desc[i].actual_length;
+ status = urb->iso_frame_desc[i].status;
+ pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+ if (status) {
+ DBG(3, "Error in isochronous frame");
+ (*f)->state = F_ERROR;
+ continue;
+ }
+
+ PDBGG("Isochrnous frame: length %u, #%u i", len, i);
+
+redo:
+ sof = sn9c102_find_sof_header(cam, pos, len);
+ if (likely(!sof)) {
+ eof = sn9c102_find_eof_header(cam, pos, len);
+ if ((*f)->state == F_GRABBING) {
+end_of_frame:
+ img = len;
+
+ if (eof)
+ img = (eof > pos) ? eof - pos - 1 : 0;
+
+ if ((*f)->buf.bytesused+img > imagesize) {
+ u32 b;
+ b = (*f)->buf.bytesused + img -
+ imagesize;
+ img = imagesize - (*f)->buf.bytesused;
+ DBG(3, "Expected EOF not found: "
+ "video frame cut");
+ if (eof)
+ DBG(3, "Exceeded limit: +%u "
+ "bytes", (unsigned)(b));
+ }
+
+ memcpy((*f)->bufmem + (*f)->buf.bytesused, pos,
+ img);
+
+ if ((*f)->buf.bytesused == 0)
+ do_gettimeofday(&(*f)->buf.timestamp);
+
+ (*f)->buf.bytesused += img;
+
+ if ((*f)->buf.bytesused == imagesize ||
+ (cam->sensor.pix_format.pixelformat ==
+ V4L2_PIX_FMT_SN9C10X && eof)) {
+ u32 b;
+ b = (*f)->buf.bytesused;
+ (*f)->state = F_DONE;
+ (*f)->buf.sequence= ++cam->frame_count;
+ spin_lock(&cam->queue_lock);
+ list_move_tail(&(*f)->frame,
+ &cam->outqueue);
+ if (!list_empty(&cam->inqueue))
+ (*f) = list_entry(
+ cam->inqueue.next,
+ struct sn9c102_frame_t,
+ frame );
+ else
+ (*f) = NULL;
+ spin_unlock(&cam->queue_lock);
+ memcpy(cam->sysfs.frame_header,
+ cam->sof_header, soflen);
+ DBG(3, "Video frame captured: %lu "
+ "bytes", (unsigned long)(b));
+
+ if (!(*f))
+ goto resubmit_urb;
+
+ } else if (eof) {
+ (*f)->state = F_ERROR;
+ DBG(3, "Not expected EOF after %lu "
+ "bytes of image data",
+ (unsigned long)
+ ((*f)->buf.bytesused));
+ }
+
+ if (sof) /* (1) */
+ goto start_of_frame;
+
+ } else if (eof) {
+ DBG(3, "EOF without SOF");
+ continue;
+
+ } else {
+ PDBGG("Ignoring pointless isochronous frame");
+ continue;
+ }
+
+ } else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) {
+start_of_frame:
+ (*f)->state = F_GRABBING;
+ (*f)->buf.bytesused = 0;
+ len -= (sof - pos);
+ pos = sof;
+ DBG(3, "SOF detected: new video frame");
+ if (len)
+ goto redo;
+
+ } else if ((*f)->state == F_GRABBING) {
+ eof = sn9c102_find_eof_header(cam, pos, len);
+ if (eof && eof < sof)
+ goto end_of_frame; /* (1) */
+ else {
+ if (cam->sensor.pix_format.pixelformat ==
+ V4L2_PIX_FMT_SN9C10X) {
+ eof = sof - soflen;
+ goto end_of_frame;
+ } else {
+ DBG(3, "SOF before expected EOF after "
+ "%lu bytes of image data",
+ (unsigned long)
+ ((*f)->buf.bytesused));
+ goto start_of_frame;
+ }
+ }
+ }
+ }
+
+resubmit_urb:
+ urb->dev = cam->usbdev;
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0 && err != -EPERM) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "usb_submit_urb() failed");
+ }
+
+ wake_up_interruptible(&cam->wait_frame);
+}
+
+
+static int sn9c102_start_transfer(struct sn9c102_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ struct urb* urb;
+ const unsigned int sn9c102_wMaxPacketSize[] = {0, 128, 256, 384, 512,
+ 680, 800, 900, 1023};
+ const unsigned int sn9c103_wMaxPacketSize[] = {0, 128, 256, 384, 512,
+ 680, 800, 900, 1003};
+ const unsigned int psz = (cam->bridge == BRIDGE_SN9C103) ?
+ sn9c103_wMaxPacketSize[SN9C102_ALTERNATE_SETTING] :
+ sn9c102_wMaxPacketSize[SN9C102_ALTERNATE_SETTING];
+ s8 i, j;
+ int err = 0;
+
+ for (i = 0; i < SN9C102_URBS; i++) {
+ cam->transfer_buffer[i] = kzalloc(SN9C102_ISO_PACKETS * psz,
+ GFP_KERNEL);
+ if (!cam->transfer_buffer[i]) {
+ err = -ENOMEM;
+ DBG(1, "Not enough memory");
+ goto free_buffers;
+ }
+ }
+
+ for (i = 0; i < SN9C102_URBS; i++) {
+ urb = usb_alloc_urb(SN9C102_ISO_PACKETS, GFP_KERNEL);
+ cam->urb[i] = urb;
+ if (!urb) {
+ err = -ENOMEM;
+ DBG(1, "usb_alloc_urb() failed");
+ goto free_urbs;
+ }
+ urb->dev = udev;
+ urb->context = cam;
+ urb->pipe = usb_rcvisocpipe(udev, 1);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->number_of_packets = SN9C102_ISO_PACKETS;
+ urb->complete = sn9c102_urb_complete;
+ urb->transfer_buffer = cam->transfer_buffer[i];
+ urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS;
+ urb->interval = 1;
+ for (j = 0; j < SN9C102_ISO_PACKETS; j++) {
+ urb->iso_frame_desc[j].offset = psz * j;
+ urb->iso_frame_desc[j].length = psz;
+ }
+ }
+
+ /* Enable video */
+ if (!(cam->reg[0x01] & 0x04)) {
+ err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01);
+ if (err) {
+ err = -EIO;
+ DBG(1, "I/O hardware error");
+ goto free_urbs;
+ }
+ }
+
+ err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING);
+ if (err) {
+ DBG(1, "usb_set_interface() failed");
+ goto free_urbs;
+ }
+
+ cam->frame_current = NULL;
+
+ for (i = 0; i < SN9C102_URBS; i++) {
+ err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+ if (err) {
+ for (j = i-1; j >= 0; j--)
+ usb_kill_urb(cam->urb[j]);
+ DBG(1, "usb_submit_urb() failed, error %d", err);
+ goto free_urbs;
+ }
+ }
+
+ return 0;
+
+free_urbs:
+ for (i = 0; (i < SN9C102_URBS) && cam->urb[i]; i++)
+ usb_free_urb(cam->urb[i]);
+
+free_buffers:
+ for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++)
+ kfree(cam->transfer_buffer[i]);
+
+ return err;
+}
+
+
+static int sn9c102_stop_transfer(struct sn9c102_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ s8 i;
+ int err = 0;
+
+ if (cam->state & DEV_DISCONNECTED)
+ return 0;
+
+ for (i = SN9C102_URBS-1; i >= 0; i--) {
+ usb_kill_urb(cam->urb[i]);
+ usb_free_urb(cam->urb[i]);
+ kfree(cam->transfer_buffer[i]);
+ }
+
+ err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+ if (err)
+ DBG(3, "usb_set_interface() failed");
+
+ return err;
+}
+
+
+static int sn9c102_stream_interrupt(struct sn9c102_device* cam)
+{
+ long timeout;
+
+ cam->stream = STREAM_INTERRUPT;
+ timeout = wait_event_timeout(cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED),
+ SN9C102_URB_TIMEOUT);
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ else if (cam->stream != STREAM_OFF) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "URB timeout reached. The camera is misconfigured. "
+ "To use it, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count)
+{
+ char str[5];
+ char* endp;
+ unsigned long val;
+
+ if (len < 4) {
+ strncpy(str, buff, len);
+ str[len+1] = '\0';
+ } else {
+ strncpy(str, buff, 4);
+ str[4] = '\0';
+ }
+
+ val = simple_strtoul(str, &endp, 0);
+
+ *count = 0;
+ if (val <= 0xff)
+ *count = (ssize_t)(endp - str);
+ if ((*count) && (len == *count+1) && (buff[*count] == '\n'))
+ *count += 1;
+
+ return (u8)val;
+}
+
+/*
+ NOTE 1: being inside one of the following methods implies that the v4l
+ device exists for sure (see kobjects and reference counters)
+ NOTE 2: buffers are PAGE_SIZE long
+*/
+
+static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ count = sprintf(buf, "%u\n", cam->sysfs.reg);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ u8 index;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ index = sn9c102_strtou8(buf, len, &count);
+ if (index > 0x1f || !count) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EINVAL;
+ }
+
+ cam->sysfs.reg = index;
+
+ DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t sn9c102_show_val(struct class_device* cd, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+ int val;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EIO;
+ }
+
+ count = sprintf(buf, "%d\n", val);
+
+ DBG(3, "Read bytes: %zd", count);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_val(struct class_device* cd, const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ u8 value;
+ ssize_t count;
+ int err;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EINVAL;
+ }
+
+ err = sn9c102_write_reg(cam, value, cam->sysfs.reg);
+ if (err) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EIO;
+ }
+
+ DBG(2, "Written SN9C10X reg. 0x%02X, val. 0x%02X",
+ cam->sysfs.reg, value);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg);
+
+ DBG(3, "Read bytes: %zd", count);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ u8 index;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ index = sn9c102_strtou8(buf, len, &count);
+ if (!count) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EINVAL;
+ }
+
+ cam->sysfs.i2c_reg = index;
+
+ DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+ int val;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENOSYS;
+ }
+
+ if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EIO;
+ }
+
+ count = sprintf(buf, "%d\n", val);
+
+ DBG(3, "Read bytes: %zd", count);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ u8 value;
+ ssize_t count;
+ int err;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENOSYS;
+ }
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EINVAL;
+ }
+
+ err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value);
+ if (err) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EIO;
+ }
+
+ DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X",
+ cam->sysfs.i2c_reg, value);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_green(struct class_device* cd, const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ enum sn9c102_bridge bridge;
+ ssize_t res = 0;
+ u8 value;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ bridge = cam->bridge;
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count)
+ return -EINVAL;
+
+ switch (bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ if (value > 0x0f)
+ return -EINVAL;
+ if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+ break;
+ case BRIDGE_SN9C103:
+ if (value > 0x7f)
+ return -EINVAL;
+ if ((res = sn9c102_store_reg(cd, "0x04", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+ break;
+ }
+
+ return res;
+}
+
+
+static ssize_t
+sn9c102_store_blue(struct class_device* cd, const char* buf, size_t len)
+{
+ ssize_t res = 0;
+ u8 value;
+ ssize_t count;
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count || value > 0x7f)
+ return -EINVAL;
+
+ if ((res = sn9c102_store_reg(cd, "0x06", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+
+ return res;
+}
+
+
+static ssize_t
+sn9c102_store_red(struct class_device* cd, const char* buf, size_t len)
+{
+ ssize_t res = 0;
+ u8 value;
+ ssize_t count;
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count || value > 0x7f)
+ return -EINVAL;
+
+ if ((res = sn9c102_store_reg(cd, "0x05", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+
+ return res;
+}
+
+
+static ssize_t sn9c102_show_frame_header(struct class_device* cd, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam)
+ return -ENODEV;
+
+ count = sizeof(cam->sysfs.frame_header);
+ memcpy(buf, cam->sysfs.frame_header, count);
+
+ DBG(3, "Frame header, read bytes: %zd", count);
+
+ return count;
+}
+
+
+static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR,
+ sn9c102_show_reg, sn9c102_store_reg);
+static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR,
+ sn9c102_show_val, sn9c102_store_val);
+static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
+ sn9c102_show_i2c_reg, sn9c102_store_i2c_reg);
+static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
+ sn9c102_show_i2c_val, sn9c102_store_i2c_val);
+static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green);
+static CLASS_DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue);
+static CLASS_DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red);
+static CLASS_DEVICE_ATTR(frame_header, S_IRUGO,
+ sn9c102_show_frame_header, NULL);
+
+
+static void sn9c102_create_sysfs(struct sn9c102_device* cam)
+{
+ struct video_device *v4ldev = cam->v4ldev;
+
+ video_device_create_file(v4ldev, &class_device_attr_reg);
+ video_device_create_file(v4ldev, &class_device_attr_val);
+ video_device_create_file(v4ldev, &class_device_attr_frame_header);
+ if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102)
+ video_device_create_file(v4ldev, &class_device_attr_green);
+ else if (cam->bridge == BRIDGE_SN9C103) {
+ video_device_create_file(v4ldev, &class_device_attr_blue);
+ video_device_create_file(v4ldev, &class_device_attr_red);
+ }
+ if (cam->sensor.sysfs_ops) {
+ video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
+ video_device_create_file(v4ldev, &class_device_attr_i2c_val);
+ }
+}
+#endif /* CONFIG_VIDEO_ADV_DEBUG */
+
+/*****************************************************************************/
+
+static int
+sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, 0x18);
+ else
+ err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, 0x18);
+
+ return err ? -EIO : 0;
+}
+
+
+static int
+sn9c102_set_compression(struct sn9c102_device* cam,
+ struct v4l2_jpegcompression* compression)
+{
+ int err = 0;
+
+ if (compression->quality == 0)
+ err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, 0x17);
+ else if (compression->quality == 1)
+ err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, 0x17);
+
+ return err ? -EIO : 0;
+}
+
+
+static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale)
+{
+ u8 r = 0;
+ int err = 0;
+
+ if (scale == 1)
+ r = cam->reg[0x18] & 0xcf;
+ else if (scale == 2) {
+ r = cam->reg[0x18] & 0xcf;
+ r |= 0x10;
+ } else if (scale == 4)
+ r = cam->reg[0x18] | 0x20;
+
+ err += sn9c102_write_reg(cam, r, 0x18);
+ if (err)
+ return -EIO;
+
+ PDBGG("Scaling factor: %u", scale);
+
+ return 0;
+}
+
+
+static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left),
+ v_start = (u8)(rect->top - s->cropcap.bounds.top),
+ h_size = (u8)(rect->width / 16),
+ v_size = (u8)(rect->height / 16);
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+ err += sn9c102_write_reg(cam, h_size, 0x15);
+ err += sn9c102_write_reg(cam, v_size, 0x16);
+ if (err)
+ return -EIO;
+
+ PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size "
+ "%u %u %u %u", h_start, v_start, h_size, v_size);
+
+ return 0;
+}
+
+
+static int sn9c102_init(struct sn9c102_device* cam)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ struct v4l2_queryctrl *qctrl;
+ struct v4l2_rect* rect;
+ u8 i = 0;
+ int err = 0;
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ init_waitqueue_head(&cam->open);
+ qctrl = s->qctrl;
+ rect = &(s->cropcap.defrect);
+ } else { /* use current values */
+ qctrl = s->_qctrl;
+ rect = &(s->_rect);
+ }
+
+ err += sn9c102_set_scale(cam, rect->width / s->pix_format.width);
+ err += sn9c102_set_crop(cam, rect);
+ if (err)
+ return err;
+
+ if (s->init) {
+ err = s->init(cam);
+ if (err) {
+ DBG(3, "Sensor initialization failed");
+ return err;
+ }
+ }
+
+ if (!(cam->state & DEV_INITIALIZED))
+ cam->compression.quality = cam->reg[0x17] & 0x01 ? 0 : 1;
+ else
+ err += sn9c102_set_compression(cam, &cam->compression);
+ err += sn9c102_set_pix_format(cam, &s->pix_format);
+ if (s->set_pix_format)
+ err += s->set_pix_format(cam, &s->pix_format);
+ if (err)
+ return err;
+
+ if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
+ DBG(3, "Compressed video format is active, quality %d",
+ cam->compression.quality);
+ else
+ DBG(3, "Uncompressed video format is active");
+
+ if (s->set_crop)
+ if ((err = s->set_crop(cam, rect))) {
+ DBG(3, "set_crop() failed");
+ return err;
+ }
+
+ if (s->set_ctrl) {
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (s->qctrl[i].id != 0 &&
+ !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) {
+ ctrl.id = s->qctrl[i].id;
+ ctrl.value = qctrl[i].default_value;
+ err = s->set_ctrl(cam, &ctrl);
+ if (err) {
+ DBG(3, "Set %s control failed",
+ s->qctrl[i].name);
+ return err;
+ }
+ DBG(3, "Image sensor supports '%s' control",
+ s->qctrl[i].name);
+ }
+ }
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ mutex_init(&cam->fileop_mutex);
+ spin_lock_init(&cam->queue_lock);
+ init_waitqueue_head(&cam->wait_frame);
+ init_waitqueue_head(&cam->wait_stream);
+ cam->nreadbuffers = 2;
+ memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
+ memcpy(&(s->_rect), &(s->cropcap.defrect),
+ sizeof(struct v4l2_rect));
+ cam->state |= DEV_INITIALIZED;
+ }
+
+ DBG(2, "Initialization succeeded");
+ return 0;
+}
+
+
+static void sn9c102_release_resources(struct sn9c102_device* cam)
+{
+ mutex_lock(&sn9c102_sysfs_lock);
+
+ DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
+ video_set_drvdata(cam->v4ldev, NULL);
+ video_unregister_device(cam->v4ldev);
+
+ usb_put_dev(cam->usbdev);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ kfree(cam->control_buffer);
+}
+
+/*****************************************************************************/
+
+static int sn9c102_open(struct inode* inode, struct file* filp)
+{
+ struct sn9c102_device* cam;
+ int err = 0;
+
+ /*
+ This is the only safe way to prevent race conditions with
+ disconnect
+ */
+ if (!down_read_trylock(&sn9c102_disconnect))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(video_devdata(filp));
+
+ if (mutex_lock_interruptible(&cam->dev_mutex)) {
+ up_read(&sn9c102_disconnect);
+ return -ERESTARTSYS;
+ }
+
+ if (cam->users) {
+ DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor);
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (filp->f_flags & O_NDELAY)) {
+ err = -EWOULDBLOCK;
+ goto out;
+ }
+ mutex_unlock(&cam->dev_mutex);
+ err = wait_event_interruptible_exclusive(cam->open,
+ cam->state & DEV_DISCONNECTED
+ || !cam->users);
+ if (err) {
+ up_read(&sn9c102_disconnect);
+ return err;
+ }
+ if (cam->state & DEV_DISCONNECTED) {
+ up_read(&sn9c102_disconnect);
+ return -ENODEV;
+ }
+ mutex_lock(&cam->dev_mutex);
+ }
+
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ err = sn9c102_init(cam);
+ if (err) {
+ DBG(1, "Initialization failed again. "
+ "I will retry on next open().");
+ goto out;
+ }
+ cam->state &= ~DEV_MISCONFIGURED;
+ }
+
+ if ((err = sn9c102_start_transfer(cam)))
+ goto out;
+
+ filp->private_data = cam;
+ cam->users++;
+ cam->io = IO_NONE;
+ cam->stream = STREAM_OFF;
+ cam->nbuffers = 0;
+ cam->frame_count = 0;
+ sn9c102_empty_framequeues(cam);
+
+ DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
+
+out:
+ mutex_unlock(&cam->dev_mutex);
+ up_read(&sn9c102_disconnect);
+ return err;
+}
+
+
+static int sn9c102_release(struct inode* inode, struct file* filp)
+{
+ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+
+ mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */
+
+ sn9c102_stop_transfer(cam);
+
+ sn9c102_release_buffers(cam);
+
+ if (cam->state & DEV_DISCONNECTED) {
+ sn9c102_release_resources(cam);
+ mutex_unlock(&cam->dev_mutex);
+ kfree(cam);
+ return 0;
+ }
+
+ cam->users--;
+ wake_up_interruptible_nr(&cam->open, 1);
+
+ DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);
+
+ mutex_unlock(&cam->dev_mutex);
+
+ return 0;
+}
+
+
+static ssize_t
+sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
+{
+ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ struct sn9c102_frame_t* f, * i;
+ unsigned long lock_flags;
+ long timeout;
+ int err = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (cam->io == IO_MMAP) {
+ DBG(3, "Close and open the device again to choose "
+ "the read method");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) {
+ DBG(1, "read() failed, not enough memory");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENOMEM;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (list_empty(&cam->inqueue)) {
+ if (!list_empty(&cam->outqueue))
+ sn9c102_empty_framequeues(cam);
+ sn9c102_queue_unusedframes(cam);
+ }
+
+ if (!count) {
+ mutex_unlock(&cam->fileop_mutex);
+ return 0;
+ }
+
+ if (list_empty(&cam->outqueue)) {
+ if (filp->f_flags & O_NONBLOCK) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EAGAIN;
+ }
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0) {
+ mutex_unlock(&cam->fileop_mutex);
+ return timeout;
+ }
+ if (cam->state & DEV_DISCONNECTED) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+ if (!timeout || (cam->state & DEV_MISCONFIGURED)) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+ }
+
+ f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame);
+
+ if (count > f->buf.bytesused)
+ count = f->buf.bytesused;
+
+ if (copy_to_user(buf, f->bufmem, count)) {
+ err = -EFAULT;
+ goto exit;
+ }
+ *f_pos += count;
+
+exit:
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(i, &cam->outqueue, frame)
+ i->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ sn9c102_queue_unusedframes(cam);
+
+ PDBGG("Frame #%lu, bytes read: %zu",
+ (unsigned long)f->buf.index, count);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return count;
+}
+
+
+static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
+{
+ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ struct sn9c102_frame_t* f;
+ unsigned long lock_flags;
+ unsigned int mask = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return POLLERR;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ goto error;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ goto error;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!sn9c102_request_buffers(cam, cam->nreadbuffers,
+ IO_READ)) {
+ DBG(1, "poll() failed, not enough memory");
+ goto error;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (cam->io == IO_READ) {
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(f, &cam->outqueue, frame)
+ f->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ sn9c102_queue_unusedframes(cam);
+ }
+
+ poll_wait(filp, &cam->wait_frame, wait);
+
+ if (!list_empty(&cam->outqueue))
+ mask |= POLLIN | POLLRDNORM;
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return mask;
+
+error:
+ mutex_unlock(&cam->fileop_mutex);
+ return POLLERR;
+}
+
+
+static void sn9c102_vm_open(struct vm_area_struct* vma)
+{
+ struct sn9c102_frame_t* f = vma->vm_private_data;
+ f->vma_use_count++;
+}
+
+
+static void sn9c102_vm_close(struct vm_area_struct* vma)
+{
+ /* NOTE: buffers are not freed here */
+ struct sn9c102_frame_t* f = vma->vm_private_data;
+ f->vma_use_count--;
+}
+
+
+static struct vm_operations_struct sn9c102_vm_ops = {
+ .open = sn9c102_vm_open,
+ .close = sn9c102_vm_close,
+};
+
+
+static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ unsigned long size = vma->vm_end - vma->vm_start,
+ start = vma->vm_start;
+ void *pos;
+ u32 i;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
+ size != PAGE_ALIGN(cam->frame[0].buf.length)) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+ if (i == cam->nbuffers) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED;
+
+ pos = cam->frame[i].bufmem;
+ while (size > 0) { /* size is page-aligned */
+ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vma->vm_ops = &sn9c102_vm_ops;
+ vma->vm_private_data = &cam->frame[i];
+
+ sn9c102_vm_open(vma);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+static int
+sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_capability cap = {
+ .driver = "sn9c102",
+ .version = SN9C102_MODULE_VERSION_CODE,
+ .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING,
+ };
+
+ strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
+ if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
+ strlcpy(cap.bus_info, cam->usbdev->dev.bus_id,
+ sizeof(cap.bus_info));
+
+ if (copy_to_user(arg, &cap, sizeof(cap)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_input i;
+
+ if (copy_from_user(&i, arg, sizeof(i)))
+ return -EFAULT;
+
+ if (i.index)
+ return -EINVAL;
+
+ memset(&i, 0, sizeof(i));
+ strcpy(i.name, "Camera");
+ i.type = V4L2_INPUT_TYPE_CAMERA;
+
+ if (copy_to_user(arg, &i, sizeof(i)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg)
+{
+ int index = 0;
+
+ if (copy_to_user(arg, &index, sizeof(index)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg)
+{
+ int index;
+
+ if (copy_from_user(&index, arg, sizeof(index)))
+ return -EFAULT;
+
+ if (index != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_queryctrl qc;
+ u8 i;
+
+ if (copy_from_user(&qc, arg, sizeof(qc)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (qc.id && qc.id == s->qctrl[i].id) {
+ memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
+ if (copy_to_user(arg, &qc, sizeof(qc)))
+ return -EFAULT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+
+static int
+sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ int err = 0;
+ u8 i;
+
+ if (!s->get_ctrl && !s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ if (!s->get_ctrl) {
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (ctrl.id && ctrl.id == s->qctrl[i].id) {
+ ctrl.value = s->_qctrl[i].default_value;
+ goto exit;
+ }
+ return -EINVAL;
+ } else
+ err = s->get_ctrl(cam, &ctrl);
+
+exit:
+ if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
+ return -EFAULT;
+
+ return err;
+}
+
+
+static int
+sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ u8 i;
+ int err = 0;
+
+ if (!s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (ctrl.id == s->qctrl[i].id) {
+ if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+ if (ctrl.value < s->qctrl[i].minimum ||
+ ctrl.value > s->qctrl[i].maximum)
+ return -ERANGE;
+ ctrl.value -= ctrl.value % s->qctrl[i].step;
+ break;
+ }
+
+ if ((err = s->set_ctrl(cam, &ctrl)))
+ return err;
+
+ s->_qctrl[i].default_value = ctrl.value;
+
+ PDBGG("VIDIOC_S_CTRL: id %lu, value %lu",
+ (unsigned long)ctrl.id, (unsigned long)ctrl.value);
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_cropcap* cc = &(cam->sensor.cropcap);
+
+ cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cc->pixelaspect.numerator = 1;
+ cc->pixelaspect.denominator = 1;
+
+ if (copy_to_user(arg, cc, sizeof(*cc)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_crop crop = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ };
+
+ memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
+
+ if (copy_to_user(arg, &crop, sizeof(crop)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_crop crop;
+ struct v4l2_rect* rect;
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ struct v4l2_pix_format* pix_format = &(s->pix_format);
+ u8 scale;
+ const enum sn9c102_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&crop, arg, sizeof(crop)))
+ return -EFAULT;
+
+ rect = &(crop.c);
+
+ if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (cam->module_param.force_munmap)
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_CROP failed. "
+ "Unmap the buffers first.");
+ return -EINVAL;
+ }
+
+ /* Preserve R,G or B origin */
+ rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L;
+ rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L;
+
+ if (rect->width < 16)
+ rect->width = 16;
+ if (rect->height < 16)
+ rect->height = 16;
+ if (rect->width > bounds->width)
+ rect->width = bounds->width;
+ if (rect->height > bounds->height)
+ rect->height = bounds->height;
+ if (rect->left < bounds->left)
+ rect->left = bounds->left;
+ if (rect->top < bounds->top)
+ rect->top = bounds->top;
+ if (rect->left + rect->width > bounds->left + bounds->width)
+ rect->left = bounds->left+bounds->width - rect->width;
+ if (rect->top + rect->height > bounds->top + bounds->height)
+ rect->top = bounds->top+bounds->height - rect->height;
+
+ rect->width &= ~15L;
+ rect->height &= ~15L;
+
+ if (SN9C102_PRESERVE_IMGSCALE) {
+ /* Calculate the actual scaling factor */
+ u32 a, b;
+ a = rect->width * rect->height;
+ b = pix_format->width * pix_format->height;
+ scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1;
+ } else
+ scale = 1;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
+ return err;
+
+ if (copy_to_user(arg, &crop, sizeof(crop))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ if (cam->module_param.force_munmap || cam->io == IO_READ)
+ sn9c102_release_buffers(cam);
+
+ err = sn9c102_set_crop(cam, rect);
+ if (s->set_crop)
+ err += s->set_crop(cam, rect);
+ err += sn9c102_set_scale(cam, scale);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ s->pix_format.width = rect->width/scale;
+ s->pix_format.height = rect->height/scale;
+ memcpy(&(s->_rect), rect, sizeof(*rect));
+
+ if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+ nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -ENOMEM;
+ }
+
+ if (cam->io == IO_READ)
+ sn9c102_empty_framequeues(cam);
+ else if (cam->module_param.force_munmap)
+ sn9c102_requeue_outqueue(cam);
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_fmtdesc fmtd;
+
+ if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
+ return -EFAULT;
+
+ if (fmtd.index == 0) {
+ strcpy(fmtd.description, "bayer rgb");
+ fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8;
+ } else if (fmtd.index == 1) {
+ strcpy(fmtd.description, "compressed");
+ fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X;
+ fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
+ } else
+ return -EINVAL;
+
+ fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
+
+ if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_format format;
+ struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format);
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X)
+ ? 0 : (pfmt->width * pfmt->priv) / 8;
+ pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
+ pfmt->field = V4L2_FIELD_NONE;
+ memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
+
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
+ void __user * arg)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_format format;
+ struct v4l2_pix_format* pix;
+ struct v4l2_pix_format* pfmt = &(s->pix_format);
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ struct v4l2_rect rect;
+ u8 scale;
+ const enum sn9c102_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ pix = &(format.fmt.pix);
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memcpy(&rect, &(s->_rect), sizeof(rect));
+
+ { /* calculate the actual scaling factor */
+ u32 a, b;
+ a = rect.width * rect.height;
+ b = pix->width * pix->height;
+ scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1;
+ }
+
+ rect.width = scale * pix->width;
+ rect.height = scale * pix->height;
+
+ if (rect.width < 16)
+ rect.width = 16;
+ if (rect.height < 16)
+ rect.height = 16;
+ if (rect.width > bounds->left + bounds->width - rect.left)
+ rect.width = bounds->left + bounds->width - rect.left;
+ if (rect.height > bounds->top + bounds->height - rect.top)
+ rect.height = bounds->top + bounds->height - rect.top;
+
+ rect.width &= ~15L;
+ rect.height &= ~15L;
+
+ { /* adjust the scaling factor */
+ u32 a, b;
+ a = rect.width * rect.height;
+ b = pix->width * pix->height;
+ scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1;
+ }
+
+ pix->width = rect.width / scale;
+ pix->height = rect.height / scale;
+
+ if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
+ pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
+ pix->pixelformat = pfmt->pixelformat;
+ pix->priv = pfmt->priv; /* bpp */
+ pix->colorspace = pfmt->colorspace;
+ pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ ? 0 : (pix->width * pix->priv) / 8;
+ pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8);
+ pix->field = V4L2_FIELD_NONE;
+
+ if (cmd == VIDIOC_TRY_FMT) {
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+ return 0;
+ }
+
+ if (cam->module_param.force_munmap)
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_FMT failed. Unmap the "
+ "buffers first.");
+ return -EINVAL;
+ }
+
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
+ return err;
+
+ if (copy_to_user(arg, &format, sizeof(format))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ if (cam->module_param.force_munmap || cam->io == IO_READ)
+ sn9c102_release_buffers(cam);
+
+ err += sn9c102_set_pix_format(cam, pix);
+ err += sn9c102_set_crop(cam, &rect);
+ if (s->set_pix_format)
+ err += s->set_pix_format(cam, pix);
+ if (s->set_crop)
+ err += s->set_crop(cam, &rect);
+ err += sn9c102_set_scale(cam, scale);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ memcpy(pfmt, pix, sizeof(*pix));
+ memcpy(&(s->_rect), &rect, sizeof(rect));
+
+ if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+ nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -ENOMEM;
+ }
+
+ if (cam->io == IO_READ)
+ sn9c102_empty_framequeues(cam);
+ else if (cam->module_param.force_munmap)
+ sn9c102_requeue_outqueue(cam);
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg)
+{
+ if (copy_to_user(arg, &cam->compression,
+ sizeof(cam->compression)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_jpegcompression jc;
+ const enum sn9c102_stream_state stream = cam->stream;
+ int err = 0;
+
+ if (copy_from_user(&jc, arg, sizeof(jc)))
+ return -EFAULT;
+
+ if (jc.quality != 0 && jc.quality != 1)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
+ return err;
+
+ err += sn9c102_set_compression(cam, &jc);
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
+ "problems. To use the camera, close and open "
+ "/dev/video%d again.", cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ cam->compression.quality = jc.quality;
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_requestbuffers rb;
+ u32 i;
+ int err;
+
+ if (copy_from_user(&rb, arg, sizeof(rb)))
+ return -EFAULT;
+
+ if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ rb.memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (cam->io == IO_READ) {
+ DBG(3, "Close and open the device again to choose the mmap "
+ "I/O method");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are "
+ "still mapped.");
+ return -EINVAL;
+ }
+
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
+ return err;
+
+ sn9c102_empty_framequeues(cam);
+
+ sn9c102_release_buffers(cam);
+ if (rb.count)
+ rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP);
+
+ if (copy_to_user(arg, &rb, sizeof(rb))) {
+ sn9c102_release_buffers(cam);
+ cam->io = IO_NONE;
+ return -EFAULT;
+ }
+
+ cam->io = rb.count ? IO_MMAP : IO_NONE;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_buffer b;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
+
+ if (cam->frame[b.index].vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (cam->frame[b.index].state == F_DONE)
+ b.flags |= V4L2_BUF_FLAG_DONE;
+ else if (cam->frame[b.index].state != F_UNUSED)
+ b.flags |= V4L2_BUF_FLAG_QUEUED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_qbuf(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_buffer b;
+ unsigned long lock_flags;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->frame[b.index].state != F_UNUSED)
+ return -EINVAL;
+
+ cam->frame[b.index].state = F_QUEUED;
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ PDBGG("Frame #%lu queued", (unsigned long)b.index);
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp,
+ void __user * arg)
+{
+ struct v4l2_buffer b;
+ struct sn9c102_frame_t *f;
+ unsigned long lock_flags;
+ long timeout;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&cam->outqueue)) {
+ if (cam->stream == STREAM_OFF)
+ return -EINVAL;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0)
+ return timeout;
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ if (!timeout || (cam->state & DEV_MISCONFIGURED))
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame);
+ list_del(cam->outqueue.next);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ f->state = F_UNUSED;
+
+ memcpy(&b, &f->buf, sizeof(b));
+ if (f->vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index);
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg)
+{
+ int type;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&cam->inqueue))
+ return -EINVAL;
+
+ cam->stream = STREAM_ON;
+
+ DBG(3, "Stream on");
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_streamoff(struct sn9c102_device* cam, void __user * arg)
+{
+ int type, err;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
+ return err;
+
+ sn9c102_empty_framequeues(cam);
+
+ DBG(3, "Stream off");
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_g_parm(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_streamparm sp;
+
+ if (copy_from_user(&sp, arg, sizeof(sp)))
+ return -EFAULT;
+
+ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp.parm.capture.extendedmode = 0;
+ sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_s_parm(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_streamparm sp;
+
+ if (copy_from_user(&sp, arg, sizeof(sp)))
+ return -EFAULT;
+
+ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp.parm.capture.extendedmode = 0;
+
+ if (sp.parm.capture.readbuffers == 0)
+ sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+ if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES)
+ sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ cam->nreadbuffers = sp.parm.capture.readbuffers;
+
+ return 0;
+}
+
+
+static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
+ unsigned int cmd, void __user * arg)
+{
+ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+
+ switch (cmd) {
+
+ case VIDIOC_QUERYCAP:
+ return sn9c102_vidioc_querycap(cam, arg);
+
+ case VIDIOC_ENUMINPUT:
+ return sn9c102_vidioc_enuminput(cam, arg);
+
+ case VIDIOC_G_INPUT:
+ return sn9c102_vidioc_g_input(cam, arg);
+
+ case VIDIOC_S_INPUT:
+ return sn9c102_vidioc_s_input(cam, arg);
+
+ case VIDIOC_QUERYCTRL:
+ return sn9c102_vidioc_query_ctrl(cam, arg);
+
+ case VIDIOC_G_CTRL:
+ return sn9c102_vidioc_g_ctrl(cam, arg);
+
+ case VIDIOC_S_CTRL_OLD:
+ case VIDIOC_S_CTRL:
+ return sn9c102_vidioc_s_ctrl(cam, arg);
+
+ case VIDIOC_CROPCAP_OLD:
+ case VIDIOC_CROPCAP:
+ return sn9c102_vidioc_cropcap(cam, arg);
+
+ case VIDIOC_G_CROP:
+ return sn9c102_vidioc_g_crop(cam, arg);
+
+ case VIDIOC_S_CROP:
+ return sn9c102_vidioc_s_crop(cam, arg);
+
+ case VIDIOC_ENUM_FMT:
+ return sn9c102_vidioc_enum_fmt(cam, arg);
+
+ case VIDIOC_G_FMT:
+ return sn9c102_vidioc_g_fmt(cam, arg);
+
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT:
+ return sn9c102_vidioc_try_s_fmt(cam, cmd, arg);
+
+ case VIDIOC_G_JPEGCOMP:
+ return sn9c102_vidioc_g_jpegcomp(cam, arg);
+
+ case VIDIOC_S_JPEGCOMP:
+ return sn9c102_vidioc_s_jpegcomp(cam, arg);
+
+ case VIDIOC_REQBUFS:
+ return sn9c102_vidioc_reqbufs(cam, arg);
+
+ case VIDIOC_QUERYBUF:
+ return sn9c102_vidioc_querybuf(cam, arg);
+
+ case VIDIOC_QBUF:
+ return sn9c102_vidioc_qbuf(cam, arg);
+
+ case VIDIOC_DQBUF:
+ return sn9c102_vidioc_dqbuf(cam, filp, arg);
+
+ case VIDIOC_STREAMON:
+ return sn9c102_vidioc_streamon(cam, arg);
+
+ case VIDIOC_STREAMOFF:
+ return sn9c102_vidioc_streamoff(cam, arg);
+
+ case VIDIOC_G_PARM:
+ return sn9c102_vidioc_g_parm(cam, arg);
+
+ case VIDIOC_S_PARM_OLD:
+ case VIDIOC_S_PARM:
+ return sn9c102_vidioc_s_parm(cam, arg);
+
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_QUERYSTD:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_QUERYMENU:
+ return -EINVAL;
+
+ default:
+ return -EINVAL;
+
+ }
+}
+
+
+static int sn9c102_ioctl(struct inode* inode, struct file* filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ int err = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ V4LDBG(3, "sn9c102", cmd);
+
+ err = sn9c102_ioctl_v4l2(inode, filp, cmd, (void __user *)arg);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return err;
+}
+
+/*****************************************************************************/
+
+static struct file_operations sn9c102_fops = {
+ .owner = THIS_MODULE,
+ .open = sn9c102_open,
+ .release = sn9c102_release,
+ .ioctl = sn9c102_ioctl,
+ .read = sn9c102_read,
+ .poll = sn9c102_poll,
+ .mmap = sn9c102_mmap,
+ .llseek = no_llseek,
+};
+
+/*****************************************************************************/
+
+/* It exists a single interface only. We do not need to validate anything. */
+static int
+sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct sn9c102_device* cam;
+ static unsigned int dev_nr = 0;
+ unsigned int i;
+ int err = 0, r;
+
+ if (!(cam = kzalloc(sizeof(struct sn9c102_device), GFP_KERNEL)))
+ return -ENOMEM;
+
+ cam->usbdev = udev;
+
+ if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) {
+ DBG(1, "kmalloc() failed");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ if (!(cam->v4ldev = video_device_alloc())) {
+ DBG(1, "video_device_alloc() failed");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ mutex_init(&cam->dev_mutex);
+
+ r = sn9c102_read_reg(cam, 0x00);
+ if (r < 0 || r != 0x10) {
+ DBG(1, "Sorry, this is not a SN9C10x based camera "
+ "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct);
+ err = -ENODEV;
+ goto fail;
+ }
+
+ cam->bridge = (id->idProduct & 0xffc0) == 0x6080 ?
+ BRIDGE_SN9C103 : BRIDGE_SN9C102;
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ DBG(2, "SN9C10[12] PC Camera Controller detected "
+ "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct);
+ break;
+ case BRIDGE_SN9C103:
+ DBG(2, "SN9C103 PC Camera Controller detected "
+ "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct);
+ break;
+ }
+
+ for (i = 0; sn9c102_sensor_table[i]; i++) {
+ err = sn9c102_sensor_table[i](cam);
+ if (!err)
+ break;
+ }
+
+ if (!err) {
+ DBG(2, "%s image sensor detected", cam->sensor.name);
+ DBG(3, "Support for %s maintained by %s",
+ cam->sensor.name, cam->sensor.maintainer);
+ } else {
+ DBG(1, "No supported image sensor detected");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (sn9c102_init(cam)) {
+ DBG(1, "Initialization failed. I will retry on open().");
+ cam->state |= DEV_MISCONFIGURED;
+ }
+
+ strcpy(cam->v4ldev->name, "SN9C10x PC Camera");
+ cam->v4ldev->owner = THIS_MODULE;
+ cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
+ cam->v4ldev->hardware = 0;
+ cam->v4ldev->fops = &sn9c102_fops;
+ cam->v4ldev->minor = video_nr[dev_nr];
+ cam->v4ldev->release = video_device_release;
+ video_set_drvdata(cam->v4ldev, cam);
+
+ mutex_lock(&cam->dev_mutex);
+
+ err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
+ video_nr[dev_nr]);
+ if (err) {
+ DBG(1, "V4L2 device registration failed");
+ if (err == -ENFILE && video_nr[dev_nr] == -1)
+ DBG(1, "Free /dev/videoX node not found");
+ video_nr[dev_nr] = -1;
+ dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
+ mutex_unlock(&cam->dev_mutex);
+ goto fail;
+ }
+
+ DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
+
+ cam->module_param.force_munmap = force_munmap[dev_nr];
+ cam->module_param.frame_timeout = frame_timeout[dev_nr];
+
+ dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ sn9c102_create_sysfs(cam);
+ DBG(2, "Optional device control through 'sysfs' interface ready");
+#endif
+
+ usb_set_intfdata(intf, cam);
+
+ mutex_unlock(&cam->dev_mutex);
+
+ return 0;
+
+fail:
+ if (cam) {
+ kfree(cam->control_buffer);
+ if (cam->v4ldev)
+ video_device_release(cam->v4ldev);
+ kfree(cam);
+ }
+ return err;
+}
+
+
+static void sn9c102_usb_disconnect(struct usb_interface* intf)
+{
+ struct sn9c102_device* cam = usb_get_intfdata(intf);
+
+ if (!cam)
+ return;
+
+ down_write(&sn9c102_disconnect);
+
+ mutex_lock(&cam->dev_mutex);
+
+ DBG(2, "Disconnecting %s...", cam->v4ldev->name);
+
+ wake_up_interruptible_all(&cam->open);
+
+ if (cam->users) {
+ DBG(2, "Device /dev/video%d is open! Deregistration and "
+ "memory deallocation are deferred on close.",
+ cam->v4ldev->minor);
+ cam->state |= DEV_MISCONFIGURED;
+ sn9c102_stop_transfer(cam);
+ cam->state |= DEV_DISCONNECTED;
+ wake_up_interruptible(&cam->wait_frame);
+ wake_up(&cam->wait_stream);
+ usb_get_dev(cam->usbdev);
+ } else {
+ cam->state |= DEV_DISCONNECTED;
+ sn9c102_release_resources(cam);
+ }
+
+ mutex_unlock(&cam->dev_mutex);
+
+ if (!cam->users)
+ kfree(cam);
+
+ up_write(&sn9c102_disconnect);
+}
+
+
+static struct usb_driver sn9c102_usb_driver = {
+ .name = "sn9c102",
+ .id_table = sn9c102_id_table,
+ .probe = sn9c102_usb_probe,
+ .disconnect = sn9c102_usb_disconnect,
+};
+
+/*****************************************************************************/
+
+static int __init sn9c102_module_init(void)
+{
+ int err = 0;
+
+ KDBG(2, SN9C102_MODULE_NAME " v" SN9C102_MODULE_VERSION);
+ KDBG(3, SN9C102_MODULE_AUTHOR);
+
+ if ((err = usb_register(&sn9c102_usb_driver)))
+ KDBG(1, "usb_register() failed");
+
+ return err;
+}
+
+
+static void __exit sn9c102_module_exit(void)
+{
+ usb_deregister(&sn9c102_usb_driver);
+}
+
+
+module_init(sn9c102_module_init);
+module_exit(sn9c102_module_exit);
diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/video/sn9c102/sn9c102_hv7131d.c
new file mode 100644
index 000000000000..46c12ec3ca62
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_hv7131d.c
@@ -0,0 +1,271 @@
+/***************************************************************************
+ * Plug-in for HV7131D image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor hv7131d;
+
+
+static int hv7131d_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x00, 0x10);
+ err += sn9c102_write_reg(cam, 0x00, 0x11);
+ err += sn9c102_write_reg(cam, 0x00, 0x14);
+ err += sn9c102_write_reg(cam, 0x60, 0x17);
+ err += sn9c102_write_reg(cam, 0x0e, 0x18);
+ err += sn9c102_write_reg(cam, 0xf2, 0x19);
+
+ err += sn9c102_i2c_write(cam, 0x01, 0x04);
+ err += sn9c102_i2c_write(cam, 0x02, 0x00);
+ err += sn9c102_i2c_write(cam, 0x28, 0x00);
+
+ return err;
+}
+
+
+static int hv7131d_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ {
+ int r1 = sn9c102_i2c_read(cam, 0x26),
+ r2 = sn9c102_i2c_read(cam, 0x27);
+ if (r1 < 0 || r2 < 0)
+ return -EIO;
+ ctrl->value = (r1 << 8) | (r2 & 0xff);
+ }
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0)
+ return -EIO;
+ ctrl->value = 0x3f - (ctrl->value & 0x3f);
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0)
+ return -EIO;
+ ctrl->value = 0x3f - (ctrl->value & 0x3f);
+ return 0;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0)
+ return -EIO;
+ ctrl->value = 0x3f - (ctrl->value & 0x3f);
+ return 0;
+ case SN9C102_V4L2_CID_RESET_LEVEL:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0)
+ return -EIO;
+ ctrl->value &= 0x3f;
+ return 0;
+ case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x34)) < 0)
+ return -EIO;
+ ctrl->value &= 0x07;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int hv7131d_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x26, ctrl->value >> 8);
+ err += sn9c102_i2c_write(cam, 0x27, ctrl->value & 0xff);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x31, 0x3f - ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x33, 0x3f - ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x32, 0x3f - ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_RESET_LEVEL:
+ err += sn9c102_i2c_write(cam, 0x30, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE:
+ err += sn9c102_i2c_write(cam, 0x34, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int hv7131d_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &hv7131d;
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int hv7131d_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x42, 0x19);
+ else
+ err += sn9c102_write_reg(cam, 0xf2, 0x19);
+
+ return err;
+}
+
+
+static struct sn9c102_sensor hv7131d = {
+ .name = "HV7131D",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x11,
+ .init = &hv7131d_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x0250,
+ .maximum = 0xffff,
+ .step = 0x0001,
+ .default_value = 0x0250,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x20,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x1e,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_RESET_LEVEL,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "reset level",
+ .minimum = 0x19,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x30,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "pixel bias voltage",
+ .minimum = 0x00,
+ .maximum = 0x07,
+ .step = 0x01,
+ .default_value = 0x02,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &hv7131d_get_ctrl,
+ .set_ctrl = &hv7131d_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &hv7131d_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &hv7131d_set_pix_format
+};
+
+
+int sn9c102_probe_hv7131d(struct sn9c102_device* cam)
+{
+ int r0 = 0, r1 = 0, err = 0;
+
+ err += sn9c102_write_reg(cam, 0x01, 0x01);
+ err += sn9c102_write_reg(cam, 0x00, 0x01);
+ err += sn9c102_write_reg(cam, 0x28, 0x17);
+ if (err)
+ return -EIO;
+
+ r0 = sn9c102_i2c_try_read(cam, &hv7131d, 0x00);
+ r1 = sn9c102_i2c_try_read(cam, &hv7131d, 0x01);
+ if (r0 < 0 || r1 < 0)
+ return -EIO;
+
+ if (r0 != 0x00 && r1 != 0x04)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &hv7131d);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_mi0343.c b/drivers/media/video/sn9c102/sn9c102_mi0343.c
new file mode 100644
index 000000000000..d9aa7a61095d
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_mi0343.c
@@ -0,0 +1,363 @@
+/***************************************************************************
+ * Plug-in for MI-0343 image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor mi0343;
+static u8 mi0343_i2c_data[5+1];
+
+
+static int mi0343_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x00, 0x10);
+ err += sn9c102_write_reg(cam, 0x00, 0x11);
+ err += sn9c102_write_reg(cam, 0x0a, 0x14);
+ err += sn9c102_write_reg(cam, 0x40, 0x01);
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+ err += sn9c102_write_reg(cam, 0x07, 0x18);
+ err += sn9c102_write_reg(cam, 0xa0, 0x19);
+
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+ 0x0d, 0x00, 0x01, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+ 0x0d, 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+ 0x03, 0x01, 0xe1, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+ 0x04, 0x02, 0x81, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+ 0x05, 0x00, 0x17, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+ 0x06, 0x00, 0x11, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+ 0x62, 0x04, 0x9a, 0, 0);
+
+ return err;
+}
+
+
+static int mi0343_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+ 0x09, 2+1, mi0343_i2c_data) < 0)
+ return -EIO;
+ ctrl->value = mi0343_i2c_data[2];
+ return 0;
+ case V4L2_CID_GAIN:
+ if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+ 0x35, 2+1, mi0343_i2c_data) < 0)
+ return -EIO;
+ break;
+ case V4L2_CID_HFLIP:
+ if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+ 0x20, 2+1, mi0343_i2c_data) < 0)
+ return -EIO;
+ ctrl->value = mi0343_i2c_data[3] & 0x20 ? 1 : 0;
+ return 0;
+ case V4L2_CID_VFLIP:
+ if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+ 0x20, 2+1, mi0343_i2c_data) < 0)
+ return -EIO;
+ ctrl->value = mi0343_i2c_data[3] & 0x80 ? 1 : 0;
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+ 0x2d, 2+1, mi0343_i2c_data) < 0)
+ return -EIO;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+ 0x2c, 2+1, mi0343_i2c_data) < 0)
+ return -EIO;
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+ 0x2e, 2+1, mi0343_i2c_data) < 0)
+ return -EIO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ case V4L2_CID_RED_BALANCE:
+ case V4L2_CID_BLUE_BALANCE:
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ ctrl->value = mi0343_i2c_data[3] | (mi0343_i2c_data[2] << 8);
+ if (ctrl->value >= 0x10 && ctrl->value <= 0x3f)
+ ctrl->value -= 0x10;
+ else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f)
+ ctrl->value -= 0x60;
+ else if (ctrl->value >= 0xe0 && ctrl->value <= 0xff)
+ ctrl->value -= 0xe0;
+ }
+
+ return 0;
+}
+
+
+static int mi0343_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ u16 reg = 0;
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ case V4L2_CID_RED_BALANCE:
+ case V4L2_CID_BLUE_BALANCE:
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if (ctrl->value <= (0x3f-0x10))
+ reg = 0x10 + ctrl->value;
+ else if (ctrl->value <= ((0x3f-0x10) + (0x7f-0x60)))
+ reg = 0x60 + (ctrl->value - (0x3f-0x10));
+ else
+ reg = 0xe0 + (ctrl->value - (0x3f-0x10) - (0x7f-0x60));
+ break;
+ }
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+ mi0343.i2c_slave_id,
+ 0x09, ctrl->value, 0x00,
+ 0, 0);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+ mi0343.i2c_slave_id,
+ 0x35, reg >> 8, reg & 0xff,
+ 0, 0);
+ break;
+ case V4L2_CID_HFLIP:
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+ mi0343.i2c_slave_id,
+ 0x20, ctrl->value ? 0x40:0x00,
+ ctrl->value ? 0x20:0x00,
+ 0, 0);
+ break;
+ case V4L2_CID_VFLIP:
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+ mi0343.i2c_slave_id,
+ 0x20, ctrl->value ? 0x80:0x00,
+ ctrl->value ? 0x80:0x00,
+ 0, 0);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+ mi0343.i2c_slave_id,
+ 0x2d, reg >> 8, reg & 0xff,
+ 0, 0);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+ mi0343.i2c_slave_id,
+ 0x2c, reg >> 8, reg & 0xff,
+ 0, 0);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+ mi0343.i2c_slave_id,
+ 0x2b, reg >> 8, reg & 0xff,
+ 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+ mi0343.i2c_slave_id,
+ 0x2e, reg >> 8, reg & 0xff,
+ 0, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int mi0343_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &mi0343;
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int mi0343_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) {
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+ mi0343.i2c_slave_id,
+ 0x0a, 0x00, 0x03, 0, 0);
+ err += sn9c102_write_reg(cam, 0x20, 0x19);
+ } else {
+ err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+ mi0343.i2c_slave_id,
+ 0x0a, 0x00, 0x05, 0, 0);
+ err += sn9c102_write_reg(cam, 0xa0, 0x19);
+ }
+
+ return err;
+}
+
+
+static struct sn9c102_sensor mi0343 = {
+ .name = "MI-0343",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x5d,
+ .init = &mi0343_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x06,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),/*0x6d*/
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = ((0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0)),
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &mi0343_get_ctrl,
+ .set_ctrl = &mi0343_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &mi0343_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &mi0343_set_pix_format
+};
+
+
+int sn9c102_probe_mi0343(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x01, 0x01);
+ err += sn9c102_write_reg(cam, 0x00, 0x01);
+ err += sn9c102_write_reg(cam, 0x28, 0x17);
+ if (err)
+ return -EIO;
+
+ if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00,
+ 2, mi0343_i2c_data) < 0)
+ return -EIO;
+
+ if (mi0343_i2c_data[4] != 0x32 && mi0343_i2c_data[3] != 0xe3)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &mi0343);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_ov7630.c b/drivers/media/video/sn9c102/sn9c102_ov7630.c
new file mode 100644
index 000000000000..42852b7cb042
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_ov7630.c
@@ -0,0 +1,401 @@
+/***************************************************************************
+ * Plug-in for OV7630 image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2005-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor ov7630;
+
+
+static int ov7630_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x00, 0x14);
+ err += sn9c102_write_reg(cam, 0x60, 0x17);
+ err += sn9c102_write_reg(cam, 0x0f, 0x18);
+ err += sn9c102_write_reg(cam, 0x50, 0x19);
+
+ err += sn9c102_i2c_write(cam, 0x12, 0x80);
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
+ err += sn9c102_i2c_write(cam, 0x15, 0x34);
+ err += sn9c102_i2c_write(cam, 0x16, 0x03);
+ err += sn9c102_i2c_write(cam, 0x17, 0x1c);
+ err += sn9c102_i2c_write(cam, 0x18, 0xbd);
+ err += sn9c102_i2c_write(cam, 0x19, 0x06);
+ err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
+ err += sn9c102_i2c_write(cam, 0x1b, 0x04);
+ err += sn9c102_i2c_write(cam, 0x20, 0xf6);
+ err += sn9c102_i2c_write(cam, 0x23, 0xee);
+ err += sn9c102_i2c_write(cam, 0x26, 0xa0);
+ err += sn9c102_i2c_write(cam, 0x27, 0x9a);
+ err += sn9c102_i2c_write(cam, 0x28, 0xa0);
+ err += sn9c102_i2c_write(cam, 0x29, 0x30);
+ err += sn9c102_i2c_write(cam, 0x2a, 0xa0);
+ err += sn9c102_i2c_write(cam, 0x2b, 0x1f);
+ err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
+ err += sn9c102_i2c_write(cam, 0x30, 0x24);
+ err += sn9c102_i2c_write(cam, 0x32, 0x86);
+ err += sn9c102_i2c_write(cam, 0x60, 0xa9);
+ err += sn9c102_i2c_write(cam, 0x61, 0x42);
+ err += sn9c102_i2c_write(cam, 0x65, 0x00);
+ err += sn9c102_i2c_write(cam, 0x69, 0x38);
+ err += sn9c102_i2c_write(cam, 0x6f, 0x88);
+ err += sn9c102_i2c_write(cam, 0x70, 0x0b);
+ err += sn9c102_i2c_write(cam, 0x71, 0x00);
+ err += sn9c102_i2c_write(cam, 0x74, 0x21);
+ err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
+
+ return err;
+}
+
+
+static int ov7630_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x10, ctrl->value >> 2);
+ err += sn9c102_i2c_write(cam, 0x76, ctrl->value & 0x03);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x02, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x01, ctrl->value);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x00, ctrl->value);
+ break;
+ case V4L2_CID_CONTRAST:
+ err += ctrl->value ? sn9c102_i2c_write(cam, 0x05,
+ (ctrl->value-1) | 0x20)
+ : sn9c102_i2c_write(cam, 0x05, 0x00);
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ err += sn9c102_i2c_write(cam, 0x06, ctrl->value);
+ break;
+ case V4L2_CID_SATURATION:
+ err += sn9c102_i2c_write(cam, 0x03, ctrl->value << 4);
+ break;
+ case V4L2_CID_HUE:
+ err += ctrl->value ? sn9c102_i2c_write(cam, 0x04,
+ (ctrl->value-1) | 0x20)
+ : sn9c102_i2c_write(cam, 0x04, 0x00);
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
+ break;
+ case V4L2_CID_WHITENESS:
+ err += sn9c102_i2c_write(cam, 0x0d, ctrl->value);
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x12, (ctrl->value << 2) | 0x78);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ err += sn9c102_i2c_write(cam, 0x13, ctrl->value);
+ break;
+ case V4L2_CID_VFLIP:
+ err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7));
+ break;
+ case V4L2_CID_BLACK_LEVEL:
+ err += sn9c102_i2c_write(cam, 0x25, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_BRIGHT_LEVEL:
+ err += sn9c102_i2c_write(cam, 0x24, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_GAMMA:
+ err += sn9c102_i2c_write(cam, 0x14, (ctrl->value << 2) | 0x80);
+ break;
+ case SN9C102_V4L2_CID_BAND_FILTER:
+ err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int ov7630_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &ov7630;
+ int err = 0;
+ u8 v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
+
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int ov7630_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x20, 0x19);
+ else
+ err += sn9c102_write_reg(cam, 0x50, 0x19);
+
+ return err;
+}
+
+
+static struct sn9c102_sensor ov7630 = {
+ .name = "OV7630",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .sysfs_ops = SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x21,
+ .init = &ov7630_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x14,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "hue",
+ .minimum = 0x00,
+ .maximum = 0x1f+1,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "saturation",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x08,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "contrast",
+ .minimum = 0x00,
+ .maximum = 0x1f+1,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x000,
+ .maximum = 0x3ff,
+ .step = 0x001,
+ .default_value = 0x83<<2,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x3a,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x77,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "brightness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0xa0,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_DO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "white balance background: blue",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x20,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_WHITENESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "white balance background: red",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x20,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto white balance",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x01,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain & exposure mode",
+ .minimum = 0x00,
+ .maximum = 0x03,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x01,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLACK_LEVEL,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "black pixel ratio",
+ .minimum = 0x01,
+ .maximum = 0x9a,
+ .step = 0x01,
+ .default_value = 0x8a,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_BRIGHT_LEVEL,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "bright pixel ratio",
+ .minimum = 0x01,
+ .maximum = 0x9a,
+ .step = 0x01,
+ .default_value = 0x10,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_BAND_FILTER,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "band filter",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "rgb gamma",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ },
+ .set_ctrl = &ov7630_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &ov7630_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &ov7630_set_pix_format
+};
+
+
+int sn9c102_probe_ov7630(struct sn9c102_device* cam)
+{
+ const struct usb_device_id ov7630_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x602c), },
+ { USB_DEVICE(0x0c45, 0x602d), },
+ { USB_DEVICE(0x0c45, 0x608f), },
+ { USB_DEVICE(0x0c45, 0x60b0), },
+ { }
+ };
+ int err = 0;
+
+ if (!sn9c102_match_id(cam, ov7630_id_table))
+ return -ENODEV;
+
+ err += sn9c102_write_reg(cam, 0x01, 0x01);
+ err += sn9c102_write_reg(cam, 0x00, 0x01);
+ err += sn9c102_write_reg(cam, 0x28, 0x17);
+ if (err)
+ return -EIO;
+
+ err += sn9c102_i2c_try_write(cam, &ov7630, 0x0b, 0);
+ if (err)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &ov7630);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_pas106b.c b/drivers/media/video/sn9c102/sn9c102_pas106b.c
new file mode 100644
index 000000000000..b1dee78abe04
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_pas106b.c
@@ -0,0 +1,307 @@
+/***************************************************************************
+ * Plug-in for PAS106B image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/delay.h>
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor pas106b;
+
+
+static int pas106b_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x00, 0x10);
+ err += sn9c102_write_reg(cam, 0x00, 0x11);
+ err += sn9c102_write_reg(cam, 0x00, 0x14);
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+ err += sn9c102_write_reg(cam, 0x20, 0x19);
+ err += sn9c102_write_reg(cam, 0x09, 0x18);
+
+ err += sn9c102_i2c_write(cam, 0x02, 0x0c);
+ err += sn9c102_i2c_write(cam, 0x05, 0x5a);
+ err += sn9c102_i2c_write(cam, 0x06, 0x88);
+ err += sn9c102_i2c_write(cam, 0x07, 0x80);
+ err += sn9c102_i2c_write(cam, 0x10, 0x06);
+ err += sn9c102_i2c_write(cam, 0x11, 0x06);
+ err += sn9c102_i2c_write(cam, 0x12, 0x00);
+ err += sn9c102_i2c_write(cam, 0x14, 0x02);
+ err += sn9c102_i2c_write(cam, 0x13, 0x01);
+
+ msleep(400);
+
+ return err;
+}
+
+
+static int pas106b_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ {
+ int r1 = sn9c102_i2c_read(cam, 0x03),
+ r2 = sn9c102_i2c_read(cam, 0x04);
+ if (r1 < 0 || r2 < 0)
+ return -EIO;
+ ctrl->value = (r1 << 4) | (r2 & 0x0f);
+ }
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0)
+ return -EIO;
+ ctrl->value &= 0x1f;
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0)
+ return -EIO;
+ ctrl->value &= 0x1f;
+ return 0;
+ case V4L2_CID_GAIN:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0)
+ return -EIO;
+ ctrl->value &= 0x1f;
+ return 0;
+ case V4L2_CID_CONTRAST:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0)
+ return -EIO;
+ ctrl->value &= 0x07;
+ return 0;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x0a)) < 0)
+ return -EIO;
+ ctrl->value = (ctrl->value & 0x1f) << 1;
+ return 0;
+ case SN9C102_V4L2_CID_DAC_MAGNITUDE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0)
+ return -EIO;
+ ctrl->value &= 0xf8;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int pas106b_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x03, ctrl->value >> 4);
+ err += sn9c102_i2c_write(cam, 0x04, ctrl->value & 0x0f);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x0e, ctrl->value);
+ break;
+ case V4L2_CID_CONTRAST:
+ err += sn9c102_i2c_write(cam, 0x0f, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x0a, ctrl->value >> 1);
+ err += sn9c102_i2c_write(cam, 0x0b, ctrl->value >> 1);
+ break;
+ case SN9C102_V4L2_CID_DAC_MAGNITUDE:
+ err += sn9c102_i2c_write(cam, 0x08, ctrl->value << 3);
+ break;
+ default:
+ return -EINVAL;
+ }
+ err += sn9c102_i2c_write(cam, 0x13, 0x01);
+
+ return err ? -EIO : 0;
+}
+
+
+static int pas106b_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &pas106b;
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int pas106b_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x2c, 0x17);
+ else
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+
+ return err;
+}
+
+
+static struct sn9c102_sensor pas106b = {
+ .name = "PAS106B",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x40,
+ .init = &pas106b_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x125,
+ .maximum = 0xfff,
+ .step = 0x001,
+ .default_value = 0x140,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x0d,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "contrast",
+ .minimum = 0x00,
+ .maximum = 0x07,
+ .step = 0x01,
+ .default_value = 0x00, /* 0x00~0x03 have same effect */
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x04,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x06,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x3e,
+ .step = 0x02,
+ .default_value = 0x02,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_DAC_MAGNITUDE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "DAC magnitude",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x01,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &pas106b_get_ctrl,
+ .set_ctrl = &pas106b_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ },
+ .set_crop = &pas106b_set_crop,
+ .pix_format = {
+ .width = 352,
+ .height = 288,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8, /* we use this field as 'bits per pixel' */
+ },
+ .set_pix_format = &pas106b_set_pix_format
+};
+
+
+int sn9c102_probe_pas106b(struct sn9c102_device* cam)
+{
+ int r0 = 0, r1 = 0, err = 0;
+ unsigned int pid = 0;
+
+ /*
+ Minimal initialization to enable the I2C communication
+ NOTE: do NOT change the values!
+ */
+ err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */
+ err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */
+ err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */
+ if (err)
+ return -EIO;
+
+ r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00);
+ r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01);
+
+ if (r0 < 0 || r1 < 0)
+ return -EIO;
+
+ pid = (r0 << 11) | ((r1 & 0xf0) >> 4);
+ if (pid != 0x007)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &pas106b);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bca.c b/drivers/media/video/sn9c102/sn9c102_pas202bca.c
new file mode 100644
index 000000000000..3453237055bb
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_pas202bca.c
@@ -0,0 +1,238 @@
+/***************************************************************************
+ * Plug-in for PAS202BCA image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/delay.h>
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor pas202bca;
+
+
+static int pas202bca_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x00, 0x10);
+ err += sn9c102_write_reg(cam, 0x00, 0x11);
+ err += sn9c102_write_reg(cam, 0x00, 0x14);
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+ err += sn9c102_write_reg(cam, 0x30, 0x19);
+ err += sn9c102_write_reg(cam, 0x09, 0x18);
+
+ err += sn9c102_i2c_write(cam, 0x02, 0x14);
+ err += sn9c102_i2c_write(cam, 0x03, 0x40);
+ err += sn9c102_i2c_write(cam, 0x0d, 0x2c);
+ err += sn9c102_i2c_write(cam, 0x0e, 0x01);
+ err += sn9c102_i2c_write(cam, 0x0f, 0xa9);
+ err += sn9c102_i2c_write(cam, 0x10, 0x08);
+ err += sn9c102_i2c_write(cam, 0x13, 0x63);
+ err += sn9c102_i2c_write(cam, 0x15, 0x70);
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
+
+ msleep(400);
+
+ return err;
+}
+
+
+static int pas202bca_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x24, 0x17);
+ else
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+
+ return err;
+}
+
+
+static int pas202bca_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6);
+ err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x07, ctrl->value);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x08, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_DAC_MAGNITUDE:
+ err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
+
+ return err ? -EIO : 0;
+}
+
+
+static int pas202bca_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &pas202bca;
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static struct sn9c102_sensor pas202bca = {
+ .name = "PAS202BCA",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x40,
+ .init = &pas202bca_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x01e5,
+ .maximum = 0x3fff,
+ .step = 0x0001,
+ .default_value = 0x01e5,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x0c,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x01,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x05,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_DAC_MAGNITUDE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "DAC magnitude",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x04,
+ .flags = 0,
+ },
+ },
+ .set_ctrl = &pas202bca_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &pas202bca_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &pas202bca_set_pix_format
+};
+
+
+int sn9c102_probe_pas202bca(struct sn9c102_device* cam)
+{
+ const struct usb_device_id pas202bca_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x60af), },
+ { }
+ };
+ int err = 0;
+
+ if (!sn9c102_match_id(cam,pas202bca_id_table))
+ return -ENODEV;
+
+ err += sn9c102_write_reg(cam, 0x01, 0x01);
+ err += sn9c102_write_reg(cam, 0x40, 0x01);
+ err += sn9c102_write_reg(cam, 0x28, 0x17);
+ if (err)
+ return -EIO;
+
+ if (sn9c102_i2c_try_write(cam, &pas202bca, 0x10, 0)) /* try to write */
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &pas202bca);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
new file mode 100644
index 000000000000..d068616ab337
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
@@ -0,0 +1,293 @@
+/***************************************************************************
+ * Plug-in for PAS202BCB image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio *
+ * <medaglia@undl.org.br> *
+ * http://cadu.homelinux.com:8080/ *
+ * *
+ * DAC Magnitude, exposure and green gain controls added by *
+ * Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/delay.h>
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor pas202bcb;
+
+
+static int pas202bcb_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x00, 0x10);
+ err += sn9c102_write_reg(cam, 0x00, 0x11);
+ err += sn9c102_write_reg(cam, 0x00, 0x14);
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+ err += sn9c102_write_reg(cam, 0x30, 0x19);
+ err += sn9c102_write_reg(cam, 0x09, 0x18);
+
+ err += sn9c102_i2c_write(cam, 0x02, 0x14);
+ err += sn9c102_i2c_write(cam, 0x03, 0x40);
+ err += sn9c102_i2c_write(cam, 0x0d, 0x2c);
+ err += sn9c102_i2c_write(cam, 0x0e, 0x01);
+ err += sn9c102_i2c_write(cam, 0x0f, 0xa9);
+ err += sn9c102_i2c_write(cam, 0x10, 0x08);
+ err += sn9c102_i2c_write(cam, 0x13, 0x63);
+ err += sn9c102_i2c_write(cam, 0x15, 0x70);
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
+
+ msleep(400);
+
+ return err;
+}
+
+
+static int pas202bcb_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ {
+ int r1 = sn9c102_i2c_read(cam, 0x04),
+ r2 = sn9c102_i2c_read(cam, 0x05);
+ if (r1 < 0 || r2 < 0)
+ return -EIO;
+ ctrl->value = (r1 << 6) | (r2 & 0x3f);
+ }
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0)
+ return -EIO;
+ ctrl->value &= 0x0f;
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x07)) < 0)
+ return -EIO;
+ ctrl->value &= 0x0f;
+ return 0;
+ case V4L2_CID_GAIN:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0)
+ return -EIO;
+ ctrl->value &= 0x1f;
+ return 0;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0)
+ return -EIO;
+ ctrl->value &= 0x0f;
+ return 0;
+ case SN9C102_V4L2_CID_DAC_MAGNITUDE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0)
+ return -EIO;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int pas202bcb_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x24, 0x17);
+ else
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+
+ return err;
+}
+
+
+static int pas202bcb_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6);
+ err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x07, ctrl->value);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x08, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_DAC_MAGNITUDE:
+ err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
+
+ return err ? -EIO : 0;
+}
+
+
+static int pas202bcb_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &pas202bcb;
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static struct sn9c102_sensor pas202bcb = {
+ .name = "PAS202BCB",
+ .maintainer = "Carlos Eduardo Medaglia Dyonisio "
+ "<medaglia@undl.org.br>",
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x40,
+ .init = &pas202bcb_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x01e5,
+ .maximum = 0x3fff,
+ .step = 0x0001,
+ .default_value = 0x01e5,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x0c,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x01,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x05,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_DAC_MAGNITUDE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "DAC magnitude",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x04,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &pas202bcb_get_ctrl,
+ .set_ctrl = &pas202bcb_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &pas202bcb_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &pas202bcb_set_pix_format
+};
+
+
+int sn9c102_probe_pas202bcb(struct sn9c102_device* cam)
+{
+ int r0 = 0, r1 = 0, err = 0;
+ unsigned int pid = 0;
+
+ /*
+ * Minimal initialization to enable the I2C communication
+ * NOTE: do NOT change the values!
+ */
+ err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */
+ err += sn9c102_write_reg(cam, 0x40, 0x01); /* sensor power on */
+ err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */
+ if (err)
+ return -EIO;
+
+ r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00);
+ r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01);
+
+ if (r0 < 0 || r1 < 0)
+ return -EIO;
+
+ pid = (r0 << 4) | ((r1 & 0xf0) >> 4);
+ if (pid != 0x017)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &pas202bcb);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_sensor.h b/drivers/media/video/sn9c102/sn9c102_sensor.h
new file mode 100644
index 000000000000..2afd9e9d09bb
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_sensor.h
@@ -0,0 +1,389 @@
+/***************************************************************************
+ * API for image sensors connected to the SN9C10x PC Camera Controllers *
+ * *
+ * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _SN9C102_SENSOR_H_
+#define _SN9C102_SENSOR_H_
+
+#include <linux/usb.h>
+#include <linux/videodev.h>
+#include <linux/device.h>
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <asm/types.h>
+
+struct sn9c102_device;
+struct sn9c102_sensor;
+
+/*****************************************************************************/
+
+/*
+ OVERVIEW.
+ This is a small interface that allows you to add support for any CCD/CMOS
+ image sensors connected to the SN9C10X bridges. The entire API is documented
+ below. In the most general case, to support a sensor there are three steps
+ you have to follow:
+ 1) define the main "sn9c102_sensor" structure by setting the basic fields;
+ 2) write a probing function to be called by the core module when the USB
+ camera is recognized, then add both the USB ids and the name of that
+ function to the two corresponding tables SENSOR_TABLE and ID_TABLE (see
+ below);
+ 3) implement the methods that you want/need (and fill the rest of the main
+ structure accordingly).
+ "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do
+ NOT need to touch the source code of the core module for the things to work
+ properly, unless you find bugs or flaws in it. Finally, do not forget to
+ read the V4L2 API for completeness.
+*/
+
+/*****************************************************************************/
+
+/*
+ Probing functions: on success, you must attach the sensor to the camera
+ by calling sn9c102_attach_sensor() provided below.
+ To enable the I2C communication, you might need to perform a really basic
+ initialization of the SN9C10X chip by using the write function declared
+ ahead.
+ Functions must return 0 on success, the appropriate error otherwise.
+*/
+extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam);
+extern int sn9c102_probe_mi0343(struct sn9c102_device* cam);
+extern int sn9c102_probe_ov7630(struct sn9c102_device* cam);
+extern int sn9c102_probe_pas106b(struct sn9c102_device* cam);
+extern int sn9c102_probe_pas202bca(struct sn9c102_device* cam);
+extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam);
+extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam);
+extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam);
+
+/*
+ Add the above entries to this table. Be sure to add the entry in the right
+ place, since, on failure, the next probing routine is called according to
+ the order of the list below, from top to bottom.
+*/
+#define SN9C102_SENSOR_TABLE \
+static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { \
+ &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ \
+ &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ \
+ &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ \
+ &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ \
+ &sn9c102_probe_pas202bca, /* detection mostly based on USB pid/vid */ \
+ &sn9c102_probe_ov7630, /* detection mostly based on USB pid/vid */ \
+ &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ \
+ &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ \
+ NULL, \
+};
+
+/* Device identification */
+extern struct sn9c102_device*
+sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id);
+
+/* Attach a probed sensor to the camera. */
+extern void
+sn9c102_attach_sensor(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor);
+
+/*
+ Each SN9C10x camera has proper PID/VID identifiers.
+ SN9C103 supports multiple interfaces, but we only handle the video class
+ interface.
+*/
+#define SN9C102_USB_DEVICE(vend, prod, intclass) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_CLASS, \
+ .idVendor = (vend), \
+ .idProduct = (prod), \
+ .bInterfaceClass = (intclass)
+
+#define SN9C102_ID_TABLE \
+static const struct usb_device_id sn9c102_id_table[] = { \
+ { USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x6007), }, \
+ { USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \
+ { USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \
+ { USB_DEVICE(0x0c45, 0x6024), }, \
+ { USB_DEVICE(0x0c45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x6028), }, /* PAS202BCB */ \
+ { USB_DEVICE(0x0c45, 0x6029), }, /* PAS106B */ \
+ { USB_DEVICE(0x0c45, 0x602a), }, /* HV7131D */ \
+ { USB_DEVICE(0x0c45, 0x602b), }, /* MI-0343 */ \
+ { USB_DEVICE(0x0c45, 0x602c), }, /* OV7630 */ \
+ { USB_DEVICE(0x0c45, 0x602d), }, \
+ { USB_DEVICE(0x0c45, 0x602e), }, /* OV7630 */ \
+ { USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x6080, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x6082, 0xff), }, /* MI0343 & MI0360 */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x6083, 0xff), }, /* HV7131[D|E1] */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x6088, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x608a, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x608b, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131/R */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x608e, 0xff), }, /* CIS-VF10 */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x608f, 0xff), }, /* OV7630 */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60a0, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60a2, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60a3, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60a8, 0xff), }, /* PAS106B */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60aa, 0xff), }, /* TAS5130D1B */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60ab, 0xff), }, /* TAS5110C1B */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60ac, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60ae, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60af, 0xff), }, /* PAS202BCB */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60b0, 0xff), }, /* OV7630 (?) */ \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60b2, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60b3, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60b8, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60ba, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60bb, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60bc, 0xff), }, \
+ { SN9C102_USB_DEVICE(0x0c45, 0x60be, 0xff), }, \
+ { } \
+};
+
+/*****************************************************************************/
+
+/*
+ Read/write routines: they always return -1 on error, 0 or the read value
+ otherwise. NOTE that a real read operation is not supported by the SN9C10X
+ chip for some of its registers. To work around this problem, a pseudo-read
+ call is provided instead: it returns the last successfully written value
+ on the register (0 if it has never been written), the usual -1 on error.
+*/
+
+/* The "try" I2C I/O versions are used when probing the sensor */
+extern int sn9c102_i2c_try_write(struct sn9c102_device*,struct sn9c102_sensor*,
+ u8 address, u8 value);
+extern int sn9c102_i2c_try_read(struct sn9c102_device*,struct sn9c102_sensor*,
+ u8 address);
+
+/*
+ These must be used if and only if the sensor doesn't implement the standard
+ I2C protocol. There are a number of good reasons why you must use the
+ single-byte versions of these functions: do not abuse. The first function
+ writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C10X
+ chip. The second one programs the registers 0x09 and 0x10 with data0 and
+ data1, and places the n bytes read from the sensor register table in the
+ buffer pointed by 'buffer'. Both the functions return -1 on error; the write
+ version returns 0 on success, while the read version returns the first read
+ byte.
+*/
+extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor, u8 n,
+ u8 data0, u8 data1, u8 data2, u8 data3,
+ u8 data4, u8 data5);
+extern int sn9c102_i2c_try_raw_read(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor, u8 data0,
+ u8 data1, u8 n, u8 buffer[]);
+
+/* To be used after the sensor struct has been attached to the camera struct */
+extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value);
+extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address);
+
+/* I/O on registers in the bridge. Could be used by the sensor methods too */
+extern int sn9c102_write_regs(struct sn9c102_device*, u8* buff, u16 index);
+extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index);
+extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index);
+
+/*
+ NOTE: there are no exported debugging functions. To uniform the output you
+ must use the dev_info()/dev_warn()/dev_err() macros defined in device.h,
+ already included here, the argument being the struct device '&usbdev->dev'
+ of the sensor structure. Do NOT use these macros before the sensor is
+ attached or the kernel will crash! However, you should not need to notify
+ the user about common errors or other messages, since this is done by the
+ master module.
+*/
+
+/*****************************************************************************/
+
+enum sn9c102_i2c_sysfs_ops {
+ SN9C102_I2C_READ = 0x01,
+ SN9C102_I2C_WRITE = 0x02,
+};
+
+enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */
+ SN9C102_I2C_100KHZ = 0x01,
+ SN9C102_I2C_400KHZ = 0x02,
+};
+
+enum sn9c102_i2c_interface {
+ SN9C102_I2C_2WIRES,
+ SN9C102_I2C_3WIRES,
+};
+
+#define SN9C102_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10
+
+struct sn9c102_sensor {
+ char name[32], /* sensor name */
+ maintainer[64]; /* name of the mantainer <email> */
+
+ /* Supported operations through the 'sysfs' interface */
+ enum sn9c102_i2c_sysfs_ops sysfs_ops;
+
+ /*
+ These sensor capabilities must be provided if the SN9C10X controller
+ needs to communicate through the sensor serial interface by using
+ at least one of the i2c functions available.
+ */
+ enum sn9c102_i2c_frequency frequency;
+ enum sn9c102_i2c_interface interface;
+
+ /*
+ This identifier must be provided if the image sensor implements
+ the standard I2C protocol.
+ */
+ u8 i2c_slave_id; /* reg. 0x09 */
+
+ /*
+ NOTE: Where not noted,most of the functions below are not mandatory.
+ Set to null if you do not implement them. If implemented,
+ they must return 0 on success, the proper error otherwise.
+ */
+
+ int (*init)(struct sn9c102_device* cam);
+ /*
+ This function will be called after the sensor has been attached.
+ It should be used to initialize the sensor only, but may also
+ configure part of the SN9C10X chip if necessary. You don't need to
+ setup picture settings like brightness, contrast, etc.. here, if
+ the corrisponding controls are implemented (see below), since
+ they are adjusted in the core driver by calling the set_ctrl()
+ method after init(), where the arguments are the default values
+ specified in the v4l2_queryctrl list of supported controls;
+ Same suggestions apply for other settings, _if_ the corresponding
+ methods are present; if not, the initialization must configure the
+ sensor according to the default configuration structures below.
+ */
+
+ struct v4l2_queryctrl qctrl[SN9C102_MAX_CTRLS];
+ /*
+ Optional list of default controls, defined as indicated in the
+ V4L2 API. Menu type controls are not handled by this interface.
+ */
+
+ int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl);
+ int (*set_ctrl)(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl);
+ /*
+ You must implement at least the set_ctrl method if you have defined
+ the list above. The returned value must follow the V4L2
+ specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER
+ are not supported by this driver, so do not implement them. Also,
+ you don't have to check whether the passed values are out of bounds,
+ given that this is done by the core module.
+ */
+
+ struct v4l2_cropcap cropcap;
+ /*
+ Think the image sensor as a grid of R,G,B monochromatic pixels
+ disposed according to a particular Bayer pattern, which describes
+ the complete array of pixels, from (0,0) to (xmax, ymax). We will
+ use this coordinate system from now on. It is assumed the sensor
+ chip can be programmed to capture/transmit a subsection of that
+ array of pixels: we will call this subsection "active window".
+ It is not always true that the largest achievable active window can
+ cover the whole array of pixels. The V4L2 API defines another
+ area called "source rectangle", which, in turn, is a subrectangle of
+ the active window. The SN9C10X chip is always programmed to read the
+ source rectangle.
+ The bounds of both the active window and the source rectangle are
+ specified in the cropcap substructures 'bounds' and 'defrect'.
+ By default, the source rectangle should cover the largest possible
+ area. Again, it is not always true that the largest source rectangle
+ can cover the entire active window, although it is a rare case for
+ the hardware we have. The bounds of the source rectangle _must_ be
+ multiple of 16 and must use the same coordinate system as indicated
+ before; their centers shall align initially.
+ If necessary, the sensor chip must be initialized during init() to
+ set the bounds of the active sensor window; however, by default, it
+ usually covers the largest achievable area (maxwidth x maxheight)
+ of pixels, so no particular initialization is needed, if you have
+ defined the correct default bounds in the structures.
+ See the V4L2 API for further details.
+ NOTE: once you have defined the bounds of the active window
+ (struct cropcap.bounds) you must not change them.anymore.
+ Only 'bounds' and 'defrect' fields are mandatory, other fields
+ will be ignored.
+ */
+
+ int (*set_crop)(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect);
+ /*
+ To be called on VIDIOC_C_SETCROP. The core module always calls a
+ default routine which configures the appropriate SN9C10X regs (also
+ scaling), but you may need to override/adjust specific stuff.
+ 'rect' contains width and height values that are multiple of 16: in
+ case you override the default function, you always have to program
+ the chip to match those values; on error return the corresponding
+ error code without rolling back.
+ NOTE: in case, you must program the SN9C10X chip to get rid of
+ blank pixels or blank lines at the _start_ of each line or
+ frame after each HSYNC or VSYNC, so that the image starts with
+ real RGB data (see regs 0x12, 0x13) (having set H_SIZE and,
+ V_SIZE you don't have to care about blank pixels or blank
+ lines at the end of each line or frame).
+ */
+
+ struct v4l2_pix_format pix_format;
+ /*
+ What you have to define here are: 1) initial 'width' and 'height' of
+ the target rectangle 2) the initial 'pixelformat', which can be
+ either V4L2_PIX_FMT_SN9C10X (for compressed video) or
+ V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate the
+ number of bits per pixel for uncompressed video, 8 or 9 (despite the
+ current value of 'pixelformat').
+ NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4
+ of cropcap.defrect.width and cropcap.defrect.height. I
+ suggest 1/1.
+ NOTE 2: The initial compression quality is defined by the first bit
+ of reg 0x17 during the initialization of the image sensor.
+ NOTE 3: as said above, you have to program the SN9C10X chip to get
+ rid of any blank pixels, so that the output of the sensor
+ matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR).
+ */
+
+ int (*set_pix_format)(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix);
+ /*
+ To be called on VIDIOC_S_FMT, when switching from the SBGGR8 to
+ SN9C10X pixel format or viceversa. On error return the corresponding
+ error code without rolling back.
+ */
+
+ /*
+ Do NOT write to the data below, it's READ ONLY. It is used by the
+ core module to store successfully updated values of the above
+ settings, for rollbacks..etc..in case of errors during atomic I/O
+ */
+ struct v4l2_queryctrl _qctrl[SN9C102_MAX_CTRLS];
+ struct v4l2_rect _rect;
+};
+
+/*****************************************************************************/
+
+/* Private ioctl's for control settings supported by some image sensors */
+#define SN9C102_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE
+#define SN9C102_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1
+#define SN9C102_V4L2_CID_RESET_LEVEL V4L2_CID_PRIVATE_BASE + 2
+#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE V4L2_CID_PRIVATE_BASE + 3
+#define SN9C102_V4L2_CID_GAMMA V4L2_CID_PRIVATE_BASE + 4
+#define SN9C102_V4L2_CID_BAND_FILTER V4L2_CID_PRIVATE_BASE + 5
+#define SN9C102_V4L2_CID_BRIGHT_LEVEL V4L2_CID_PRIVATE_BASE + 6
+
+#endif /* _SN9C102_SENSOR_H_ */
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
new file mode 100644
index 000000000000..2e08c552f40a
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
@@ -0,0 +1,159 @@
+/***************************************************************************
+ * Plug-in for TAS5110C1B image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor tas5110c1b;
+
+
+static int tas5110c1b_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x01, 0x01);
+ err += sn9c102_write_reg(cam, 0x44, 0x01);
+ err += sn9c102_write_reg(cam, 0x00, 0x10);
+ err += sn9c102_write_reg(cam, 0x00, 0x11);
+ err += sn9c102_write_reg(cam, 0x0a, 0x14);
+ err += sn9c102_write_reg(cam, 0x60, 0x17);
+ err += sn9c102_write_reg(cam, 0x06, 0x18);
+ err += sn9c102_write_reg(cam, 0xfb, 0x19);
+
+ err += sn9c102_i2c_write(cam, 0xc0, 0x80);
+
+ return err;
+}
+
+
+static int tas5110c1b_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int tas5110c1b_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &tas5110c1b;
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ /* Don't change ! */
+ err += sn9c102_write_reg(cam, 0x14, 0x1a);
+ err += sn9c102_write_reg(cam, 0x0a, 0x1b);
+ err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19);
+
+ return err;
+}
+
+
+static int tas5110c1b_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x2b, 0x19);
+ else
+ err += sn9c102_write_reg(cam, 0xfb, 0x19);
+
+ return err;
+}
+
+
+static struct sn9c102_sensor tas5110c1b = {
+ .name = "TAS5110C1B",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .sysfs_ops = SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_3WIRES,
+ .init = &tas5110c1b_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0xf6,
+ .step = 0x01,
+ .default_value = 0x40,
+ .flags = 0,
+ },
+ },
+ .set_ctrl = &tas5110c1b_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ },
+ .set_crop = &tas5110c1b_set_crop,
+ .pix_format = {
+ .width = 352,
+ .height = 288,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &tas5110c1b_set_pix_format
+};
+
+
+int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam)
+{
+ const struct usb_device_id tas5110c1b_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x6001), },
+ { USB_DEVICE(0x0c45, 0x6005), },
+ { USB_DEVICE(0x0c45, 0x60ab), },
+ { }
+ };
+
+ /* Sensor detection is based on USB pid/vid */
+ if (!sn9c102_match_id(cam, tas5110c1b_id_table))
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &tas5110c1b);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
new file mode 100644
index 000000000000..c7b339740bbf
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
@@ -0,0 +1,169 @@
+/***************************************************************************
+ * Plug-in for TAS5130D1B image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor tas5130d1b;
+
+
+static int tas5130d1b_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x01, 0x01);
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+ err += sn9c102_write_reg(cam, 0x04, 0x01);
+ err += sn9c102_write_reg(cam, 0x01, 0x10);
+ err += sn9c102_write_reg(cam, 0x00, 0x11);
+ err += sn9c102_write_reg(cam, 0x00, 0x14);
+ err += sn9c102_write_reg(cam, 0x60, 0x17);
+ err += sn9c102_write_reg(cam, 0x07, 0x18);
+
+ return err;
+}
+
+
+static int tas5130d1b_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value);
+ break;
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int tas5130d1b_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &tas5130d1b;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12;
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ /* Do NOT change! */
+ err += sn9c102_write_reg(cam, 0x1f, 0x1a);
+ err += sn9c102_write_reg(cam, 0x1a, 0x1b);
+ err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19);
+
+ return err;
+}
+
+
+static int tas5130d1b_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x63, 0x19);
+ else
+ err += sn9c102_write_reg(cam, 0xf3, 0x19);
+
+ return err;
+}
+
+
+static struct sn9c102_sensor tas5130d1b = {
+ .name = "TAS5130D1B",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .sysfs_ops = SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_3WIRES,
+ .init = &tas5130d1b_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0xf6,
+ .step = 0x02,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0x47,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ },
+ .set_ctrl = &tas5130d1b_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &tas5130d1b_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &tas5130d1b_set_pix_format
+};
+
+
+int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam)
+{
+ const struct usb_device_id tas5130d1b_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x6025), },
+ { USB_DEVICE(0x0c45, 0x60aa), },
+ { }
+ };
+
+ /* Sensor detection is based on USB pid/vid */
+ if (!sn9c102_match_id(cam, tas5130d1b_id_table))
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &tas5130d1b);
+
+ return 0;
+}
diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c
new file mode 100644
index 000000000000..9636da20748d
--- /dev/null
+++ b/drivers/media/video/stv680.c
@@ -0,0 +1,1508 @@
+/*
+ * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net)
+ *
+ * Thanks to STMicroelectronics for information on the usb commands, and
+ * to Steve Miller at STM for his help and encouragement while I was
+ * writing this driver.
+ *
+ * This driver is based heavily on the
+ * Endpoints (formerly known as AOX) se401 USB Camera Driver
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
+ *
+ * Still somewhat based on the Linux ov511 driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * History:
+ * ver 0.1 October, 2001. Initial attempt.
+ *
+ * ver 0.2 November, 2001. Fixed asbility to resize, added brightness
+ * function, made more stable (?)
+ *
+ * ver 0.21 Nov, 2001. Added gamma correction and white balance,
+ * due to Alexander Schwartz. Still trying to
+ * improve stablility. Moved stuff into stv680.h
+ *
+ * ver 0.22 Nov, 2001. Added sharpen function (by Michael Sweet,
+ * mike@easysw.com) from GIMP, also used in pencam.
+ * Simple, fast, good integer math routine.
+ *
+ * ver 0.23 Dec, 2001 (gkh)
+ * Took out sharpen function, ran code through
+ * Lindent, and did other minor tweaks to get
+ * things to work properly with 2.5.1
+ *
+ * ver 0.24 Jan, 2002 (kjs)
+ * Fixed the problem with webcam crashing after
+ * two pictures. Changed the way pic is halved to
+ * improve quality. Got rid of green line around
+ * frame. Fix brightness reset when changing size
+ * bug. Adjusted gamma filters slightly.
+ *
+ * ver 0.25 Jan, 2002 (kjs)
+ * Fixed a bug in which the driver sometimes attempted
+ * to set to a non-supported size. This allowed
+ * gnomemeeting to work.
+ * Fixed proc entry removal bug.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/errno.h>
+#include <linux/videodev.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+
+#include "stv680.h"
+
+static int video_nr = -1;
+static int swapRGB = 0; /* default for auto sleect */
+static int swapRGB_on = 0; /* default to allow auto select; -1=swap never, +1= swap always */
+
+static unsigned int debug = 0;
+
+#define PDEBUG(level, fmt, args...) \
+ do { \
+ if (debug >= level) \
+ info("[%s:%d] " fmt, __FUNCTION__, __LINE__ , ## args); \
+ } while (0)
+
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.25"
+#define DRIVER_AUTHOR "Kevin Sisson <kjsisson@bellsouth.net>"
+#define DRIVER_DESC "STV0680 USB Camera Driver"
+
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_LICENSE ("GPL");
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC (debug, "Debug enabled or not");
+module_param(swapRGB_on, int, 0);
+MODULE_PARM_DESC (swapRGB_on, "Red/blue swap: 1=always, 0=auto, -1=never");
+module_param(video_nr, int, 0);
+
+/********************************************************************
+ *
+ * Memory management
+ *
+ * This is a shameless copy from the USB-cpia driver (linux kernel
+ * version 2.3.29 or so, I have no idea what this code actually does ;).
+ * Actually it seems to be a copy of a shameless copy of the bttv-driver.
+ * Or that is a copy of a shameless copy of ... (To the powers: is there
+ * no generic kernel-function to do this sort of stuff?)
+ *
+ * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says
+ * there will be one, but apparentely not yet -jerdfelt
+ *
+ * So I copied it again for the ov511 driver -claudio
+ *
+ * Same for the se401 driver -Jeroen
+ *
+ * And the STV0680 driver - Kevin
+ ********************************************************************/
+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 (size > 0) {
+ SetPageReserved(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;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree (mem);
+}
+
+
+/*********************************************************************
+ * pencam read/write functions
+ ********************************************************************/
+
+static int stv_sndctrl (int set, struct usb_stv *stv680, unsigned short req, unsigned short value, unsigned char *buffer, int size)
+{
+ int ret = -1;
+
+ switch (set) {
+ case 0: /* 0xc1 */
+ ret = usb_control_msg (stv680->udev,
+ usb_rcvctrlpipe (stv680->udev, 0),
+ req,
+ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT),
+ value, 0, buffer, size, PENCAM_TIMEOUT);
+ break;
+
+ case 1: /* 0x41 */
+ ret = usb_control_msg (stv680->udev,
+ usb_sndctrlpipe (stv680->udev, 0),
+ req,
+ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT),
+ value, 0, buffer, size, PENCAM_TIMEOUT);
+ break;
+
+ case 2: /* 0x80 */
+ ret = usb_control_msg (stv680->udev,
+ usb_rcvctrlpipe (stv680->udev, 0),
+ req,
+ (USB_DIR_IN | USB_RECIP_DEVICE),
+ value, 0, buffer, size, PENCAM_TIMEOUT);
+ break;
+
+ case 3: /* 0x40 */
+ ret = usb_control_msg (stv680->udev,
+ usb_sndctrlpipe (stv680->udev, 0),
+ req,
+ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE),
+ value, 0, buffer, size, PENCAM_TIMEOUT);
+ break;
+
+ }
+ if ((ret < 0) && (req != 0x0a)) {
+ PDEBUG (1, "STV(e): usb_control_msg error %i, request = 0x%x, error = %i", set, req, ret);
+ }
+ return ret;
+}
+
+static int stv_set_config (struct usb_stv *dev, int configuration, int interface, int alternate)
+{
+
+ if (configuration != dev->udev->actconfig->desc.bConfigurationValue
+ || usb_reset_configuration (dev->udev) < 0) {
+ PDEBUG (1, "STV(e): FAILED to reset configuration %i", configuration);
+ return -1;
+ }
+ if (usb_set_interface (dev->udev, interface, alternate) < 0) {
+ PDEBUG (1, "STV(e): FAILED to set alternate interface %i", alternate);
+ return -1;
+ }
+ return 0;
+}
+
+static int stv_stop_video (struct usb_stv *dev)
+{
+ int i;
+ unsigned char *buf;
+
+ buf = kmalloc (40, GFP_KERNEL);
+ if (buf == NULL) {
+ PDEBUG (0, "STV(e): Out of (small buf) memory");
+ return -1;
+ }
+
+ /* this is a high priority command; it stops all lower order commands */
+ if ((i = stv_sndctrl (1, dev, 0x04, 0x0000, buf, 0x0)) < 0) {
+ i = stv_sndctrl (0, dev, 0x80, 0, buf, 0x02); /* Get Last Error; 2 = busy */
+ PDEBUG (1, "STV(i): last error: %i, command = 0x%x", buf[0], buf[1]);
+ } else {
+ PDEBUG (1, "STV(i): Camera reset to idle mode.");
+ }
+
+ if ((i = stv_set_config (dev, 1, 0, 0)) < 0)
+ PDEBUG (1, "STV(e): Reset config during exit failed");
+
+ /* get current mode */
+ buf[0] = 0xf0;
+ if ((i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08)) != 0x08) /* get mode */
+ PDEBUG (0, "STV(e): Stop_video: problem setting original mode");
+ if (dev->origMode != buf[0]) {
+ memset (buf, 0, 8);
+ buf[0] = (unsigned char) dev->origMode;
+ if ((i = stv_sndctrl (3, dev, 0x07, 0x0100, buf, 0x08)) != 0x08) {
+ PDEBUG (0, "STV(e): Stop_video: Set_Camera_Mode failed");
+ i = -1;
+ }
+ buf[0] = 0xf0;
+ i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08);
+ if ((i != 0x08) || (buf[0] != dev->origMode)) {
+ PDEBUG (0, "STV(e): camera NOT set to original resolution.");
+ i = -1;
+ } else
+ PDEBUG (0, "STV(i): Camera set to original resolution");
+ }
+ /* origMode */
+ kfree(buf);
+ return i;
+}
+
+static int stv_set_video_mode (struct usb_stv *dev)
+{
+ int i, stop_video = 1;
+ unsigned char *buf;
+
+ buf = kmalloc (40, GFP_KERNEL);
+ if (buf == NULL) {
+ PDEBUG (0, "STV(e): Out of (small buf) memory");
+ return -1;
+ }
+
+ if ((i = stv_set_config (dev, 1, 0, 0)) < 0) {
+ kfree(buf);
+ return i;
+ }
+
+ i = stv_sndctrl (2, dev, 0x06, 0x0100, buf, 0x12);
+ if (!(i > 0) && (buf[8] == 0x53) && (buf[9] == 0x05)) {
+ PDEBUG (1, "STV(e): Could not get descriptor 0100.");
+ goto error;
+ }
+
+ /* set alternate interface 1 */
+ if ((i = stv_set_config (dev, 1, 0, 1)) < 0)
+ goto error;
+
+ if ((i = stv_sndctrl (0, dev, 0x85, 0, buf, 0x10)) != 0x10)
+ goto error;
+ PDEBUG (1, "STV(i): Setting video mode.");
+ /* Switch to Video mode: 0x0100 = VGA (640x480), 0x0000 = CIF (352x288) 0x0300 = QVGA (320x240) */
+ if ((i = stv_sndctrl (1, dev, 0x09, dev->VideoMode, buf, 0x0)) < 0) {
+ stop_video = 0;
+ goto error;
+ }
+ goto exit;
+
+error:
+ kfree(buf);
+ if (stop_video == 1)
+ stv_stop_video (dev);
+ return -1;
+
+exit:
+ kfree(buf);
+ return 0;
+}
+
+static int stv_init (struct usb_stv *stv680)
+{
+ int i = 0;
+ unsigned char *buffer;
+ unsigned long int bufsize;
+
+ buffer = kzalloc (40, GFP_KERNEL);
+ if (buffer == NULL) {
+ PDEBUG (0, "STV(e): Out of (small buf) memory");
+ return -1;
+ }
+ udelay (100);
+
+ /* set config 1, interface 0, alternate 0 */
+ if ((i = stv_set_config (stv680, 1, 0, 0)) < 0) {
+ kfree(buffer);
+ PDEBUG (0, "STV(e): set config 1,0,0 failed");
+ return -1;
+ }
+ /* ping camera to be sure STV0680 is present */
+ if ((i = stv_sndctrl (0, stv680, 0x88, 0x5678, buffer, 0x02)) != 0x02)
+ goto error;
+ if ((buffer[0] != 0x56) || (buffer[1] != 0x78)) {
+ PDEBUG (1, "STV(e): camera ping failed!!");
+ goto error;
+ }
+
+ /* get camera descriptor */
+ if ((i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x09)) != 0x09)
+ goto error;
+ i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x22);
+ if (!(i >= 0) && (buffer[7] == 0xa0) && (buffer[8] == 0x23)) {
+ PDEBUG (1, "STV(e): Could not get descriptor 0200.");
+ goto error;
+ }
+ if ((i = stv_sndctrl (0, stv680, 0x8a, 0, buffer, 0x02)) != 0x02)
+ goto error;
+ if ((i = stv_sndctrl (0, stv680, 0x8b, 0, buffer, 0x24)) != 0x24)
+ goto error;
+ if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10)
+ goto error;
+
+ stv680->SupportedModes = buffer[7];
+ i = stv680->SupportedModes;
+ stv680->CIF = 0;
+ stv680->VGA = 0;
+ stv680->QVGA = 0;
+ if (i & 1)
+ stv680->CIF = 1;
+ if (i & 2)
+ stv680->VGA = 1;
+ if (i & 8)
+ stv680->QVGA = 1;
+ if (stv680->SupportedModes == 0) {
+ PDEBUG (0, "STV(e): There are NO supported STV680 modes!!");
+ i = -1;
+ goto error;
+ } else {
+ if (stv680->CIF)
+ PDEBUG (0, "STV(i): CIF is supported");
+ if (stv680->QVGA)
+ PDEBUG (0, "STV(i): QVGA is supported");
+ }
+ /* FW rev, ASIC rev, sensor ID */
+ PDEBUG (1, "STV(i): Firmware rev is %i.%i", buffer[0], buffer[1]);
+ PDEBUG (1, "STV(i): ASIC rev is %i.%i", buffer[2], buffer[3]);
+ PDEBUG (1, "STV(i): Sensor ID is %i", (buffer[4]*16) + (buffer[5]>>4));
+
+ /* set alternate interface 1 */
+ if ((i = stv_set_config (stv680, 1, 0, 1)) < 0)
+ goto error;
+
+ if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10)
+ goto error;
+ if ((i = stv_sndctrl (0, stv680, 0x8d, 0, buffer, 0x08)) != 0x08)
+ goto error;
+ i = buffer[3];
+ PDEBUG (0, "STV(i): Camera has %i pictures.", i);
+
+ /* get current mode */
+ if ((i = stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08)) != 0x08)
+ goto error;
+ stv680->origMode = buffer[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */
+
+ /* This will attemp CIF mode, if supported. If not, set to QVGA */
+ memset (buffer, 0, 8);
+ if (stv680->CIF)
+ buffer[0] = 0x00;
+ else if (stv680->QVGA)
+ buffer[0] = 0x03;
+ if ((i = stv_sndctrl (3, stv680, 0x07, 0x0100, buffer, 0x08)) != 0x08) {
+ PDEBUG (0, "STV(i): Set_Camera_Mode failed");
+ i = -1;
+ goto error;
+ }
+ buffer[0] = 0xf0;
+ stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08);
+ if (((stv680->CIF == 1) && (buffer[0] != 0x00)) || ((stv680->QVGA == 1) && (buffer[0] != 0x03))) {
+ PDEBUG (0, "STV(e): Error setting camera video mode!");
+ i = -1;
+ goto error;
+ } else {
+ if (buffer[0] == 0) {
+ stv680->VideoMode = 0x0000;
+ PDEBUG (0, "STV(i): Video Mode set to CIF");
+ }
+ if (buffer[0] == 0x03) {
+ stv680->VideoMode = 0x0300;
+ PDEBUG (0, "STV(i): Video Mode set to QVGA");
+ }
+ }
+ if ((i = stv_sndctrl (0, stv680, 0x8f, 0, buffer, 0x10)) != 0x10)
+ goto error;
+ bufsize = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3]);
+ stv680->cwidth = (buffer[4] << 8) | (buffer[5]); /* ->camera = 322, 356, 644 */
+ stv680->cheight = (buffer[6] << 8) | (buffer[7]); /* ->camera = 242, 292, 484 */
+ stv680->origGain = buffer[12];
+
+ goto exit;
+
+error:
+ i = stv_sndctrl (0, stv680, 0x80, 0, buffer, 0x02); /* Get Last Error */
+ PDEBUG (1, "STV(i): last error: %i, command = 0x%x", buffer[0], buffer[1]);
+ kfree(buffer);
+ return -1;
+
+exit:
+ kfree(buffer);
+
+ /* video = 320x240, 352x288 */
+ if (stv680->CIF == 1) {
+ stv680->maxwidth = 352;
+ stv680->maxheight = 288;
+ stv680->vwidth = 352;
+ stv680->vheight = 288;
+ }
+ if (stv680->QVGA == 1) {
+ stv680->maxwidth = 320;
+ stv680->maxheight = 240;
+ stv680->vwidth = 320;
+ stv680->vheight = 240;
+ }
+
+ stv680->rawbufsize = bufsize; /* must be ./. by 8 */
+ stv680->maxframesize = bufsize * 3; /* RGB size */
+ PDEBUG (2, "STV(i): cwidth = %i, cheight = %i", stv680->cwidth, stv680->cheight);
+ PDEBUG (1, "STV(i): width = %i, height = %i, rawbufsize = %li", stv680->vwidth, stv680->vheight, stv680->rawbufsize);
+
+ /* some default values */
+ stv680->bulk_in_endpointAddr = 0x82;
+ stv680->dropped = 0;
+ stv680->error = 0;
+ stv680->framecount = 0;
+ stv680->readcount = 0;
+ stv680->streaming = 0;
+ /* bright, white, colour, hue, contrast are set by software, not in stv0680 */
+ stv680->brightness = 32767;
+ stv680->chgbright = 0;
+ stv680->whiteness = 0; /* only for greyscale */
+ stv680->colour = 32767;
+ stv680->contrast = 32767;
+ stv680->hue = 32767;
+ stv680->palette = STV_VIDEO_PALETTE;
+ stv680->depth = 24; /* rgb24 bits */
+ if ((swapRGB_on == 0) && (swapRGB == 0))
+ PDEBUG (1, "STV(i): swapRGB is (auto) OFF");
+ else if ((swapRGB_on == 0) && (swapRGB == 1))
+ PDEBUG (1, "STV(i): swapRGB is (auto) ON");
+ else if (swapRGB_on == 1)
+ PDEBUG (1, "STV(i): swapRGB is (forced) ON");
+ else if (swapRGB_on == -1)
+ PDEBUG (1, "STV(i): swapRGB is (forced) OFF");
+
+ if (stv_set_video_mode (stv680) < 0) {
+ PDEBUG (0, "STV(e): Could not set video mode in stv_init");
+ return -1;
+ }
+
+ return 0;
+}
+
+/***************** last of pencam routines *******************/
+
+/****************************************************************************
+ * sysfs
+ ***************************************************************************/
+#define stv680_file(name, variable, field) \
+static ssize_t show_##name(struct class_device *class_dev, char *buf) \
+{ \
+ struct video_device *vdev = to_video_device(class_dev); \
+ struct usb_stv *stv = video_get_drvdata(vdev); \
+ return sprintf(buf, field, stv->variable); \
+} \
+static CLASS_DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
+
+stv680_file(model, camera_name, "%s\n");
+stv680_file(in_use, user, "%d\n");
+stv680_file(streaming, streaming, "%d\n");
+stv680_file(palette, palette, "%i\n");
+stv680_file(frames_total, readcount, "%d\n");
+stv680_file(frames_read, framecount, "%d\n");
+stv680_file(packets_dropped, dropped, "%d\n");
+stv680_file(decoding_errors, error, "%d\n");
+
+static void stv680_create_sysfs_files(struct video_device *vdev)
+{
+ video_device_create_file(vdev, &class_device_attr_model);
+ video_device_create_file(vdev, &class_device_attr_in_use);
+ video_device_create_file(vdev, &class_device_attr_streaming);
+ video_device_create_file(vdev, &class_device_attr_palette);
+ video_device_create_file(vdev, &class_device_attr_frames_total);
+ video_device_create_file(vdev, &class_device_attr_frames_read);
+ video_device_create_file(vdev, &class_device_attr_packets_dropped);
+ video_device_create_file(vdev, &class_device_attr_decoding_errors);
+}
+
+static void stv680_remove_sysfs_files(struct video_device *vdev)
+{
+ video_device_remove_file(vdev, &class_device_attr_model);
+ video_device_remove_file(vdev, &class_device_attr_in_use);
+ video_device_remove_file(vdev, &class_device_attr_streaming);
+ video_device_remove_file(vdev, &class_device_attr_palette);
+ video_device_remove_file(vdev, &class_device_attr_frames_total);
+ video_device_remove_file(vdev, &class_device_attr_frames_read);
+ video_device_remove_file(vdev, &class_device_attr_packets_dropped);
+ video_device_remove_file(vdev, &class_device_attr_decoding_errors);
+}
+
+/********************************************************************
+ * Camera control
+ *******************************************************************/
+
+static int stv680_get_pict (struct usb_stv *stv680, struct video_picture *p)
+{
+ /* This sets values for v4l interface. max/min = 65535/0 */
+
+ p->brightness = stv680->brightness;
+ p->whiteness = stv680->whiteness; /* greyscale */
+ p->colour = stv680->colour;
+ p->contrast = stv680->contrast;
+ p->hue = stv680->hue;
+ p->palette = stv680->palette;
+ p->depth = stv680->depth;
+ return 0;
+}
+
+static int stv680_set_pict (struct usb_stv *stv680, struct video_picture *p)
+{
+ /* See above stv680_get_pict */
+
+ if (p->palette != STV_VIDEO_PALETTE) {
+ PDEBUG (2, "STV(e): Palette set error in _set_pic");
+ return 1;
+ }
+
+ if (stv680->brightness != p->brightness) {
+ stv680->chgbright = 1;
+ stv680->brightness = p->brightness;
+ }
+
+ stv680->whiteness = p->whiteness; /* greyscale */
+ stv680->colour = p->colour;
+ stv680->contrast = p->contrast;
+ stv680->hue = p->hue;
+ stv680->palette = p->palette;
+ stv680->depth = p->depth;
+
+ return 0;
+}
+
+static void stv680_video_irq (struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_stv *stv680 = urb->context;
+ int length = urb->actual_length;
+
+ if (length < stv680->rawbufsize)
+ PDEBUG (2, "STV(i): Lost data in transfer: exp %li, got %i", stv680->rawbufsize, length);
+
+ /* ohoh... */
+ if (!stv680->streaming)
+ return;
+
+ if (!stv680->udev) {
+ PDEBUG (0, "STV(e): device vapourished in video_irq");
+ return;
+ }
+
+ /* 0 sized packets happen if we are to fast, but sometimes the camera
+ keeps sending them forever...
+ */
+ if (length && !urb->status) {
+ stv680->nullpackets = 0;
+ switch (stv680->scratch[stv680->scratch_next].state) {
+ case BUFFER_READY:
+ case BUFFER_BUSY:
+ stv680->dropped++;
+ break;
+
+ case BUFFER_UNUSED:
+ memcpy (stv680->scratch[stv680->scratch_next].data,
+ (unsigned char *) urb->transfer_buffer, length);
+ stv680->scratch[stv680->scratch_next].state = BUFFER_READY;
+ stv680->scratch[stv680->scratch_next].length = length;
+ if (waitqueue_active (&stv680->wq)) {
+ wake_up_interruptible (&stv680->wq);
+ }
+ stv680->scratch_overflow = 0;
+ stv680->scratch_next++;
+ if (stv680->scratch_next >= STV680_NUMSCRATCH)
+ stv680->scratch_next = 0;
+ break;
+ } /* switch */
+ } else {
+ stv680->nullpackets++;
+ if (stv680->nullpackets > STV680_MAX_NULLPACKETS) {
+ if (waitqueue_active (&stv680->wq)) {
+ wake_up_interruptible (&stv680->wq);
+ }
+ }
+ } /* if - else */
+
+ /* Resubmit urb for new data */
+ urb->status = 0;
+ urb->dev = stv680->udev;
+ if (usb_submit_urb (urb, GFP_ATOMIC))
+ PDEBUG (0, "STV(e): urb burned down in video irq");
+ return;
+} /* _video_irq */
+
+static int stv680_start_stream (struct usb_stv *stv680)
+{
+ struct urb *urb;
+ int err = 0, i;
+
+ stv680->streaming = 1;
+
+ /* Do some memory allocation */
+ for (i = 0; i < STV680_NUMFRAMES; i++) {
+ stv680->frame[i].data = stv680->fbuf + i * stv680->maxframesize;
+ stv680->frame[i].curpix = 0;
+ }
+ /* packet size = 4096 */
+ for (i = 0; i < STV680_NUMSBUF; i++) {
+ stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
+ if (stv680->sbuf[i].data == NULL) {
+ PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i);
+ return -1;
+ }
+ }
+
+ stv680->scratch_next = 0;
+ stv680->scratch_use = 0;
+ stv680->scratch_overflow = 0;
+ for (i = 0; i < STV680_NUMSCRATCH; i++) {
+ stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
+ if (stv680->scratch[i].data == NULL) {
+ PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i);
+ return -1;
+ }
+ stv680->scratch[i].state = BUFFER_UNUSED;
+ }
+
+ for (i = 0; i < STV680_NUMSBUF; i++) {
+ urb = usb_alloc_urb (0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ /* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */
+ usb_fill_bulk_urb (urb, stv680->udev,
+ usb_rcvbulkpipe (stv680->udev, stv680->bulk_in_endpointAddr),
+ stv680->sbuf[i].data, stv680->rawbufsize,
+ stv680_video_irq, stv680);
+ stv680->urb[i] = urb;
+ err = usb_submit_urb (stv680->urb[i], GFP_KERNEL);
+ if (err)
+ PDEBUG (0, "STV(e): urb burned down in start stream");
+ } /* i STV680_NUMSBUF */
+
+ stv680->framecount = 0;
+ return 0;
+}
+
+static int stv680_stop_stream (struct usb_stv *stv680)
+{
+ int i;
+
+ if (!stv680->streaming || !stv680->udev)
+ return 1;
+
+ stv680->streaming = 0;
+
+ for (i = 0; i < STV680_NUMSBUF; i++)
+ if (stv680->urb[i]) {
+ usb_kill_urb (stv680->urb[i]);
+ usb_free_urb (stv680->urb[i]);
+ stv680->urb[i] = NULL;
+ kfree(stv680->sbuf[i].data);
+ }
+ for (i = 0; i < STV680_NUMSCRATCH; i++) {
+ kfree(stv680->scratch[i].data);
+ stv680->scratch[i].data = NULL;
+ }
+
+ return 0;
+}
+
+static int stv680_set_size (struct usb_stv *stv680, int width, int height)
+{
+ int wasstreaming = stv680->streaming;
+
+ /* Check to see if we need to change */
+ if ((stv680->vwidth == width) && (stv680->vheight == height))
+ return 0;
+
+ PDEBUG (1, "STV(i): size request for %i x %i", width, height);
+ /* Check for a valid mode */
+ if ((!width || !height) || ((width & 1) || (height & 1))) {
+ PDEBUG (1, "STV(e): set_size error: request: v.width = %i, v.height = %i actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight);
+ return 1;
+ }
+
+ if ((width < (stv680->maxwidth / 2)) || (height < (stv680->maxheight / 2))) {
+ width = stv680->maxwidth / 2;
+ height = stv680->maxheight / 2;
+ } else if ((width >= 158) && (width <= 166) && (stv680->QVGA == 1)) {
+ width = 160;
+ height = 120;
+ } else if ((width >= 172) && (width <= 180) && (stv680->CIF == 1)) {
+ width = 176;
+ height = 144;
+ } else if ((width >= 318) && (width <= 350) && (stv680->QVGA == 1)) {
+ width = 320;
+ height = 240;
+ } else if ((width >= 350) && (width <= 358) && (stv680->CIF == 1)) {
+ width = 352;
+ height = 288;
+ } else {
+ PDEBUG (1, "STV(e): request for non-supported size: request: v.width = %i, v.height = %i actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight);
+ return 1;
+ }
+
+ /* Stop a current stream and start it again at the new size */
+ if (wasstreaming)
+ stv680_stop_stream (stv680);
+ stv680->vwidth = width;
+ stv680->vheight = height;
+ PDEBUG (1, "STV(i): size set to %i x %i", stv680->vwidth, stv680->vheight);
+ if (wasstreaming)
+ stv680_start_stream (stv680);
+
+ return 0;
+}
+
+/**********************************************************************
+ * Video Decoding
+ **********************************************************************/
+
+/******* routines from the pencam program; hey, they work! ********/
+
+/*
+ * STV0680 Vision Camera Chipset Driver
+ * Copyright (C) 2000 Adam Harrison <adam@antispin.org>
+*/
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define AD(x, y, w) (((y)*(w)+(x))*3)
+
+static void bayer_unshuffle (struct usb_stv *stv680, struct stv680_scratch *buffer)
+{
+ int x, y, i;
+ int w = stv680->cwidth;
+ int vw = stv680->cwidth, vh = stv680->cheight;
+ unsigned int p = 0;
+ int colour = 0, bayer = 0;
+ unsigned char *raw = buffer->data;
+ struct stv680_frame *frame = &stv680->frame[stv680->curframe];
+ unsigned char *output = frame->data;
+ unsigned char *temp = frame->data;
+ int offset = buffer->offset;
+
+ if (frame->curpix == 0) {
+ if (frame->grabstate == FRAME_READY) {
+ frame->grabstate = FRAME_GRABBING;
+ }
+ }
+ if (offset != frame->curpix) { /* Regard frame as lost :( */
+ frame->curpix = 0;
+ stv680->error++;
+ return;
+ }
+
+ if ((stv680->vwidth == 320) || (stv680->vwidth == 160)) {
+ vw = 320;
+ vh = 240;
+ }
+ if ((stv680->vwidth == 352) || (stv680->vwidth == 176)) {
+ vw = 352;
+ vh = 288;
+ }
+
+ memset (output, 0, 3 * vw * vh); /* clear output matrix. */
+
+ for (y = 0; y < vh; y++) {
+ for (x = 0; x < vw; x++) {
+ if (x & 1)
+ p = *(raw + y * w + (x >> 1));
+ else
+ p = *(raw + y * w + (x >> 1) + (w >> 1));
+
+ if (y & 1)
+ bayer = 2;
+ else
+ bayer = 0;
+ if (x & 1)
+ bayer++;
+
+ switch (bayer) {
+ case 0:
+ case 3:
+ colour = 1;
+ break;
+ case 1:
+ colour = 0;
+ break;
+ case 2:
+ colour = 2;
+ break;
+ }
+ i = (y * vw + x) * 3;
+ *(output + i + colour) = (unsigned char) p;
+ } /* for x */
+
+ } /* for y */
+
+ /****** gamma correction plus hardcoded white balance */
+ /* Thanks to Alexander Schwartx <alexander.schwartx@gmx.net> for this code.
+ Correction values red[], green[], blue[], are generated by
+ (pow(i/256.0, GAMMA)*255.0)*white balanceRGB where GAMMA=0.55, 1<i<255.
+ White balance (RGB)= 1.0, 1.17, 1.48. Values are calculated as double float and
+ converted to unsigned char. Values are in stv680.h */
+
+ for (y = 0; y < vh; y++) {
+ for (x = 0; x < vw; x++) {
+ i = (y * vw + x) * 3;
+ *(output + i) = red[*(output + i)];
+ *(output + i + 1) = green[*(output + i + 1)];
+ *(output + i + 2) = blue[*(output + i + 2)];
+ }
+ }
+
+ /****** bayer demosaic ******/
+ for (y = 1; y < (vh - 1); y++) {
+ for (x = 1; x < (vw - 1); x++) { /* work out pixel type */
+ if (y & 1)
+ bayer = 0;
+ else
+ bayer = 2;
+ if (!(x & 1))
+ bayer++;
+
+ switch (bayer) {
+ case 0: /* green. blue lr, red tb */
+ *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y, vw) + BLUE) + (int) *(output + AD (x + 1, y, vw) + BLUE)) >> 1;
+ *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x, y - 1, vw) + RED) + (int) *(output + AD (x, y + 1, vw) + RED)) >> 1;
+ break;
+
+ case 1: /* blue. green lrtb, red diagonals */
+ *(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2;
+ *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y - 1, vw) + RED) + (int) *(output + AD (x - 1, y + 1, vw) + RED) + (int) *(output + AD (x + 1, y - 1, vw) + RED) + (int) *(output + AD (x + 1, y + 1, vw) + RED)) >> 2;
+ break;
+
+ case 2: /* red. green lrtb, blue diagonals */
+ *(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2;
+ *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y - 1, vw) + BLUE) + (int) *(output + AD (x + 1, y - 1, vw) + BLUE) + (int) *(output + AD (x - 1, y + 1, vw) + BLUE) + (int) *(output + AD (x + 1, y + 1, vw) + BLUE)) >> 2;
+ break;
+
+ case 3: /* green. red lr, blue tb */
+ *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y, vw) + RED) + (int) *(output + AD (x + 1, y, vw) + RED)) >> 1;
+ *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x, y - 1, vw) + BLUE) + (int) *(output + AD (x, y + 1, vw) + BLUE)) >> 1;
+ break;
+ } /* switch */
+ } /* for x */
+ } /* for y - end demosaic */
+
+ /* fix top and bottom row, left and right side */
+ i = vw * 3;
+ memcpy (output, (output + i), i);
+ memcpy ((output + (vh * i)), (output + ((vh - 1) * i)), i);
+ for (y = 0; y < vh; y++) {
+ i = y * vw * 3;
+ memcpy ((output + i), (output + i + 3), 3);
+ memcpy ((output + i + (vw * 3)), (output + i + (vw - 1) * 3), 3);
+ }
+
+ /* process all raw data, then trim to size if necessary */
+ if ((stv680->vwidth == 160) || (stv680->vwidth == 176)) {
+ i = 0;
+ for (y = 0; y < vh; y++) {
+ if (!(y & 1)) {
+ for (x = 0; x < vw; x++) {
+ p = (y * vw + x) * 3;
+ if (!(x & 1)) {
+ *(output + i) = *(output + p);
+ *(output + i + 1) = *(output + p + 1);
+ *(output + i + 2) = *(output + p + 2);
+ i += 3;
+ }
+ } /* for x */
+ }
+ } /* for y */
+ }
+ /* reset to proper width */
+ if ((stv680->vwidth == 160)) {
+ vw = 160;
+ vh = 120;
+ }
+ if ((stv680->vwidth == 176)) {
+ vw = 176;
+ vh = 144;
+ }
+
+ /* output is RGB; some programs want BGR */
+ /* swapRGB_on=0 -> program decides; swapRGB_on=1, always swap */
+ /* swapRGB_on=-1, never swap */
+ if (((swapRGB == 1) && (swapRGB_on != -1)) || (swapRGB_on == 1)) {
+ for (y = 0; y < vh; y++) {
+ for (x = 0; x < vw; x++) {
+ i = (y * vw + x) * 3;
+ *(temp) = *(output + i);
+ *(output + i) = *(output + i + 2);
+ *(output + i + 2) = *(temp);
+ }
+ }
+ }
+ /* brightness */
+ if (stv680->chgbright == 1) {
+ if (stv680->brightness >= 32767) {
+ p = (stv680->brightness - 32767) / 256;
+ for (x = 0; x < (vw * vh * 3); x++) {
+ if ((*(output + x) + (unsigned char) p) > 255)
+ *(output + x) = 255;
+ else
+ *(output + x) += (unsigned char) p;
+ } /* for */
+ } else {
+ p = (32767 - stv680->brightness) / 256;
+ for (x = 0; x < (vw * vh * 3); x++) {
+ if ((unsigned char) p > *(output + x))
+ *(output + x) = 0;
+ else
+ *(output + x) -= (unsigned char) p;
+ } /* for */
+ } /* else */
+ }
+ /* if */
+ frame->curpix = 0;
+ frame->curlinepix = 0;
+ frame->grabstate = FRAME_DONE;
+ stv680->framecount++;
+ stv680->readcount++;
+ if (stv680->frame[(stv680->curframe + 1) & (STV680_NUMFRAMES - 1)].grabstate == FRAME_READY) {
+ stv680->curframe = (stv680->curframe + 1) & (STV680_NUMFRAMES - 1);
+ }
+
+} /* bayer_unshuffle */
+
+/******* end routines from the pencam program *********/
+
+static int stv680_newframe (struct usb_stv *stv680, int framenr)
+{
+ int errors = 0;
+
+ while (stv680->streaming && (stv680->frame[framenr].grabstate == FRAME_READY || stv680->frame[framenr].grabstate == FRAME_GRABBING)) {
+ if (!stv680->frame[framenr].curpix) {
+ errors++;
+ }
+ wait_event_interruptible (stv680->wq, (stv680->scratch[stv680->scratch_use].state == BUFFER_READY));
+
+ if (stv680->nullpackets > STV680_MAX_NULLPACKETS) {
+ stv680->nullpackets = 0;
+ PDEBUG (2, "STV(i): too many null length packets, restarting capture");
+ stv680_stop_stream (stv680);
+ stv680_start_stream (stv680);
+ } else {
+ if (stv680->scratch[stv680->scratch_use].state != BUFFER_READY) {
+ stv680->frame[framenr].grabstate = FRAME_ERROR;
+ PDEBUG (2, "STV(e): FRAME_ERROR in _newframe");
+ return -EIO;
+ }
+ stv680->scratch[stv680->scratch_use].state = BUFFER_BUSY;
+
+ bayer_unshuffle (stv680, &stv680->scratch[stv680->scratch_use]);
+
+ stv680->scratch[stv680->scratch_use].state = BUFFER_UNUSED;
+ stv680->scratch_use++;
+ if (stv680->scratch_use >= STV680_NUMSCRATCH)
+ stv680->scratch_use = 0;
+ if (errors > STV680_MAX_ERRORS) {
+ errors = 0;
+ PDEBUG (2, "STV(i): too many errors, restarting capture");
+ stv680_stop_stream (stv680);
+ stv680_start_stream (stv680);
+ }
+ } /* else */
+ } /* while */
+ return 0;
+}
+
+/*********************************************************************
+ * Video4Linux
+ *********************************************************************/
+
+static int stv_open (struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct usb_stv *stv680 = video_get_drvdata(dev);
+ int err = 0;
+
+ /* we are called with the BKL held */
+ stv680->user = 1;
+ err = stv_init (stv680); /* main initialization routine for camera */
+
+ if (err >= 0) {
+ stv680->fbuf = rvmalloc (stv680->maxframesize * STV680_NUMFRAMES);
+ if (!stv680->fbuf) {
+ PDEBUG (0, "STV(e): Could not rvmalloc frame bufer");
+ err = -ENOMEM;
+ }
+ file->private_data = dev;
+ }
+ if (err)
+ stv680->user = 0;
+
+ return err;
+}
+
+static int stv_close (struct inode *inode, struct file *file)
+{
+ struct video_device *dev = file->private_data;
+ struct usb_stv *stv680 = video_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < STV680_NUMFRAMES; i++)
+ stv680->frame[i].grabstate = FRAME_UNUSED;
+ if (stv680->streaming)
+ stv680_stop_stream (stv680);
+
+ if ((i = stv_stop_video (stv680)) < 0)
+ PDEBUG (1, "STV(e): stop_video failed in stv_close");
+
+ rvfree (stv680->fbuf, stv680->maxframesize * STV680_NUMFRAMES);
+ stv680->user = 0;
+
+ if (stv680->removed) {
+ kfree(stv680);
+ stv680 = NULL;
+ PDEBUG (0, "STV(i): device unregistered");
+ }
+ file->private_data = NULL;
+ return 0;
+}
+
+static int stv680_do_ioctl (struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = file->private_data;
+ struct usb_stv *stv680 = video_get_drvdata(vdev);
+
+ if (!stv680->udev)
+ return -EIO;
+
+ switch (cmd) {
+ case VIDIOCGCAP:{
+ struct video_capability *b = arg;
+
+ strcpy (b->name, stv680->camera_name);
+ b->type = VID_TYPE_CAPTURE;
+ b->channels = 1;
+ b->audios = 0;
+ b->maxwidth = stv680->maxwidth;
+ b->maxheight = stv680->maxheight;
+ b->minwidth = stv680->maxwidth / 2;
+ b->minheight = stv680->maxheight / 2;
+ return 0;
+ }
+ case VIDIOCGCHAN:{
+ struct video_channel *v = arg;
+
+ if (v->channel != 0)
+ return -EINVAL;
+ v->flags = 0;
+ v->tuners = 0;
+ v->type = VIDEO_TYPE_CAMERA;
+ strcpy (v->name, "STV Camera");
+ return 0;
+ }
+ case VIDIOCSCHAN:{
+ struct video_channel *v = arg;
+ if (v->channel != 0)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGPICT:{
+ struct video_picture *p = arg;
+
+ stv680_get_pict (stv680, p);
+ return 0;
+ }
+ case VIDIOCSPICT:{
+ struct video_picture *p = arg;
+
+ if (stv680_set_pict (stv680, p))
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCSWIN:{
+ struct video_window *vw = arg;
+
+ if (vw->flags)
+ return -EINVAL;
+ if (vw->clipcount)
+ return -EINVAL;
+ if (vw->width != stv680->vwidth) {
+ if (stv680_set_size (stv680, vw->width, vw->height)) {
+ PDEBUG (2, "STV(e): failed (from user) set size in VIDIOCSWIN");
+ return -EINVAL;
+ }
+ }
+ return 0;
+ }
+ case VIDIOCGWIN:{
+ struct video_window *vw = arg;
+
+ vw->x = 0; /* FIXME */
+ vw->y = 0;
+ vw->chromakey = 0;
+ vw->flags = 0;
+ vw->clipcount = 0;
+ vw->width = stv680->vwidth;
+ vw->height = stv680->vheight;
+ return 0;
+ }
+ case VIDIOCGMBUF:{
+ struct video_mbuf *vm = arg;
+ int i;
+
+ memset (vm, 0, sizeof (*vm));
+ vm->size = STV680_NUMFRAMES * stv680->maxframesize;
+ vm->frames = STV680_NUMFRAMES;
+ for (i = 0; i < STV680_NUMFRAMES; i++)
+ vm->offsets[i] = stv680->maxframesize * i;
+ return 0;
+ }
+ case VIDIOCMCAPTURE:{
+ struct video_mmap *vm = arg;
+
+ if (vm->format != STV_VIDEO_PALETTE) {
+ PDEBUG (2, "STV(i): VIDIOCMCAPTURE vm.format (%i) != VIDEO_PALETTE (%i)",
+ vm->format, STV_VIDEO_PALETTE);
+ if ((vm->format == 3) && (swapRGB_on == 0)) {
+ PDEBUG (2, "STV(i): VIDIOCMCAPTURE swapRGB is (auto) ON");
+ /* this may fix those apps (e.g., xawtv) that want BGR */
+ swapRGB = 1;
+ }
+ return -EINVAL;
+ }
+ if (vm->frame >= STV680_NUMFRAMES) {
+ PDEBUG (2, "STV(e): VIDIOCMCAPTURE vm.frame > NUMFRAMES");
+ return -EINVAL;
+ }
+ if ((stv680->frame[vm->frame].grabstate == FRAME_ERROR)
+ || (stv680->frame[vm->frame].grabstate == FRAME_GRABBING)) {
+ PDEBUG (2, "STV(e): VIDIOCMCAPTURE grabstate (%i) error",
+ stv680->frame[vm->frame].grabstate);
+ return -EBUSY;
+ }
+ /* Is this according to the v4l spec??? */
+ if (stv680->vwidth != vm->width) {
+ if (stv680_set_size (stv680, vm->width, vm->height)) {
+ PDEBUG (2, "STV(e): VIDIOCMCAPTURE set_size failed");
+ return -EINVAL;
+ }
+ }
+ stv680->frame[vm->frame].grabstate = FRAME_READY;
+
+ if (!stv680->streaming)
+ stv680_start_stream (stv680);
+
+ return 0;
+ }
+ case VIDIOCSYNC:{
+ int *frame = arg;
+ int ret = 0;
+
+ if (*frame < 0 || *frame >= STV680_NUMFRAMES) {
+ PDEBUG (2, "STV(e): Bad frame # in VIDIOCSYNC");
+ return -EINVAL;
+ }
+ ret = stv680_newframe (stv680, *frame);
+ stv680->frame[*frame].grabstate = FRAME_UNUSED;
+ return ret;
+ }
+ case VIDIOCGFBUF:{
+ struct video_buffer *vb = arg;
+
+ memset (vb, 0, sizeof (*vb));
+ return 0;
+ }
+ case VIDIOCKEY:
+ return 0;
+ case VIDIOCCAPTURE:
+ {
+ PDEBUG (2, "STV(e): VIDIOCCAPTURE failed");
+ return -EINVAL;
+ }
+ case VIDIOCSFBUF:
+ case VIDIOCGTUNER:
+ case VIDIOCSTUNER:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ } /* end switch */
+
+ return 0;
+}
+
+static int stv680_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, stv680_do_ioctl);
+}
+
+static int stv680_mmap (struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = file->private_data;
+ struct usb_stv *stv680 = video_get_drvdata(dev);
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end-vma->vm_start;
+ unsigned long page, pos;
+
+ mutex_lock(&stv680->lock);
+
+ if (stv680->udev == NULL) {
+ mutex_unlock(&stv680->lock);
+ return -EIO;
+ }
+ if (size > (((STV680_NUMFRAMES * stv680->maxframesize) + PAGE_SIZE - 1)
+ & ~(PAGE_SIZE - 1))) {
+ mutex_unlock(&stv680->lock);
+ return -EINVAL;
+ }
+ pos = (unsigned long) stv680->fbuf;
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
+ mutex_unlock(&stv680->lock);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+ mutex_unlock(&stv680->lock);
+
+ return 0;
+}
+
+static ssize_t stv680_read (struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *dev = file->private_data;
+ unsigned long int realcount = count;
+ int ret = 0;
+ struct usb_stv *stv680 = video_get_drvdata(dev);
+ unsigned long int i;
+
+ if (STV680_NUMFRAMES != 2) {
+ PDEBUG (0, "STV(e): STV680_NUMFRAMES needs to be 2!");
+ return -1;
+ }
+ if (stv680->udev == NULL)
+ return -EIO;
+ if (realcount > (stv680->vwidth * stv680->vheight * 3))
+ realcount = stv680->vwidth * stv680->vheight * 3;
+
+ /* Shouldn't happen: */
+ if (stv680->frame[0].grabstate == FRAME_GRABBING) {
+ PDEBUG (2, "STV(e): FRAME_GRABBING in stv680_read");
+ return -EBUSY;
+ }
+ stv680->frame[0].grabstate = FRAME_READY;
+ stv680->frame[1].grabstate = FRAME_UNUSED;
+ stv680->curframe = 0;
+
+ if (!stv680->streaming)
+ stv680_start_stream (stv680);
+
+ if (!stv680->streaming) {
+ ret = stv680_newframe (stv680, 0); /* ret should = 0 */
+ }
+
+ ret = stv680_newframe (stv680, 0);
+
+ if (!ret) {
+ if ((i = copy_to_user (buf, stv680->frame[0].data, realcount)) != 0) {
+ PDEBUG (2, "STV(e): copy_to_user frame 0 failed, ret count = %li", i);
+ return -EFAULT;
+ }
+ } else {
+ realcount = ret;
+ }
+ stv680->frame[0].grabstate = FRAME_UNUSED;
+ return realcount;
+} /* stv680_read */
+
+static struct file_operations stv680_fops = {
+ .owner = THIS_MODULE,
+ .open = stv_open,
+ .release = stv_close,
+ .read = stv680_read,
+ .mmap = stv680_mmap,
+ .ioctl = stv680_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+static struct video_device stv680_template = {
+ .owner = THIS_MODULE,
+ .name = "STV0680 USB camera",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_SE401,
+ .fops = &stv680_fops,
+ .release = video_device_release,
+ .minor = -1,
+};
+
+static int stv680_probe (struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_host_interface *interface;
+ struct usb_stv *stv680 = NULL;
+ char *camera_name = NULL;
+ int retval = 0;
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1) {
+ PDEBUG (0, "STV(e): Number of Configurations != 1");
+ return -ENODEV;
+ }
+
+ interface = &intf->altsetting[0];
+ /* Is it a STV680? */
+ if ((le16_to_cpu(dev->descriptor.idVendor) == USB_PENCAM_VENDOR_ID) &&
+ (le16_to_cpu(dev->descriptor.idProduct) == USB_PENCAM_PRODUCT_ID)) {
+ camera_name = "STV0680";
+ PDEBUG (0, "STV(i): STV0680 camera found.");
+ } else if ((le16_to_cpu(dev->descriptor.idVendor) == USB_CREATIVEGOMINI_VENDOR_ID) &&
+ (le16_to_cpu(dev->descriptor.idProduct) == USB_CREATIVEGOMINI_PRODUCT_ID)) {
+ camera_name = "Creative WebCam Go Mini";
+ PDEBUG (0, "STV(i): Creative WebCam Go Mini found.");
+ } else {
+ PDEBUG (0, "STV(e): Vendor/Product ID do not match STV0680 or Creative WebCam Go Mini values.");
+ PDEBUG (0, "STV(e): Check that the STV0680 or Creative WebCam Go Mini camera is connected to the computer.");
+ retval = -ENODEV;
+ goto error;
+ }
+ /* We found one */
+ if ((stv680 = kzalloc (sizeof (*stv680), GFP_KERNEL)) == NULL) {
+ PDEBUG (0, "STV(e): couldn't kmalloc stv680 struct.");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ stv680->udev = dev;
+ stv680->camera_name = camera_name;
+
+ stv680->vdev = video_device_alloc();
+ if (!stv680->vdev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+ memcpy(stv680->vdev, &stv680_template, sizeof(stv680_template));
+ stv680->vdev->dev = &intf->dev;
+ video_set_drvdata(stv680->vdev, stv680);
+
+ memcpy (stv680->vdev->name, stv680->camera_name, strlen (stv680->camera_name));
+ init_waitqueue_head (&stv680->wq);
+ mutex_init (&stv680->lock);
+ wmb ();
+
+ if (video_register_device (stv680->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
+ PDEBUG (0, "STV(e): video_register_device failed");
+ retval = -EIO;
+ goto error_vdev;
+ }
+ PDEBUG (0, "STV(i): registered new video device: video%d", stv680->vdev->minor);
+
+ usb_set_intfdata (intf, stv680);
+ stv680_create_sysfs_files(stv680->vdev);
+ return 0;
+
+error_vdev:
+ video_device_release(stv680->vdev);
+error:
+ kfree(stv680);
+ return retval;
+}
+
+static inline void usb_stv680_remove_disconnected (struct usb_stv *stv680)
+{
+ int i;
+
+ stv680->udev = NULL;
+ stv680->frame[0].grabstate = FRAME_ERROR;
+ stv680->frame[1].grabstate = FRAME_ERROR;
+ stv680->streaming = 0;
+
+ wake_up_interruptible (&stv680->wq);
+
+ for (i = 0; i < STV680_NUMSBUF; i++)
+ if (stv680->urb[i]) {
+ usb_kill_urb (stv680->urb[i]);
+ usb_free_urb (stv680->urb[i]);
+ stv680->urb[i] = NULL;
+ kfree(stv680->sbuf[i].data);
+ }
+ for (i = 0; i < STV680_NUMSCRATCH; i++)
+ kfree(stv680->scratch[i].data);
+ PDEBUG (0, "STV(i): %s disconnected", stv680->camera_name);
+
+ /* Free the memory */
+ kfree(stv680);
+}
+
+static void stv680_disconnect (struct usb_interface *intf)
+{
+ struct usb_stv *stv680 = usb_get_intfdata (intf);
+
+ usb_set_intfdata (intf, NULL);
+
+ if (stv680) {
+ /* We don't want people trying to open up the device */
+ if (stv680->vdev) {
+ stv680_remove_sysfs_files(stv680->vdev);
+ video_unregister_device(stv680->vdev);
+ stv680->vdev = NULL;
+ }
+ if (!stv680->user) {
+ usb_stv680_remove_disconnected (stv680);
+ } else {
+ stv680->removed = 1;
+ }
+ }
+}
+
+static struct usb_driver stv680_driver = {
+ .name = "stv680",
+ .probe = stv680_probe,
+ .disconnect = stv680_disconnect,
+ .id_table = device_table
+};
+
+/********************************************************************
+ * Module routines
+ ********************************************************************/
+
+static int __init usb_stv680_init (void)
+{
+ if (usb_register (&stv680_driver) < 0) {
+ PDEBUG (0, "STV(e): Could not setup STV0680 driver");
+ return -1;
+ }
+ PDEBUG (0, "STV(i): usb camera driver version %s registering", DRIVER_VERSION);
+
+ info(DRIVER_DESC " " DRIVER_VERSION);
+ return 0;
+}
+
+static void __exit usb_stv680_exit (void)
+{
+ usb_deregister (&stv680_driver);
+ PDEBUG (0, "STV(i): driver deregistered");
+}
+
+module_init (usb_stv680_init);
+module_exit (usb_stv680_exit);
diff --git a/drivers/media/video/stv680.h b/drivers/media/video/stv680.h
new file mode 100644
index 000000000000..ea46e0001e6d
--- /dev/null
+++ b/drivers/media/video/stv680.h
@@ -0,0 +1,227 @@
+/****************************************************************************
+ *
+ * Filename: stv680.h
+ *
+ * Description:
+ * This is a USB driver for STV0680 based usb video cameras.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************************/
+
+/* size of usb transfers */
+#define STV680_PACKETSIZE 4096
+
+/* number of queued bulk transfers to use, may have problems if > 1 */
+#define STV680_NUMSBUF 1
+
+/* number of frames supported by the v4l part */
+#define STV680_NUMFRAMES 2
+
+/* scratch buffers for passing data to the decoders: 2 or 4 are good */
+#define STV680_NUMSCRATCH 2
+
+/* number of nul sized packets to receive before kicking the camera */
+#define STV680_MAX_NULLPACKETS 200
+
+/* number of decoding errors before kicking the camera */
+#define STV680_MAX_ERRORS 100
+
+#define USB_PENCAM_VENDOR_ID 0x0553
+#define USB_PENCAM_PRODUCT_ID 0x0202
+
+#define USB_CREATIVEGOMINI_VENDOR_ID 0x041e
+#define USB_CREATIVEGOMINI_PRODUCT_ID 0x4007
+
+#define PENCAM_TIMEOUT 1000
+/* fmt 4 */
+#define STV_VIDEO_PALETTE VIDEO_PALETTE_RGB24
+
+static struct usb_device_id device_table[] = {
+ {USB_DEVICE (USB_PENCAM_VENDOR_ID, USB_PENCAM_PRODUCT_ID)},
+ {USB_DEVICE (USB_CREATIVEGOMINI_VENDOR_ID, USB_CREATIVEGOMINI_PRODUCT_ID)},
+ {}
+};
+MODULE_DEVICE_TABLE (usb, device_table);
+
+struct stv680_sbuf {
+ unsigned char *data;
+};
+
+enum {
+ FRAME_UNUSED, /* Unused (no MCAPTURE) */
+ FRAME_READY, /* Ready to start grabbing */
+ FRAME_GRABBING, /* In the process of being grabbed into */
+ FRAME_DONE, /* Finished grabbing, but not been synced yet */
+ FRAME_ERROR, /* Something bad happened while processing */
+};
+
+enum {
+ BUFFER_UNUSED,
+ BUFFER_READY,
+ BUFFER_BUSY,
+ BUFFER_DONE,
+};
+
+/* raw camera data <- sbuf (urb transfer buf) */
+struct stv680_scratch {
+ unsigned char *data;
+ volatile int state;
+ int offset;
+ int length;
+};
+
+/* processed data for display ends up here, after bayer */
+struct stv680_frame {
+ unsigned char *data; /* Frame buffer */
+ volatile int grabstate; /* State of grabbing */
+ unsigned char *curline;
+ int curlinepix;
+ int curpix;
+};
+
+/* this is almost the video structure uvd_t, with extra parameters for stv */
+struct usb_stv {
+ struct video_device *vdev;
+
+ struct usb_device *udev;
+
+ unsigned char bulk_in_endpointAddr; /* __u8 the address of the bulk in endpoint */
+ char *camera_name;
+
+ unsigned int VideoMode; /* 0x0100 = VGA, 0x0000 = CIF, 0x0300 = QVGA */
+ int SupportedModes;
+ int CIF;
+ int VGA;
+ int QVGA;
+ int cwidth; /* camera width */
+ int cheight; /* camera height */
+ int maxwidth; /* max video width */
+ int maxheight; /* max video height */
+ int vwidth; /* current width for video window */
+ int vheight; /* current height for video window */
+ unsigned long int rawbufsize;
+ unsigned long int maxframesize; /* rawbufsize * 3 for RGB */
+
+ int origGain;
+ int origMode; /* original camera mode */
+
+ struct mutex lock; /* to lock the structure */
+ int user; /* user count for exclusive use */
+ int removed; /* device disconnected */
+ int streaming; /* Are we streaming video? */
+ char *fbuf; /* Videodev buffer area */
+ struct urb *urb[STV680_NUMSBUF]; /* # of queued bulk transfers */
+ int curframe; /* Current receiving frame */
+ struct stv680_frame frame[STV680_NUMFRAMES]; /* # frames supported by v4l part */
+ int readcount;
+ int framecount;
+ int error;
+ int dropped;
+ int scratch_next;
+ int scratch_use;
+ int scratch_overflow;
+ struct stv680_scratch scratch[STV680_NUMSCRATCH]; /* for decoders */
+ struct stv680_sbuf sbuf[STV680_NUMSBUF];
+
+ unsigned int brightness;
+ unsigned int chgbright;
+ unsigned int whiteness;
+ unsigned int colour;
+ unsigned int contrast;
+ unsigned int hue;
+ unsigned int palette;
+ unsigned int depth; /* rgb24 in bits */
+
+ wait_queue_head_t wq; /* Processes waiting */
+
+ int nullpackets;
+};
+
+
+static const unsigned char red[256] = {
+ 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 25, 30, 35, 38, 42,
+ 44, 47, 50, 53, 54, 57, 59, 61, 63, 65, 67, 69,
+ 71, 71, 73, 75, 77, 78, 80, 81, 82, 84, 85, 87,
+ 88, 89, 90, 91, 93, 94, 95, 97, 98, 98, 99, 101,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, 116, 116, 117, 118, 119, 120, 121, 122, 123, 124,
+ 125, 125, 126, 127, 128, 129, 129, 130, 131, 132, 133, 134,
+ 134, 135, 135, 136, 137, 138, 139, 140, 140, 141, 142, 143,
+ 143, 143, 144, 145, 146, 147, 147, 148, 149, 150, 150, 151,
+ 152, 152, 152, 153, 154, 154, 155, 156, 157, 157, 158, 159,
+ 159, 160, 161, 161, 161, 162, 163, 163, 164, 165, 165, 166,
+ 167, 167, 168, 168, 169, 170, 170, 170, 171, 171, 172, 173,
+ 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 179, 179,
+ 180, 180, 181, 181, 182, 183, 183, 184, 184, 185, 185, 186,
+ 187, 187, 188, 188, 188, 188, 189, 190, 190, 191, 191, 192,
+ 192, 193, 193, 194, 195, 195, 196, 196, 197, 197, 197, 197,
+ 198, 198, 199, 199, 200, 201, 201, 202, 202, 203, 203, 204,
+ 204, 205, 205, 206, 206, 206, 206, 207, 207, 208, 208, 209,
+ 209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 214, 215,
+ 215, 215, 215, 216, 216, 217, 217, 218, 218, 218, 219, 219,
+ 220, 220, 221, 221
+};
+
+static const unsigned char green[256] = {
+ 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 28, 34, 39, 43, 47,
+ 50, 53, 56, 59, 61, 64, 66, 68, 71, 73, 75, 77,
+ 79, 80, 82, 84, 86, 87, 89, 91, 92, 94, 95, 97,
+ 98, 100, 101, 102, 104, 105, 106, 108, 109, 110, 111, 113,
+ 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149,
+ 150, 151, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159,
+ 160, 160, 161, 162, 163, 164, 164, 165, 166, 167, 167, 168,
+ 169, 170, 170, 171, 172, 172, 173, 174, 175, 175, 176, 177,
+ 177, 178, 179, 179, 180, 181, 182, 182, 183, 184, 184, 185,
+ 186, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193,
+ 193, 194, 194, 195, 196, 196, 197, 198, 198, 199, 199, 200,
+ 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207,
+ 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214,
+ 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220,
+ 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227,
+ 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233,
+ 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
+ 239, 240, 240, 241, 241, 242, 242, 243, 243, 243, 244, 244,
+ 245, 245, 246, 246
+};
+
+static const unsigned char blue[256] = {
+ 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 30, 37, 42, 47, 51,
+ 55, 58, 61, 64, 67, 70, 72, 74, 78, 80, 82, 84,
+ 86, 88, 90, 92, 94, 95, 97, 100, 101, 103, 104, 106,
+ 107, 110, 111, 112, 114, 115, 116, 118, 119, 121, 122, 124,
+ 125, 126, 127, 128, 129, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 154, 155, 156, 157, 158, 158, 159, 160, 161, 162, 163,
+ 165, 166, 166, 167, 168, 169, 170, 171, 171, 172, 173, 174,
+ 176, 176, 177, 178, 179, 180, 180, 181, 182, 183, 183, 184,
+ 185, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194,
+ 194, 195, 196, 196, 198, 199, 200, 200, 201, 202, 202, 203,
+ 204, 204, 205, 205, 206, 207, 207, 209, 210, 210, 211, 212,
+ 212, 213, 213, 214, 215, 215, 216, 217, 217, 218, 218, 220,
+ 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227,
+ 228, 228, 229, 229, 231, 231, 232, 233, 233, 234, 234, 235,
+ 235, 236, 236, 237, 238, 238, 239, 239, 240, 240, 242, 242,
+ 243, 243, 244, 244, 245, 246, 246, 247, 247, 248, 248, 249,
+ 249, 250, 250, 251, 251, 253, 253, 254, 254, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255
+};
diff --git a/drivers/media/video/usbvideo/Makefile b/drivers/media/video/usbvideo/Makefile
new file mode 100644
index 000000000000..ed410a5ee8c9
--- /dev/null
+++ b/drivers/media/video/usbvideo/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_USB_IBMCAM) += ibmcam.o usbvideo.o ultracam.o
+obj-$(CONFIG_USB_KONICAWC) += konicawc.o usbvideo.o
+obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o
+
diff --git a/drivers/media/video/usbvideo/ibmcam.c b/drivers/media/video/usbvideo/ibmcam.c
new file mode 100644
index 000000000000..a42c22294124
--- /dev/null
+++ b/drivers/media/video/usbvideo/ibmcam.c
@@ -0,0 +1,3932 @@
+/*
+ * USB IBM C-It Video Camera driver
+ *
+ * Supports Xirlink C-It Video Camera, IBM PC Camera,
+ * IBM NetCamera and Veo Stingray.
+ *
+ * This driver is based on earlier work of:
+ *
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Randy Dunlap
+ *
+ * 5/24/00 Removed optional (and unnecessary) locking of the driver while
+ * the device remains plugged in. Corrected race conditions in ibmcam_open
+ * and ibmcam_probe() routines using this as a guideline:
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "usbvideo.h"
+
+#define IBMCAM_VENDOR_ID 0x0545
+#define IBMCAM_PRODUCT_ID 0x8080
+#define NETCAM_PRODUCT_ID 0x8002 /* IBM NetCamera, close to model 2 */
+#define VEO_800C_PRODUCT_ID 0x800C /* Veo Stingray, repackaged Model 2 */
+#define VEO_800D_PRODUCT_ID 0x800D /* Veo Stingray, repackaged Model 4 */
+
+#define MAX_IBMCAM 4 /* How many devices we allow to connect */
+#define USES_IBMCAM_PUTPIXEL 0 /* 0=Fast/oops 1=Slow/secure */
+
+/* Header signatures */
+
+/* Model 1 header: 00 FF 00 xx */
+#define HDRSIG_MODEL1_128x96 0x06 /* U Y V Y ... */
+#define HDRSIG_MODEL1_176x144 0x0e /* U Y V Y ... */
+#define HDRSIG_MODEL1_352x288 0x00 /* V Y U Y ... */
+
+#define IBMCAM_MODEL_1 1 /* XVP-501, 3 interfaces, rev. 0.02 */
+#define IBMCAM_MODEL_2 2 /* KSX-X9903, 2 interfaces, rev. 3.0a */
+#define IBMCAM_MODEL_3 3 /* KSX-X9902, 2 interfaces, rev. 3.01 */
+#define IBMCAM_MODEL_4 4 /* IBM NetCamera, 0545/8002/3.0a */
+
+/* Video sizes supported */
+#define VIDEOSIZE_128x96 VIDEOSIZE(128, 96)
+#define VIDEOSIZE_176x144 VIDEOSIZE(176,144)
+#define VIDEOSIZE_352x288 VIDEOSIZE(352,288)
+#define VIDEOSIZE_320x240 VIDEOSIZE(320,240)
+#define VIDEOSIZE_352x240 VIDEOSIZE(352,240)
+#define VIDEOSIZE_640x480 VIDEOSIZE(640,480)
+#define VIDEOSIZE_160x120 VIDEOSIZE(160,120)
+
+/* Video sizes supported */
+enum {
+ SIZE_128x96 = 0,
+ SIZE_160x120,
+ SIZE_176x144,
+ SIZE_320x240,
+ SIZE_352x240,
+ SIZE_352x288,
+ SIZE_640x480,
+ /* Add/remove/rearrange items before this line */
+ SIZE_LastItem
+};
+
+/*
+ * This structure lives in uvd->user field.
+ */
+typedef struct {
+ int initialized; /* Had we already sent init sequence? */
+ int camera_model; /* What type of IBM camera we got? */
+ int has_hdr;
+} ibmcam_t;
+#define IBMCAM_T(uvd) ((ibmcam_t *)((uvd)->user_data))
+
+static struct usbvideo *cams;
+
+static int debug;
+
+static int flags; /* = FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */
+
+static const int min_canvasWidth = 8;
+static const int min_canvasHeight = 4;
+
+static int lighting = 1; /* Medium */
+
+#define SHARPNESS_MIN 0
+#define SHARPNESS_MAX 6
+static int sharpness = 4; /* Low noise, good details */
+
+#define FRAMERATE_MIN 0
+#define FRAMERATE_MAX 6
+static int framerate = -1;
+
+static int size = SIZE_352x288;
+
+/*
+ * Here we define several initialization variables. They may
+ * be used to automatically set color, hue, brightness and
+ * contrast to desired values. This is particularly useful in
+ * case of webcams (which have no controls and no on-screen
+ * output) and also when a client V4L software is used that
+ * does not have some of those controls. In any case it's
+ * good to have startup values as options.
+ *
+ * These values are all in [0..255] range. This simplifies
+ * operation. Note that actual values of V4L variables may
+ * be scaled up (as much as << 8). User can see that only
+ * on overlay output, however, or through a V4L client.
+ */
+static int init_brightness = 128;
+static int init_contrast = 192;
+static int init_color = 128;
+static int init_hue = 128;
+static int hue_correction = 128;
+
+/* Settings for camera model 2 */
+static int init_model2_rg2 = -1;
+static int init_model2_sat = -1;
+static int init_model2_yb = -1;
+
+/* 01.01.08 - Added for RCA video in support -LO */
+/* Settings for camera model 3 */
+static int init_model3_input = 0;
+
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)");
+module_param(flags, int, 0);
+MODULE_PARM_DESC(flags, "Bitfield: 0=VIDIOCSYNC, 1=B/W, 2=show hints, 3=show stats, 4=test pattern, 5=separate frames, 6=clean frames");
+module_param(framerate, int, 0);
+MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)");
+module_param(lighting, int, 0);
+MODULE_PARM_DESC(lighting, "Photosensitivity: 0=bright, 1=medium (default), 2=low light");
+module_param(sharpness, int, 0);
+MODULE_PARM_DESC(sharpness, "Model1 noise reduction: 0=smooth, 6=sharp (default=4)");
+module_param(size, int, 0);
+MODULE_PARM_DESC(size, "Image size: 0=128x96 1=160x120 2=176x144 3=320x240 4=352x240 5=352x288 6=640x480 (default=5)");
+module_param(init_brightness, int, 0);
+MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)");
+module_param(init_contrast, int, 0);
+MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)");
+module_param(init_color, int, 0);
+MODULE_PARM_DESC(init_color, "Color preconfiguration: 0-255 (default=128)");
+module_param(init_hue, int, 0);
+MODULE_PARM_DESC(init_hue, "Hue preconfiguration: 0-255 (default=128)");
+module_param(hue_correction, int, 0);
+MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)");
+
+module_param(init_model2_rg2, int, 0);
+MODULE_PARM_DESC(init_model2_rg2, "Model2 preconfiguration: 0-255 (default=47)");
+module_param(init_model2_sat, int, 0);
+MODULE_PARM_DESC(init_model2_sat, "Model2 preconfiguration: 0-255 (default=52)");
+module_param(init_model2_yb, int, 0);
+MODULE_PARM_DESC(init_model2_yb, "Model2 preconfiguration: 0-255 (default=160)");
+
+/* 01.01.08 - Added for RCA video in support -LO */
+module_param(init_model3_input, int, 0);
+MODULE_PARM_DESC(init_model3_input, "Model3 input: 0=CCD 1=RCA");
+
+MODULE_AUTHOR ("Dmitri");
+MODULE_DESCRIPTION ("IBM/Xirlink C-it USB Camera Driver for Linux (c) 2000");
+MODULE_LICENSE("GPL");
+
+/* Still mysterious i2c commands */
+static const unsigned short unknown_88 = 0x0088;
+static const unsigned short unknown_89 = 0x0089;
+static const unsigned short bright_3x[3] = { 0x0031, 0x0032, 0x0033 };
+static const unsigned short contrast_14 = 0x0014;
+static const unsigned short light_27 = 0x0027;
+static const unsigned short sharp_13 = 0x0013;
+
+/* i2c commands for Model 2 cameras */
+static const unsigned short mod2_brightness = 0x001a; /* $5b .. $ee; default=$5a */
+static const unsigned short mod2_set_framerate = 0x001c; /* 0 (fast).. $1F (slow) */
+static const unsigned short mod2_color_balance_rg2 = 0x001e; /* 0 (red) .. $7F (green) */
+static const unsigned short mod2_saturation = 0x0020; /* 0 (b/w) - $7F (full color) */
+static const unsigned short mod2_color_balance_yb = 0x0022; /* 0..$7F, $50 is about right */
+static const unsigned short mod2_hue = 0x0024; /* 0..$7F, $70 is about right */
+static const unsigned short mod2_sensitivity = 0x0028; /* 0 (min) .. $1F (max) */
+
+struct struct_initData {
+ unsigned char req;
+ unsigned short value;
+ unsigned short index;
+};
+
+/*
+ * ibmcam_size_to_videosize()
+ *
+ * This procedure converts module option 'size' into the actual
+ * videosize_t that defines the image size in pixels. We need
+ * simplified 'size' because user wants a simple enumerated list
+ * of choices, not an infinite set of possibilities.
+ */
+static videosize_t ibmcam_size_to_videosize(int size)
+{
+ videosize_t vs = VIDEOSIZE_352x288;
+ RESTRICT_TO_RANGE(size, 0, (SIZE_LastItem-1));
+ switch (size) {
+ case SIZE_128x96:
+ vs = VIDEOSIZE_128x96;
+ break;
+ case SIZE_160x120:
+ vs = VIDEOSIZE_160x120;
+ break;
+ case SIZE_176x144:
+ vs = VIDEOSIZE_176x144;
+ break;
+ case SIZE_320x240:
+ vs = VIDEOSIZE_320x240;
+ break;
+ case SIZE_352x240:
+ vs = VIDEOSIZE_352x240;
+ break;
+ case SIZE_352x288:
+ vs = VIDEOSIZE_352x288;
+ break;
+ case SIZE_640x480:
+ vs = VIDEOSIZE_640x480;
+ break;
+ default:
+ err("size=%d. is not valid", size);
+ break;
+ }
+ return vs;
+}
+
+/*
+ * ibmcam_find_header()
+ *
+ * Locate one of supported header markers in the queue.
+ * Once found, remove all preceding bytes AND the marker (4 bytes)
+ * from the data pump queue. Whatever follows must be video lines.
+ *
+ * History:
+ * 1/21/00 Created.
+ */
+static enum ParseState ibmcam_find_header(struct uvd *uvd) /* FIXME: Add frame here */
+{
+ struct usbvideo_frame *frame;
+ ibmcam_t *icam;
+
+ if ((uvd->curframe) < 0 || (uvd->curframe >= USBVIDEO_NUMFRAMES)) {
+ err("ibmcam_find_header: Illegal frame %d.", uvd->curframe);
+ return scan_EndParse;
+ }
+ icam = IBMCAM_T(uvd);
+ assert(icam != NULL);
+ frame = &uvd->frame[uvd->curframe];
+ icam->has_hdr = 0;
+ switch (icam->camera_model) {
+ case IBMCAM_MODEL_1:
+ {
+ const int marker_len = 4;
+ while (RingQueue_GetLength(&uvd->dp) >= marker_len) {
+ if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) &&
+ (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF) &&
+ (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00))
+ {
+#if 0 /* This code helps to detect new frame markers */
+ info("Header sig: 00 FF 00 %02X", RING_QUEUE_PEEK(&uvd->dp, 3));
+#endif
+ frame->header = RING_QUEUE_PEEK(&uvd->dp, 3);
+ if ((frame->header == HDRSIG_MODEL1_128x96) ||
+ (frame->header == HDRSIG_MODEL1_176x144) ||
+ (frame->header == HDRSIG_MODEL1_352x288))
+ {
+#if 0
+ info("Header found.");
+#endif
+ RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len);
+ icam->has_hdr = 1;
+ break;
+ }
+ }
+ /* If we are still here then this doesn't look like a header */
+ RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
+ }
+ break;
+ }
+ case IBMCAM_MODEL_2:
+case IBMCAM_MODEL_4:
+ {
+ int marker_len = 0;
+ switch (uvd->videosize) {
+ case VIDEOSIZE_176x144:
+ marker_len = 10;
+ break;
+ default:
+ marker_len = 2;
+ break;
+ }
+ while (RingQueue_GetLength(&uvd->dp) >= marker_len) {
+ if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) &&
+ (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF))
+ {
+#if 0
+ info("Header found.");
+#endif
+ RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len);
+ icam->has_hdr = 1;
+ frame->header = HDRSIG_MODEL1_176x144;
+ break;
+ }
+ /* If we are still here then this doesn't look like a header */
+ RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
+ }
+ break;
+ }
+ case IBMCAM_MODEL_3:
+ { /*
+ * Headers: (one precedes every frame). nc=no compression,
+ * bq=best quality bf=best frame rate.
+ *
+ * 176x144: 00 FF 02 { 0A=nc CA=bq EA=bf }
+ * 320x240: 00 FF 02 { 08=nc 28=bq 68=bf }
+ * 640x480: 00 FF 03 { 08=nc 28=bq 68=bf }
+ *
+ * Bytes '00 FF' seem to indicate header. Other two bytes
+ * encode the frame type. This is a set of bit fields that
+ * encode image size, compression type etc. These fields
+ * do NOT contain frame number because all frames carry
+ * the same header.
+ */
+ const int marker_len = 4;
+ while (RingQueue_GetLength(&uvd->dp) >= marker_len) {
+ if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) &&
+ (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF) &&
+ (RING_QUEUE_PEEK(&uvd->dp, 2) != 0xFF))
+ {
+ /*
+ * Combine 2 bytes of frame type into one
+ * easy to use value
+ */
+ unsigned long byte3, byte4;
+
+ byte3 = RING_QUEUE_PEEK(&uvd->dp, 2);
+ byte4 = RING_QUEUE_PEEK(&uvd->dp, 3);
+ frame->header = (byte3 << 8) | byte4;
+#if 0
+ info("Header found.");
+#endif
+ RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len);
+ icam->has_hdr = 1;
+ break;
+ }
+ /* If we are still here then this doesn't look like a header */
+ RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (!icam->has_hdr) {
+ if (uvd->debug > 2)
+ info("Skipping frame, no header");
+ return scan_EndParse;
+ }
+
+ /* Header found */
+ icam->has_hdr = 1;
+ uvd->stats.header_count++;
+ frame->scanstate = ScanState_Lines;
+ frame->curline = 0;
+
+ if (flags & FLAGS_FORCE_TESTPATTERN) {
+ usbvideo_TestPattern(uvd, 1, 1);
+ return scan_NextFrame;
+ }
+ return scan_Continue;
+}
+
+/*
+ * ibmcam_parse_lines()
+ *
+ * Parse one line (interlaced) from the buffer, put
+ * decoded RGB value into the current frame buffer
+ * and add the written number of bytes (RGB) to
+ * the *pcopylen.
+ *
+ * History:
+ * 21-Jan-2000 Created.
+ * 12-Oct-2000 Reworked to reflect interlaced nature of the data.
+ */
+static enum ParseState ibmcam_parse_lines(
+ struct uvd *uvd,
+ struct usbvideo_frame *frame,
+ long *pcopylen)
+{
+ unsigned char *f;
+ ibmcam_t *icam;
+ unsigned int len, scanLength, scanHeight, order_uv, order_yc;
+ int v4l_linesize; /* V4L line offset */
+ const int hue_corr = (uvd->vpic.hue - 0x8000) >> 10; /* -32..+31 */
+ const int hue2_corr = (hue_correction - 128) / 4; /* -32..+31 */
+ const int ccm = 128; /* Color correction median - see below */
+ int y, u, v, i, frame_done=0, color_corr;
+ static unsigned char lineBuffer[640*3];
+ unsigned const char *chromaLine, *lumaLine;
+
+ assert(uvd != NULL);
+ assert(frame != NULL);
+ icam = IBMCAM_T(uvd);
+ assert(icam != NULL);
+ color_corr = (uvd->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/
+ RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1);
+
+ v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL;
+
+ if (IBMCAM_T(uvd)->camera_model == IBMCAM_MODEL_4) {
+ /* Model 4 frame markers do not carry image size identification */
+ switch (uvd->videosize) {
+ case VIDEOSIZE_128x96:
+ case VIDEOSIZE_160x120:
+ case VIDEOSIZE_176x144:
+ scanLength = VIDEOSIZE_X(uvd->videosize);
+ scanHeight = VIDEOSIZE_Y(uvd->videosize);
+ break;
+ default:
+ err("ibmcam_parse_lines: Wrong mode.");
+ return scan_Out;
+ }
+ order_yc = 1; /* order_yc: true=Yc false=cY ('c'=either U or V) */
+ order_uv = 1; /* Always true in this algorithm */
+ } else {
+ switch (frame->header) {
+ case HDRSIG_MODEL1_128x96:
+ scanLength = 128;
+ scanHeight = 96;
+ order_uv = 1; /* U Y V Y ... */
+ break;
+ case HDRSIG_MODEL1_176x144:
+ scanLength = 176;
+ scanHeight = 144;
+ order_uv = 1; /* U Y V Y ... */
+ break;
+ case HDRSIG_MODEL1_352x288:
+ scanLength = 352;
+ scanHeight = 288;
+ order_uv = 0; /* Y V Y V ... */
+ break;
+ default:
+ err("Unknown header signature 00 FF 00 %02lX", frame->header);
+ return scan_NextFrame;
+ }
+ /* order_yc: true=Yc false=cY ('c'=either U or V) */
+ order_yc = (IBMCAM_T(uvd)->camera_model == IBMCAM_MODEL_2);
+ }
+
+ len = scanLength * 3;
+ assert(len <= sizeof(lineBuffer));
+
+ /*
+ * Lines are organized this way:
+ *
+ * I420:
+ * ~~~~
+ * <scanLength->
+ * ___________________________________
+ * |-----Y-----|---UVUVUV...UVUV-----| \
+ * |-----------+---------------------| \
+ * |<-- 176 -->|<------ 176*2 ------>| Total 72. lines (interlaced)
+ * |... ... | ... | /
+ * |<-- 352 -->|<------ 352*2 ------>| Total 144. lines (interlaced)
+ * |___________|_____________________| /
+ * \ \
+ * lumaLine chromaLine
+ */
+
+ /* Make sure there's enough data for the entire line */
+ if (RingQueue_GetLength(&uvd->dp) < len)
+ return scan_Out;
+
+ /* Suck one line out of the ring queue */
+ RingQueue_Dequeue(&uvd->dp, lineBuffer, len);
+
+ /*
+ * Make sure that our writing into output buffer
+ * will not exceed the buffer. Mind that we may write
+ * not into current output scanline but in several after
+ * it as well (if we enlarge image vertically.)
+ */
+ if ((frame->curline + 2) >= VIDEOSIZE_Y(frame->request))
+ return scan_NextFrame;
+
+ /*
+ * Now we are sure that entire line (representing all 'scanLength'
+ * pixels from the camera) is available in the buffer. We
+ * start copying the line left-aligned to the V4L buffer.
+ * If the camera line is shorter then we should pad the V4L
+ * buffer with something (black) to complete the line.
+ */
+ assert(frame->data != NULL);
+ f = frame->data + (v4l_linesize * frame->curline);
+
+ /*
+ * To obtain chrominance data from the 'chromaLine' use this:
+ * v = chromaLine[0]; // 0-1:[0], 2-3:[4], 4-5:[8]...
+ * u = chromaLine[2]; // 0-1:[2], 2-3:[6], 4-5:[10]...
+ *
+ * Indices must be calculated this way:
+ * v_index = (i >> 1) << 2;
+ * u_index = (i >> 1) << 2 + 2;
+ *
+ * where 'i' is the column number [0..VIDEOSIZE_X(frame->request)-1]
+ */
+ lumaLine = lineBuffer;
+ chromaLine = lineBuffer + scanLength;
+ for (i = 0; i < VIDEOSIZE_X(frame->request); i++)
+ {
+ unsigned char rv, gv, bv; /* RGB components */
+
+ /* Check for various visual debugging hints (colorized pixels) */
+ if ((flags & FLAGS_DISPLAY_HINTS) && (icam->has_hdr)) {
+ /*
+ * This is bad and should not happen. This means that
+ * we somehow overshoot the line and encountered new
+ * frame! Obviously our camera/V4L frame size is out
+ * of whack. This cyan dot will help you to figure
+ * out where exactly the new frame arrived.
+ */
+ if (icam->has_hdr == 1) {
+ bv = 0; /* Yellow marker */
+ gv = 0xFF;
+ rv = 0xFF;
+ } else {
+ bv = 0xFF; /* Cyan marker */
+ gv = 0xFF;
+ rv = 0;
+ }
+ icam->has_hdr = 0;
+ goto make_pixel;
+ }
+
+ /*
+ * Check if we are still in range. We may be out of range if our
+ * V4L canvas is wider or taller than the camera "native" image.
+ * Then we quickly fill the remainder of the line with zeros to
+ * make black color and quit the horizontal scanning loop.
+ */
+ if (((frame->curline + 2) >= scanHeight) || (i >= scanLength)) {
+ const int j = i * V4L_BYTES_PER_PIXEL;
+#if USES_IBMCAM_PUTPIXEL
+ /* Refresh 'f' because we don't use it much with PUTPIXEL */
+ f = frame->data + (v4l_linesize * frame->curline) + j;
+#endif
+ memset(f, 0, v4l_linesize - j);
+ break;
+ }
+
+ y = lumaLine[i];
+ if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */
+ rv = gv = bv = y;
+ else {
+ int off_0, off_2;
+
+ off_0 = (i >> 1) << 2;
+ off_2 = off_0 + 2;
+
+ if (order_yc) {
+ off_0++;
+ off_2++;
+ }
+ if (!order_uv) {
+ off_0 += 2;
+ off_2 -= 2;
+ }
+ u = chromaLine[off_0] + hue_corr;
+ v = chromaLine[off_2] + hue2_corr;
+
+ /* Apply color correction */
+ if (color_corr != 0) {
+ /* Magnify up to 2 times, reduce down to zero saturation */
+ u = 128 + ((ccm + color_corr) * (u - 128)) / ccm;
+ v = 128 + ((ccm + color_corr) * (v - 128)) / ccm;
+ }
+ YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv);
+ }
+
+ make_pixel:
+ /*
+ * The purpose of creating the pixel here, in one,
+ * dedicated place is that we may need to make the
+ * pixel wider and taller than it actually is. This
+ * may be used if camera generates small frames for
+ * sake of frame rate (or any other reason.)
+ *
+ * The output data consists of B, G, R bytes
+ * (in this order).
+ */
+#if USES_IBMCAM_PUTPIXEL
+ RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv);
+#else
+ *f++ = bv;
+ *f++ = gv;
+ *f++ = rv;
+#endif
+ /*
+ * Typically we do not decide within a legitimate frame
+ * that we want to end the frame. However debugging code
+ * may detect marker of new frame within the data. Then
+ * this condition activates. The 'data' pointer is already
+ * pointing at the new marker, so we'd better leave it as is.
+ */
+ if (frame_done)
+ break; /* End scanning of lines */
+ }
+ /*
+ * Account for number of bytes that we wrote into output V4L frame.
+ * We do it here, after we are done with the scanline, because we
+ * may fill more than one output scanline if we do vertical
+ * enlargement.
+ */
+ frame->curline += 2;
+ if (pcopylen != NULL)
+ *pcopylen += 2 * v4l_linesize;
+ frame->deinterlace = Deinterlace_FillOddLines;
+
+ if (frame_done || (frame->curline >= VIDEOSIZE_Y(frame->request)))
+ return scan_NextFrame;
+ else
+ return scan_Continue;
+}
+
+/*
+ * ibmcam_model2_320x240_parse_lines()
+ *
+ * This procedure deals with a weird RGB format that is produced by IBM
+ * camera model 2 in modes 320x240 and above; 'x' below is 159 or 175,
+ * depending on horizontal size of the picture:
+ *
+ * <--- 160 or 176 pairs of RA,RB bytes ----->
+ * *-----------------------------------------* \
+ * | RA0 | RB0 | RA1 | RB1 | ... | RAx | RBx | \ This is pair of horizontal lines,
+ * |-----+-----+-----+-----+ ... +-----+-----| *- or one interlaced line, total
+ * | B0 | G0 | B1 | G1 | ... | Bx | Gx | / 120 or 144 such pairs which yield
+ * |=====+=====+=====+=====+ ... +=====+=====| / 240 or 288 lines after deinterlacing.
+ *
+ * Each group of FOUR bytes (RAi, RBi, Bi, Gi) where i=0..frame_width/2-1
+ * defines ONE pixel. Therefore this format yields 176x144 "decoded"
+ * resolution at best. I do not know why camera sends such format - the
+ * previous model (1) just used interlaced I420 and everyone was happy.
+ *
+ * I do not know what is the difference between RAi and RBi bytes. Both
+ * seemingly represent R component, but slightly vary in value (so that
+ * the picture looks a bit colored if one or another is used). I use
+ * them both as R component in attempt to at least partially recover the
+ * lost resolution.
+ */
+static enum ParseState ibmcam_model2_320x240_parse_lines(
+ struct uvd *uvd,
+ struct usbvideo_frame *frame,
+ long *pcopylen)
+{
+ unsigned char *f, *la, *lb;
+ unsigned int len;
+ int v4l_linesize; /* V4L line offset */
+ int i, j, frame_done=0, color_corr;
+ int scanLength, scanHeight;
+ static unsigned char lineBuffer[352*2];
+
+ switch (uvd->videosize) {
+ case VIDEOSIZE_320x240:
+ case VIDEOSIZE_352x240:
+ case VIDEOSIZE_352x288:
+ scanLength = VIDEOSIZE_X(uvd->videosize);
+ scanHeight = VIDEOSIZE_Y(uvd->videosize);
+ break;
+ default:
+ err("ibmcam_model2_320x240_parse_lines: Wrong mode.");
+ return scan_Out;
+ }
+
+ color_corr = (uvd->vpic.colour) >> 8; /* 0..+255 */
+ v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL;
+
+ len = scanLength * 2; /* See explanation above */
+ assert(len <= sizeof(lineBuffer));
+
+ /* Make sure there's enough data for the entire line */
+ if (RingQueue_GetLength(&uvd->dp) < len)
+ return scan_Out;
+
+ /* Suck one line out of the ring queue */
+ RingQueue_Dequeue(&uvd->dp, lineBuffer, len);
+
+ /*
+ * Make sure that our writing into output buffer
+ * will not exceed the buffer. Mind that we may write
+ * not into current output scanline but in several after
+ * it as well (if we enlarge image vertically.)
+ */
+ if ((frame->curline + 2) >= VIDEOSIZE_Y(frame->request))
+ return scan_NextFrame;
+
+ la = lineBuffer;
+ lb = lineBuffer + scanLength;
+
+ /*
+ * Now we are sure that entire line (representing all
+ * VIDEOSIZE_X(frame->request)
+ * pixels from the camera) is available in the scratch buffer. We
+ * start copying the line left-aligned to the V4L buffer (which
+ * might be larger - not smaller, hopefully). If the camera
+ * line is shorter then we should pad the V4L buffer with something
+ * (black in this case) to complete the line.
+ */
+ f = frame->data + (v4l_linesize * frame->curline);
+
+ /* Fill the 2-line strip */
+ for (i = 0; i < VIDEOSIZE_X(frame->request); i++) {
+ int y, rv, gv, bv; /* RGB components */
+
+ j = i & (~1);
+
+ /* Check for various visual debugging hints (colorized pixels) */
+ if ((flags & FLAGS_DISPLAY_HINTS) && (IBMCAM_T(uvd)->has_hdr)) {
+ if (IBMCAM_T(uvd)->has_hdr == 1) {
+ bv = 0; /* Yellow marker */
+ gv = 0xFF;
+ rv = 0xFF;
+ } else {
+ bv = 0xFF; /* Cyan marker */
+ gv = 0xFF;
+ rv = 0;
+ }
+ IBMCAM_T(uvd)->has_hdr = 0;
+ goto make_pixel;
+ }
+
+ /*
+ * Check if we are still in range. We may be out of range if our
+ * V4L canvas is wider or taller than the camera "native" image.
+ * Then we quickly fill the remainder of the line with zeros to
+ * make black color and quit the horizontal scanning loop.
+ */
+ if (((frame->curline + 2) >= scanHeight) || (i >= scanLength)) {
+ const int j = i * V4L_BYTES_PER_PIXEL;
+#if USES_IBMCAM_PUTPIXEL
+ /* Refresh 'f' because we don't use it much with PUTPIXEL */
+ f = frame->data + (v4l_linesize * frame->curline) + j;
+#endif
+ memset(f, 0, v4l_linesize - j);
+ break;
+ }
+
+ /*
+ * Here I use RA and RB components, one per physical pixel.
+ * This causes fine vertical grid on the picture but may improve
+ * horizontal resolution. If you prefer replicating, use this:
+ * rv = la[j + 0]; ... or ... rv = la[j + 1];
+ * then the pixel will be replicated.
+ */
+ rv = la[i];
+ gv = lb[j + 1];
+ bv = lb[j + 0];
+
+ y = (rv + gv + bv) / 3; /* Brightness (badly calculated) */
+
+ if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */
+ rv = gv = bv = y;
+ else if (color_corr != 128) {
+
+ /* Calculate difference between color and brightness */
+ rv -= y;
+ gv -= y;
+ bv -= y;
+
+ /* Scale differences */
+ rv = (rv * color_corr) / 128;
+ gv = (gv * color_corr) / 128;
+ bv = (bv * color_corr) / 128;
+
+ /* Reapply brightness */
+ rv += y;
+ gv += y;
+ bv += y;
+
+ /* Watch for overflows */
+ RESTRICT_TO_RANGE(rv, 0, 255);
+ RESTRICT_TO_RANGE(gv, 0, 255);
+ RESTRICT_TO_RANGE(bv, 0, 255);
+ }
+
+ make_pixel:
+ RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv);
+ }
+ /*
+ * Account for number of bytes that we wrote into output V4L frame.
+ * We do it here, after we are done with the scanline, because we
+ * may fill more than one output scanline if we do vertical
+ * enlargement.
+ */
+ frame->curline += 2;
+ *pcopylen += v4l_linesize * 2;
+ frame->deinterlace = Deinterlace_FillOddLines;
+
+ if (frame_done || (frame->curline >= VIDEOSIZE_Y(frame->request)))
+ return scan_NextFrame;
+ else
+ return scan_Continue;
+}
+
+static enum ParseState ibmcam_model3_parse_lines(
+ struct uvd *uvd,
+ struct usbvideo_frame *frame,
+ long *pcopylen)
+{
+ unsigned char *data;
+ const unsigned char *color;
+ unsigned int len;
+ int v4l_linesize; /* V4L line offset */
+ const int hue_corr = (uvd->vpic.hue - 0x8000) >> 10; /* -32..+31 */
+ const int hue2_corr = (hue_correction - 128) / 4; /* -32..+31 */
+ const int ccm = 128; /* Color correction median - see below */
+ int i, u, v, rw, data_w=0, data_h=0, color_corr;
+ static unsigned char lineBuffer[640*3];
+
+ color_corr = (uvd->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/
+ RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1);
+
+ v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL;
+
+ /* The header tells us what sort of data is in this frame */
+ switch (frame->header) {
+ /*
+ * Uncompressed modes (that are easy to decode).
+ */
+ case 0x0308:
+ data_w = 640;
+ data_h = 480;
+ break;
+ case 0x0208:
+ data_w = 320;
+ data_h = 240;
+ break;
+ case 0x020A:
+ data_w = 160;
+ data_h = 120;
+ break;
+ /*
+ * Compressed modes (ViCE - that I don't know how to decode).
+ */
+ case 0x0328: /* 640x480, best quality compression */
+ case 0x0368: /* 640x480, best frame rate compression */
+ case 0x0228: /* 320x240, best quality compression */
+ case 0x0268: /* 320x240, best frame rate compression */
+ case 0x02CA: /* 160x120, best quality compression */
+ case 0x02EA: /* 160x120, best frame rate compression */
+ /* Do nothing with this - not supported */
+ err("Unsupported mode $%04lx", frame->header);
+ return scan_NextFrame;
+ default:
+ /* Catch unknown headers, may help in learning new headers */
+ err("Strange frame->header=$%08lx", frame->header);
+ return scan_NextFrame;
+ }
+
+ /*
+ * Make sure that our writing into output buffer
+ * will not exceed the buffer. Note that we may write
+ * not into current output scanline but in several after
+ * it as well (if we enlarge image vertically.)
+ */
+ if ((frame->curline + 1) >= data_h) {
+ if (uvd->debug >= 3)
+ info("Reached line %d. (frame is done)", frame->curline);
+ return scan_NextFrame;
+ }
+
+ /* Make sure there's enough data for the entire line */
+ len = 3 * data_w; /* <y-data> <uv-data> */
+ assert(len <= sizeof(lineBuffer));
+
+ /* Make sure there's enough data for the entire line */
+ if (RingQueue_GetLength(&uvd->dp) < len)
+ return scan_Out;
+
+ /* Suck one line out of the ring queue */
+ RingQueue_Dequeue(&uvd->dp, lineBuffer, len);
+
+ data = lineBuffer;
+ color = data + data_w; /* Point to where color planes begin */
+
+ /* Bottom-to-top scanning */
+ rw = (int)VIDEOSIZE_Y(frame->request) - (int)(frame->curline) - 1;
+ RESTRICT_TO_RANGE(rw, 0, VIDEOSIZE_Y(frame->request)-1);
+
+ for (i = 0; i < VIDEOSIZE_X(frame->request); i++) {
+ int y, rv, gv, bv; /* RGB components */
+
+ if (i < data_w) {
+ y = data[i]; /* Luminosity is the first line */
+
+ /* Apply static color correction */
+ u = color[i*2] + hue_corr;
+ v = color[i*2 + 1] + hue2_corr;
+
+ /* Apply color correction */
+ if (color_corr != 0) {
+ /* Magnify up to 2 times, reduce down to zero saturation */
+ u = 128 + ((ccm + color_corr) * (u - 128)) / ccm;
+ v = 128 + ((ccm + color_corr) * (v - 128)) / ccm;
+ }
+ } else
+ y = 0, u = v = 128;
+
+ YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv);
+ RGB24_PUTPIXEL(frame, i, rw, rv, gv, bv); /* Done by deinterlacing now */
+ }
+ frame->deinterlace = Deinterlace_FillEvenLines;
+
+ /*
+ * Account for number of bytes that we wrote into output V4L frame.
+ * We do it here, after we are done with the scanline, because we
+ * may fill more than one output scanline if we do vertical
+ * enlargement.
+ */
+ frame->curline += 2;
+ *pcopylen += 2 * v4l_linesize;
+
+ if (frame->curline >= VIDEOSIZE_Y(frame->request)) {
+ if (uvd->debug >= 3) {
+ info("All requested lines (%ld.) done.",
+ VIDEOSIZE_Y(frame->request));
+ }
+ return scan_NextFrame;
+ } else
+ return scan_Continue;
+}
+
+/*
+ * ibmcam_model4_128x96_parse_lines()
+ *
+ * This decoder is for one strange data format that is produced by Model 4
+ * camera only in 128x96 mode. This is RGB format and here is its description.
+ * First of all, this is non-interlaced stream, meaning that all scan lines
+ * are present in the datastream. There are 96 consecutive blocks of data
+ * that describe all 96 lines of the image. Each block is 5*128 bytes long
+ * and carries R, G, B components. The format of the block is shown in the
+ * code below. First 128*2 bytes are interleaved R and G components. Then
+ * we have a gap (junk data) 64 bytes long. Then follow B and something
+ * else, also interleaved (this makes another 128*2 bytes). After that
+ * probably another 64 bytes of junk follow.
+ *
+ * History:
+ * 10-Feb-2001 Created.
+ */
+static enum ParseState ibmcam_model4_128x96_parse_lines(
+ struct uvd *uvd,
+ struct usbvideo_frame *frame,
+ long *pcopylen)
+{
+ const unsigned char *data_rv, *data_gv, *data_bv;
+ unsigned int len;
+ int i, v4l_linesize; /* V4L line offset */
+ const int data_w=128, data_h=96;
+ static unsigned char lineBuffer[128*5];
+
+ v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL;
+
+ /*
+ * Make sure that our writing into output buffer
+ * will not exceed the buffer. Note that we may write
+ * not into current output scanline but in several after
+ * it as well (if we enlarge image vertically.)
+ */
+ if ((frame->curline + 1) >= data_h) {
+ if (uvd->debug >= 3)
+ info("Reached line %d. (frame is done)", frame->curline);
+ return scan_NextFrame;
+ }
+
+ /*
+ * RGRGRG .... RGRG_____________B?B?B? ... B?B?____________
+ * <---- 128*2 ---><---- 64 ---><--- 128*2 ---><--- 64 --->
+ */
+
+ /* Make sure there's enough data for the entire line */
+ len = 5 * data_w;
+ assert(len <= sizeof(lineBuffer));
+
+ /* Make sure there's enough data for the entire line */
+ if (RingQueue_GetLength(&uvd->dp) < len)
+ return scan_Out;
+
+ /* Suck one line out of the ring queue */
+ RingQueue_Dequeue(&uvd->dp, lineBuffer, len);
+
+ data_rv = lineBuffer;
+ data_gv = lineBuffer + 1;
+ data_bv = lineBuffer + data_w*2 + data_w/2;
+ for (i = 0; i < VIDEOSIZE_X(frame->request); i++) {
+ int rv, gv, bv; /* RGB components */
+ if (i < data_w) {
+ const int j = i * 2;
+ gv = data_rv[j];
+ rv = data_gv[j];
+ bv = data_bv[j];
+ if (flags & FLAGS_MONOCHROME) {
+ unsigned long y;
+ y = rv + gv + bv;
+ y /= 3;
+ if (y > 0xFF)
+ y = 0xFF;
+ rv = gv = bv = (unsigned char) y;
+ }
+ } else {
+ rv = gv = bv = 0;
+ }
+ RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv);
+ }
+ frame->deinterlace = Deinterlace_None;
+ frame->curline++;
+ *pcopylen += v4l_linesize;
+
+ if (frame->curline >= VIDEOSIZE_Y(frame->request)) {
+ if (uvd->debug >= 3) {
+ info("All requested lines (%ld.) done.",
+ VIDEOSIZE_Y(frame->request));
+ }
+ return scan_NextFrame;
+ } else
+ return scan_Continue;
+}
+
+/*
+ * ibmcam_ProcessIsocData()
+ *
+ * Generic routine to parse the ring queue data. It employs either
+ * ibmcam_find_header() or ibmcam_parse_lines() to do most
+ * of work.
+ *
+ * History:
+ * 1/21/00 Created.
+ */
+static void ibmcam_ProcessIsocData(struct uvd *uvd,
+ struct usbvideo_frame *frame)
+{
+ enum ParseState newstate;
+ long copylen = 0;
+ int mod = IBMCAM_T(uvd)->camera_model;
+
+ while (1) {
+ newstate = scan_Out;
+ if (RingQueue_GetLength(&uvd->dp) > 0) {
+ if (frame->scanstate == ScanState_Scanning) {
+ newstate = ibmcam_find_header(uvd);
+ } else if (frame->scanstate == ScanState_Lines) {
+ if ((mod == IBMCAM_MODEL_2) &&
+ ((uvd->videosize == VIDEOSIZE_352x288) ||
+ (uvd->videosize == VIDEOSIZE_320x240) ||
+ (uvd->videosize == VIDEOSIZE_352x240)))
+ {
+ newstate = ibmcam_model2_320x240_parse_lines(
+ uvd, frame, &copylen);
+ } else if (mod == IBMCAM_MODEL_4) {
+ /*
+ * Model 4 cameras (IBM NetCamera) use Model 2 decoder (RGB)
+ * for 320x240 and above; 160x120 and 176x144 uses Model 1
+ * decoder (YUV), and 128x96 mode uses ???
+ */
+ if ((uvd->videosize == VIDEOSIZE_352x288) ||
+ (uvd->videosize == VIDEOSIZE_320x240) ||
+ (uvd->videosize == VIDEOSIZE_352x240))
+ {
+ newstate = ibmcam_model2_320x240_parse_lines(uvd, frame, &copylen);
+ } else if (uvd->videosize == VIDEOSIZE_128x96) {
+ newstate = ibmcam_model4_128x96_parse_lines(uvd, frame, &copylen);
+ } else {
+ newstate = ibmcam_parse_lines(uvd, frame, &copylen);
+ }
+ } else if (mod == IBMCAM_MODEL_3) {
+ newstate = ibmcam_model3_parse_lines(uvd, frame, &copylen);
+ } else {
+ newstate = ibmcam_parse_lines(uvd, frame, &copylen);
+ }
+ }
+ }
+ if (newstate == scan_Continue)
+ continue;
+ else if ((newstate == scan_NextFrame) || (newstate == scan_Out))
+ break;
+ else
+ return; /* scan_EndParse */
+ }
+
+ if (newstate == scan_NextFrame) {
+ frame->frameState = FrameState_Done;
+ uvd->curframe = -1;
+ uvd->stats.frame_num++;
+ if ((mod == IBMCAM_MODEL_2) || (mod == IBMCAM_MODEL_4)) {
+ /* Need software contrast adjustment for those cameras */
+ frame->flags |= USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST;
+ }
+ }
+
+ /* Update the frame's uncompressed length. */
+ frame->seqRead_Length += copylen;
+
+#if 0
+ {
+ static unsigned char j=0;
+ memset(frame->data, j++, uvd->max_frame_size);
+ frame->frameState = FrameState_Ready;
+ }
+#endif
+}
+
+/*
+ * ibmcam_veio()
+ *
+ * History:
+ * 1/27/00 Added check for dev == NULL; this happens if camera is unplugged.
+ */
+static int ibmcam_veio(
+ struct uvd *uvd,
+ unsigned char req,
+ unsigned short value,
+ unsigned short index)
+{
+ static const char proc[] = "ibmcam_veio";
+ unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */;
+ int i;
+
+ if (!CAMERA_IS_OPERATIONAL(uvd))
+ return 0;
+
+ if (req == 1) {
+ i = usb_control_msg(
+ uvd->dev,
+ usb_rcvctrlpipe(uvd->dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+ value,
+ index,
+ cp,
+ sizeof(cp),
+ 1000);
+#if 0
+ info("USB => %02x%02x%02x%02x%02x%02x%02x%02x "
+ "(req=$%02x val=$%04x ind=$%04x)",
+ cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7],
+ req, value, index);
+#endif
+ } else {
+ i = usb_control_msg(
+ uvd->dev,
+ usb_sndctrlpipe(uvd->dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+ value,
+ index,
+ NULL,
+ 0,
+ 1000);
+ }
+ if (i < 0) {
+ err("%s: ERROR=%d. Camera stopped; Reconnect or reload driver.",
+ proc, i);
+ uvd->last_error = i;
+ }
+ return i;
+}
+
+/*
+ * ibmcam_calculate_fps()
+ *
+ * This procedure roughly calculates the real frame rate based
+ * on FPS code (framerate=NNN option). Actual FPS differs
+ * slightly depending on lighting conditions, so that actual frame
+ * rate is determined by the camera. Since I don't know how to ask
+ * the camera what FPS is now I have to use the FPS code instead.
+ *
+ * The FPS code is in range [0..6], 0 is slowest, 6 is fastest.
+ * Corresponding real FPS should be in range [3..30] frames per second.
+ * The conversion formula is obvious:
+ *
+ * real_fps = 3 + (fps_code * 4.5)
+ *
+ * History:
+ * 1/18/00 Created.
+ */
+static int ibmcam_calculate_fps(struct uvd *uvd)
+{
+ return 3 + framerate*4 + framerate/2;
+}
+
+/*
+ * ibmcam_send_FF_04_02()
+ *
+ * This procedure sends magic 3-command prefix to the camera.
+ * The purpose of this prefix is not known.
+ *
+ * History:
+ * 1/2/00 Created.
+ */
+static void ibmcam_send_FF_04_02(struct uvd *uvd)
+{
+ ibmcam_veio(uvd, 0, 0x00FF, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0004, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0002, 0x0124);
+}
+
+static void ibmcam_send_00_04_06(struct uvd *uvd)
+{
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0004, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0006, 0x0124);
+}
+
+static void ibmcam_send_x_00(struct uvd *uvd, unsigned short x)
+{
+ ibmcam_veio(uvd, 0, x, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0124);
+}
+
+static void ibmcam_send_x_00_05(struct uvd *uvd, unsigned short x)
+{
+ ibmcam_send_x_00(uvd, x);
+ ibmcam_veio(uvd, 0, 0x0005, 0x0124);
+}
+
+static void ibmcam_send_x_00_05_02(struct uvd *uvd, unsigned short x)
+{
+ ibmcam_veio(uvd, 0, x, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0005, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0002, 0x0124);
+}
+
+static void ibmcam_send_x_01_00_05(struct uvd *uvd, unsigned short x)
+{
+ ibmcam_veio(uvd, 0, x, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0005, 0x0124);
+}
+
+static void ibmcam_send_x_00_05_02_01(struct uvd *uvd, unsigned short x)
+{
+ ibmcam_veio(uvd, 0, x, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0005, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0002, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0124);
+}
+
+static void ibmcam_send_x_00_05_02_08_01(struct uvd *uvd, unsigned short x)
+{
+ ibmcam_veio(uvd, 0, x, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0005, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0002, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0008, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0124);
+}
+
+static void ibmcam_Packet_Format1(struct uvd *uvd, unsigned char fkey, unsigned char val)
+{
+ ibmcam_send_x_01_00_05(uvd, unknown_88);
+ ibmcam_send_x_00_05(uvd, fkey);
+ ibmcam_send_x_00_05_02_08_01(uvd, val);
+ ibmcam_send_x_00_05(uvd, unknown_88);
+ ibmcam_send_x_00_05_02_01(uvd, fkey);
+ ibmcam_send_x_00_05(uvd, unknown_89);
+ ibmcam_send_x_00(uvd, fkey);
+ ibmcam_send_00_04_06(uvd);
+ ibmcam_veio(uvd, 1, 0x0000, 0x0126);
+ ibmcam_send_FF_04_02(uvd);
+}
+
+static void ibmcam_PacketFormat2(struct uvd *uvd, unsigned char fkey, unsigned char val)
+{
+ ibmcam_send_x_01_00_05 (uvd, unknown_88);
+ ibmcam_send_x_00_05 (uvd, fkey);
+ ibmcam_send_x_00_05_02 (uvd, val);
+}
+
+static void ibmcam_model2_Packet2(struct uvd *uvd)
+{
+ ibmcam_veio(uvd, 0, 0x00ff, 0x012d);
+ ibmcam_veio(uvd, 0, 0xfea3, 0x0124);
+}
+
+static void ibmcam_model2_Packet1(struct uvd *uvd, unsigned short v1, unsigned short v2)
+{
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x00ff, 0x012e);
+ ibmcam_veio(uvd, 0, v1, 0x012f);
+ ibmcam_veio(uvd, 0, 0x00ff, 0x0130);
+ ibmcam_veio(uvd, 0, 0xc719, 0x0124);
+ ibmcam_veio(uvd, 0, v2, 0x0127);
+
+ ibmcam_model2_Packet2(uvd);
+}
+
+/*
+ * ibmcam_model3_Packet1()
+ *
+ * 00_0078_012d
+ * 00_0097_012f
+ * 00_d141_0124
+ * 00_0096_0127
+ * 00_fea8_0124
+*/
+static void ibmcam_model3_Packet1(struct uvd *uvd, unsigned short v1, unsigned short v2)
+{
+ ibmcam_veio(uvd, 0, 0x0078, 0x012d);
+ ibmcam_veio(uvd, 0, v1, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, v2, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+}
+
+static void ibmcam_model4_BrightnessPacket(struct uvd *uvd, int i)
+{
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0026, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, i, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0038, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0004, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+}
+
+/*
+ * ibmcam_adjust_contrast()
+ *
+ * The contrast value changes from 0 (high contrast) to 15 (low contrast).
+ * This is in reverse to usual order of things (such as TV controls), so
+ * we reverse it again here.
+ *
+ * TODO: we probably don't need to send the setup 5 times...
+ *
+ * History:
+ * 1/2/00 Created.
+ */
+static void ibmcam_adjust_contrast(struct uvd *uvd)
+{
+ unsigned char a_contrast = uvd->vpic.contrast >> 12;
+ unsigned char new_contrast;
+
+ if (a_contrast >= 16)
+ a_contrast = 15;
+ new_contrast = 15 - a_contrast;
+ if (new_contrast == uvd->vpic_old.contrast)
+ return;
+ uvd->vpic_old.contrast = new_contrast;
+ switch (IBMCAM_T(uvd)->camera_model) {
+ case IBMCAM_MODEL_1:
+ {
+ const int ntries = 5;
+ int i;
+ for (i=0; i < ntries; i++) {
+ ibmcam_Packet_Format1(uvd, contrast_14, new_contrast);
+ ibmcam_send_FF_04_02(uvd);
+ }
+ break;
+ }
+ case IBMCAM_MODEL_2:
+ case IBMCAM_MODEL_4:
+ /* Models 2, 4 do not have this control; implemented in software. */
+ break;
+ case IBMCAM_MODEL_3:
+ { /* Preset hardware values */
+ static const struct {
+ unsigned short cv1;
+ unsigned short cv2;
+ unsigned short cv3;
+ } cv[7] = {
+ { 0x05, 0x05, 0x0f }, /* Minimum */
+ { 0x04, 0x04, 0x16 },
+ { 0x02, 0x03, 0x16 },
+ { 0x02, 0x08, 0x16 },
+ { 0x01, 0x0c, 0x16 },
+ { 0x01, 0x0e, 0x16 },
+ { 0x01, 0x10, 0x16 } /* Maximum */
+ };
+ int i = a_contrast / 2;
+ RESTRICT_TO_RANGE(i, 0, 6);
+ ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */
+ ibmcam_model3_Packet1(uvd, 0x0067, cv[i].cv1);
+ ibmcam_model3_Packet1(uvd, 0x005b, cv[i].cv2);
+ ibmcam_model3_Packet1(uvd, 0x005c, cv[i].cv3);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0114);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */
+ usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * ibmcam_change_lighting_conditions()
+ *
+ * Camera model 1:
+ * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low.
+ *
+ * Camera model 2:
+ * We have 16 levels of lighting, 0 for bright light and up to 15 for
+ * low light. But values above 5 or so are useless because camera is
+ * not really capable to produce anything worth viewing at such light.
+ * This setting may be altered only in certain camera state.
+ *
+ * Low lighting forces slower FPS. Lighting is set as a module parameter.
+ *
+ * History:
+ * 1/5/00 Created.
+ * 2/20/00 Added support for Model 2 cameras.
+ */
+static void ibmcam_change_lighting_conditions(struct uvd *uvd)
+{
+ static const char proc[] = "ibmcam_change_lighting_conditions";
+
+ if (debug > 0)
+ info("%s: Set lighting to %hu.", proc, lighting);
+
+ switch (IBMCAM_T(uvd)->camera_model) {
+ case IBMCAM_MODEL_1:
+ {
+ const int ntries = 5;
+ int i;
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, light_27, (unsigned short) lighting);
+ break;
+ }
+ case IBMCAM_MODEL_2:
+#if 0
+ /*
+ * This command apparently requires camera to be stopped. My
+ * experiments showed that it -is- possible to alter the lighting
+ * conditions setting "on the fly", but why bother? This setting does
+ * not work reliably in all cases, so I decided simply to leave the
+ * setting where Xirlink put it - in the camera setup phase. This code
+ * is commented out because it does not work at -any- moment, so its
+ * presence makes no sense. You may use it for experiments.
+ */
+ ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop camera */
+ ibmcam_model2_Packet1(uvd, mod2_sensitivity, lighting);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Start camera */
+#endif
+ break;
+ case IBMCAM_MODEL_3:
+ case IBMCAM_MODEL_4:
+ default:
+ break;
+ }
+}
+
+/*
+ * ibmcam_set_sharpness()
+ *
+ * Cameras model 1 have internal smoothing feature. It is controlled by value in
+ * range [0..6], where 0 is most smooth and 6 is most sharp (raw image, I guess).
+ * Recommended value is 4. Cameras model 2 do not have this feature at all.
+ */
+static void ibmcam_set_sharpness(struct uvd *uvd)
+{
+ static const char proc[] = "ibmcam_set_sharpness";
+
+ switch (IBMCAM_T(uvd)->camera_model) {
+ case IBMCAM_MODEL_1:
+ {
+ static const unsigned short sa[] = { 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a };
+ unsigned short i, sv;
+
+ RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX);
+ if (debug > 0)
+ info("%s: Set sharpness to %hu.", proc, sharpness);
+
+ sv = sa[sharpness - SHARPNESS_MIN];
+ for (i=0; i < 2; i++) {
+ ibmcam_send_x_01_00_05 (uvd, unknown_88);
+ ibmcam_send_x_00_05 (uvd, sharp_13);
+ ibmcam_send_x_00_05_02 (uvd, sv);
+ }
+ break;
+ }
+ case IBMCAM_MODEL_2:
+ case IBMCAM_MODEL_4:
+ /* Models 2, 4 do not have this control */
+ break;
+ case IBMCAM_MODEL_3:
+ { /*
+ * "Use a table of magic numbers.
+ * This setting doesn't really change much.
+ * But that's how Windows does it."
+ */
+ static const struct {
+ unsigned short sv1;
+ unsigned short sv2;
+ unsigned short sv3;
+ unsigned short sv4;
+ } sv[7] = {
+ { 0x00, 0x00, 0x05, 0x14 }, /* Smoothest */
+ { 0x01, 0x04, 0x05, 0x14 },
+ { 0x02, 0x04, 0x05, 0x14 },
+ { 0x03, 0x04, 0x05, 0x14 },
+ { 0x03, 0x05, 0x05, 0x14 },
+ { 0x03, 0x06, 0x05, 0x14 },
+ { 0x03, 0x07, 0x05, 0x14 } /* Sharpest */
+ };
+ RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX);
+ RESTRICT_TO_RANGE(sharpness, 0, 6);
+ ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */
+ ibmcam_model3_Packet1(uvd, 0x0060, sv[sharpness].sv1);
+ ibmcam_model3_Packet1(uvd, 0x0061, sv[sharpness].sv2);
+ ibmcam_model3_Packet1(uvd, 0x0062, sv[sharpness].sv3);
+ ibmcam_model3_Packet1(uvd, 0x0063, sv[sharpness].sv4);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0114);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */
+ usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+ ibmcam_veio(uvd, 0, 0x0001, 0x0113);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * ibmcam_set_brightness()
+ *
+ * This procedure changes brightness of the picture.
+ */
+static void ibmcam_set_brightness(struct uvd *uvd)
+{
+ static const char proc[] = "ibmcam_set_brightness";
+ static const unsigned short n = 1;
+
+ if (debug > 0)
+ info("%s: Set brightness to %hu.", proc, uvd->vpic.brightness);
+
+ switch (IBMCAM_T(uvd)->camera_model) {
+ case IBMCAM_MODEL_1:
+ {
+ unsigned short i, j, bv[3];
+ bv[0] = bv[1] = bv[2] = uvd->vpic.brightness >> 10;
+ if (bv[0] == (uvd->vpic_old.brightness >> 10))
+ return;
+ uvd->vpic_old.brightness = bv[0];
+ for (j=0; j < 3; j++)
+ for (i=0; i < n; i++)
+ ibmcam_Packet_Format1(uvd, bright_3x[j], bv[j]);
+ break;
+ }
+ case IBMCAM_MODEL_2:
+ {
+ unsigned short i, j;
+ i = uvd->vpic.brightness >> 12; /* 0 .. 15 */
+ j = 0x60 + i * ((0xee - 0x60) / 16); /* 0x60 .. 0xee or so */
+ if (uvd->vpic_old.brightness == j)
+ break;
+ uvd->vpic_old.brightness = j;
+ ibmcam_model2_Packet1(uvd, mod2_brightness, j);
+ break;
+ }
+ case IBMCAM_MODEL_3:
+ {
+ /* Model 3: Brightness range 'i' in [0x0C..0x3F] */
+ unsigned short i =
+ 0x0C + (uvd->vpic.brightness / (0xFFFF / (0x3F - 0x0C + 1)));
+ RESTRICT_TO_RANGE(i, 0x0C, 0x3F);
+ if (uvd->vpic_old.brightness == i)
+ break;
+ uvd->vpic_old.brightness = i;
+ ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */
+ ibmcam_model3_Packet1(uvd, 0x0036, i);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0114);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */
+ usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+ ibmcam_veio(uvd, 0, 0x0001, 0x0113);
+ break;
+ }
+ case IBMCAM_MODEL_4:
+ {
+ /* Model 4: Brightness range 'i' in [0x04..0xb4] */
+ unsigned short i = 0x04 + (uvd->vpic.brightness / (0xFFFF / (0xb4 - 0x04 + 1)));
+ RESTRICT_TO_RANGE(i, 0x04, 0xb4);
+ if (uvd->vpic_old.brightness == i)
+ break;
+ uvd->vpic_old.brightness = i;
+ ibmcam_model4_BrightnessPacket(uvd, i);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void ibmcam_set_hue(struct uvd *uvd)
+{
+ switch (IBMCAM_T(uvd)->camera_model) {
+ case IBMCAM_MODEL_2:
+ {
+ unsigned short hue = uvd->vpic.hue >> 9; /* 0 .. 7F */
+ if (uvd->vpic_old.hue == hue)
+ return;
+ uvd->vpic_old.hue = hue;
+ ibmcam_model2_Packet1(uvd, mod2_hue, hue);
+ /* ibmcam_model2_Packet1(uvd, mod2_saturation, sat); */
+ break;
+ }
+ case IBMCAM_MODEL_3:
+ {
+#if 0 /* This seems not to work. No problem, will fix programmatically */
+ unsigned short hue = 0x05 + (uvd->vpic.hue / (0xFFFF / (0x37 - 0x05 + 1)));
+ RESTRICT_TO_RANGE(hue, 0x05, 0x37);
+ if (uvd->vpic_old.hue == hue)
+ return;
+ uvd->vpic_old.hue = hue;
+ ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */
+ ibmcam_model3_Packet1(uvd, 0x007e, hue);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0114);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */
+ usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+ ibmcam_veio(uvd, 0, 0x0001, 0x0113);
+#endif
+ break;
+ }
+ case IBMCAM_MODEL_4:
+ {
+ unsigned short r_gain, g_gain, b_gain, hue;
+
+ /*
+ * I am not sure r/g/b_gain variables exactly control gain
+ * of those channels. Most likely they subtly change some
+ * very internal image processing settings in the camera.
+ * In any case, here is what they do, and feel free to tweak:
+ *
+ * r_gain: seriously affects red gain
+ * g_gain: seriously affects green gain
+ * b_gain: seriously affects blue gain
+ * hue: changes average color from violet (0) to red (0xFF)
+ *
+ * These settings are preset for a decent white balance in
+ * 320x240, 352x288 modes. Low-res modes exhibit higher contrast
+ * and therefore may need different values here.
+ */
+ hue = 20 + (uvd->vpic.hue >> 9);
+ switch (uvd->videosize) {
+ case VIDEOSIZE_128x96:
+ r_gain = 90;
+ g_gain = 166;
+ b_gain = 175;
+ break;
+ case VIDEOSIZE_160x120:
+ r_gain = 70;
+ g_gain = 166;
+ b_gain = 185;
+ break;
+ case VIDEOSIZE_176x144:
+ r_gain = 160;
+ g_gain = 175;
+ b_gain = 185;
+ break;
+ default:
+ r_gain = 120;
+ g_gain = 166;
+ b_gain = 175;
+ break;
+ }
+ RESTRICT_TO_RANGE(hue, 1, 0x7f);
+
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x001e, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, g_gain, 0x0127); /* Green gain */
+ ibmcam_veio(uvd, 0, r_gain, 0x012e); /* Red gain */
+ ibmcam_veio(uvd, 0, b_gain, 0x0130); /* Blue gain */
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, hue, 0x012d); /* Hue */
+ ibmcam_veio(uvd, 0, 0xf545, 0x0124);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * ibmcam_adjust_picture()
+ *
+ * This procedure gets called from V4L interface to update picture settings.
+ * Here we change brightness and contrast.
+ */
+static void ibmcam_adjust_picture(struct uvd *uvd)
+{
+ ibmcam_adjust_contrast(uvd);
+ ibmcam_set_brightness(uvd);
+ ibmcam_set_hue(uvd);
+}
+
+static int ibmcam_model1_setup(struct uvd *uvd)
+{
+ const int ntries = 5;
+ int i;
+
+ ibmcam_veio(uvd, 1, 0x00, 0x0128);
+ ibmcam_veio(uvd, 1, 0x00, 0x0100);
+ ibmcam_veio(uvd, 0, 0x01, 0x0100); /* LED On */
+ ibmcam_veio(uvd, 1, 0x00, 0x0100);
+ ibmcam_veio(uvd, 0, 0x81, 0x0100); /* LED Off */
+ ibmcam_veio(uvd, 1, 0x00, 0x0100);
+ ibmcam_veio(uvd, 0, 0x01, 0x0100); /* LED On */
+ ibmcam_veio(uvd, 0, 0x01, 0x0108);
+
+ ibmcam_veio(uvd, 0, 0x03, 0x0112);
+ ibmcam_veio(uvd, 1, 0x00, 0x0115);
+ ibmcam_veio(uvd, 0, 0x06, 0x0115);
+ ibmcam_veio(uvd, 1, 0x00, 0x0116);
+ ibmcam_veio(uvd, 0, 0x44, 0x0116);
+ ibmcam_veio(uvd, 1, 0x00, 0x0116);
+ ibmcam_veio(uvd, 0, 0x40, 0x0116);
+ ibmcam_veio(uvd, 1, 0x00, 0x0115);
+ ibmcam_veio(uvd, 0, 0x0e, 0x0115);
+ ibmcam_veio(uvd, 0, 0x19, 0x012c);
+
+ ibmcam_Packet_Format1(uvd, 0x00, 0x1e);
+ ibmcam_Packet_Format1(uvd, 0x39, 0x0d);
+ ibmcam_Packet_Format1(uvd, 0x39, 0x09);
+ ibmcam_Packet_Format1(uvd, 0x3b, 0x00);
+ ibmcam_Packet_Format1(uvd, 0x28, 0x22);
+ ibmcam_Packet_Format1(uvd, light_27, 0);
+ ibmcam_Packet_Format1(uvd, 0x2b, 0x1f);
+ ibmcam_Packet_Format1(uvd, 0x39, 0x08);
+
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x2c, 0x00);
+
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x30, 0x14);
+
+ ibmcam_PacketFormat2(uvd, 0x39, 0x02);
+ ibmcam_PacketFormat2(uvd, 0x01, 0xe1);
+ ibmcam_PacketFormat2(uvd, 0x02, 0xcd);
+ ibmcam_PacketFormat2(uvd, 0x03, 0xcd);
+ ibmcam_PacketFormat2(uvd, 0x04, 0xfa);
+ ibmcam_PacketFormat2(uvd, 0x3f, 0xff);
+ ibmcam_PacketFormat2(uvd, 0x39, 0x00);
+
+ ibmcam_PacketFormat2(uvd, 0x39, 0x02);
+ ibmcam_PacketFormat2(uvd, 0x0a, 0x37);
+ ibmcam_PacketFormat2(uvd, 0x0b, 0xb8);
+ ibmcam_PacketFormat2(uvd, 0x0c, 0xf3);
+ ibmcam_PacketFormat2(uvd, 0x0d, 0xe3);
+ ibmcam_PacketFormat2(uvd, 0x0e, 0x0d);
+ ibmcam_PacketFormat2(uvd, 0x0f, 0xf2);
+ ibmcam_PacketFormat2(uvd, 0x10, 0xd5);
+ ibmcam_PacketFormat2(uvd, 0x11, 0xba);
+ ibmcam_PacketFormat2(uvd, 0x12, 0x53);
+ ibmcam_PacketFormat2(uvd, 0x3f, 0xff);
+ ibmcam_PacketFormat2(uvd, 0x39, 0x00);
+
+ ibmcam_PacketFormat2(uvd, 0x39, 0x02);
+ ibmcam_PacketFormat2(uvd, 0x16, 0x00);
+ ibmcam_PacketFormat2(uvd, 0x17, 0x28);
+ ibmcam_PacketFormat2(uvd, 0x18, 0x7d);
+ ibmcam_PacketFormat2(uvd, 0x19, 0xbe);
+ ibmcam_PacketFormat2(uvd, 0x3f, 0xff);
+ ibmcam_PacketFormat2(uvd, 0x39, 0x00);
+
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x00, 0x18);
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x13, 0x18);
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x14, 0x06);
+
+ /* This is default brightness */
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x31, 0x37);
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x32, 0x46);
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x33, 0x55);
+
+ ibmcam_Packet_Format1(uvd, 0x2e, 0x04);
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x2d, 0x04);
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x29, 0x80);
+ ibmcam_Packet_Format1(uvd, 0x2c, 0x01);
+ ibmcam_Packet_Format1(uvd, 0x30, 0x17);
+ ibmcam_Packet_Format1(uvd, 0x39, 0x08);
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x34, 0x00);
+
+ ibmcam_veio(uvd, 0, 0x00, 0x0101);
+ ibmcam_veio(uvd, 0, 0x00, 0x010a);
+
+ switch (uvd->videosize) {
+ case VIDEOSIZE_128x96:
+ ibmcam_veio(uvd, 0, 0x80, 0x0103);
+ ibmcam_veio(uvd, 0, 0x60, 0x0105);
+ ibmcam_veio(uvd, 0, 0x0c, 0x010b);
+ ibmcam_veio(uvd, 0, 0x04, 0x011b); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x0b, 0x011d);
+ ibmcam_veio(uvd, 0, 0x00, 0x011e); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x00, 0x0129);
+ break;
+ case VIDEOSIZE_176x144:
+ ibmcam_veio(uvd, 0, 0xb0, 0x0103);
+ ibmcam_veio(uvd, 0, 0x8f, 0x0105);
+ ibmcam_veio(uvd, 0, 0x06, 0x010b);
+ ibmcam_veio(uvd, 0, 0x04, 0x011b); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x0d, 0x011d);
+ ibmcam_veio(uvd, 0, 0x00, 0x011e); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x03, 0x0129);
+ break;
+ case VIDEOSIZE_352x288:
+ ibmcam_veio(uvd, 0, 0xb0, 0x0103);
+ ibmcam_veio(uvd, 0, 0x90, 0x0105);
+ ibmcam_veio(uvd, 0, 0x02, 0x010b);
+ ibmcam_veio(uvd, 0, 0x04, 0x011b); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x05, 0x011d);
+ ibmcam_veio(uvd, 0, 0x00, 0x011e); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x00, 0x0129);
+ break;
+ }
+
+ ibmcam_veio(uvd, 0, 0xff, 0x012b);
+
+ /* This is another brightness - don't know why */
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x31, 0xc3);
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x32, 0xd2);
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, 0x33, 0xe1);
+
+ /* Default contrast */
+ for (i=0; i < ntries; i++)
+ ibmcam_Packet_Format1(uvd, contrast_14, 0x0a);
+
+ /* Default sharpness */
+ for (i=0; i < 2; i++)
+ ibmcam_PacketFormat2(uvd, sharp_13, 0x1a); /* Level 4 FIXME */
+
+ /* Default lighting conditions */
+ ibmcam_Packet_Format1(uvd, light_27, lighting); /* 0=Bright 2=Low */
+
+ /* Assorted init */
+
+ switch (uvd->videosize) {
+ case VIDEOSIZE_128x96:
+ ibmcam_Packet_Format1(uvd, 0x2b, 0x1e);
+ ibmcam_veio(uvd, 0, 0xc9, 0x0119); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x80, 0x0109); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x36, 0x0102);
+ ibmcam_veio(uvd, 0, 0x1a, 0x0104);
+ ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x2b, 0x011c);
+ ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */
+#if 0
+ ibmcam_veio(uvd, 0, 0x00, 0x0106);
+ ibmcam_veio(uvd, 0, 0x38, 0x0107);
+#else
+ ibmcam_veio(uvd, 0, 0x02, 0x0106);
+ ibmcam_veio(uvd, 0, 0x2a, 0x0107);
+#endif
+ break;
+ case VIDEOSIZE_176x144:
+ ibmcam_Packet_Format1(uvd, 0x2b, 0x1e);
+ ibmcam_veio(uvd, 0, 0xc9, 0x0119); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x80, 0x0109); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x04, 0x0102);
+ ibmcam_veio(uvd, 0, 0x02, 0x0104);
+ ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x2b, 0x011c);
+ ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x01, 0x0106);
+ ibmcam_veio(uvd, 0, 0xca, 0x0107);
+ break;
+ case VIDEOSIZE_352x288:
+ ibmcam_Packet_Format1(uvd, 0x2b, 0x1f);
+ ibmcam_veio(uvd, 0, 0xc9, 0x0119); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x80, 0x0109); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x08, 0x0102);
+ ibmcam_veio(uvd, 0, 0x01, 0x0104);
+ ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x2f, 0x011c);
+ ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */
+ ibmcam_veio(uvd, 0, 0x03, 0x0106);
+ ibmcam_veio(uvd, 0, 0xf6, 0x0107);
+ break;
+ }
+ return (CAMERA_IS_OPERATIONAL(uvd) ? 0 : -EFAULT);
+}
+
+static int ibmcam_model2_setup(struct uvd *uvd)
+{
+ ibmcam_veio(uvd, 0, 0x0000, 0x0100); /* LED on */
+ ibmcam_veio(uvd, 1, 0x0000, 0x0116);
+ ibmcam_veio(uvd, 0, 0x0060, 0x0116);
+ ibmcam_veio(uvd, 0, 0x0002, 0x0112);
+ ibmcam_veio(uvd, 0, 0x00bc, 0x012c);
+ ibmcam_veio(uvd, 0, 0x0008, 0x012b);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0108);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0133);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0102);
+ switch (uvd->videosize) {
+ case VIDEOSIZE_176x144:
+ ibmcam_veio(uvd, 0, 0x002c, 0x0103); /* All except 320x240 */
+ ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */
+ ibmcam_veio(uvd, 0, 0x0024, 0x0105); /* 176x144, 352x288 */
+ ibmcam_veio(uvd, 0, 0x00b9, 0x010a); /* Unique to this mode */
+ ibmcam_veio(uvd, 0, 0x0038, 0x0119); /* Unique to this mode */
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */
+ ibmcam_veio(uvd, 0, 0x0090, 0x0107); /* Unique to every mode*/
+ break;
+ case VIDEOSIZE_320x240:
+ ibmcam_veio(uvd, 0, 0x0028, 0x0103); /* Unique to this mode */
+ ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */
+ ibmcam_veio(uvd, 0, 0x001e, 0x0105); /* 320x240, 352x240 */
+ ibmcam_veio(uvd, 0, 0x0039, 0x010a); /* All except 176x144 */
+ ibmcam_veio(uvd, 0, 0x0070, 0x0119); /* All except 176x144 */
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */
+ ibmcam_veio(uvd, 0, 0x0098, 0x0107); /* Unique to every mode*/
+ break;
+ case VIDEOSIZE_352x240:
+ ibmcam_veio(uvd, 0, 0x002c, 0x0103); /* All except 320x240 */
+ ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */
+ ibmcam_veio(uvd, 0, 0x001e, 0x0105); /* 320x240, 352x240 */
+ ibmcam_veio(uvd, 0, 0x0039, 0x010a); /* All except 176x144 */
+ ibmcam_veio(uvd, 0, 0x0070, 0x0119); /* All except 176x144 */
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */
+ ibmcam_veio(uvd, 0, 0x00da, 0x0107); /* Unique to every mode*/
+ break;
+ case VIDEOSIZE_352x288:
+ ibmcam_veio(uvd, 0, 0x002c, 0x0103); /* All except 320x240 */
+ ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */
+ ibmcam_veio(uvd, 0, 0x0024, 0x0105); /* 176x144, 352x288 */
+ ibmcam_veio(uvd, 0, 0x0039, 0x010a); /* All except 176x144 */
+ ibmcam_veio(uvd, 0, 0x0070, 0x0119); /* All except 176x144 */
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */
+ ibmcam_veio(uvd, 0, 0x00fe, 0x0107); /* Unique to every mode*/
+ break;
+ }
+ return (CAMERA_IS_OPERATIONAL(uvd) ? 0 : -EFAULT);
+}
+
+/*
+ * ibmcam_model1_setup_after_video_if()
+ *
+ * This code adds finishing touches to the video data interface.
+ * Here we configure the frame rate and turn on the LED.
+ */
+static void ibmcam_model1_setup_after_video_if(struct uvd *uvd)
+{
+ unsigned short internal_frame_rate;
+
+ RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX);
+ internal_frame_rate = FRAMERATE_MAX - framerate; /* 0=Fast 6=Slow */
+ ibmcam_veio(uvd, 0, 0x01, 0x0100); /* LED On */
+ ibmcam_veio(uvd, 0, internal_frame_rate, 0x0111);
+ ibmcam_veio(uvd, 0, 0x01, 0x0114);
+ ibmcam_veio(uvd, 0, 0xc0, 0x010c);
+}
+
+static void ibmcam_model2_setup_after_video_if(struct uvd *uvd)
+{
+ unsigned short setup_model2_rg2, setup_model2_sat, setup_model2_yb;
+
+ ibmcam_veio(uvd, 0, 0x0000, 0x0100); /* LED on */
+
+ switch (uvd->videosize) {
+ case VIDEOSIZE_176x144:
+ ibmcam_veio(uvd, 0, 0x0050, 0x0111);
+ ibmcam_veio(uvd, 0, 0x00d0, 0x0111);
+ break;
+ case VIDEOSIZE_320x240:
+ case VIDEOSIZE_352x240:
+ case VIDEOSIZE_352x288:
+ ibmcam_veio(uvd, 0, 0x0040, 0x0111);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+ break;
+ }
+ ibmcam_veio(uvd, 0, 0x009b, 0x010f);
+ ibmcam_veio(uvd, 0, 0x00bb, 0x010f);
+
+ /*
+ * Hardware settings, may affect CMOS sensor; not user controls!
+ * -------------------------------------------------------------
+ * 0x0004: no effect
+ * 0x0006: hardware effect
+ * 0x0008: no effect
+ * 0x000a: stops video stream, probably important h/w setting
+ * 0x000c: changes color in hardware manner (not user setting)
+ * 0x0012: changes number of colors (does not affect speed)
+ * 0x002a: no effect
+ * 0x002c: hardware setting (related to scan lines)
+ * 0x002e: stops video stream, probably important h/w setting
+ */
+ ibmcam_model2_Packet1(uvd, 0x000a, 0x005c);
+ ibmcam_model2_Packet1(uvd, 0x0004, 0x0000);
+ ibmcam_model2_Packet1(uvd, 0x0006, 0x00fb);
+ ibmcam_model2_Packet1(uvd, 0x0008, 0x0000);
+ ibmcam_model2_Packet1(uvd, 0x000c, 0x0009);
+ ibmcam_model2_Packet1(uvd, 0x0012, 0x000a);
+ ibmcam_model2_Packet1(uvd, 0x002a, 0x0000);
+ ibmcam_model2_Packet1(uvd, 0x002c, 0x0000);
+ ibmcam_model2_Packet1(uvd, 0x002e, 0x0008);
+
+ /*
+ * Function 0x0030 pops up all over the place. Apparently
+ * it is a hardware control register, with every bit assigned to
+ * do something.
+ */
+ ibmcam_model2_Packet1(uvd, 0x0030, 0x0000);
+
+ /*
+ * Magic control of CMOS sensor. Only lower values like
+ * 0-3 work, and picture shifts left or right. Don't change.
+ */
+ switch (uvd->videosize) {
+ case VIDEOSIZE_176x144:
+ ibmcam_model2_Packet1(uvd, 0x0014, 0x0002);
+ ibmcam_model2_Packet1(uvd, 0x0016, 0x0002); /* Horizontal shift */
+ ibmcam_model2_Packet1(uvd, 0x0018, 0x004a); /* Another hardware setting */
+ break;
+ case VIDEOSIZE_320x240:
+ ibmcam_model2_Packet1(uvd, 0x0014, 0x0009);
+ ibmcam_model2_Packet1(uvd, 0x0016, 0x0005); /* Horizontal shift */
+ ibmcam_model2_Packet1(uvd, 0x0018, 0x0044); /* Another hardware setting */
+ break;
+ case VIDEOSIZE_352x240:
+ /* This mode doesn't work as Windows programs it; changed to work */
+ ibmcam_model2_Packet1(uvd, 0x0014, 0x0009); /* Windows sets this to 8 */
+ ibmcam_model2_Packet1(uvd, 0x0016, 0x0003); /* Horizontal shift */
+ ibmcam_model2_Packet1(uvd, 0x0018, 0x0044); /* Windows sets this to 0x0045 */
+ break;
+ case VIDEOSIZE_352x288:
+ ibmcam_model2_Packet1(uvd, 0x0014, 0x0003);
+ ibmcam_model2_Packet1(uvd, 0x0016, 0x0002); /* Horizontal shift */
+ ibmcam_model2_Packet1(uvd, 0x0018, 0x004a); /* Another hardware setting */
+ break;
+ }
+
+ ibmcam_model2_Packet1(uvd, mod2_brightness, 0x005a);
+
+ /*
+ * We have our own frame rate setting varying from 0 (slowest) to 6 (fastest).
+ * The camera model 2 allows frame rate in range [0..0x1F] where 0 is also the
+ * slowest setting. However for all practical reasons high settings make no
+ * sense because USB is not fast enough to support high FPS. Be aware that
+ * the picture datastream will be severely disrupted if you ask for
+ * frame rate faster than allowed for the video size - see below:
+ *
+ * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz):
+ * -----------------------------------------------------------------
+ * 176x144: [6..31]
+ * 320x240: [8..31]
+ * 352x240: [10..31]
+ * 352x288: [16..31] I have to raise lower threshold for stability...
+ *
+ * As usual, slower FPS provides better sensitivity.
+ */
+ {
+ short hw_fps=31, i_framerate;
+
+ RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX);
+ i_framerate = FRAMERATE_MAX - framerate + FRAMERATE_MIN;
+ switch (uvd->videosize) {
+ case VIDEOSIZE_176x144:
+ hw_fps = 6 + i_framerate*4;
+ break;
+ case VIDEOSIZE_320x240:
+ hw_fps = 8 + i_framerate*3;
+ break;
+ case VIDEOSIZE_352x240:
+ hw_fps = 10 + i_framerate*2;
+ break;
+ case VIDEOSIZE_352x288:
+ hw_fps = 28 + i_framerate/2;
+ break;
+ }
+ if (uvd->debug > 0)
+ info("Framerate (hardware): %hd.", hw_fps);
+ RESTRICT_TO_RANGE(hw_fps, 0, 31);
+ ibmcam_model2_Packet1(uvd, mod2_set_framerate, hw_fps);
+ }
+
+ /*
+ * This setting does not visibly affect pictures; left it here
+ * because it was present in Windows USB data stream. This function
+ * does not allow arbitrary values and apparently is a bit mask, to
+ * be activated only at appropriate time. Don't change it randomly!
+ */
+ switch (uvd->videosize) {
+ case VIDEOSIZE_176x144:
+ ibmcam_model2_Packet1(uvd, 0x0026, 0x00c2);
+ break;
+ case VIDEOSIZE_320x240:
+ ibmcam_model2_Packet1(uvd, 0x0026, 0x0044);
+ break;
+ case VIDEOSIZE_352x240:
+ ibmcam_model2_Packet1(uvd, 0x0026, 0x0046);
+ break;
+ case VIDEOSIZE_352x288:
+ ibmcam_model2_Packet1(uvd, 0x0026, 0x0048);
+ break;
+ }
+
+ ibmcam_model2_Packet1(uvd, mod2_sensitivity, lighting);
+
+ if (init_model2_rg2 >= 0) {
+ RESTRICT_TO_RANGE(init_model2_rg2, 0, 255);
+ setup_model2_rg2 = init_model2_rg2;
+ } else
+ setup_model2_rg2 = 0x002f;
+
+ if (init_model2_sat >= 0) {
+ RESTRICT_TO_RANGE(init_model2_sat, 0, 255);
+ setup_model2_sat = init_model2_sat;
+ } else
+ setup_model2_sat = 0x0034;
+
+ if (init_model2_yb >= 0) {
+ RESTRICT_TO_RANGE(init_model2_yb, 0, 255);
+ setup_model2_yb = init_model2_yb;
+ } else
+ setup_model2_yb = 0x00a0;
+
+ ibmcam_model2_Packet1(uvd, mod2_color_balance_rg2, setup_model2_rg2);
+ ibmcam_model2_Packet1(uvd, mod2_saturation, setup_model2_sat);
+ ibmcam_model2_Packet1(uvd, mod2_color_balance_yb, setup_model2_yb);
+ ibmcam_model2_Packet1(uvd, mod2_hue, uvd->vpic.hue >> 9); /* 0 .. 7F */;
+
+ /* Hardware control command */
+ ibmcam_model2_Packet1(uvd, 0x0030, 0x0004);
+
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go camera, go! */
+ usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+}
+
+static void ibmcam_model4_setup_after_video_if(struct uvd *uvd)
+{
+ switch (uvd->videosize) {
+ case VIDEOSIZE_128x96:
+ ibmcam_veio(uvd, 0, 0x0000, 0x0100);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+ ibmcam_veio(uvd, 0, 0x00bc, 0x012c);
+ ibmcam_veio(uvd, 0, 0x0080, 0x012b);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0108);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0133);
+ ibmcam_veio(uvd, 0, 0x009b, 0x010f);
+ ibmcam_veio(uvd, 0, 0x00bb, 0x010f);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x000a, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x005c, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0004, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00fb, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+ ibmcam_veio(uvd, 0, 0x000c, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0009, 0x012e);
+ ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0012, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0008, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x002a, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0000, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0034, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0070, 0x0119);
+ ibmcam_veio(uvd, 0, 0x00d2, 0x0107);
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+ ibmcam_veio(uvd, 0, 0x005e, 0x0107);
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+ ibmcam_veio(uvd, 0, 0x00d0, 0x0111);
+ ibmcam_veio(uvd, 0, 0x0039, 0x010a);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0102);
+ ibmcam_veio(uvd, 0, 0x0028, 0x0103);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0104);
+ ibmcam_veio(uvd, 0, 0x001e, 0x0105);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0016, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x000a, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0014, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012e);
+ ibmcam_veio(uvd, 0, 0x001a, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a0a, 0x0124);
+ ibmcam_veio(uvd, 0, 0x005a, 0x012d);
+ ibmcam_veio(uvd, 0, 0x9545, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0018, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0043, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+ ibmcam_veio(uvd, 0, 0x001c, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00eb, 0x012e);
+ ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0032, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0036, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x001e, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0017, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0013, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0031, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0017, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0078, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0004, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+ break;
+ case VIDEOSIZE_160x120:
+ ibmcam_veio(uvd, 0, 0x0000, 0x0100);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+ ibmcam_veio(uvd, 0, 0x00bc, 0x012c);
+ ibmcam_veio(uvd, 0, 0x0080, 0x012b);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0108);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0133);
+ ibmcam_veio(uvd, 0, 0x009b, 0x010f);
+ ibmcam_veio(uvd, 0, 0x00bb, 0x010f);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x000a, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x005c, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0004, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00fb, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+ ibmcam_veio(uvd, 0, 0x000c, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0009, 0x012e);
+ ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0012, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0008, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x002a, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0000, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0034, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0038, 0x0119);
+ ibmcam_veio(uvd, 0, 0x00d8, 0x0107);
+ ibmcam_veio(uvd, 0, 0x0002, 0x0106);
+ ibmcam_veio(uvd, 0, 0x00d0, 0x0111);
+ ibmcam_veio(uvd, 0, 0x00b9, 0x010a);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0102);
+ ibmcam_veio(uvd, 0, 0x0028, 0x0103);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0104);
+ ibmcam_veio(uvd, 0, 0x001e, 0x0105);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0016, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x000b, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0014, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012e);
+ ibmcam_veio(uvd, 0, 0x001a, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a0a, 0x0124);
+ ibmcam_veio(uvd, 0, 0x005a, 0x012d);
+ ibmcam_veio(uvd, 0, 0x9545, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0018, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0043, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+ ibmcam_veio(uvd, 0, 0x001c, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00c7, 0x012e);
+ ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0032, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0025, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0036, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x001e, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0048, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0035, 0x012e);
+ ibmcam_veio(uvd, 0, 0x00d0, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0048, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0090, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0004, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+ break;
+ case VIDEOSIZE_176x144:
+ ibmcam_veio(uvd, 0, 0x0000, 0x0100);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+ ibmcam_veio(uvd, 0, 0x00bc, 0x012c);
+ ibmcam_veio(uvd, 0, 0x0080, 0x012b);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0108);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0133);
+ ibmcam_veio(uvd, 0, 0x009b, 0x010f);
+ ibmcam_veio(uvd, 0, 0x00bb, 0x010f);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x000a, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x005c, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0004, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00fb, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+ ibmcam_veio(uvd, 0, 0x000c, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0009, 0x012e);
+ ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0012, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0008, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x002a, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0000, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0034, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0038, 0x0119);
+ ibmcam_veio(uvd, 0, 0x00d6, 0x0107);
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+ ibmcam_veio(uvd, 0, 0x0018, 0x0107);
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+ ibmcam_veio(uvd, 0, 0x00d0, 0x0111);
+ ibmcam_veio(uvd, 0, 0x00b9, 0x010a);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0102);
+ ibmcam_veio(uvd, 0, 0x002c, 0x0103);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0104);
+ ibmcam_veio(uvd, 0, 0x0024, 0x0105);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0016, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0007, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0014, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0001, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012e);
+ ibmcam_veio(uvd, 0, 0x001a, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a0a, 0x0124);
+ ibmcam_veio(uvd, 0, 0x005e, 0x012d);
+ ibmcam_veio(uvd, 0, 0x9545, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0018, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0049, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+ ibmcam_veio(uvd, 0, 0x001c, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00c7, 0x012e);
+ ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0032, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0028, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0036, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x001e, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0010, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0013, 0x012e);
+ ibmcam_veio(uvd, 0, 0x002a, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0010, 0x012d);
+ ibmcam_veio(uvd, 0, 0x006d, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0004, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+ break;
+ case VIDEOSIZE_320x240:
+ ibmcam_veio(uvd, 0, 0x0000, 0x0100);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+ ibmcam_veio(uvd, 0, 0x00bc, 0x012c);
+ ibmcam_veio(uvd, 0, 0x0080, 0x012b);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0108);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0133);
+ ibmcam_veio(uvd, 0, 0x009b, 0x010f);
+ ibmcam_veio(uvd, 0, 0x00bb, 0x010f);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x000a, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x005c, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0004, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00fb, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+ ibmcam_veio(uvd, 0, 0x000c, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0009, 0x012e);
+ ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0012, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0008, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x002a, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0000, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0034, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0070, 0x0119);
+ ibmcam_veio(uvd, 0, 0x00d2, 0x0107);
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+ ibmcam_veio(uvd, 0, 0x005e, 0x0107);
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+ ibmcam_veio(uvd, 0, 0x00d0, 0x0111);
+ ibmcam_veio(uvd, 0, 0x0039, 0x010a);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0102);
+ ibmcam_veio(uvd, 0, 0x0028, 0x0103);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0104);
+ ibmcam_veio(uvd, 0, 0x001e, 0x0105);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0016, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x000a, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0014, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012e);
+ ibmcam_veio(uvd, 0, 0x001a, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a0a, 0x0124);
+ ibmcam_veio(uvd, 0, 0x005a, 0x012d);
+ ibmcam_veio(uvd, 0, 0x9545, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0018, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0043, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+ ibmcam_veio(uvd, 0, 0x001c, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00eb, 0x012e);
+ ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0032, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0036, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x001e, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0017, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0013, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0031, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0017, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0078, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0004, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+ break;
+ case VIDEOSIZE_352x288:
+ ibmcam_veio(uvd, 0, 0x0000, 0x0100);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+ ibmcam_veio(uvd, 0, 0x00bc, 0x012c);
+ ibmcam_veio(uvd, 0, 0x0080, 0x012b);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0108);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0133);
+ ibmcam_veio(uvd, 0, 0x009b, 0x010f);
+ ibmcam_veio(uvd, 0, 0x00bb, 0x010f);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x000a, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x005c, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0004, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00fb, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+ ibmcam_veio(uvd, 0, 0x000c, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0009, 0x012e);
+ ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0012, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0008, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x002a, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0000, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0034, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0070, 0x0119);
+ ibmcam_veio(uvd, 0, 0x00f2, 0x0107);
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+ ibmcam_veio(uvd, 0, 0x008c, 0x0107);
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x0111);
+ ibmcam_veio(uvd, 0, 0x0039, 0x010a);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0102);
+ ibmcam_veio(uvd, 0, 0x002c, 0x0103);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0104);
+ ibmcam_veio(uvd, 0, 0x0024, 0x0105);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0016, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0006, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0014, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0002, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012e);
+ ibmcam_veio(uvd, 0, 0x001a, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a0a, 0x0124);
+ ibmcam_veio(uvd, 0, 0x005e, 0x012d);
+ ibmcam_veio(uvd, 0, 0x9545, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0018, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0049, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd055, 0x0124);
+ ibmcam_veio(uvd, 0, 0x001c, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00cf, 0x012e);
+ ibmcam_veio(uvd, 0, 0xaa28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0032, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x0130);
+ ibmcam_veio(uvd, 0, 0x82a8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0036, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0008, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0xfffa, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x001e, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0010, 0x0127);
+ ibmcam_veio(uvd, 0, 0x0013, 0x012e);
+ ibmcam_veio(uvd, 0, 0x0025, 0x0130);
+ ibmcam_veio(uvd, 0, 0x8a28, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0010, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0048, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd145, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
+ ibmcam_veio(uvd, 0, 0x0038, 0x012f);
+ ibmcam_veio(uvd, 0, 0xd141, 0x0124);
+ ibmcam_veio(uvd, 0, 0x0004, 0x0127);
+ ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+ break;
+ }
+ usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+}
+
+static void ibmcam_model3_setup_after_video_if(struct uvd *uvd)
+{
+ int i;
+ /*
+ * 01.01.08 - Added for RCA video in support -LO
+ * This struct is used to init the Model3 cam to use the RCA video in port
+ * instead of the CCD sensor.
+ */
+ static const struct struct_initData initData[] = {
+ {0, 0x0000, 0x010c},
+ {0, 0x0006, 0x012c},
+ {0, 0x0078, 0x012d},
+ {0, 0x0046, 0x012f},
+ {0, 0xd141, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfea8, 0x0124},
+ {1, 0x0000, 0x0116},
+ {0, 0x0064, 0x0116},
+ {1, 0x0000, 0x0115},
+ {0, 0x0003, 0x0115},
+ {0, 0x0008, 0x0123},
+ {0, 0x0000, 0x0117},
+ {0, 0x0000, 0x0112},
+ {0, 0x0080, 0x0100},
+ {0, 0x0000, 0x0100},
+ {1, 0x0000, 0x0116},
+ {0, 0x0060, 0x0116},
+ {0, 0x0002, 0x0112},
+ {0, 0x0000, 0x0123},
+ {0, 0x0001, 0x0117},
+ {0, 0x0040, 0x0108},
+ {0, 0x0019, 0x012c},
+ {0, 0x0040, 0x0116},
+ {0, 0x000a, 0x0115},
+ {0, 0x000b, 0x0115},
+ {0, 0x0078, 0x012d},
+ {0, 0x0046, 0x012f},
+ {0, 0xd141, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfea8, 0x0124},
+ {0, 0x0064, 0x0116},
+ {0, 0x0000, 0x0115},
+ {0, 0x0001, 0x0115},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00aa, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00f2, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x000f, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00f8, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00fc, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00f9, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x003c, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0027, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0019, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0021, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0006, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0045, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002a, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x000e, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002b, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00f4, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002c, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0004, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002d, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0014, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002e, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0003, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002f, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0003, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0014, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0053, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0x0000, 0x0101},
+ {0, 0x00a0, 0x0103},
+ {0, 0x0078, 0x0105},
+ {0, 0x0000, 0x010a},
+ {0, 0x0024, 0x010b},
+ {0, 0x0028, 0x0119},
+ {0, 0x0088, 0x011b},
+ {0, 0x0002, 0x011d},
+ {0, 0x0003, 0x011e},
+ {0, 0x0000, 0x0129},
+ {0, 0x00fc, 0x012b},
+ {0, 0x0008, 0x0102},
+ {0, 0x0000, 0x0104},
+ {0, 0x0008, 0x011a},
+ {0, 0x0028, 0x011c},
+ {0, 0x0021, 0x012a},
+ {0, 0x0000, 0x0118},
+ {0, 0x0000, 0x0132},
+ {0, 0x0000, 0x0109},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0031, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00dc, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0032, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0020, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0030, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0008, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0x0003, 0x0106},
+ {0, 0x0062, 0x0107},
+ {0, 0x0003, 0x0111},
+ };
+#define NUM_INIT_DATA
+
+ unsigned short compression = 0; /* 0=none, 7=best frame rate */
+ int f_rate; /* 0=Fastest 7=slowest */
+
+ if (IBMCAM_T(uvd)->initialized)
+ return;
+
+ /* Internal frame rate is controlled by f_rate value */
+ f_rate = 7 - framerate;
+ RESTRICT_TO_RANGE(f_rate, 0, 7);
+
+ ibmcam_veio(uvd, 0, 0x0000, 0x0100);
+ ibmcam_veio(uvd, 1, 0x0000, 0x0116);
+ ibmcam_veio(uvd, 0, 0x0060, 0x0116);
+ ibmcam_veio(uvd, 0, 0x0002, 0x0112);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0123);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0117);
+ ibmcam_veio(uvd, 0, 0x0040, 0x0108);
+ ibmcam_veio(uvd, 0, 0x0019, 0x012c);
+ ibmcam_veio(uvd, 0, 0x0060, 0x0116);
+ ibmcam_veio(uvd, 0, 0x0002, 0x0115);
+ ibmcam_veio(uvd, 0, 0x0003, 0x0115);
+ ibmcam_veio(uvd, 1, 0x0000, 0x0115);
+ ibmcam_veio(uvd, 0, 0x000b, 0x0115);
+ ibmcam_model3_Packet1(uvd, 0x000a, 0x0040);
+ ibmcam_model3_Packet1(uvd, 0x000b, 0x00f6);
+ ibmcam_model3_Packet1(uvd, 0x000c, 0x0002);
+ ibmcam_model3_Packet1(uvd, 0x000d, 0x0020);
+ ibmcam_model3_Packet1(uvd, 0x000e, 0x0033);
+ ibmcam_model3_Packet1(uvd, 0x000f, 0x0007);
+ ibmcam_model3_Packet1(uvd, 0x0010, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x0011, 0x0070);
+ ibmcam_model3_Packet1(uvd, 0x0012, 0x0030);
+ ibmcam_model3_Packet1(uvd, 0x0013, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x0014, 0x0001);
+ ibmcam_model3_Packet1(uvd, 0x0015, 0x0001);
+ ibmcam_model3_Packet1(uvd, 0x0016, 0x0001);
+ ibmcam_model3_Packet1(uvd, 0x0017, 0x0001);
+ ibmcam_model3_Packet1(uvd, 0x0018, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x001e, 0x00c3);
+ ibmcam_model3_Packet1(uvd, 0x0020, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x0028, 0x0010);
+ ibmcam_model3_Packet1(uvd, 0x0029, 0x0054);
+ ibmcam_model3_Packet1(uvd, 0x002a, 0x0013);
+ ibmcam_model3_Packet1(uvd, 0x002b, 0x0007);
+ ibmcam_model3_Packet1(uvd, 0x002d, 0x0028);
+ ibmcam_model3_Packet1(uvd, 0x002e, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x0031, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x0032, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x0033, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x0034, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x0035, 0x0038);
+ ibmcam_model3_Packet1(uvd, 0x003a, 0x0001);
+ ibmcam_model3_Packet1(uvd, 0x003c, 0x001e);
+ ibmcam_model3_Packet1(uvd, 0x003f, 0x000a);
+ ibmcam_model3_Packet1(uvd, 0x0041, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x0046, 0x003f);
+ ibmcam_model3_Packet1(uvd, 0x0047, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x0050, 0x0005);
+ ibmcam_model3_Packet1(uvd, 0x0052, 0x001a);
+ ibmcam_model3_Packet1(uvd, 0x0053, 0x0003);
+ ibmcam_model3_Packet1(uvd, 0x005a, 0x006b);
+ ibmcam_model3_Packet1(uvd, 0x005d, 0x001e);
+ ibmcam_model3_Packet1(uvd, 0x005e, 0x0030);
+ ibmcam_model3_Packet1(uvd, 0x005f, 0x0041);
+ ibmcam_model3_Packet1(uvd, 0x0064, 0x0008);
+ ibmcam_model3_Packet1(uvd, 0x0065, 0x0015);
+ ibmcam_model3_Packet1(uvd, 0x0068, 0x000f);
+ ibmcam_model3_Packet1(uvd, 0x0079, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x007a, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x007c, 0x003f);
+ ibmcam_model3_Packet1(uvd, 0x0082, 0x000f);
+ ibmcam_model3_Packet1(uvd, 0x0085, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x0099, 0x0000);
+ ibmcam_model3_Packet1(uvd, 0x009b, 0x0023);
+ ibmcam_model3_Packet1(uvd, 0x009c, 0x0022);
+ ibmcam_model3_Packet1(uvd, 0x009d, 0x0096);
+ ibmcam_model3_Packet1(uvd, 0x009e, 0x0096);
+ ibmcam_model3_Packet1(uvd, 0x009f, 0x000a);
+
+ switch (uvd->videosize) {
+ case VIDEOSIZE_160x120:
+ ibmcam_veio(uvd, 0, 0x0000, 0x0101); /* Same on 176x144, 320x240 */
+ ibmcam_veio(uvd, 0, 0x00a0, 0x0103); /* Same on 176x144, 320x240 */
+ ibmcam_veio(uvd, 0, 0x0078, 0x0105); /* Same on 176x144, 320x240 */
+ ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */
+ ibmcam_veio(uvd, 0, 0x0024, 0x010b); /* Differs everywhere */
+ ibmcam_veio(uvd, 0, 0x00a9, 0x0119);
+ ibmcam_veio(uvd, 0, 0x0016, 0x011b);
+ ibmcam_veio(uvd, 0, 0x0002, 0x011d); /* Same on 176x144, 320x240 */
+ ibmcam_veio(uvd, 0, 0x0003, 0x011e); /* Same on 176x144, 640x480 */
+ ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */
+ ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */
+ ibmcam_veio(uvd, 0, 0x0018, 0x0102);
+ ibmcam_veio(uvd, 0, 0x0004, 0x0104);
+ ibmcam_veio(uvd, 0, 0x0004, 0x011a);
+ ibmcam_veio(uvd, 0, 0x0028, 0x011c);
+ ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */
+ ibmcam_veio(uvd, 0, 0x0000, 0x0118);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0132);
+ ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */
+ ibmcam_veio(uvd, 0, compression, 0x0109);
+ break;
+ case VIDEOSIZE_320x240:
+ ibmcam_veio(uvd, 0, 0x0000, 0x0101); /* Same on 176x144, 320x240 */
+ ibmcam_veio(uvd, 0, 0x00a0, 0x0103); /* Same on 176x144, 320x240 */
+ ibmcam_veio(uvd, 0, 0x0078, 0x0105); /* Same on 176x144, 320x240 */
+ ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */
+ ibmcam_veio(uvd, 0, 0x0028, 0x010b); /* Differs everywhere */
+ ibmcam_veio(uvd, 0, 0x0002, 0x011d); /* Same */
+ ibmcam_veio(uvd, 0, 0x0000, 0x011e);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */
+ ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */
+ /* 4 commands from 160x120 skipped */
+ ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */
+ ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */
+ ibmcam_veio(uvd, 0, compression, 0x0109);
+ ibmcam_veio(uvd, 0, 0x00d9, 0x0119);
+ ibmcam_veio(uvd, 0, 0x0006, 0x011b);
+ ibmcam_veio(uvd, 0, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
+ ibmcam_veio(uvd, 0, 0x0010, 0x0104);
+ ibmcam_veio(uvd, 0, 0x0004, 0x011a);
+ ibmcam_veio(uvd, 0, 0x003f, 0x011c);
+ ibmcam_veio(uvd, 0, 0x001c, 0x0118);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0132);
+ break;
+ case VIDEOSIZE_640x480:
+ ibmcam_veio(uvd, 0, 0x00f0, 0x0105);
+ ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */
+ ibmcam_veio(uvd, 0, 0x0038, 0x010b); /* Differs everywhere */
+ ibmcam_veio(uvd, 0, 0x00d9, 0x0119); /* Same on 320x240, 640x480 */
+ ibmcam_veio(uvd, 0, 0x0006, 0x011b); /* Same on 320x240, 640x480 */
+ ibmcam_veio(uvd, 0, 0x0004, 0x011d); /* NC */
+ ibmcam_veio(uvd, 0, 0x0003, 0x011e); /* Same on 176x144, 640x480 */
+ ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */
+ ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */
+ ibmcam_veio(uvd, 0, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
+ ibmcam_veio(uvd, 0, 0x0016, 0x0104); /* NC */
+ ibmcam_veio(uvd, 0, 0x0004, 0x011a); /* Same on 320x240, 640x480 */
+ ibmcam_veio(uvd, 0, 0x003f, 0x011c); /* Same on 320x240, 640x480 */
+ ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */
+ ibmcam_veio(uvd, 0, 0x001c, 0x0118); /* Same on 320x240, 640x480 */
+ ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */
+ ibmcam_veio(uvd, 0, compression, 0x0109);
+ ibmcam_veio(uvd, 0, 0x0040, 0x0101);
+ ibmcam_veio(uvd, 0, 0x0040, 0x0103);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0132); /* Same on 320x240, 640x480 */
+ break;
+ }
+ ibmcam_model3_Packet1(uvd, 0x007e, 0x000e); /* Hue */
+ ibmcam_model3_Packet1(uvd, 0x0036, 0x0011); /* Brightness */
+ ibmcam_model3_Packet1(uvd, 0x0060, 0x0002); /* Sharpness */
+ ibmcam_model3_Packet1(uvd, 0x0061, 0x0004); /* Sharpness */
+ ibmcam_model3_Packet1(uvd, 0x0062, 0x0005); /* Sharpness */
+ ibmcam_model3_Packet1(uvd, 0x0063, 0x0014); /* Sharpness */
+ ibmcam_model3_Packet1(uvd, 0x0096, 0x00a0); /* Red gain */
+ ibmcam_model3_Packet1(uvd, 0x0097, 0x0096); /* Blue gain */
+ ibmcam_model3_Packet1(uvd, 0x0067, 0x0001); /* Contrast */
+ ibmcam_model3_Packet1(uvd, 0x005b, 0x000c); /* Contrast */
+ ibmcam_model3_Packet1(uvd, 0x005c, 0x0016); /* Contrast */
+ ibmcam_model3_Packet1(uvd, 0x0098, 0x000b);
+ ibmcam_model3_Packet1(uvd, 0x002c, 0x0003); /* Was 1, broke 640x480 */
+ ibmcam_model3_Packet1(uvd, 0x002f, 0x002a);
+ ibmcam_model3_Packet1(uvd, 0x0030, 0x0029);
+ ibmcam_model3_Packet1(uvd, 0x0037, 0x0002);
+ ibmcam_model3_Packet1(uvd, 0x0038, 0x0059);
+ ibmcam_model3_Packet1(uvd, 0x003d, 0x002e);
+ ibmcam_model3_Packet1(uvd, 0x003e, 0x0028);
+ ibmcam_model3_Packet1(uvd, 0x0078, 0x0005);
+ ibmcam_model3_Packet1(uvd, 0x007b, 0x0011);
+ ibmcam_model3_Packet1(uvd, 0x007d, 0x004b);
+ ibmcam_model3_Packet1(uvd, 0x007f, 0x0022);
+ ibmcam_model3_Packet1(uvd, 0x0080, 0x000c);
+ ibmcam_model3_Packet1(uvd, 0x0081, 0x000b);
+ ibmcam_model3_Packet1(uvd, 0x0083, 0x00fd);
+ ibmcam_model3_Packet1(uvd, 0x0086, 0x000b);
+ ibmcam_model3_Packet1(uvd, 0x0087, 0x000b);
+ ibmcam_model3_Packet1(uvd, 0x007e, 0x000e);
+ ibmcam_model3_Packet1(uvd, 0x0096, 0x00a0); /* Red gain */
+ ibmcam_model3_Packet1(uvd, 0x0097, 0x0096); /* Blue gain */
+ ibmcam_model3_Packet1(uvd, 0x0098, 0x000b);
+
+ switch (uvd->videosize) {
+ case VIDEOSIZE_160x120:
+ ibmcam_veio(uvd, 0, 0x0002, 0x0106);
+ ibmcam_veio(uvd, 0, 0x0008, 0x0107);
+ ibmcam_veio(uvd, 0, f_rate, 0x0111); /* Frame rate */
+ ibmcam_model3_Packet1(uvd, 0x001f, 0x0000); /* Same */
+ ibmcam_model3_Packet1(uvd, 0x0039, 0x001f); /* Same */
+ ibmcam_model3_Packet1(uvd, 0x003b, 0x003c); /* Same */
+ ibmcam_model3_Packet1(uvd, 0x0040, 0x000a);
+ ibmcam_model3_Packet1(uvd, 0x0051, 0x000a);
+ break;
+ case VIDEOSIZE_320x240:
+ ibmcam_veio(uvd, 0, 0x0003, 0x0106);
+ ibmcam_veio(uvd, 0, 0x0062, 0x0107);
+ ibmcam_veio(uvd, 0, f_rate, 0x0111); /* Frame rate */
+ ibmcam_model3_Packet1(uvd, 0x001f, 0x0000); /* Same */
+ ibmcam_model3_Packet1(uvd, 0x0039, 0x001f); /* Same */
+ ibmcam_model3_Packet1(uvd, 0x003b, 0x003c); /* Same */
+ ibmcam_model3_Packet1(uvd, 0x0040, 0x0008);
+ ibmcam_model3_Packet1(uvd, 0x0051, 0x000b);
+ break;
+ case VIDEOSIZE_640x480:
+ ibmcam_veio(uvd, 0, 0x0002, 0x0106); /* Adjustments */
+ ibmcam_veio(uvd, 0, 0x00b4, 0x0107); /* Adjustments */
+ ibmcam_veio(uvd, 0, f_rate, 0x0111); /* Frame rate */
+ ibmcam_model3_Packet1(uvd, 0x001f, 0x0002); /* !Same */
+ ibmcam_model3_Packet1(uvd, 0x0039, 0x003e); /* !Same */
+ ibmcam_model3_Packet1(uvd, 0x0040, 0x0008);
+ ibmcam_model3_Packet1(uvd, 0x0051, 0x000a);
+ break;
+ }
+
+ /* 01.01.08 - Added for RCA video in support -LO */
+ if(init_model3_input) {
+ if (debug > 0)
+ info("Setting input to RCA.");
+ for (i=0; i < ARRAY_SIZE(initData); i++) {
+ ibmcam_veio(uvd, initData[i].req, initData[i].value, initData[i].index);
+ }
+ }
+
+ ibmcam_veio(uvd, 0, 0x0001, 0x0114);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+ usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+}
+
+/*
+ * ibmcam_video_stop()
+ *
+ * This code tells camera to stop streaming. The interface remains
+ * configured and bandwidth - claimed.
+ */
+static void ibmcam_video_stop(struct uvd *uvd)
+{
+ switch (IBMCAM_T(uvd)->camera_model) {
+ case IBMCAM_MODEL_1:
+ ibmcam_veio(uvd, 0, 0x00, 0x010c);
+ ibmcam_veio(uvd, 0, 0x00, 0x010c);
+ ibmcam_veio(uvd, 0, 0x01, 0x0114);
+ ibmcam_veio(uvd, 0, 0xc0, 0x010c);
+ ibmcam_veio(uvd, 0, 0x00, 0x010c);
+ ibmcam_send_FF_04_02(uvd);
+ ibmcam_veio(uvd, 1, 0x00, 0x0100);
+ ibmcam_veio(uvd, 0, 0x81, 0x0100); /* LED Off */
+ break;
+ case IBMCAM_MODEL_2:
+case IBMCAM_MODEL_4:
+ ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop the camera */
+
+ ibmcam_model2_Packet1(uvd, 0x0030, 0x0004);
+
+ ibmcam_veio(uvd, 0, 0x0080, 0x0100); /* LED Off */
+ ibmcam_veio(uvd, 0, 0x0020, 0x0111);
+ ibmcam_veio(uvd, 0, 0x00a0, 0x0111);
+
+ ibmcam_model2_Packet1(uvd, 0x0030, 0x0002);
+
+ ibmcam_veio(uvd, 0, 0x0020, 0x0111);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0112);
+ break;
+ case IBMCAM_MODEL_3:
+#if 1
+ ibmcam_veio(uvd, 0, 0x0000, 0x010c);
+
+ /* Here we are supposed to select video interface alt. setting 0 */
+ ibmcam_veio(uvd, 0, 0x0006, 0x012c);
+
+ ibmcam_model3_Packet1(uvd, 0x0046, 0x0000);
+
+ ibmcam_veio(uvd, 1, 0x0000, 0x0116);
+ ibmcam_veio(uvd, 0, 0x0064, 0x0116);
+ ibmcam_veio(uvd, 1, 0x0000, 0x0115);
+ ibmcam_veio(uvd, 0, 0x0003, 0x0115);
+ ibmcam_veio(uvd, 0, 0x0008, 0x0123);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0117);
+ ibmcam_veio(uvd, 0, 0x0000, 0x0112);
+ ibmcam_veio(uvd, 0, 0x0080, 0x0100);
+ IBMCAM_T(uvd)->initialized = 0;
+#endif
+ break;
+ } /* switch */
+}
+
+/*
+ * ibmcam_reinit_iso()
+ *
+ * This procedure sends couple of commands to the camera and then
+ * resets the video pipe. This sequence was observed to reinit the
+ * camera or, at least, to initiate ISO data stream.
+ *
+ * History:
+ * 1/2/00 Created.
+ */
+static void ibmcam_reinit_iso(struct uvd *uvd, int do_stop)
+{
+ switch (IBMCAM_T(uvd)->camera_model) {
+ case IBMCAM_MODEL_1:
+ if (do_stop)
+ ibmcam_video_stop(uvd);
+ ibmcam_veio(uvd, 0, 0x0001, 0x0114);
+ ibmcam_veio(uvd, 0, 0x00c0, 0x010c);
+ usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
+ ibmcam_model1_setup_after_video_if(uvd);
+ break;
+ case IBMCAM_MODEL_2:
+ ibmcam_model2_setup_after_video_if(uvd);
+ break;
+ case IBMCAM_MODEL_3:
+ ibmcam_video_stop(uvd);
+ ibmcam_model3_setup_after_video_if(uvd);
+ break;
+ case IBMCAM_MODEL_4:
+ ibmcam_model4_setup_after_video_if(uvd);
+ break;
+ }
+}
+
+static void ibmcam_video_start(struct uvd *uvd)
+{
+ ibmcam_change_lighting_conditions(uvd);
+ ibmcam_set_sharpness(uvd);
+ ibmcam_reinit_iso(uvd, 0);
+}
+
+/*
+ * Return negative code on failure, 0 on success.
+ */
+static int ibmcam_setup_on_open(struct uvd *uvd)
+{
+ int setup_ok = 0; /* Success by default */
+ /* Send init sequence only once, it's large! */
+ if (!IBMCAM_T(uvd)->initialized) { /* FIXME rename */
+ switch (IBMCAM_T(uvd)->camera_model) {
+ case IBMCAM_MODEL_1:
+ setup_ok = ibmcam_model1_setup(uvd);
+ break;
+ case IBMCAM_MODEL_2:
+ setup_ok = ibmcam_model2_setup(uvd);
+ break;
+ case IBMCAM_MODEL_3:
+ case IBMCAM_MODEL_4:
+ /* We do all setup when Isoc stream is requested */
+ break;
+ }
+ IBMCAM_T(uvd)->initialized = (setup_ok != 0);
+ }
+ return setup_ok;
+}
+
+static void ibmcam_configure_video(struct uvd *uvd)
+{
+ if (uvd == NULL)
+ return;
+
+ RESTRICT_TO_RANGE(init_brightness, 0, 255);
+ RESTRICT_TO_RANGE(init_contrast, 0, 255);
+ RESTRICT_TO_RANGE(init_color, 0, 255);
+ RESTRICT_TO_RANGE(init_hue, 0, 255);
+ RESTRICT_TO_RANGE(hue_correction, 0, 255);
+
+ memset(&uvd->vpic, 0, sizeof(uvd->vpic));
+ memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old));
+
+ uvd->vpic.colour = init_color << 8;
+ uvd->vpic.hue = init_hue << 8;
+ uvd->vpic.brightness = init_brightness << 8;
+ uvd->vpic.contrast = init_contrast << 8;
+ uvd->vpic.whiteness = 105 << 8; /* This one isn't used */
+ uvd->vpic.depth = 24;
+ uvd->vpic.palette = VIDEO_PALETTE_RGB24;
+
+ memset(&uvd->vcap, 0, sizeof(uvd->vcap));
+ strcpy(uvd->vcap.name, "IBM USB Camera");
+ uvd->vcap.type = VID_TYPE_CAPTURE;
+ uvd->vcap.channels = 1;
+ uvd->vcap.audios = 0;
+ uvd->vcap.maxwidth = VIDEOSIZE_X(uvd->canvas);
+ uvd->vcap.maxheight = VIDEOSIZE_Y(uvd->canvas);
+ uvd->vcap.minwidth = min_canvasWidth;
+ uvd->vcap.minheight = min_canvasHeight;
+
+ memset(&uvd->vchan, 0, sizeof(uvd->vchan));
+ uvd->vchan.flags = 0;
+ uvd->vchan.tuners = 0;
+ uvd->vchan.channel = 0;
+ uvd->vchan.type = VIDEO_TYPE_CAMERA;
+ strcpy(uvd->vchan.name, "Camera");
+}
+
+/*
+ * ibmcam_probe()
+ *
+ * This procedure queries device descriptor and accepts the interface
+ * if it looks like IBM C-it camera.
+ *
+ * History:
+ * 22-Jan-2000 Moved camera init code to ibmcam_open()
+ * 27=Jan-2000 Changed to use static structures, added locking.
+ * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT).
+ * 03-Jul-2000 Fixed endianness bug.
+ * 12-Nov-2000 Reworked to comply with new probe() signature.
+ * 23-Jan-2001 Added compatibility with 2.2.x kernels.
+ */
+static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id *devid)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct uvd *uvd = NULL;
+ int ix, i, nas, model=0, canvasX=0, canvasY=0;
+ int actInterface=-1, inactInterface=-1, maxPS=0;
+ __u8 ifnum = intf->altsetting->desc.bInterfaceNumber;
+ unsigned char video_ep = 0;
+
+ if (debug >= 1)
+ info("ibmcam_probe(%p,%u.)", intf, ifnum);
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -ENODEV;
+
+ /* Check the version/revision */
+ switch (le16_to_cpu(dev->descriptor.bcdDevice)) {
+ case 0x0002:
+ if (ifnum != 2)
+ return -ENODEV;
+ model = IBMCAM_MODEL_1;
+ break;
+ case 0x030A:
+ if (ifnum != 0)
+ return -ENODEV;
+ if ((le16_to_cpu(dev->descriptor.idProduct) == NETCAM_PRODUCT_ID) ||
+ (le16_to_cpu(dev->descriptor.idProduct) == VEO_800D_PRODUCT_ID))
+ model = IBMCAM_MODEL_4;
+ else
+ model = IBMCAM_MODEL_2;
+ break;
+ case 0x0301:
+ if (ifnum != 0)
+ return -ENODEV;
+ model = IBMCAM_MODEL_3;
+ break;
+ default:
+ err("IBM camera with revision 0x%04x is not supported.",
+ le16_to_cpu(dev->descriptor.bcdDevice));
+ return -ENODEV;
+ }
+
+ /* Print detailed info on what we found so far */
+ do {
+ char *brand = NULL;
+ switch (le16_to_cpu(dev->descriptor.idProduct)) {
+ case NETCAM_PRODUCT_ID:
+ brand = "IBM NetCamera";
+ break;
+ case VEO_800C_PRODUCT_ID:
+ brand = "Veo Stingray [800C]";
+ break;
+ case VEO_800D_PRODUCT_ID:
+ brand = "Veo Stingray [800D]";
+ break;
+ case IBMCAM_PRODUCT_ID:
+ default:
+ brand = "IBM PC Camera"; /* a.k.a. Xirlink C-It */
+ break;
+ }
+ info("%s USB camera found (model %d, rev. 0x%04x)",
+ brand, model, le16_to_cpu(dev->descriptor.bcdDevice));
+ } while (0);
+
+ /* Validate found interface: must have one ISO endpoint */
+ nas = intf->num_altsetting;
+ if (debug > 0)
+ info("Number of alternate settings=%d.", nas);
+ if (nas < 2) {
+ err("Too few alternate settings for this camera!");
+ return -ENODEV;
+ }
+ /* Validate all alternate settings */
+ for (ix=0; ix < nas; ix++) {
+ const struct usb_host_interface *interface;
+ const struct usb_endpoint_descriptor *endpoint;
+
+ interface = &intf->altsetting[ix];
+ i = interface->desc.bAlternateSetting;
+ if (interface->desc.bNumEndpoints != 1) {
+ err("Interface %d. has %u. endpoints!",
+ ifnum, (unsigned)(interface->desc.bNumEndpoints));
+ return -ENODEV;
+ }
+ endpoint = &interface->endpoint[0].desc;
+ if (video_ep == 0)
+ video_ep = endpoint->bEndpointAddress;
+ else if (video_ep != endpoint->bEndpointAddress) {
+ err("Alternate settings have different endpoint addresses!");
+ return -ENODEV;
+ }
+ if ((endpoint->bmAttributes & 0x03) != 0x01) {
+ err("Interface %d. has non-ISO endpoint!", ifnum);
+ return -ENODEV;
+ }
+ if ((endpoint->bEndpointAddress & 0x80) == 0) {
+ err("Interface %d. has ISO OUT endpoint!", ifnum);
+ return -ENODEV;
+ }
+ if (le16_to_cpu(endpoint->wMaxPacketSize) == 0) {
+ if (inactInterface < 0)
+ inactInterface = i;
+ else {
+ err("More than one inactive alt. setting!");
+ return -ENODEV;
+ }
+ } else {
+ if (actInterface < 0) {
+ actInterface = i;
+ maxPS = le16_to_cpu(endpoint->wMaxPacketSize);
+ if (debug > 0)
+ info("Active setting=%d. maxPS=%d.", i, maxPS);
+ } else
+ err("More than one active alt. setting! Ignoring #%d.", i);
+ }
+ }
+ if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0)) {
+ err("Failed to recognize the camera!");
+ return -ENODEV;
+ }
+
+ /* Validate options */
+ switch (model) {
+ case IBMCAM_MODEL_1:
+ RESTRICT_TO_RANGE(lighting, 0, 2);
+ RESTRICT_TO_RANGE(size, SIZE_128x96, SIZE_352x288);
+ if (framerate < 0)
+ framerate = 2;
+ canvasX = 352;
+ canvasY = 288;
+ break;
+ case IBMCAM_MODEL_2:
+ RESTRICT_TO_RANGE(lighting, 0, 15);
+ RESTRICT_TO_RANGE(size, SIZE_176x144, SIZE_352x240);
+ if (framerate < 0)
+ framerate = 2;
+ canvasX = 352;
+ canvasY = 240;
+ break;
+ case IBMCAM_MODEL_3:
+ RESTRICT_TO_RANGE(lighting, 0, 15); /* FIXME */
+ switch (size) {
+ case SIZE_160x120:
+ canvasX = 160;
+ canvasY = 120;
+ if (framerate < 0)
+ framerate = 2;
+ RESTRICT_TO_RANGE(framerate, 0, 5);
+ break;
+ default:
+ info("IBM camera: using 320x240");
+ size = SIZE_320x240;
+ /* No break here */
+ case SIZE_320x240:
+ canvasX = 320;
+ canvasY = 240;
+ if (framerate < 0)
+ framerate = 3;
+ RESTRICT_TO_RANGE(framerate, 0, 5);
+ break;
+ case SIZE_640x480:
+ canvasX = 640;
+ canvasY = 480;
+ framerate = 0; /* Slowest, and maybe even that is too fast */
+ break;
+ }
+ break;
+ case IBMCAM_MODEL_4:
+ RESTRICT_TO_RANGE(lighting, 0, 2);
+ switch (size) {
+ case SIZE_128x96:
+ canvasX = 128;
+ canvasY = 96;
+ break;
+ case SIZE_160x120:
+ canvasX = 160;
+ canvasY = 120;
+ break;
+ default:
+ info("IBM NetCamera: using 176x144");
+ size = SIZE_176x144;
+ /* No break here */
+ case SIZE_176x144:
+ canvasX = 176;
+ canvasY = 144;
+ break;
+ case SIZE_320x240:
+ canvasX = 320;
+ canvasY = 240;
+ break;
+ case SIZE_352x288:
+ canvasX = 352;
+ canvasY = 288;
+ break;
+ }
+ break;
+ default:
+ err("IBM camera: Model %d. not supported!", model);
+ return -ENODEV;
+ }
+
+ uvd = usbvideo_AllocateDevice(cams);
+ if (uvd != NULL) {
+ /* Here uvd is a fully allocated uvd object */
+ uvd->flags = flags;
+ uvd->debug = debug;
+ uvd->dev = dev;
+ uvd->iface = ifnum;
+ uvd->ifaceAltInactive = inactInterface;
+ uvd->ifaceAltActive = actInterface;
+ uvd->video_endp = video_ep;
+ uvd->iso_packet_len = maxPS;
+ uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24;
+ uvd->defaultPalette = VIDEO_PALETTE_RGB24;
+ uvd->canvas = VIDEOSIZE(canvasX, canvasY);
+ uvd->videosize = ibmcam_size_to_videosize(size);
+
+ /* Initialize ibmcam-specific data */
+ assert(IBMCAM_T(uvd) != NULL);
+ IBMCAM_T(uvd)->camera_model = model;
+ IBMCAM_T(uvd)->initialized = 0;
+
+ ibmcam_configure_video(uvd);
+
+ i = usbvideo_RegisterVideoDevice(uvd);
+ if (i != 0) {
+ err("usbvideo_RegisterVideoDevice() failed.");
+ uvd = NULL;
+ }
+ }
+ usb_set_intfdata (intf, uvd);
+ return 0;
+}
+
+
+static struct usb_device_id id_table[] = {
+ { USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x0002, 0x0002) }, /* Model 1 */
+ { USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x030a, 0x030a) }, /* Model 2 */
+ { USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x0301, 0x0301) }, /* Model 3 */
+ { USB_DEVICE_VER(IBMCAM_VENDOR_ID, NETCAM_PRODUCT_ID, 0x030a, 0x030a) }, /* Model 4 */
+ { USB_DEVICE_VER(IBMCAM_VENDOR_ID, VEO_800C_PRODUCT_ID, 0x030a, 0x030a) }, /* Model 2 */
+ { USB_DEVICE_VER(IBMCAM_VENDOR_ID, VEO_800D_PRODUCT_ID, 0x030a, 0x030a) }, /* Model 4 */
+ { } /* Terminating entry */
+};
+
+/*
+ * ibmcam_init()
+ *
+ * This code is run to initialize the driver.
+ *
+ * History:
+ * 1/27/00 Reworked to use statically allocated ibmcam structures.
+ * 21/10/00 Completely redesigned to use usbvideo services.
+ */
+static int __init ibmcam_init(void)
+{
+ struct usbvideo_cb cbTbl;
+ memset(&cbTbl, 0, sizeof(cbTbl));
+ cbTbl.probe = ibmcam_probe;
+ cbTbl.setupOnOpen = ibmcam_setup_on_open;
+ cbTbl.videoStart = ibmcam_video_start;
+ cbTbl.videoStop = ibmcam_video_stop;
+ cbTbl.processData = ibmcam_ProcessIsocData;
+ cbTbl.postProcess = usbvideo_DeinterlaceFrame;
+ cbTbl.adjustPicture = ibmcam_adjust_picture;
+ cbTbl.getFPS = ibmcam_calculate_fps;
+ return usbvideo_register(
+ &cams,
+ MAX_IBMCAM,
+ sizeof(ibmcam_t),
+ "ibmcam",
+ &cbTbl,
+ THIS_MODULE,
+ id_table);
+}
+
+static void __exit ibmcam_cleanup(void)
+{
+ usbvideo_Deregister(&cams);
+}
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+module_init(ibmcam_init);
+module_exit(ibmcam_cleanup);
diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c
new file mode 100644
index 000000000000..e2ede583518f
--- /dev/null
+++ b/drivers/media/video/usbvideo/konicawc.c
@@ -0,0 +1,978 @@
+/*
+ * konicawc.c - konica webcam driver
+ *
+ * Author: Simon Evans <spse@secret.org.uk>
+ *
+ * Copyright (C) 2002 Simon Evans
+ *
+ * Licence: GPL
+ *
+ * Driver for USB webcams based on Konica chipset. This
+ * chipset is used in Intel YC76 camera.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/usb_input.h>
+
+#include "usbvideo.h"
+
+#define MAX_BRIGHTNESS 108
+#define MAX_CONTRAST 108
+#define MAX_SATURATION 108
+#define MAX_SHARPNESS 108
+#define MAX_WHITEBAL 372
+#define MAX_SPEED 6
+
+
+#define MAX_CAMERAS 1
+
+#define DRIVER_VERSION "v1.4"
+#define DRIVER_DESC "Konica Webcam driver"
+
+enum ctrl_req {
+ SetWhitebal = 0x01,
+ SetBrightness = 0x02,
+ SetSharpness = 0x03,
+ SetContrast = 0x04,
+ SetSaturation = 0x05,
+};
+
+
+enum frame_sizes {
+ SIZE_160X120 = 0,
+ SIZE_160X136 = 1,
+ SIZE_176X144 = 2,
+ SIZE_320X240 = 3,
+
+};
+
+#define MAX_FRAME_SIZE SIZE_320X240
+
+static struct usbvideo *cams;
+
+#ifdef CONFIG_USB_DEBUG
+static int debug;
+#define DEBUG(n, format, arg...) \
+ if (n <= debug) { \
+ printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \
+ }
+#else
+#define DEBUG(n, arg...)
+static const int debug = 0;
+#endif
+
+
+/* Some default values for initial camera settings,
+ can be set by modprobe */
+
+static int size;
+static int speed = 6; /* Speed (fps) 0 (slowest) to 6 (fastest) */
+static int brightness = MAX_BRIGHTNESS/2;
+static int contrast = MAX_CONTRAST/2;
+static int saturation = MAX_SATURATION/2;
+static int sharpness = MAX_SHARPNESS/2;
+static int whitebal = 3*(MAX_WHITEBAL/4);
+
+static const int spd_to_iface[] = { 1, 0, 3, 2, 4, 5, 6 };
+
+/* These FPS speeds are from the windows config box. They are
+ * indexed on size (0-2) and speed (0-6). Divide by 3 to get the
+ * real fps.
+ */
+
+static const int spd_to_fps[][7] = { { 24, 40, 48, 60, 72, 80, 100 },
+ { 24, 40, 48, 60, 72, 80, 100 },
+ { 18, 30, 36, 45, 54, 60, 75 },
+ { 6, 10, 12, 15, 18, 21, 25 } };
+
+struct cam_size {
+ u16 width;
+ u16 height;
+ u8 cmd;
+};
+
+static const struct cam_size camera_sizes[] = { { 160, 120, 0x7 },
+ { 160, 136, 0xa },
+ { 176, 144, 0x4 },
+ { 320, 240, 0x5 } };
+
+struct konicawc {
+ u8 brightness; /* camera uses 0 - 9, x11 for real value */
+ u8 contrast; /* as above */
+ u8 saturation; /* as above */
+ u8 sharpness; /* as above */
+ u8 white_bal; /* 0 - 33, x11 for real value */
+ u8 speed; /* Stored as 0 - 6, used as index in spd_to_* (above) */
+ u8 size; /* Frame Size */
+ int height;
+ int width;
+ struct urb *sts_urb[USBVIDEO_NUMSBUF];
+ u8 sts_buf[USBVIDEO_NUMSBUF][FRAMES_PER_DESC];
+ struct urb *last_data_urb;
+ int lastframe;
+ int cur_frame_size; /* number of bytes in current frame size */
+ int maxline; /* number of lines per frame */
+ int yplanesz; /* Number of bytes in the Y plane */
+ unsigned int buttonsts:1;
+#ifdef CONFIG_INPUT
+ struct input_dev *input;
+ char input_physname[64];
+#endif
+};
+
+
+#define konicawc_set_misc(uvd, req, value, index) konicawc_ctrl_msg(uvd, USB_DIR_OUT, req, value, index, NULL, 0)
+#define konicawc_get_misc(uvd, req, value, index, buf, sz) konicawc_ctrl_msg(uvd, USB_DIR_IN, req, value, index, buf, sz)
+#define konicawc_set_value(uvd, value, index) konicawc_ctrl_msg(uvd, USB_DIR_OUT, 2, value, index, NULL, 0)
+
+
+static int konicawc_ctrl_msg(struct uvd *uvd, u8 dir, u8 request, u16 value, u16 index, void *buf, int len)
+{
+ int retval = usb_control_msg(uvd->dev,
+ dir ? usb_rcvctrlpipe(uvd->dev, 0) : usb_sndctrlpipe(uvd->dev, 0),
+ request, 0x40 | dir, value, index, buf, len, 1000);
+ return retval < 0 ? retval : 0;
+}
+
+
+static inline void konicawc_camera_on(struct uvd *uvd)
+{
+ DEBUG(0, "camera on");
+ konicawc_set_misc(uvd, 0x2, 1, 0x0b);
+}
+
+
+static inline void konicawc_camera_off(struct uvd *uvd)
+{
+ DEBUG(0, "camera off");
+ konicawc_set_misc(uvd, 0x2, 0, 0x0b);
+}
+
+
+static void konicawc_set_camera_size(struct uvd *uvd)
+{
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+
+ konicawc_set_misc(uvd, 0x2, camera_sizes[cam->size].cmd, 0x08);
+ cam->width = camera_sizes[cam->size].width;
+ cam->height = camera_sizes[cam->size].height;
+ cam->yplanesz = cam->height * cam->width;
+ cam->cur_frame_size = (cam->yplanesz * 3) / 2;
+ cam->maxline = cam->yplanesz / 256;
+ uvd->videosize = VIDEOSIZE(cam->width, cam->height);
+}
+
+
+static int konicawc_setup_on_open(struct uvd *uvd)
+{
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+
+ DEBUG(1, "setting brightness to %d (%d)", cam->brightness,
+ cam->brightness * 11);
+ konicawc_set_value(uvd, cam->brightness, SetBrightness);
+ DEBUG(1, "setting white balance to %d (%d)", cam->white_bal,
+ cam->white_bal * 11);
+ konicawc_set_value(uvd, cam->white_bal, SetWhitebal);
+ DEBUG(1, "setting contrast to %d (%d)", cam->contrast,
+ cam->contrast * 11);
+ konicawc_set_value(uvd, cam->contrast, SetContrast);
+ DEBUG(1, "setting saturation to %d (%d)", cam->saturation,
+ cam->saturation * 11);
+ konicawc_set_value(uvd, cam->saturation, SetSaturation);
+ DEBUG(1, "setting sharpness to %d (%d)", cam->sharpness,
+ cam->sharpness * 11);
+ konicawc_set_value(uvd, cam->sharpness, SetSharpness);
+ konicawc_set_camera_size(uvd);
+ cam->lastframe = -2;
+ cam->buttonsts = 0;
+ return 0;
+}
+
+
+static void konicawc_adjust_picture(struct uvd *uvd)
+{
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+
+ konicawc_camera_off(uvd);
+ DEBUG(1, "new brightness: %d", uvd->vpic.brightness);
+ uvd->vpic.brightness = (uvd->vpic.brightness > MAX_BRIGHTNESS) ? MAX_BRIGHTNESS : uvd->vpic.brightness;
+ if(cam->brightness != uvd->vpic.brightness / 11) {
+ cam->brightness = uvd->vpic.brightness / 11;
+ DEBUG(1, "setting brightness to %d (%d)", cam->brightness,
+ cam->brightness * 11);
+ konicawc_set_value(uvd, cam->brightness, SetBrightness);
+ }
+
+ DEBUG(1, "new contrast: %d", uvd->vpic.contrast);
+ uvd->vpic.contrast = (uvd->vpic.contrast > MAX_CONTRAST) ? MAX_CONTRAST : uvd->vpic.contrast;
+ if(cam->contrast != uvd->vpic.contrast / 11) {
+ cam->contrast = uvd->vpic.contrast / 11;
+ DEBUG(1, "setting contrast to %d (%d)", cam->contrast,
+ cam->contrast * 11);
+ konicawc_set_value(uvd, cam->contrast, SetContrast);
+ }
+ konicawc_camera_on(uvd);
+}
+
+#ifdef CONFIG_INPUT
+
+static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev)
+{
+ struct input_dev *input_dev;
+
+ usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
+ strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
+
+ cam->input = input_dev = input_allocate_device();
+ if (!input_dev) {
+ warn("Not enough memory for camera's input device\n");
+ return;
+ }
+
+ input_dev->name = "Konicawc snapshot button";
+ input_dev->phys = cam->input_physname;
+ usb_to_input_id(dev, &input_dev->id);
+ input_dev->cdev.dev = &dev->dev;
+
+ input_dev->evbit[0] = BIT(EV_KEY);
+ input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);
+
+ input_dev->private = cam;
+
+ input_register_device(cam->input);
+}
+
+static void konicawc_unregister_input(struct konicawc *cam)
+{
+ if (cam->input) {
+ input_unregister_device(cam->input);
+ cam->input = NULL;
+ }
+}
+
+static void konicawc_report_buttonstat(struct konicawc *cam)
+{
+ if (cam->input) {
+ input_report_key(cam->input, BTN_0, cam->buttonsts);
+ input_sync(cam->input);
+ }
+}
+
+#else
+
+static inline void konicawc_register_input(struct konicawc *cam, struct usb_device *dev) { }
+static inline void konicawc_unregister_input(struct konicawc *cam) { }
+static inline void konicawc_report_buttonstat(struct konicawc *cam) { }
+
+#endif /* CONFIG_INPUT */
+
+static int konicawc_compress_iso(struct uvd *uvd, struct urb *dataurb, struct urb *stsurb)
+{
+ char *cdata;
+ int i, totlen = 0;
+ unsigned char *status = stsurb->transfer_buffer;
+ int keep = 0, discard = 0, bad = 0;
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+
+ for (i = 0; i < dataurb->number_of_packets; i++) {
+ int button = cam->buttonsts;
+ unsigned char sts;
+ int n = dataurb->iso_frame_desc[i].actual_length;
+ int st = dataurb->iso_frame_desc[i].status;
+ cdata = dataurb->transfer_buffer +
+ dataurb->iso_frame_desc[i].offset;
+
+ /* Detect and ignore errored packets */
+ if (st < 0) {
+ DEBUG(1, "Data error: packet=%d. len=%d. status=%d.",
+ i, n, st);
+ uvd->stats.iso_err_count++;
+ continue;
+ }
+
+ /* Detect and ignore empty packets */
+ if (n <= 0) {
+ uvd->stats.iso_skip_count++;
+ continue;
+ }
+
+ /* See what the status data said about the packet */
+ sts = *(status+stsurb->iso_frame_desc[i].offset);
+
+ /* sts: 0x80-0xff: frame start with frame number (ie 0-7f)
+ * otherwise:
+ * bit 0 0: keep packet
+ * 1: drop packet (padding data)
+ *
+ * bit 4 0 button not clicked
+ * 1 button clicked
+ * button is used to `take a picture' (in software)
+ */
+
+ if(sts < 0x80) {
+ button = !!(sts & 0x40);
+ sts &= ~0x40;
+ }
+
+ /* work out the button status, but don't do
+ anything with it for now */
+
+ if(button != cam->buttonsts) {
+ DEBUG(2, "button: %sclicked", button ? "" : "un");
+ cam->buttonsts = button;
+ konicawc_report_buttonstat(cam);
+ }
+
+ if(sts == 0x01) { /* drop frame */
+ discard++;
+ continue;
+ }
+
+ if((sts > 0x01) && (sts < 0x80)) {
+ info("unknown status %2.2x", sts);
+ bad++;
+ continue;
+ }
+ if(!sts && cam->lastframe == -2) {
+ DEBUG(2, "dropping frame looking for image start");
+ continue;
+ }
+
+ keep++;
+ if(sts & 0x80) { /* frame start */
+ unsigned char marker[] = { 0, 0xff, 0, 0x00 };
+
+ if(cam->lastframe == -2) {
+ DEBUG(2, "found initial image");
+ cam->lastframe = -1;
+ }
+
+ marker[3] = sts & 0x7F;
+ RingQueue_Enqueue(&uvd->dp, marker, 4);
+ totlen += 4;
+ }
+
+ totlen += n; /* Little local accounting */
+ RingQueue_Enqueue(&uvd->dp, cdata, n);
+ }
+ DEBUG(8, "finished: keep = %d discard = %d bad = %d added %d bytes",
+ keep, discard, bad, totlen);
+ return totlen;
+}
+
+
+static void resubmit_urb(struct uvd *uvd, struct urb *urb)
+{
+ int i, ret;
+ for (i = 0; i < FRAMES_PER_DESC; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ }
+ urb->dev = uvd->dev;
+ urb->status = 0;
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ DEBUG(3, "submitting urb of length %d", urb->transfer_buffer_length);
+ if(ret)
+ err("usb_submit_urb error (%d)", ret);
+
+}
+
+
+static void konicawc_isoc_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct uvd *uvd = urb->context;
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+
+ /* We don't want to do anything if we are about to be removed! */
+ if (!CAMERA_IS_OPERATIONAL(uvd))
+ return;
+
+ if (!uvd->streaming) {
+ DEBUG(1, "Not streaming, but interrupt!");
+ return;
+ }
+
+ DEBUG(3, "got frame %d len = %d buflen =%d", urb->start_frame, urb->actual_length, urb->transfer_buffer_length);
+
+ uvd->stats.urb_count++;
+
+ if (urb->transfer_buffer_length > 32) {
+ cam->last_data_urb = urb;
+ return;
+ }
+ /* Copy the data received into ring queue */
+ if(cam->last_data_urb) {
+ int len = 0;
+ if(urb->start_frame != cam->last_data_urb->start_frame)
+ err("Lost sync on frames");
+ else if (!urb->status && !cam->last_data_urb->status)
+ len = konicawc_compress_iso(uvd, cam->last_data_urb, urb);
+
+ resubmit_urb(uvd, cam->last_data_urb);
+ resubmit_urb(uvd, urb);
+ cam->last_data_urb = NULL;
+ uvd->stats.urb_length = len;
+ uvd->stats.data_count += len;
+ if(len)
+ RingQueue_WakeUpInterruptible(&uvd->dp);
+ return;
+ }
+ return;
+}
+
+
+static int konicawc_start_data(struct uvd *uvd)
+{
+ struct usb_device *dev = uvd->dev;
+ int i, errFlag;
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+ int pktsz;
+ struct usb_interface *intf;
+ struct usb_host_interface *interface = NULL;
+
+ intf = usb_ifnum_to_if(dev, uvd->iface);
+ if (intf)
+ interface = usb_altnum_to_altsetting(intf,
+ spd_to_iface[cam->speed]);
+ if (!interface)
+ return -ENXIO;
+ pktsz = le16_to_cpu(interface->endpoint[1].desc.wMaxPacketSize);
+ DEBUG(1, "pktsz = %d", pktsz);
+ if (!CAMERA_IS_OPERATIONAL(uvd)) {
+ err("Camera is not operational");
+ return -EFAULT;
+ }
+ uvd->curframe = -1;
+ konicawc_camera_on(uvd);
+ /* Alternate interface 1 is is the biggest frame size */
+ i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive);
+ if (i < 0) {
+ err("usb_set_interface error");
+ uvd->last_error = i;
+ return -EBUSY;
+ }
+
+ /* We double buffer the Iso lists */
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ int j, k;
+ struct urb *urb = uvd->sbuf[i].urb;
+ urb->dev = dev;
+ urb->context = uvd;
+ urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp);
+ urb->interval = 1;
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = uvd->sbuf[i].data;
+ urb->complete = konicawc_isoc_irq;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = pktsz * FRAMES_PER_DESC;
+ for (j=k=0; j < FRAMES_PER_DESC; j++, k += pktsz) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length = pktsz;
+ }
+
+ urb = cam->sts_urb[i];
+ urb->dev = dev;
+ urb->context = uvd;
+ urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1);
+ urb->interval = 1;
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = cam->sts_buf[i];
+ urb->complete = konicawc_isoc_irq;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = FRAMES_PER_DESC;
+ for (j=0; j < FRAMES_PER_DESC; j++) {
+ urb->iso_frame_desc[j].offset = j;
+ urb->iso_frame_desc[j].length = 1;
+ }
+ }
+
+ cam->last_data_urb = NULL;
+
+ /* Submit all URBs */
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ errFlag = usb_submit_urb(cam->sts_urb[i], GFP_KERNEL);
+ if (errFlag)
+ err("usb_submit_isoc(%d) ret %d", i, errFlag);
+
+ errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL);
+ if (errFlag)
+ err ("usb_submit_isoc(%d) ret %d", i, errFlag);
+ }
+
+ uvd->streaming = 1;
+ DEBUG(1, "streaming=1 video_endp=$%02x", uvd->video_endp);
+ return 0;
+}
+
+
+static void konicawc_stop_data(struct uvd *uvd)
+{
+ int i, j;
+ struct konicawc *cam;
+
+ if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL))
+ return;
+
+ konicawc_camera_off(uvd);
+ uvd->streaming = 0;
+ cam = (struct konicawc *)uvd->user_data;
+ cam->last_data_urb = NULL;
+
+ /* Unschedule all of the iso td's */
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ usb_kill_urb(uvd->sbuf[i].urb);
+ usb_kill_urb(cam->sts_urb[i]);
+ }
+
+ if (!uvd->remove_pending) {
+ /* Set packet size to 0 */
+ j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive);
+ if (j < 0) {
+ err("usb_set_interface() error %d.", j);
+ uvd->last_error = j;
+ }
+ }
+}
+
+
+static void konicawc_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame)
+{
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+ int maxline = cam->maxline;
+ int yplanesz = cam->yplanesz;
+
+ assert(frame != NULL);
+
+ DEBUG(5, "maxline = %d yplanesz = %d", maxline, yplanesz);
+ DEBUG(3, "Frame state = %d", frame->scanstate);
+
+ if(frame->scanstate == ScanState_Scanning) {
+ int drop = 0;
+ int curframe;
+ int fdrops = 0;
+ DEBUG(3, "Searching for marker, queue len = %d", RingQueue_GetLength(&uvd->dp));
+ while(RingQueue_GetLength(&uvd->dp) >= 4) {
+ if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) &&
+ (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) &&
+ (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) &&
+ (RING_QUEUE_PEEK(&uvd->dp, 3) < 0x80)) {
+ curframe = RING_QUEUE_PEEK(&uvd->dp, 3);
+ if(cam->lastframe >= 0) {
+ fdrops = (0x80 + curframe - cam->lastframe) & 0x7F;
+ fdrops--;
+ if(fdrops) {
+ info("Dropped %d frames (%d -> %d)", fdrops,
+ cam->lastframe, curframe);
+ }
+ }
+ cam->lastframe = curframe;
+ frame->curline = 0;
+ frame->scanstate = ScanState_Lines;
+ RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4);
+ break;
+ }
+ RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
+ drop++;
+ }
+ if(drop)
+ DEBUG(2, "dropped %d bytes looking for new frame", drop);
+ }
+
+ if(frame->scanstate == ScanState_Scanning)
+ return;
+
+ /* Try to move data from queue into frame buffer
+ * We get data in blocks of 384 bytes made up of:
+ * 256 Y, 64 U, 64 V.
+ * This needs to be written out as a Y plane, a U plane and a V plane.
+ */
+
+ while ( frame->curline < maxline && (RingQueue_GetLength(&uvd->dp) >= 384)) {
+ /* Y */
+ RingQueue_Dequeue(&uvd->dp, frame->data + (frame->curline * 256), 256);
+ /* U */
+ RingQueue_Dequeue(&uvd->dp, frame->data + yplanesz + (frame->curline * 64), 64);
+ /* V */
+ RingQueue_Dequeue(&uvd->dp, frame->data + (5 * yplanesz)/4 + (frame->curline * 64), 64);
+ frame->seqRead_Length += 384;
+ frame->curline++;
+ }
+ /* See if we filled the frame */
+ if (frame->curline == maxline) {
+ DEBUG(5, "got whole frame");
+
+ frame->frameState = FrameState_Done_Hold;
+ frame->curline = 0;
+ uvd->curframe = -1;
+ uvd->stats.frame_num++;
+ }
+}
+
+
+static int konicawc_find_fps(int size, int fps)
+{
+ int i;
+
+ fps *= 3;
+ DEBUG(1, "konica_find_fps: size = %d fps = %d", size, fps);
+ if(fps <= spd_to_fps[size][0])
+ return 0;
+
+ if(fps >= spd_to_fps[size][MAX_SPEED])
+ return MAX_SPEED;
+
+ for(i = 0; i < MAX_SPEED; i++) {
+ if((fps >= spd_to_fps[size][i]) && (fps <= spd_to_fps[size][i+1])) {
+ DEBUG(2, "fps %d between %d and %d", fps, i, i+1);
+ if( (fps - spd_to_fps[size][i]) < (spd_to_fps[size][i+1] - fps))
+ return i;
+ else
+ return i+1;
+ }
+ }
+ return MAX_SPEED+1;
+}
+
+
+static int konicawc_set_video_mode(struct uvd *uvd, struct video_window *vw)
+{
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+ int newspeed = cam->speed;
+ int newsize;
+ int x = vw->width;
+ int y = vw->height;
+ int fps = vw->flags;
+
+ if(x > 0 && y > 0) {
+ DEBUG(2, "trying to find size %d,%d", x, y);
+ for(newsize = 0; newsize <= MAX_FRAME_SIZE; newsize++) {
+ if((camera_sizes[newsize].width == x) && (camera_sizes[newsize].height == y))
+ break;
+ }
+ } else {
+ newsize = cam->size;
+ }
+
+ if(newsize > MAX_FRAME_SIZE) {
+ DEBUG(1, "couldn't find size %d,%d", x, y);
+ return -EINVAL;
+ }
+
+ if(fps > 0) {
+ DEBUG(1, "trying to set fps to %d", fps);
+ newspeed = konicawc_find_fps(newsize, fps);
+ DEBUG(1, "find_fps returned %d (%d)", newspeed, spd_to_fps[newsize][newspeed]);
+ }
+
+ if(newspeed > MAX_SPEED)
+ return -EINVAL;
+
+ DEBUG(1, "setting size to %d speed to %d", newsize, newspeed);
+ if((newsize == cam->size) && (newspeed == cam->speed)) {
+ DEBUG(1, "Nothing to do");
+ return 0;
+ }
+ DEBUG(0, "setting to %dx%d @ %d fps", camera_sizes[newsize].width,
+ camera_sizes[newsize].height, spd_to_fps[newsize][newspeed]/3);
+
+ konicawc_stop_data(uvd);
+ uvd->ifaceAltActive = spd_to_iface[newspeed];
+ DEBUG(1, "new interface = %d", uvd->ifaceAltActive);
+ cam->speed = newspeed;
+
+ if(cam->size != newsize) {
+ cam->size = newsize;
+ konicawc_set_camera_size(uvd);
+ }
+
+ /* Flush the input queue and clear any current frame in progress */
+
+ RingQueue_Flush(&uvd->dp);
+ cam->lastframe = -2;
+ if(uvd->curframe != -1) {
+ uvd->frame[uvd->curframe].curline = 0;
+ uvd->frame[uvd->curframe].seqRead_Length = 0;
+ uvd->frame[uvd->curframe].seqRead_Index = 0;
+ }
+
+ konicawc_start_data(uvd);
+ return 0;
+}
+
+
+static int konicawc_calculate_fps(struct uvd *uvd)
+{
+ struct konicawc *cam = uvd->user_data;
+ return spd_to_fps[cam->size][cam->speed]/3;
+}
+
+
+static void konicawc_configure_video(struct uvd *uvd)
+{
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+ u8 buf[2];
+
+ memset(&uvd->vpic, 0, sizeof(uvd->vpic));
+ memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old));
+
+ RESTRICT_TO_RANGE(brightness, 0, MAX_BRIGHTNESS);
+ RESTRICT_TO_RANGE(contrast, 0, MAX_CONTRAST);
+ RESTRICT_TO_RANGE(saturation, 0, MAX_SATURATION);
+ RESTRICT_TO_RANGE(sharpness, 0, MAX_SHARPNESS);
+ RESTRICT_TO_RANGE(whitebal, 0, MAX_WHITEBAL);
+
+ cam->brightness = brightness / 11;
+ cam->contrast = contrast / 11;
+ cam->saturation = saturation / 11;
+ cam->sharpness = sharpness / 11;
+ cam->white_bal = whitebal / 11;
+
+ uvd->vpic.colour = 108;
+ uvd->vpic.hue = 108;
+ uvd->vpic.brightness = brightness;
+ uvd->vpic.contrast = contrast;
+ uvd->vpic.whiteness = whitebal;
+ uvd->vpic.depth = 6;
+ uvd->vpic.palette = VIDEO_PALETTE_YUV420P;
+
+ memset(&uvd->vcap, 0, sizeof(uvd->vcap));
+ strcpy(uvd->vcap.name, "Konica Webcam");
+ uvd->vcap.type = VID_TYPE_CAPTURE;
+ uvd->vcap.channels = 1;
+ uvd->vcap.audios = 0;
+ uvd->vcap.minwidth = camera_sizes[SIZE_160X120].width;
+ uvd->vcap.minheight = camera_sizes[SIZE_160X120].height;
+ uvd->vcap.maxwidth = camera_sizes[SIZE_320X240].width;
+ uvd->vcap.maxheight = camera_sizes[SIZE_320X240].height;
+
+ memset(&uvd->vchan, 0, sizeof(uvd->vchan));
+ uvd->vchan.flags = 0 ;
+ uvd->vchan.tuners = 0;
+ uvd->vchan.channel = 0;
+ uvd->vchan.type = VIDEO_TYPE_CAMERA;
+ strcpy(uvd->vchan.name, "Camera");
+
+ /* Talk to device */
+ DEBUG(1, "device init");
+ if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2))
+ DEBUG(2, "3,10 -> %2.2x %2.2x", buf[0], buf[1]);
+ if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2))
+ DEBUG(2, "3,10 -> %2.2x %2.2x", buf[0], buf[1]);
+ if(konicawc_set_misc(uvd, 0x2, 0, 0xd))
+ DEBUG(2, "2,0,d failed");
+ DEBUG(1, "setting initial values");
+}
+
+static int konicawc_probe(struct usb_interface *intf, const struct usb_device_id *devid)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct uvd *uvd = NULL;
+ int ix, i, nas;
+ int actInterface=-1, inactInterface=-1, maxPS=0;
+ unsigned char video_ep = 0;
+
+ DEBUG(1, "konicawc_probe(%p)", intf);
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -ENODEV;
+
+ info("Konica Webcam (rev. 0x%04x)", le16_to_cpu(dev->descriptor.bcdDevice));
+ RESTRICT_TO_RANGE(speed, 0, MAX_SPEED);
+
+ /* Validate found interface: must have one ISO endpoint */
+ nas = intf->num_altsetting;
+ if (nas != 8) {
+ err("Incorrect number of alternate settings (%d) for this camera!", nas);
+ return -ENODEV;
+ }
+ /* Validate all alternate settings */
+ for (ix=0; ix < nas; ix++) {
+ const struct usb_host_interface *interface;
+ const struct usb_endpoint_descriptor *endpoint;
+
+ interface = &intf->altsetting[ix];
+ i = interface->desc.bAlternateSetting;
+ if (interface->desc.bNumEndpoints != 2) {
+ err("Interface %d. has %u. endpoints!",
+ interface->desc.bInterfaceNumber,
+ (unsigned)(interface->desc.bNumEndpoints));
+ return -ENODEV;
+ }
+ endpoint = &interface->endpoint[1].desc;
+ DEBUG(1, "found endpoint: addr: 0x%2.2x maxps = 0x%4.4x",
+ endpoint->bEndpointAddress, le16_to_cpu(endpoint->wMaxPacketSize));
+ if (video_ep == 0)
+ video_ep = endpoint->bEndpointAddress;
+ else if (video_ep != endpoint->bEndpointAddress) {
+ err("Alternate settings have different endpoint addresses!");
+ return -ENODEV;
+ }
+ if ((endpoint->bmAttributes & 0x03) != 0x01) {
+ err("Interface %d. has non-ISO endpoint!",
+ interface->desc.bInterfaceNumber);
+ return -ENODEV;
+ }
+ if ((endpoint->bEndpointAddress & 0x80) == 0) {
+ err("Interface %d. has ISO OUT endpoint!",
+ interface->desc.bInterfaceNumber);
+ return -ENODEV;
+ }
+ if (le16_to_cpu(endpoint->wMaxPacketSize) == 0) {
+ if (inactInterface < 0)
+ inactInterface = i;
+ else {
+ err("More than one inactive alt. setting!");
+ return -ENODEV;
+ }
+ } else {
+ if (i == spd_to_iface[speed]) {
+ /* This one is the requested one */
+ actInterface = i;
+ }
+ }
+ if (le16_to_cpu(endpoint->wMaxPacketSize) > maxPS)
+ maxPS = le16_to_cpu(endpoint->wMaxPacketSize);
+ }
+ if(actInterface == -1) {
+ err("Cant find required endpoint");
+ return -ENODEV;
+ }
+
+ DEBUG(1, "Selecting requested active setting=%d. maxPS=%d.", actInterface, maxPS);
+
+ uvd = usbvideo_AllocateDevice(cams);
+ if (uvd != NULL) {
+ struct konicawc *cam = (struct konicawc *)(uvd->user_data);
+ /* Here uvd is a fully allocated uvd object */
+ for(i = 0; i < USBVIDEO_NUMSBUF; i++) {
+ cam->sts_urb[i] = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
+ if(cam->sts_urb[i] == NULL) {
+ while(i--) {
+ usb_free_urb(cam->sts_urb[i]);
+ }
+ err("can't allocate urbs");
+ return -ENOMEM;
+ }
+ }
+ cam->speed = speed;
+ RESTRICT_TO_RANGE(size, SIZE_160X120, SIZE_320X240);
+ cam->width = camera_sizes[size].width;
+ cam->height = camera_sizes[size].height;
+ cam->size = size;
+
+ uvd->flags = 0;
+ uvd->debug = debug;
+ uvd->dev = dev;
+ uvd->iface = intf->altsetting->desc.bInterfaceNumber;
+ uvd->ifaceAltInactive = inactInterface;
+ uvd->ifaceAltActive = actInterface;
+ uvd->video_endp = video_ep;
+ uvd->iso_packet_len = maxPS;
+ uvd->paletteBits = 1L << VIDEO_PALETTE_YUV420P;
+ uvd->defaultPalette = VIDEO_PALETTE_YUV420P;
+ uvd->canvas = VIDEOSIZE(320, 240);
+ uvd->videosize = VIDEOSIZE(cam->width, cam->height);
+
+ /* Initialize konicawc specific data */
+ konicawc_configure_video(uvd);
+
+ i = usbvideo_RegisterVideoDevice(uvd);
+ uvd->max_frame_size = (320 * 240 * 3)/2;
+ if (i != 0) {
+ err("usbvideo_RegisterVideoDevice() failed.");
+ uvd = NULL;
+ }
+
+ konicawc_register_input(cam, dev);
+ }
+
+ if (uvd) {
+ usb_set_intfdata (intf, uvd);
+ return 0;
+ }
+ return -EIO;
+}
+
+
+static void konicawc_free_uvd(struct uvd *uvd)
+{
+ int i;
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+
+ konicawc_unregister_input(cam);
+
+ for (i = 0; i < USBVIDEO_NUMSBUF; i++) {
+ usb_free_urb(cam->sts_urb[i]);
+ cam->sts_urb[i] = NULL;
+ }
+}
+
+
+static struct usb_device_id id_table[] = {
+ { USB_DEVICE(0x04c8, 0x0720) }, /* Intel YC 76 */
+ { } /* Terminating entry */
+};
+
+
+static int __init konicawc_init(void)
+{
+ struct usbvideo_cb cbTbl;
+ info(DRIVER_DESC " " DRIVER_VERSION);
+ memset(&cbTbl, 0, sizeof(cbTbl));
+ cbTbl.probe = konicawc_probe;
+ cbTbl.setupOnOpen = konicawc_setup_on_open;
+ cbTbl.processData = konicawc_process_isoc;
+ cbTbl.getFPS = konicawc_calculate_fps;
+ cbTbl.setVideoMode = konicawc_set_video_mode;
+ cbTbl.startDataPump = konicawc_start_data;
+ cbTbl.stopDataPump = konicawc_stop_data;
+ cbTbl.adjustPicture = konicawc_adjust_picture;
+ cbTbl.userFree = konicawc_free_uvd;
+ return usbvideo_register(
+ &cams,
+ MAX_CAMERAS,
+ sizeof(struct konicawc),
+ "konicawc",
+ &cbTbl,
+ THIS_MODULE,
+ id_table);
+}
+
+
+static void __exit konicawc_cleanup(void)
+{
+ usbvideo_Deregister(&cams);
+}
+
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+module_param(speed, int, 0);
+MODULE_PARM_DESC(speed, "Initial speed: 0 (slowest) - 6 (fastest)");
+module_param(size, int, 0);
+MODULE_PARM_DESC(size, "Initial Size 0: 160x120 1: 160x136 2: 176x144 3: 320x240");
+module_param(brightness, int, 0);
+MODULE_PARM_DESC(brightness, "Initial brightness 0 - 108");
+module_param(contrast, int, 0);
+MODULE_PARM_DESC(contrast, "Initial contrast 0 - 108");
+module_param(saturation, int, 0);
+MODULE_PARM_DESC(saturation, "Initial saturation 0 - 108");
+module_param(sharpness, int, 0);
+MODULE_PARM_DESC(sharpness, "Initial brightness 0 - 108");
+module_param(whitebal, int, 0);
+MODULE_PARM_DESC(whitebal, "Initial white balance 0 - 363");
+
+#ifdef CONFIG_USB_DEBUG
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)");
+#endif
+
+module_init(konicawc_init);
+module_exit(konicawc_cleanup);
diff --git a/drivers/media/video/usbvideo/ultracam.c b/drivers/media/video/usbvideo/ultracam.c
new file mode 100644
index 000000000000..75ff755224df
--- /dev/null
+++ b/drivers/media/video/usbvideo/ultracam.c
@@ -0,0 +1,679 @@
+/*
+ * USB NB Camera driver
+ *
+ * HISTORY:
+ * 25-Dec-2002 Dmitri Removed lighting, sharpness parameters, methods.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "usbvideo.h"
+
+#define ULTRACAM_VENDOR_ID 0x0461
+#define ULTRACAM_PRODUCT_ID 0x0813
+
+#define MAX_CAMERAS 4 /* How many devices we allow to connect */
+
+/*
+ * This structure lives in uvd_t->user field.
+ */
+typedef struct {
+ int initialized; /* Had we already sent init sequence? */
+ int camera_model; /* What type of IBM camera we got? */
+ int has_hdr;
+} ultracam_t;
+#define ULTRACAM_T(uvd) ((ultracam_t *)((uvd)->user_data))
+
+static struct usbvideo *cams = NULL;
+
+static int debug = 0;
+
+static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */
+
+static const int min_canvasWidth = 8;
+static const int min_canvasHeight = 4;
+
+#define FRAMERATE_MIN 0
+#define FRAMERATE_MAX 6
+static int framerate = -1;
+
+/*
+ * Here we define several initialization variables. They may
+ * be used to automatically set color, hue, brightness and
+ * contrast to desired values. This is particularly useful in
+ * case of webcams (which have no controls and no on-screen
+ * output) and also when a client V4L software is used that
+ * does not have some of those controls. In any case it's
+ * good to have startup values as options.
+ *
+ * These values are all in [0..255] range. This simplifies
+ * operation. Note that actual values of V4L variables may
+ * be scaled up (as much as << 8). User can see that only
+ * on overlay output, however, or through a V4L client.
+ */
+static int init_brightness = 128;
+static int init_contrast = 192;
+static int init_color = 128;
+static int init_hue = 128;
+static int hue_correction = 128;
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)");
+module_param(flags, int, 0);
+MODULE_PARM_DESC(flags,
+ "Bitfield: 0=VIDIOCSYNC, "
+ "1=B/W, "
+ "2=show hints, "
+ "3=show stats, "
+ "4=test pattern, "
+ "5=separate frames, "
+ "6=clean frames");
+module_param(framerate, int, 0);
+MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)");
+
+module_param(init_brightness, int, 0);
+MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)");
+module_param(init_contrast, int, 0);
+MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)");
+module_param(init_color, int, 0);
+MODULE_PARM_DESC(init_color, "Color preconfiguration: 0-255 (default=128)");
+module_param(init_hue, int, 0);
+MODULE_PARM_DESC(init_hue, "Hue preconfiguration: 0-255 (default=128)");
+module_param(hue_correction, int, 0);
+MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)");
+
+/*
+ * ultracam_ProcessIsocData()
+ *
+ * Generic routine to parse the ring queue data. It employs either
+ * ultracam_find_header() or ultracam_parse_lines() to do most
+ * of work.
+ *
+ * 02-Nov-2000 First (mostly dummy) version.
+ * 06-Nov-2000 Rewrote to dump all data into frame.
+ */
+static void ultracam_ProcessIsocData(struct uvd *uvd, struct usbvideo_frame *frame)
+{
+ int n;
+
+ assert(uvd != NULL);
+ assert(frame != NULL);
+
+ /* Try to move data from queue into frame buffer */
+ n = RingQueue_GetLength(&uvd->dp);
+ if (n > 0) {
+ int m;
+ /* See how much spare we have left */
+ m = uvd->max_frame_size - frame->seqRead_Length;
+ if (n > m)
+ n = m;
+ /* Now move that much data into frame buffer */
+ RingQueue_Dequeue(
+ &uvd->dp,
+ frame->data + frame->seqRead_Length,
+ m);
+ frame->seqRead_Length += m;
+ }
+ /* See if we filled the frame */
+ if (frame->seqRead_Length >= uvd->max_frame_size) {
+ frame->frameState = FrameState_Done;
+ uvd->curframe = -1;
+ uvd->stats.frame_num++;
+ }
+}
+
+/*
+ * ultracam_veio()
+ *
+ * History:
+ * 1/27/00 Added check for dev == NULL; this happens if camera is unplugged.
+ */
+static int ultracam_veio(
+ struct uvd *uvd,
+ unsigned char req,
+ unsigned short value,
+ unsigned short index,
+ int is_out)
+{
+ static const char proc[] = "ultracam_veio";
+ unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */;
+ int i;
+
+ if (!CAMERA_IS_OPERATIONAL(uvd))
+ return 0;
+
+ if (!is_out) {
+ i = usb_control_msg(
+ uvd->dev,
+ usb_rcvctrlpipe(uvd->dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ index,
+ cp,
+ sizeof(cp),
+ 1000);
+#if 1
+ info("USB => %02x%02x%02x%02x%02x%02x%02x%02x "
+ "(req=$%02x val=$%04x ind=$%04x)",
+ cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7],
+ req, value, index);
+#endif
+ } else {
+ i = usb_control_msg(
+ uvd->dev,
+ usb_sndctrlpipe(uvd->dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ index,
+ NULL,
+ 0,
+ 1000);
+ }
+ if (i < 0) {
+ err("%s: ERROR=%d. Camera stopped; Reconnect or reload driver.",
+ proc, i);
+ uvd->last_error = i;
+ }
+ return i;
+}
+
+/*
+ * ultracam_calculate_fps()
+ */
+static int ultracam_calculate_fps(struct uvd *uvd)
+{
+ return 3 + framerate*4 + framerate/2;
+}
+
+/*
+ * ultracam_adjust_contrast()
+ */
+static void ultracam_adjust_contrast(struct uvd *uvd)
+{
+}
+
+/*
+ * ultracam_set_brightness()
+ *
+ * This procedure changes brightness of the picture.
+ */
+static void ultracam_set_brightness(struct uvd *uvd)
+{
+}
+
+static void ultracam_set_hue(struct uvd *uvd)
+{
+}
+
+/*
+ * ultracam_adjust_picture()
+ *
+ * This procedure gets called from V4L interface to update picture settings.
+ * Here we change brightness and contrast.
+ */
+static void ultracam_adjust_picture(struct uvd *uvd)
+{
+ ultracam_adjust_contrast(uvd);
+ ultracam_set_brightness(uvd);
+ ultracam_set_hue(uvd);
+}
+
+/*
+ * ultracam_video_stop()
+ *
+ * This code tells camera to stop streaming. The interface remains
+ * configured and bandwidth - claimed.
+ */
+static void ultracam_video_stop(struct uvd *uvd)
+{
+}
+
+/*
+ * ultracam_reinit_iso()
+ *
+ * This procedure sends couple of commands to the camera and then
+ * resets the video pipe. This sequence was observed to reinit the
+ * camera or, at least, to initiate ISO data stream.
+ */
+static void ultracam_reinit_iso(struct uvd *uvd, int do_stop)
+{
+}
+
+static void ultracam_video_start(struct uvd *uvd)
+{
+ ultracam_reinit_iso(uvd, 0);
+}
+
+static int ultracam_resetPipe(struct uvd *uvd)
+{
+ usb_clear_halt(uvd->dev, uvd->video_endp);
+ return 0;
+}
+
+static int ultracam_alternateSetting(struct uvd *uvd, int setting)
+{
+ static const char proc[] = "ultracam_alternateSetting";
+ int i;
+ i = usb_set_interface(uvd->dev, uvd->iface, setting);
+ if (i < 0) {
+ err("%s: usb_set_interface error", proc);
+ uvd->last_error = i;
+ return -EBUSY;
+ }
+ return 0;
+}
+
+/*
+ * Return negative code on failure, 0 on success.
+ */
+static int ultracam_setup_on_open(struct uvd *uvd)
+{
+ int setup_ok = 0; /* Success by default */
+ /* Send init sequence only once, it's large! */
+ if (!ULTRACAM_T(uvd)->initialized) {
+ ultracam_alternateSetting(uvd, 0x04);
+ ultracam_alternateSetting(uvd, 0x00);
+ ultracam_veio(uvd, 0x02, 0x0004, 0x000b, 1);
+ ultracam_veio(uvd, 0x02, 0x0001, 0x0005, 1);
+ ultracam_veio(uvd, 0x02, 0x8000, 0x0000, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x0000, 1);
+ ultracam_veio(uvd, 0x00, 0x00b0, 0x0001, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x0002, 1);
+ ultracam_veio(uvd, 0x00, 0x000c, 0x0003, 1);
+ ultracam_veio(uvd, 0x00, 0x000b, 0x0004, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x0005, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x0006, 1);
+ ultracam_veio(uvd, 0x00, 0x0079, 0x0007, 1);
+ ultracam_veio(uvd, 0x00, 0x003b, 0x0008, 1);
+ ultracam_veio(uvd, 0x00, 0x0002, 0x000f, 1);
+ ultracam_veio(uvd, 0x00, 0x0001, 0x0010, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x0011, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00bf, 1);
+ ultracam_veio(uvd, 0x00, 0x0001, 0x00c0, 1);
+ ultracam_veio(uvd, 0x00, 0x0010, 0x00cb, 1);
+ ultracam_veio(uvd, 0x01, 0x00a4, 0x0001, 1);
+ ultracam_veio(uvd, 0x01, 0x0010, 0x0002, 1);
+ ultracam_veio(uvd, 0x01, 0x0066, 0x0007, 1);
+ ultracam_veio(uvd, 0x01, 0x000b, 0x0008, 1);
+ ultracam_veio(uvd, 0x01, 0x0034, 0x0009, 1);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 1);
+ ultracam_veio(uvd, 0x01, 0x002e, 0x000b, 1);
+ ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, 1);
+ ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, 1);
+ ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, 1);
+ ultracam_veio(uvd, 0x01, 0x00da, 0x000f, 1);
+ ultracam_veio(uvd, 0x01, 0x0036, 0x0010, 1);
+ ultracam_veio(uvd, 0x01, 0x000b, 0x0011, 1);
+ ultracam_veio(uvd, 0x01, 0x0001, 0x0012, 1);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x0013, 1);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x0014, 1);
+ ultracam_veio(uvd, 0x01, 0x0087, 0x0051, 1);
+ ultracam_veio(uvd, 0x01, 0x0040, 0x0052, 1);
+ ultracam_veio(uvd, 0x01, 0x0058, 0x0053, 1);
+ ultracam_veio(uvd, 0x01, 0x0040, 0x0054, 1);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x0040, 1);
+ ultracam_veio(uvd, 0x01, 0x0010, 0x0041, 1);
+ ultracam_veio(uvd, 0x01, 0x0020, 0x0042, 1);
+ ultracam_veio(uvd, 0x01, 0x0030, 0x0043, 1);
+ ultracam_veio(uvd, 0x01, 0x0040, 0x0044, 1);
+ ultracam_veio(uvd, 0x01, 0x0050, 0x0045, 1);
+ ultracam_veio(uvd, 0x01, 0x0060, 0x0046, 1);
+ ultracam_veio(uvd, 0x01, 0x0070, 0x0047, 1);
+ ultracam_veio(uvd, 0x01, 0x0080, 0x0048, 1);
+ ultracam_veio(uvd, 0x01, 0x0090, 0x0049, 1);
+ ultracam_veio(uvd, 0x01, 0x00a0, 0x004a, 1);
+ ultracam_veio(uvd, 0x01, 0x00b0, 0x004b, 1);
+ ultracam_veio(uvd, 0x01, 0x00c0, 0x004c, 1);
+ ultracam_veio(uvd, 0x01, 0x00d0, 0x004d, 1);
+ ultracam_veio(uvd, 0x01, 0x00e0, 0x004e, 1);
+ ultracam_veio(uvd, 0x01, 0x00f0, 0x004f, 1);
+ ultracam_veio(uvd, 0x01, 0x00ff, 0x0050, 1);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x0056, 1);
+ ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, 1);
+ ultracam_veio(uvd, 0x00, 0x0004, 0x00c2, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0002, 0x00c1, 1);
+ ultracam_veio(uvd, 0x00, 0x0020, 0x00c2, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0040, 0x00c1, 1);
+ ultracam_veio(uvd, 0x00, 0x0017, 0x00c2, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
+ ultracam_veio(uvd, 0x00, 0x00c0, 0x00c1, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
+ ultracam_veio(uvd, 0x02, 0xc040, 0x0001, 1);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x0008, 0);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x0009, 0);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 0);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x000b, 0);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x000c, 0);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x000d, 0);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x000e, 0);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x000f, 0);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x0010, 0);
+ ultracam_veio(uvd, 0x01, 0x000b, 0x0008, 1);
+ ultracam_veio(uvd, 0x01, 0x0034, 0x0009, 1);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 1);
+ ultracam_veio(uvd, 0x01, 0x002e, 0x000b, 1);
+ ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, 1);
+ ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, 1);
+ ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, 1);
+ ultracam_veio(uvd, 0x01, 0x00da, 0x000f, 1);
+ ultracam_veio(uvd, 0x01, 0x0036, 0x0010, 1);
+ ultracam_veio(uvd, 0x01, 0x0000, 0x0001, 0);
+ ultracam_veio(uvd, 0x01, 0x0064, 0x0001, 1);
+ ultracam_veio(uvd, 0x01, 0x0059, 0x0051, 1);
+ ultracam_veio(uvd, 0x01, 0x003f, 0x0052, 1);
+ ultracam_veio(uvd, 0x01, 0x0094, 0x0053, 1);
+ ultracam_veio(uvd, 0x01, 0x00ff, 0x0011, 1);
+ ultracam_veio(uvd, 0x01, 0x0003, 0x0012, 1);
+ ultracam_veio(uvd, 0x01, 0x00f7, 0x0013, 1);
+ ultracam_veio(uvd, 0x00, 0x0009, 0x0011, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x0001, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x0000, 1);
+ ultracam_veio(uvd, 0x00, 0x0020, 0x00c1, 1);
+ ultracam_veio(uvd, 0x00, 0x0010, 0x00c2, 1);
+ ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
+ ultracam_alternateSetting(uvd, 0x04);
+ ultracam_veio(uvd, 0x02, 0x0000, 0x0001, 1);
+ ultracam_veio(uvd, 0x02, 0x0000, 0x0001, 1);
+ ultracam_veio(uvd, 0x02, 0x0000, 0x0006, 1);
+ ultracam_veio(uvd, 0x02, 0x9000, 0x0007, 1);
+ ultracam_veio(uvd, 0x02, 0x0042, 0x0001, 1);
+ ultracam_veio(uvd, 0x02, 0x0000, 0x000b, 0);
+ ultracam_resetPipe(uvd);
+ ULTRACAM_T(uvd)->initialized = (setup_ok != 0);
+ }
+ return setup_ok;
+}
+
+static void ultracam_configure_video(struct uvd *uvd)
+{
+ if (uvd == NULL)
+ return;
+
+ RESTRICT_TO_RANGE(init_brightness, 0, 255);
+ RESTRICT_TO_RANGE(init_contrast, 0, 255);
+ RESTRICT_TO_RANGE(init_color, 0, 255);
+ RESTRICT_TO_RANGE(init_hue, 0, 255);
+ RESTRICT_TO_RANGE(hue_correction, 0, 255);
+
+ memset(&uvd->vpic, 0, sizeof(uvd->vpic));
+ memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old));
+
+ uvd->vpic.colour = init_color << 8;
+ uvd->vpic.hue = init_hue << 8;
+ uvd->vpic.brightness = init_brightness << 8;
+ uvd->vpic.contrast = init_contrast << 8;
+ uvd->vpic.whiteness = 105 << 8; /* This one isn't used */
+ uvd->vpic.depth = 24;
+ uvd->vpic.palette = VIDEO_PALETTE_RGB24;
+
+ memset(&uvd->vcap, 0, sizeof(uvd->vcap));
+ strcpy(uvd->vcap.name, "IBM Ultra Camera");
+ uvd->vcap.type = VID_TYPE_CAPTURE;
+ uvd->vcap.channels = 1;
+ uvd->vcap.audios = 0;
+ uvd->vcap.maxwidth = VIDEOSIZE_X(uvd->canvas);
+ uvd->vcap.maxheight = VIDEOSIZE_Y(uvd->canvas);
+ uvd->vcap.minwidth = min_canvasWidth;
+ uvd->vcap.minheight = min_canvasHeight;
+
+ memset(&uvd->vchan, 0, sizeof(uvd->vchan));
+ uvd->vchan.flags = 0;
+ uvd->vchan.tuners = 0;
+ uvd->vchan.channel = 0;
+ uvd->vchan.type = VIDEO_TYPE_CAMERA;
+ strcpy(uvd->vchan.name, "Camera");
+}
+
+/*
+ * ultracam_probe()
+ *
+ * This procedure queries device descriptor and accepts the interface
+ * if it looks like our camera.
+ *
+ * History:
+ * 12-Nov-2000 Reworked to comply with new probe() signature.
+ * 23-Jan-2001 Added compatibility with 2.2.x kernels.
+ */
+static int ultracam_probe(struct usb_interface *intf, const struct usb_device_id *devid)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct uvd *uvd = NULL;
+ int ix, i, nas;
+ int actInterface=-1, inactInterface=-1, maxPS=0;
+ unsigned char video_ep = 0;
+
+ if (debug >= 1)
+ info("ultracam_probe(%p)", intf);
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -ENODEV;
+
+ info("IBM Ultra camera found (rev. 0x%04x)",
+ le16_to_cpu(dev->descriptor.bcdDevice));
+
+ /* Validate found interface: must have one ISO endpoint */
+ nas = intf->num_altsetting;
+ if (debug > 0)
+ info("Number of alternate settings=%d.", nas);
+ if (nas < 8) {
+ err("Too few alternate settings for this camera!");
+ return -ENODEV;
+ }
+ /* Validate all alternate settings */
+ for (ix=0; ix < nas; ix++) {
+ const struct usb_host_interface *interface;
+ const struct usb_endpoint_descriptor *endpoint;
+
+ interface = &intf->altsetting[ix];
+ i = interface->desc.bAlternateSetting;
+ if (interface->desc.bNumEndpoints != 1) {
+ err("Interface %d. has %u. endpoints!",
+ interface->desc.bInterfaceNumber,
+ (unsigned)(interface->desc.bNumEndpoints));
+ return -ENODEV;
+ }
+ endpoint = &interface->endpoint[0].desc;
+ if (video_ep == 0)
+ video_ep = endpoint->bEndpointAddress;
+ else if (video_ep != endpoint->bEndpointAddress) {
+ err("Alternate settings have different endpoint addresses!");
+ return -ENODEV;
+ }
+ if ((endpoint->bmAttributes & 0x03) != 0x01) {
+ err("Interface %d. has non-ISO endpoint!",
+ interface->desc.bInterfaceNumber);
+ return -ENODEV;
+ }
+ if ((endpoint->bEndpointAddress & 0x80) == 0) {
+ err("Interface %d. has ISO OUT endpoint!",
+ interface->desc.bInterfaceNumber);
+ return -ENODEV;
+ }
+ if (le16_to_cpu(endpoint->wMaxPacketSize) == 0) {
+ if (inactInterface < 0)
+ inactInterface = i;
+ else {
+ err("More than one inactive alt. setting!");
+ return -ENODEV;
+ }
+ } else {
+ if (actInterface < 0) {
+ actInterface = i;
+ maxPS = le16_to_cpu(endpoint->wMaxPacketSize);
+ if (debug > 0)
+ info("Active setting=%d. maxPS=%d.", i, maxPS);
+ } else {
+ /* Got another active alt. setting */
+ if (maxPS < le16_to_cpu(endpoint->wMaxPacketSize)) {
+ /* This one is better! */
+ actInterface = i;
+ maxPS = le16_to_cpu(endpoint->wMaxPacketSize);
+ if (debug > 0) {
+ info("Even better ctive setting=%d. maxPS=%d.",
+ i, maxPS);
+ }
+ }
+ }
+ }
+ }
+ if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0)) {
+ err("Failed to recognize the camera!");
+ return -ENODEV;
+ }
+
+ uvd = usbvideo_AllocateDevice(cams);
+ if (uvd != NULL) {
+ /* Here uvd is a fully allocated uvd object */
+ uvd->flags = flags;
+ uvd->debug = debug;
+ uvd->dev = dev;
+ uvd->iface = intf->altsetting->desc.bInterfaceNumber;
+ uvd->ifaceAltInactive = inactInterface;
+ uvd->ifaceAltActive = actInterface;
+ uvd->video_endp = video_ep;
+ uvd->iso_packet_len = maxPS;
+ uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24;
+ uvd->defaultPalette = VIDEO_PALETTE_RGB24;
+ uvd->canvas = VIDEOSIZE(640, 480); /* FIXME */
+ uvd->videosize = uvd->canvas; /* ultracam_size_to_videosize(size);*/
+
+ /* Initialize ibmcam-specific data */
+ assert(ULTRACAM_T(uvd) != NULL);
+ ULTRACAM_T(uvd)->camera_model = 0; /* Not used yet */
+ ULTRACAM_T(uvd)->initialized = 0;
+
+ ultracam_configure_video(uvd);
+
+ i = usbvideo_RegisterVideoDevice(uvd);
+ if (i != 0) {
+ err("usbvideo_RegisterVideoDevice() failed.");
+ uvd = NULL;
+ }
+ }
+
+ if (uvd) {
+ usb_set_intfdata (intf, uvd);
+ return 0;
+ }
+ return -EIO;
+}
+
+
+static struct usb_device_id id_table[] = {
+ { USB_DEVICE(ULTRACAM_VENDOR_ID, ULTRACAM_PRODUCT_ID) },
+ { } /* Terminating entry */
+};
+
+/*
+ * ultracam_init()
+ *
+ * This code is run to initialize the driver.
+ */
+static int __init ultracam_init(void)
+{
+ struct usbvideo_cb cbTbl;
+ memset(&cbTbl, 0, sizeof(cbTbl));
+ cbTbl.probe = ultracam_probe;
+ cbTbl.setupOnOpen = ultracam_setup_on_open;
+ cbTbl.videoStart = ultracam_video_start;
+ cbTbl.videoStop = ultracam_video_stop;
+ cbTbl.processData = ultracam_ProcessIsocData;
+ cbTbl.postProcess = usbvideo_DeinterlaceFrame;
+ cbTbl.adjustPicture = ultracam_adjust_picture;
+ cbTbl.getFPS = ultracam_calculate_fps;
+ return usbvideo_register(
+ &cams,
+ MAX_CAMERAS,
+ sizeof(ultracam_t),
+ "ultracam",
+ &cbTbl,
+ THIS_MODULE,
+ id_table);
+}
+
+static void __exit ultracam_cleanup(void)
+{
+ usbvideo_Deregister(&cams);
+}
+
+MODULE_DEVICE_TABLE(usb, id_table);
+MODULE_LICENSE("GPL");
+
+module_init(ultracam_init);
+module_exit(ultracam_cleanup);
diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c
new file mode 100644
index 000000000000..0b51fae720a9
--- /dev/null
+++ b/drivers/media/video/usbvideo/usbvideo.c
@@ -0,0 +1,2190 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+
+#include "usbvideo.h"
+
+#if defined(MAP_NR)
+#define virt_to_page(v) MAP_NR(v) /* Kernels 2.2.x */
+#endif
+
+static int video_nr = -1;
+module_param(video_nr, int, 0);
+
+/*
+ * Local prototypes.
+ */
+static void usbvideo_Disconnect(struct usb_interface *intf);
+static void usbvideo_CameraRelease(struct uvd *uvd);
+
+static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma);
+static int usbvideo_v4l_open(struct inode *inode, struct file *file);
+static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos);
+static int usbvideo_v4l_close(struct inode *inode, struct file *file);
+
+static int usbvideo_StartDataPump(struct uvd *uvd);
+static void usbvideo_StopDataPump(struct uvd *uvd);
+static int usbvideo_GetFrame(struct uvd *uvd, int frameNum);
+static int usbvideo_NewFrame(struct uvd *uvd, int framenum);
+static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd,
+ struct usbvideo_frame *frame);
+
+/*******************************/
+/* Memory management functions */
+/*******************************/
+static void *usbvideo_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 (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return mem;
+}
+
+static void usbvideo_rvfree(void *mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
+static void RingQueue_Initialize(struct RingQueue *rq)
+{
+ assert(rq != NULL);
+ init_waitqueue_head(&rq->wqh);
+}
+
+static void RingQueue_Allocate(struct RingQueue *rq, int rqLen)
+{
+ /* Make sure the requested size is a power of 2 and
+ round up if necessary. This allows index wrapping
+ using masks rather than modulo */
+
+ int i = 1;
+ assert(rq != NULL);
+ assert(rqLen > 0);
+
+ while(rqLen >> i)
+ i++;
+ if(rqLen != 1 << (i-1))
+ rqLen = 1 << i;
+
+ rq->length = rqLen;
+ rq->ri = rq->wi = 0;
+ rq->queue = usbvideo_rvmalloc(rq->length);
+ assert(rq->queue != NULL);
+}
+
+static int RingQueue_IsAllocated(const struct RingQueue *rq)
+{
+ if (rq == NULL)
+ return 0;
+ return (rq->queue != NULL) && (rq->length > 0);
+}
+
+static void RingQueue_Free(struct RingQueue *rq)
+{
+ assert(rq != NULL);
+ if (RingQueue_IsAllocated(rq)) {
+ usbvideo_rvfree(rq->queue, rq->length);
+ rq->queue = NULL;
+ rq->length = 0;
+ }
+}
+
+int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len)
+{
+ int rql, toread;
+
+ assert(rq != NULL);
+ assert(dst != NULL);
+
+ rql = RingQueue_GetLength(rq);
+ if(!rql)
+ return 0;
+
+ /* Clip requested length to available data */
+ if(len > rql)
+ len = rql;
+
+ toread = len;
+ if(rq->ri > rq->wi) {
+ /* Read data from tail */
+ int read = (toread < (rq->length - rq->ri)) ? toread : rq->length - rq->ri;
+ memcpy(dst, rq->queue + rq->ri, read);
+ toread -= read;
+ dst += read;
+ rq->ri = (rq->ri + read) & (rq->length-1);
+ }
+ if(toread) {
+ /* Read data from head */
+ memcpy(dst, rq->queue + rq->ri, toread);
+ rq->ri = (rq->ri + toread) & (rq->length-1);
+ }
+ return len;
+}
+
+EXPORT_SYMBOL(RingQueue_Dequeue);
+
+int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n)
+{
+ int enqueued = 0;
+
+ assert(rq != NULL);
+ assert(cdata != NULL);
+ assert(rq->length > 0);
+ while (n > 0) {
+ int m, q_avail;
+
+ /* Calculate the largest chunk that fits the tail of the ring */
+ q_avail = rq->length - rq->wi;
+ if (q_avail <= 0) {
+ rq->wi = 0;
+ q_avail = rq->length;
+ }
+ m = n;
+ assert(q_avail > 0);
+ if (m > q_avail)
+ m = q_avail;
+
+ memcpy(rq->queue + rq->wi, cdata, m);
+ RING_QUEUE_ADVANCE_INDEX(rq, wi, m);
+ cdata += m;
+ enqueued += m;
+ n -= m;
+ }
+ return enqueued;
+}
+
+EXPORT_SYMBOL(RingQueue_Enqueue);
+
+static void RingQueue_InterruptibleSleepOn(struct RingQueue *rq)
+{
+ assert(rq != NULL);
+ interruptible_sleep_on(&rq->wqh);
+}
+
+void RingQueue_WakeUpInterruptible(struct RingQueue *rq)
+{
+ assert(rq != NULL);
+ if (waitqueue_active(&rq->wqh))
+ wake_up_interruptible(&rq->wqh);
+}
+
+EXPORT_SYMBOL(RingQueue_WakeUpInterruptible);
+
+void RingQueue_Flush(struct RingQueue *rq)
+{
+ assert(rq != NULL);
+ rq->ri = 0;
+ rq->wi = 0;
+}
+
+EXPORT_SYMBOL(RingQueue_Flush);
+
+
+/*
+ * usbvideo_VideosizeToString()
+ *
+ * This procedure converts given videosize value to readable string.
+ *
+ * History:
+ * 07-Aug-2000 Created.
+ * 19-Oct-2000 Reworked for usbvideo module.
+ */
+static void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs)
+{
+ char tmp[40];
+ int n;
+
+ n = 1 + sprintf(tmp, "%ldx%ld", VIDEOSIZE_X(vs), VIDEOSIZE_Y(vs));
+ assert(n < sizeof(tmp));
+ if ((buf == NULL) || (bufLen < n))
+ err("usbvideo_VideosizeToString: buffer is too small.");
+ else
+ memmove(buf, tmp, n);
+}
+
+/*
+ * usbvideo_OverlayChar()
+ *
+ * History:
+ * 01-Feb-2000 Created.
+ */
+static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame,
+ int x, int y, int ch)
+{
+ static const unsigned short digits[16] = {
+ 0xF6DE, /* 0 */
+ 0x2492, /* 1 */
+ 0xE7CE, /* 2 */
+ 0xE79E, /* 3 */
+ 0xB792, /* 4 */
+ 0xF39E, /* 5 */
+ 0xF3DE, /* 6 */
+ 0xF492, /* 7 */
+ 0xF7DE, /* 8 */
+ 0xF79E, /* 9 */
+ 0x77DA, /* a */
+ 0xD75C, /* b */
+ 0xF24E, /* c */
+ 0xD6DC, /* d */
+ 0xF34E, /* e */
+ 0xF348 /* f */
+ };
+ unsigned short digit;
+ int ix, iy;
+
+ if ((uvd == NULL) || (frame == NULL))
+ return;
+
+ if (ch >= '0' && ch <= '9')
+ ch -= '0';
+ else if (ch >= 'A' && ch <= 'F')
+ ch = 10 + (ch - 'A');
+ else if (ch >= 'a' && ch <= 'f')
+ ch = 10 + (ch - 'a');
+ else
+ return;
+ digit = digits[ch];
+
+ for (iy=0; iy < 5; iy++) {
+ for (ix=0; ix < 3; ix++) {
+ if (digit & 0x8000) {
+ if (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24)) {
+/* TODO */ RGB24_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF);
+ }
+ }
+ digit = digit << 1;
+ }
+ }
+}
+
+/*
+ * usbvideo_OverlayString()
+ *
+ * History:
+ * 01-Feb-2000 Created.
+ */
+static void usbvideo_OverlayString(struct uvd *uvd, struct usbvideo_frame *frame,
+ int x, int y, const char *str)
+{
+ while (*str) {
+ usbvideo_OverlayChar(uvd, frame, x, y, *str);
+ str++;
+ x += 4; /* 3 pixels character + 1 space */
+ }
+}
+
+/*
+ * usbvideo_OverlayStats()
+ *
+ * Overlays important debugging information.
+ *
+ * History:
+ * 01-Feb-2000 Created.
+ */
+static void usbvideo_OverlayStats(struct uvd *uvd, struct usbvideo_frame *frame)
+{
+ const int y_diff = 8;
+ char tmp[16];
+ int x = 10, y=10;
+ long i, j, barLength;
+ const int qi_x1 = 60, qi_y1 = 10;
+ const int qi_x2 = VIDEOSIZE_X(frame->request) - 10, qi_h = 10;
+
+ /* Call the user callback, see if we may proceed after that */
+ if (VALID_CALLBACK(uvd, overlayHook)) {
+ if (GET_CALLBACK(uvd, overlayHook)(uvd, frame) < 0)
+ return;
+ }
+
+ /*
+ * We draw a (mostly) hollow rectangle with qi_xxx coordinates.
+ * Left edge symbolizes the queue index 0; right edge symbolizes
+ * the full capacity of the queue.
+ */
+ barLength = qi_x2 - qi_x1 - 2;
+ if ((barLength > 10) && (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24))) {
+/* TODO */ long u_lo, u_hi, q_used;
+ long m_ri, m_wi, m_lo, m_hi;
+
+ /*
+ * Determine fill zones (used areas of the queue):
+ * 0 xxxxxxx u_lo ...... uvd->dp.ri xxxxxxxx u_hi ..... uvd->dp.length
+ *
+ * if u_lo < 0 then there is no first filler.
+ */
+
+ q_used = RingQueue_GetLength(&uvd->dp);
+ if ((uvd->dp.ri + q_used) >= uvd->dp.length) {
+ u_hi = uvd->dp.length;
+ u_lo = (q_used + uvd->dp.ri) & (uvd->dp.length-1);
+ } else {
+ u_hi = (q_used + uvd->dp.ri);
+ u_lo = -1;
+ }
+
+ /* Convert byte indices into screen units */
+ m_ri = qi_x1 + ((barLength * uvd->dp.ri) / uvd->dp.length);
+ m_wi = qi_x1 + ((barLength * uvd->dp.wi) / uvd->dp.length);
+ m_lo = (u_lo > 0) ? (qi_x1 + ((barLength * u_lo) / uvd->dp.length)) : -1;
+ m_hi = qi_x1 + ((barLength * u_hi) / uvd->dp.length);
+
+ for (j=qi_y1; j < (qi_y1 + qi_h); j++) {
+ for (i=qi_x1; i < qi_x2; i++) {
+ /* Draw border lines */
+ if ((j == qi_y1) || (j == (qi_y1 + qi_h - 1)) ||
+ (i == qi_x1) || (i == (qi_x2 - 1))) {
+ RGB24_PUTPIXEL(frame, i, j, 0xFF, 0xFF, 0xFF);
+ continue;
+ }
+ /* For all other points the Y coordinate does not matter */
+ if ((i >= m_ri) && (i <= (m_ri + 3))) {
+ RGB24_PUTPIXEL(frame, i, j, 0x00, 0xFF, 0x00);
+ } else if ((i >= m_wi) && (i <= (m_wi + 3))) {
+ RGB24_PUTPIXEL(frame, i, j, 0xFF, 0x00, 0x00);
+ } else if ((i < m_lo) || ((i > m_ri) && (i < m_hi)))
+ RGB24_PUTPIXEL(frame, i, j, 0x00, 0x00, 0xFF);
+ }
+ }
+ }
+
+ sprintf(tmp, "%8lx", uvd->stats.frame_num);
+ usbvideo_OverlayString(uvd, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", uvd->stats.urb_count);
+ usbvideo_OverlayString(uvd, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", uvd->stats.urb_length);
+ usbvideo_OverlayString(uvd, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", uvd->stats.data_count);
+ usbvideo_OverlayString(uvd, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", uvd->stats.header_count);
+ usbvideo_OverlayString(uvd, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", uvd->stats.iso_skip_count);
+ usbvideo_OverlayString(uvd, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8lx", uvd->stats.iso_err_count);
+ usbvideo_OverlayString(uvd, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8x", uvd->vpic.colour);
+ usbvideo_OverlayString(uvd, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8x", uvd->vpic.hue);
+ usbvideo_OverlayString(uvd, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8x", uvd->vpic.brightness >> 8);
+ usbvideo_OverlayString(uvd, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8x", uvd->vpic.contrast >> 12);
+ usbvideo_OverlayString(uvd, frame, x, y, tmp);
+ y += y_diff;
+
+ sprintf(tmp, "%8d", uvd->vpic.whiteness >> 8);
+ usbvideo_OverlayString(uvd, frame, x, y, tmp);
+ y += y_diff;
+}
+
+/*
+ * usbvideo_ReportStatistics()
+ *
+ * This procedure prints packet and transfer statistics.
+ *
+ * History:
+ * 14-Jan-2000 Corrected default multiplier.
+ */
+static void usbvideo_ReportStatistics(const struct uvd *uvd)
+{
+ if ((uvd != NULL) && (uvd->stats.urb_count > 0)) {
+ unsigned long allPackets, badPackets, goodPackets, percent;
+ allPackets = uvd->stats.urb_count * CAMERA_URB_FRAMES;
+ badPackets = uvd->stats.iso_skip_count + uvd->stats.iso_err_count;
+ goodPackets = allPackets - badPackets;
+ /* Calculate percentage wisely, remember integer limits */
+ assert(allPackets != 0);
+ if (goodPackets < (((unsigned long)-1)/100))
+ percent = (100 * goodPackets) / allPackets;
+ else
+ percent = goodPackets / (allPackets / 100);
+ info("Packet Statistics: Total=%lu. Empty=%lu. Usage=%lu%%",
+ allPackets, badPackets, percent);
+ if (uvd->iso_packet_len > 0) {
+ unsigned long allBytes, xferBytes;
+ char multiplier = ' ';
+ allBytes = allPackets * uvd->iso_packet_len;
+ xferBytes = uvd->stats.data_count;
+ assert(allBytes != 0);
+ if (xferBytes < (((unsigned long)-1)/100))
+ percent = (100 * xferBytes) / allBytes;
+ else
+ percent = xferBytes / (allBytes / 100);
+ /* Scale xferBytes for easy reading */
+ if (xferBytes > 10*1024) {
+ xferBytes /= 1024;
+ multiplier = 'K';
+ if (xferBytes > 10*1024) {
+ xferBytes /= 1024;
+ multiplier = 'M';
+ if (xferBytes > 10*1024) {
+ xferBytes /= 1024;
+ multiplier = 'G';
+ if (xferBytes > 10*1024) {
+ xferBytes /= 1024;
+ multiplier = 'T';
+ }
+ }
+ }
+ }
+ info("Transfer Statistics: Transferred=%lu%cB Usage=%lu%%",
+ xferBytes, multiplier, percent);
+ }
+ }
+}
+
+/*
+ * usbvideo_TestPattern()
+ *
+ * Procedure forms a test pattern (yellow grid on blue background).
+ *
+ * Parameters:
+ * fullframe: if TRUE then entire frame is filled, otherwise the procedure
+ * continues from the current scanline.
+ * pmode 0: fill the frame with solid blue color (like on VCR or TV)
+ * 1: Draw a colored grid
+ *
+ * History:
+ * 01-Feb-2000 Created.
+ */
+void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode)
+{
+ struct usbvideo_frame *frame;
+ int num_cell = 0;
+ int scan_length = 0;
+ static int num_pass = 0;
+
+ if (uvd == NULL) {
+ err("%s: uvd == NULL", __FUNCTION__);
+ return;
+ }
+ if ((uvd->curframe < 0) || (uvd->curframe >= USBVIDEO_NUMFRAMES)) {
+ err("%s: uvd->curframe=%d.", __FUNCTION__, uvd->curframe);
+ return;
+ }
+
+ /* Grab the current frame */
+ frame = &uvd->frame[uvd->curframe];
+
+ /* Optionally start at the beginning */
+ if (fullframe) {
+ frame->curline = 0;
+ frame->seqRead_Length = 0;
+ }
+#if 0
+ { /* For debugging purposes only */
+ char tmp[20];
+ usbvideo_VideosizeToString(tmp, sizeof(tmp), frame->request);
+ info("testpattern: frame=%s", tmp);
+ }
+#endif
+ /* Form every scan line */
+ for (; frame->curline < VIDEOSIZE_Y(frame->request); frame->curline++) {
+ int i;
+ unsigned char *f = frame->data +
+ (VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL * frame->curline);
+ for (i=0; i < VIDEOSIZE_X(frame->request); i++) {
+ unsigned char cb=0x80;
+ unsigned char cg = 0;
+ unsigned char cr = 0;
+
+ if (pmode == 1) {
+ if (frame->curline % 32 == 0)
+ cb = 0, cg = cr = 0xFF;
+ else if (i % 32 == 0) {
+ if (frame->curline % 32 == 1)
+ num_cell++;
+ cb = 0, cg = cr = 0xFF;
+ } else {
+ cb = ((num_cell*7) + num_pass) & 0xFF;
+ cg = ((num_cell*5) + num_pass*2) & 0xFF;
+ cr = ((num_cell*3) + num_pass*3) & 0xFF;
+ }
+ } else {
+ /* Just the blue screen */
+ }
+
+ *f++ = cb;
+ *f++ = cg;
+ *f++ = cr;
+ scan_length += 3;
+ }
+ }
+
+ frame->frameState = FrameState_Done;
+ frame->seqRead_Length += scan_length;
+ ++num_pass;
+
+ /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */
+ usbvideo_OverlayStats(uvd, frame);
+}
+
+EXPORT_SYMBOL(usbvideo_TestPattern);
+
+
+#ifdef DEBUG
+/*
+ * usbvideo_HexDump()
+ *
+ * A debugging tool. Prints hex dumps.
+ *
+ * History:
+ * 29-Jul-2000 Added printing of offsets.
+ */
+void usbvideo_HexDump(const unsigned char *data, int len)
+{
+ const int bytes_per_line = 32;
+ char tmp[128]; /* 32*3 + 5 */
+ int i, k;
+
+ for (i=k=0; len > 0; i++, len--) {
+ if (i > 0 && ((i % bytes_per_line) == 0)) {
+ printk("%s\n", tmp);
+ k=0;
+ }
+ if ((i % bytes_per_line) == 0)
+ k += sprintf(&tmp[k], "%04x: ", i);
+ k += sprintf(&tmp[k], "%02x ", data[i]);
+ }
+ if (k > 0)
+ printk("%s\n", tmp);
+}
+
+EXPORT_SYMBOL(usbvideo_HexDump);
+
+#endif
+
+/* ******************************************************************** */
+
+/* XXX: this piece of crap really wants some error handling.. */
+static void usbvideo_ClientIncModCount(struct uvd *uvd)
+{
+ if (uvd == NULL) {
+ err("%s: uvd == NULL", __FUNCTION__);
+ return;
+ }
+ if (uvd->handle == NULL) {
+ err("%s: uvd->handle == NULL", __FUNCTION__);
+ return;
+ }
+ if (uvd->handle->md_module == NULL) {
+ err("%s: uvd->handle->md_module == NULL", __FUNCTION__);
+ return;
+ }
+ if (!try_module_get(uvd->handle->md_module)) {
+ err("%s: try_module_get() == 0", __FUNCTION__);
+ return;
+ }
+}
+
+static void usbvideo_ClientDecModCount(struct uvd *uvd)
+{
+ if (uvd == NULL) {
+ err("%s: uvd == NULL", __FUNCTION__);
+ return;
+ }
+ if (uvd->handle == NULL) {
+ err("%s: uvd->handle == NULL", __FUNCTION__);
+ return;
+ }
+ if (uvd->handle->md_module == NULL) {
+ err("%s: uvd->handle->md_module == NULL", __FUNCTION__);
+ return;
+ }
+ module_put(uvd->handle->md_module);
+}
+
+int usbvideo_register(
+ struct usbvideo **pCams,
+ const int num_cams,
+ const int num_extra,
+ const char *driverName,
+ const struct usbvideo_cb *cbTbl,
+ struct module *md,
+ const struct usb_device_id *id_table)
+{
+ struct usbvideo *cams;
+ int i, base_size, result;
+
+ /* Check parameters for sanity */
+ if ((num_cams <= 0) || (pCams == NULL) || (cbTbl == NULL)) {
+ err("%s: Illegal call", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ /* Check registration callback - must be set! */
+ if (cbTbl->probe == NULL) {
+ err("%s: probe() is required!", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ base_size = num_cams * sizeof(struct uvd) + sizeof(struct usbvideo);
+ cams = (struct usbvideo *) kzalloc(base_size, GFP_KERNEL);
+ if (cams == NULL) {
+ err("Failed to allocate %d. bytes for usbvideo struct", base_size);
+ return -ENOMEM;
+ }
+ dbg("%s: Allocated $%p (%d. bytes) for %d. cameras",
+ __FUNCTION__, cams, base_size, num_cams);
+
+ /* Copy callbacks, apply defaults for those that are not set */
+ memmove(&cams->cb, cbTbl, sizeof(cams->cb));
+ if (cams->cb.getFrame == NULL)
+ cams->cb.getFrame = usbvideo_GetFrame;
+ if (cams->cb.disconnect == NULL)
+ cams->cb.disconnect = usbvideo_Disconnect;
+ if (cams->cb.startDataPump == NULL)
+ cams->cb.startDataPump = usbvideo_StartDataPump;
+ if (cams->cb.stopDataPump == NULL)
+ cams->cb.stopDataPump = usbvideo_StopDataPump;
+
+ cams->num_cameras = num_cams;
+ cams->cam = (struct uvd *) &cams[1];
+ cams->md_module = md;
+ if (cams->md_module == NULL)
+ warn("%s: module == NULL!", __FUNCTION__);
+ mutex_init(&cams->lock); /* to 1 == available */
+
+ for (i = 0; i < num_cams; i++) {
+ struct uvd *up = &cams->cam[i];
+
+ up->handle = cams;
+
+ /* Allocate user_data separately because of kmalloc's limits */
+ if (num_extra > 0) {
+ up->user_size = num_cams * num_extra;
+ up->user_data = kmalloc(up->user_size, GFP_KERNEL);
+ if (up->user_data == NULL) {
+ err("%s: Failed to allocate user_data (%d. bytes)",
+ __FUNCTION__, up->user_size);
+ while (i) {
+ up = &cams->cam[--i];
+ kfree(up->user_data);
+ }
+ kfree(cams);
+ return -ENOMEM;
+ }
+ dbg("%s: Allocated cams[%d].user_data=$%p (%d. bytes)",
+ __FUNCTION__, i, up->user_data, up->user_size);
+ }
+ }
+
+ /*
+ * Register ourselves with USB stack.
+ */
+ strcpy(cams->drvName, (driverName != NULL) ? driverName : "Unknown");
+ cams->usbdrv.name = cams->drvName;
+ cams->usbdrv.probe = cams->cb.probe;
+ cams->usbdrv.disconnect = cams->cb.disconnect;
+ cams->usbdrv.id_table = id_table;
+
+ /*
+ * Update global handle to usbvideo. This is very important
+ * because probe() can be called before usb_register() returns.
+ * If the handle is not yet updated then the probe() will fail.
+ */
+ *pCams = cams;
+ result = usb_register(&cams->usbdrv);
+ if (result) {
+ for (i = 0; i < num_cams; i++) {
+ struct uvd *up = &cams->cam[i];
+ kfree(up->user_data);
+ }
+ kfree(cams);
+ }
+
+ return result;
+}
+
+EXPORT_SYMBOL(usbvideo_register);
+
+/*
+ * usbvideo_Deregister()
+ *
+ * Procedure frees all usbvideo and user data structures. Be warned that
+ * if you had some dynamically allocated components in ->user field then
+ * you should free them before calling here.
+ */
+void usbvideo_Deregister(struct usbvideo **pCams)
+{
+ struct usbvideo *cams;
+ int i;
+
+ if (pCams == NULL) {
+ err("%s: pCams == NULL", __FUNCTION__);
+ return;
+ }
+ cams = *pCams;
+ if (cams == NULL) {
+ err("%s: cams == NULL", __FUNCTION__);
+ return;
+ }
+
+ dbg("%s: Deregistering %s driver.", __FUNCTION__, cams->drvName);
+ usb_deregister(&cams->usbdrv);
+
+ dbg("%s: Deallocating cams=$%p (%d. cameras)", __FUNCTION__, cams, cams->num_cameras);
+ for (i=0; i < cams->num_cameras; i++) {
+ struct uvd *up = &cams->cam[i];
+ int warning = 0;
+
+ if (up->user_data != NULL) {
+ if (up->user_size <= 0)
+ ++warning;
+ } else {
+ if (up->user_size > 0)
+ ++warning;
+ }
+ if (warning) {
+ err("%s: Warning: user_data=$%p user_size=%d.",
+ __FUNCTION__, up->user_data, up->user_size);
+ } else {
+ dbg("%s: Freeing %d. $%p->user_data=$%p",
+ __FUNCTION__, i, up, up->user_data);
+ kfree(up->user_data);
+ }
+ }
+ /* Whole array was allocated in one chunk */
+ dbg("%s: Freed %d uvd structures",
+ __FUNCTION__, cams->num_cameras);
+ kfree(cams);
+ *pCams = NULL;
+}
+
+EXPORT_SYMBOL(usbvideo_Deregister);
+
+/*
+ * usbvideo_Disconnect()
+ *
+ * This procedure stops all driver activity. Deallocation of
+ * the interface-private structure (pointed by 'ptr') is done now
+ * (if we don't have any open files) or later, when those files
+ * are closed. After that driver should be removable.
+ *
+ * This code handles surprise removal. The uvd->user is a counter which
+ * increments on open() and decrements on close(). If we see here that
+ * this counter is not 0 then we have a client who still has us opened.
+ * We set uvd->remove_pending flag as early as possible, and after that
+ * all access to the camera will gracefully fail. These failures should
+ * prompt client to (eventually) close the video device, and then - in
+ * usbvideo_v4l_close() - we decrement uvd->uvd_used and usage counter.
+ *
+ * History:
+ * 22-Jan-2000 Added polling of MOD_IN_USE to delay removal until all users gone.
+ * 27-Jan-2000 Reworked to allow pending disconnects; see xxx_close()
+ * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT).
+ * 19-Oct-2000 Moved to usbvideo module.
+ */
+static void usbvideo_Disconnect(struct usb_interface *intf)
+{
+ struct uvd *uvd = usb_get_intfdata (intf);
+ int i;
+
+ if (uvd == NULL) {
+ err("%s($%p): Illegal call.", __FUNCTION__, intf);
+ return;
+ }
+
+ usb_set_intfdata (intf, NULL);
+
+ usbvideo_ClientIncModCount(uvd);
+ if (uvd->debug > 0)
+ info("%s(%p.)", __FUNCTION__, intf);
+
+ mutex_lock(&uvd->lock);
+ uvd->remove_pending = 1; /* Now all ISO data will be ignored */
+
+ /* At this time we ask to cancel outstanding URBs */
+ GET_CALLBACK(uvd, stopDataPump)(uvd);
+
+ for (i=0; i < USBVIDEO_NUMSBUF; i++)
+ usb_free_urb(uvd->sbuf[i].urb);
+
+ usb_put_dev(uvd->dev);
+ uvd->dev = NULL; /* USB device is no more */
+
+ video_unregister_device(&uvd->vdev);
+ if (uvd->debug > 0)
+ info("%s: Video unregistered.", __FUNCTION__);
+
+ if (uvd->user)
+ info("%s: In use, disconnect pending.", __FUNCTION__);
+ else
+ usbvideo_CameraRelease(uvd);
+ mutex_unlock(&uvd->lock);
+ info("USB camera disconnected.");
+
+ usbvideo_ClientDecModCount(uvd);
+}
+
+/*
+ * usbvideo_CameraRelease()
+ *
+ * This code does final release of uvd. This happens
+ * after the device is disconnected -and- all clients
+ * closed their files.
+ *
+ * History:
+ * 27-Jan-2000 Created.
+ */
+static void usbvideo_CameraRelease(struct uvd *uvd)
+{
+ if (uvd == NULL) {
+ err("%s: Illegal call", __FUNCTION__);
+ return;
+ }
+
+ RingQueue_Free(&uvd->dp);
+ if (VALID_CALLBACK(uvd, userFree))
+ GET_CALLBACK(uvd, userFree)(uvd);
+ uvd->uvd_used = 0; /* This is atomic, no need to take mutex */
+}
+
+/*
+ * usbvideo_find_struct()
+ *
+ * This code searches the array of preallocated (static) structures
+ * and returns index of the first one that isn't in use. Returns -1
+ * if there are no free structures.
+ *
+ * History:
+ * 27-Jan-2000 Created.
+ */
+static int usbvideo_find_struct(struct usbvideo *cams)
+{
+ int u, rv = -1;
+
+ if (cams == NULL) {
+ err("No usbvideo handle?");
+ return -1;
+ }
+ mutex_lock(&cams->lock);
+ for (u = 0; u < cams->num_cameras; u++) {
+ struct uvd *uvd = &cams->cam[u];
+ if (!uvd->uvd_used) /* This one is free */
+ {
+ uvd->uvd_used = 1; /* In use now */
+ mutex_init(&uvd->lock); /* to 1 == available */
+ uvd->dev = NULL;
+ rv = u;
+ break;
+ }
+ }
+ mutex_unlock(&cams->lock);
+ return rv;
+}
+
+static struct file_operations usbvideo_fops = {
+ .owner = THIS_MODULE,
+ .open = usbvideo_v4l_open,
+ .release =usbvideo_v4l_close,
+ .read = usbvideo_v4l_read,
+ .mmap = usbvideo_v4l_mmap,
+ .ioctl = usbvideo_v4l_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+static const struct video_device usbvideo_template = {
+ .owner = THIS_MODULE,
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_CPIA,
+ .fops = &usbvideo_fops,
+};
+
+struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams)
+{
+ int i, devnum;
+ struct uvd *uvd = NULL;
+
+ if (cams == NULL) {
+ err("No usbvideo handle?");
+ return NULL;
+ }
+
+ devnum = usbvideo_find_struct(cams);
+ if (devnum == -1) {
+ err("IBM USB camera driver: Too many devices!");
+ return NULL;
+ }
+ uvd = &cams->cam[devnum];
+ dbg("Device entry #%d. at $%p", devnum, uvd);
+
+ /* Not relying upon caller we increase module counter ourselves */
+ usbvideo_ClientIncModCount(uvd);
+
+ mutex_lock(&uvd->lock);
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
+ if (uvd->sbuf[i].urb == NULL) {
+ err("usb_alloc_urb(%d.) failed.", FRAMES_PER_DESC);
+ uvd->uvd_used = 0;
+ uvd = NULL;
+ goto allocate_done;
+ }
+ }
+ uvd->user=0;
+ uvd->remove_pending = 0;
+ uvd->last_error = 0;
+ RingQueue_Initialize(&uvd->dp);
+
+ /* Initialize video device structure */
+ uvd->vdev = usbvideo_template;
+ sprintf(uvd->vdev.name, "%.20s USB Camera", cams->drvName);
+ /*
+ * The client is free to overwrite those because we
+ * return control to the client's probe function right now.
+ */
+allocate_done:
+ mutex_unlock(&uvd->lock);
+ usbvideo_ClientDecModCount(uvd);
+ return uvd;
+}
+
+EXPORT_SYMBOL(usbvideo_AllocateDevice);
+
+int usbvideo_RegisterVideoDevice(struct uvd *uvd)
+{
+ char tmp1[20], tmp2[20]; /* Buffers for printing */
+
+ if (uvd == NULL) {
+ err("%s: Illegal call.", __FUNCTION__);
+ return -EINVAL;
+ }
+ if (uvd->video_endp == 0) {
+ info("%s: No video endpoint specified; data pump disabled.", __FUNCTION__);
+ }
+ if (uvd->paletteBits == 0) {
+ err("%s: No palettes specified!", __FUNCTION__);
+ return -EINVAL;
+ }
+ if (uvd->defaultPalette == 0) {
+ info("%s: No default palette!", __FUNCTION__);
+ }
+
+ uvd->max_frame_size = VIDEOSIZE_X(uvd->canvas) *
+ VIDEOSIZE_Y(uvd->canvas) * V4L_BYTES_PER_PIXEL;
+ usbvideo_VideosizeToString(tmp1, sizeof(tmp1), uvd->videosize);
+ usbvideo_VideosizeToString(tmp2, sizeof(tmp2), uvd->canvas);
+
+ if (uvd->debug > 0) {
+ info("%s: iface=%d. endpoint=$%02x paletteBits=$%08lx",
+ __FUNCTION__, uvd->iface, uvd->video_endp, uvd->paletteBits);
+ }
+ if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
+ err("%s: video_register_device failed", __FUNCTION__);
+ return -EPIPE;
+ }
+ if (uvd->debug > 1) {
+ info("%s: video_register_device() successful", __FUNCTION__);
+ }
+ if (uvd->dev == NULL) {
+ err("%s: uvd->dev == NULL", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ info("%s on /dev/video%d: canvas=%s videosize=%s",
+ (uvd->handle != NULL) ? uvd->handle->drvName : "???",
+ uvd->vdev.minor, tmp2, tmp1);
+
+ usb_get_dev(uvd->dev);
+ return 0;
+}
+
+EXPORT_SYMBOL(usbvideo_RegisterVideoDevice);
+
+/* ******************************************************************** */
+
+static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct uvd *uvd = file->private_data;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end-vma->vm_start;
+ unsigned long page, pos;
+
+ if (!CAMERA_IS_OPERATIONAL(uvd))
+ return -EFAULT;
+
+ if (size > (((USBVIDEO_NUMFRAMES * uvd->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+ return -EINVAL;
+
+ pos = (unsigned long) uvd->fbuf;
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * usbvideo_v4l_open()
+ *
+ * This is part of Video 4 Linux API. The driver can be opened by one
+ * client only (checks internal counter 'uvdser'). The procedure
+ * then allocates buffers needed for video processing.
+ *
+ * History:
+ * 22-Jan-2000 Rewrote, moved scratch buffer allocation here. Now the
+ * camera is also initialized here (once per connect), at
+ * expense of V4L client (it waits on open() call).
+ * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers.
+ * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT).
+ */
+static int usbvideo_v4l_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct uvd *uvd = (struct uvd *) dev;
+ const int sb_size = FRAMES_PER_DESC * uvd->iso_packet_len;
+ int i, errCode = 0;
+
+ if (uvd->debug > 1)
+ info("%s($%p)", __FUNCTION__, dev);
+
+ usbvideo_ClientIncModCount(uvd);
+ mutex_lock(&uvd->lock);
+
+ if (uvd->user) {
+ err("%s: Someone tried to open an already opened device!", __FUNCTION__);
+ errCode = -EBUSY;
+ } else {
+ /* Clear statistics */
+ memset(&uvd->stats, 0, sizeof(uvd->stats));
+
+ /* Clean pointers so we know if we allocated something */
+ for (i=0; i < USBVIDEO_NUMSBUF; i++)
+ uvd->sbuf[i].data = NULL;
+
+ /* Allocate memory for the frame buffers */
+ uvd->fbuf_size = USBVIDEO_NUMFRAMES * uvd->max_frame_size;
+ uvd->fbuf = usbvideo_rvmalloc(uvd->fbuf_size);
+ RingQueue_Allocate(&uvd->dp, RING_QUEUE_SIZE);
+ if ((uvd->fbuf == NULL) ||
+ (!RingQueue_IsAllocated(&uvd->dp))) {
+ err("%s: Failed to allocate fbuf or dp", __FUNCTION__);
+ errCode = -ENOMEM;
+ } else {
+ /* Allocate all buffers */
+ for (i=0; i < USBVIDEO_NUMFRAMES; i++) {
+ uvd->frame[i].frameState = FrameState_Unused;
+ uvd->frame[i].data = uvd->fbuf + i*(uvd->max_frame_size);
+ /*
+ * Set default sizes in case IOCTL (VIDIOCMCAPTURE)
+ * is not used (using read() instead).
+ */
+ uvd->frame[i].canvas = uvd->canvas;
+ uvd->frame[i].seqRead_Index = 0;
+ }
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ uvd->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL);
+ if (uvd->sbuf[i].data == NULL) {
+ errCode = -ENOMEM;
+ break;
+ }
+ }
+ }
+ if (errCode != 0) {
+ /* Have to free all that memory */
+ if (uvd->fbuf != NULL) {
+ usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size);
+ uvd->fbuf = NULL;
+ }
+ RingQueue_Free(&uvd->dp);
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ kfree(uvd->sbuf[i].data);
+ uvd->sbuf[i].data = NULL;
+ }
+ }
+ }
+
+ /* If so far no errors then we shall start the camera */
+ if (errCode == 0) {
+ /* Start data pump if we have valid endpoint */
+ if (uvd->video_endp != 0)
+ errCode = GET_CALLBACK(uvd, startDataPump)(uvd);
+ if (errCode == 0) {
+ if (VALID_CALLBACK(uvd, setupOnOpen)) {
+ if (uvd->debug > 1)
+ info("%s: setupOnOpen callback", __FUNCTION__);
+ errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd);
+ if (errCode < 0) {
+ err("%s: setupOnOpen callback failed (%d.).",
+ __FUNCTION__, errCode);
+ } else if (uvd->debug > 1) {
+ info("%s: setupOnOpen callback successful", __FUNCTION__);
+ }
+ }
+ if (errCode == 0) {
+ uvd->settingsAdjusted = 0;
+ if (uvd->debug > 1)
+ info("%s: Open succeeded.", __FUNCTION__);
+ uvd->user++;
+ file->private_data = uvd;
+ }
+ }
+ }
+ mutex_unlock(&uvd->lock);
+ if (errCode != 0)
+ usbvideo_ClientDecModCount(uvd);
+ if (uvd->debug > 0)
+ info("%s: Returning %d.", __FUNCTION__, errCode);
+ return errCode;
+}
+
+/*
+ * usbvideo_v4l_close()
+ *
+ * This is part of Video 4 Linux API. The procedure
+ * stops streaming and deallocates all buffers that were earlier
+ * allocated in usbvideo_v4l_open().
+ *
+ * History:
+ * 22-Jan-2000 Moved scratch buffer deallocation here.
+ * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers.
+ * 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep.
+ */
+static int usbvideo_v4l_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = file->private_data;
+ struct uvd *uvd = (struct uvd *) dev;
+ int i;
+
+ if (uvd->debug > 1)
+ info("%s($%p)", __FUNCTION__, dev);
+
+ mutex_lock(&uvd->lock);
+ GET_CALLBACK(uvd, stopDataPump)(uvd);
+ usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size);
+ uvd->fbuf = NULL;
+ RingQueue_Free(&uvd->dp);
+
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ kfree(uvd->sbuf[i].data);
+ uvd->sbuf[i].data = NULL;
+ }
+
+#if USBVIDEO_REPORT_STATS
+ usbvideo_ReportStatistics(uvd);
+#endif
+
+ uvd->user--;
+ if (uvd->remove_pending) {
+ if (uvd->debug > 0)
+ info("usbvideo_v4l_close: Final disconnect.");
+ usbvideo_CameraRelease(uvd);
+ }
+ mutex_unlock(&uvd->lock);
+ usbvideo_ClientDecModCount(uvd);
+
+ if (uvd->debug > 1)
+ info("%s: Completed.", __FUNCTION__);
+ file->private_data = NULL;
+ return 0;
+}
+
+/*
+ * usbvideo_v4l_ioctl()
+ *
+ * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
+ *
+ * History:
+ * 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings.
+ */
+static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct uvd *uvd = file->private_data;
+
+ if (!CAMERA_IS_OPERATIONAL(uvd))
+ return -EIO;
+
+ switch (cmd) {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *b = arg;
+ *b = uvd->vcap;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *v = arg;
+ *v = uvd->vchan;
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ struct video_channel *v = arg;
+ if (v->channel != 0)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture *pic = arg;
+ *pic = uvd->vpic;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture *pic = arg;
+ /*
+ * Use temporary 'video_picture' structure to preserve our
+ * own settings (such as color depth, palette) that we
+ * aren't allowing everyone (V4L client) to change.
+ */
+ uvd->vpic.brightness = pic->brightness;
+ uvd->vpic.hue = pic->hue;
+ uvd->vpic.colour = pic->colour;
+ uvd->vpic.contrast = pic->contrast;
+ uvd->settingsAdjusted = 0; /* Will force new settings */
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window *vw = arg;
+
+ if(VALID_CALLBACK(uvd, setVideoMode)) {
+ return GET_CALLBACK(uvd, setVideoMode)(uvd, vw);
+ }
+
+ if (vw->flags)
+ return -EINVAL;
+ if (vw->clipcount)
+ return -EINVAL;
+ if (vw->width != VIDEOSIZE_X(uvd->canvas))
+ return -EINVAL;
+ if (vw->height != VIDEOSIZE_Y(uvd->canvas))
+ return -EINVAL;
+
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window *vw = arg;
+
+ vw->x = 0;
+ vw->y = 0;
+ vw->width = VIDEOSIZE_X(uvd->videosize);
+ vw->height = VIDEOSIZE_Y(uvd->videosize);
+ vw->chromakey = 0;
+ if (VALID_CALLBACK(uvd, getFPS))
+ vw->flags = GET_CALLBACK(uvd, getFPS)(uvd);
+ else
+ vw->flags = 10; /* FIXME: do better! */
+ return 0;
+ }
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf *vm = arg;
+ int i;
+
+ memset(vm, 0, sizeof(*vm));
+ vm->size = uvd->max_frame_size * USBVIDEO_NUMFRAMES;
+ vm->frames = USBVIDEO_NUMFRAMES;
+ for(i = 0; i < USBVIDEO_NUMFRAMES; i++)
+ vm->offsets[i] = i * uvd->max_frame_size;
+
+ return 0;
+ }
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap *vm = arg;
+
+ if (uvd->debug >= 1) {
+ info("VIDIOCMCAPTURE: frame=%d. size=%dx%d, format=%d.",
+ vm->frame, vm->width, vm->height, vm->format);
+ }
+ /*
+ * Check if the requested size is supported. If the requestor
+ * requests too big a frame then we may be tricked into accessing
+ * outside of own preallocated frame buffer (in uvd->frame).
+ * This will cause oops or a security hole. Theoretically, we
+ * could only clamp the size down to acceptable bounds, but then
+ * we'd need to figure out how to insert our smaller buffer into
+ * larger caller's buffer... this is not an easy question. So we
+ * here just flatly reject too large requests, assuming that the
+ * caller will resubmit with smaller size. Callers should know
+ * what size we support (returned by VIDIOCGCAP). However vidcat,
+ * for one, does not care and allows to ask for any size.
+ */
+ if ((vm->width > VIDEOSIZE_X(uvd->canvas)) ||
+ (vm->height > VIDEOSIZE_Y(uvd->canvas))) {
+ if (uvd->debug > 0) {
+ info("VIDIOCMCAPTURE: Size=%dx%d too large; "
+ "allowed only up to %ldx%ld", vm->width, vm->height,
+ VIDEOSIZE_X(uvd->canvas), VIDEOSIZE_Y(uvd->canvas));
+ }
+ return -EINVAL;
+ }
+ /* Check if the palette is supported */
+ if (((1L << vm->format) & uvd->paletteBits) == 0) {
+ if (uvd->debug > 0) {
+ info("VIDIOCMCAPTURE: format=%d. not supported"
+ " (paletteBits=$%08lx)",
+ vm->format, uvd->paletteBits);
+ }
+ return -EINVAL;
+ }
+ if ((vm->frame < 0) || (vm->frame >= USBVIDEO_NUMFRAMES)) {
+ err("VIDIOCMCAPTURE: vm.frame=%d. !E [0-%d]", vm->frame, USBVIDEO_NUMFRAMES-1);
+ return -EINVAL;
+ }
+ if (uvd->frame[vm->frame].frameState == FrameState_Grabbing) {
+ /* Not an error - can happen */
+ }
+ uvd->frame[vm->frame].request = VIDEOSIZE(vm->width, vm->height);
+ uvd->frame[vm->frame].palette = vm->format;
+
+ /* Mark it as ready */
+ uvd->frame[vm->frame].frameState = FrameState_Ready;
+
+ return usbvideo_NewFrame(uvd, vm->frame);
+ }
+ case VIDIOCSYNC:
+ {
+ int *frameNum = arg;
+ int ret;
+
+ if (*frameNum < 0 || *frameNum >= USBVIDEO_NUMFRAMES)
+ return -EINVAL;
+
+ if (uvd->debug >= 1)
+ info("VIDIOCSYNC: syncing to frame %d.", *frameNum);
+ if (uvd->flags & FLAGS_NO_DECODING)
+ ret = usbvideo_GetFrame(uvd, *frameNum);
+ else if (VALID_CALLBACK(uvd, getFrame)) {
+ ret = GET_CALLBACK(uvd, getFrame)(uvd, *frameNum);
+ if ((ret < 0) && (uvd->debug >= 1)) {
+ err("VIDIOCSYNC: getFrame() returned %d.", ret);
+ }
+ } else {
+ err("VIDIOCSYNC: getFrame is not set");
+ ret = -EFAULT;
+ }
+
+ /*
+ * The frame is in FrameState_Done_Hold state. Release it
+ * right now because its data is already mapped into
+ * the user space and it's up to the application to
+ * make use of it until it asks for another frame.
+ */
+ uvd->frame[*frameNum].frameState = FrameState_Unused;
+ return ret;
+ }
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer *vb = arg;
+
+ memset(vb, 0, sizeof(*vb));
+ return 0;
+ }
+ case VIDIOCKEY:
+ return 0;
+
+ case VIDIOCCAPTURE:
+ return -EINVAL;
+
+ case VIDIOCSFBUF:
+
+ case VIDIOCGTUNER:
+ case VIDIOCSTUNER:
+
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, usbvideo_v4l_do_ioctl);
+}
+
+/*
+ * usbvideo_v4l_read()
+ *
+ * This is mostly boring stuff. We simply ask for a frame and when it
+ * arrives copy all the video data from it into user space. There is
+ * no obvious need to override this method.
+ *
+ * History:
+ * 20-Oct-2000 Created.
+ * 01-Nov-2000 Added mutex (uvd->lock).
+ */
+static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct uvd *uvd = file->private_data;
+ int noblock = file->f_flags & O_NONBLOCK;
+ int frmx = -1, i;
+ struct usbvideo_frame *frame;
+
+ if (!CAMERA_IS_OPERATIONAL(uvd) || (buf == NULL))
+ return -EFAULT;
+
+ if (uvd->debug >= 1)
+ info("%s: %Zd. bytes, noblock=%d.", __FUNCTION__, count, noblock);
+
+ mutex_lock(&uvd->lock);
+
+ /* See if a frame is completed, then use it. */
+ for(i = 0; i < USBVIDEO_NUMFRAMES; i++) {
+ if ((uvd->frame[i].frameState == FrameState_Done) ||
+ (uvd->frame[i].frameState == FrameState_Done_Hold) ||
+ (uvd->frame[i].frameState == FrameState_Error)) {
+ frmx = i;
+ break;
+ }
+ }
+
+ /* FIXME: If we don't start a frame here then who ever does? */
+ if (noblock && (frmx == -1)) {
+ count = -EAGAIN;
+ goto read_done;
+ }
+
+ /*
+ * If no FrameState_Done, look for a FrameState_Grabbing state.
+ * See if a frame is in process (grabbing), then use it.
+ * We will need to wait until it becomes cooked, of course.
+ */
+ if (frmx == -1) {
+ for(i = 0; i < USBVIDEO_NUMFRAMES; i++) {
+ if (uvd->frame[i].frameState == FrameState_Grabbing) {
+ frmx = i;
+ break;
+ }
+ }
+ }
+
+ /*
+ * If no frame is active, start one. We don't care which one
+ * it will be, so #0 is as good as any.
+ * In read access mode we don't have convenience of VIDIOCMCAPTURE
+ * to specify the requested palette (video format) on per-frame
+ * basis. This means that we have to return data in -some- format
+ * and just hope that the client knows what to do with it.
+ * The default format is configured in uvd->defaultPalette field
+ * as one of VIDEO_PALETTE_xxx values. We stuff it into the new
+ * frame and initiate the frame filling process.
+ */
+ if (frmx == -1) {
+ if (uvd->defaultPalette == 0) {
+ err("%s: No default palette; don't know what to do!", __FUNCTION__);
+ count = -EFAULT;
+ goto read_done;
+ }
+ frmx = 0;
+ /*
+ * We have no per-frame control over video size.
+ * Therefore we only can use whatever size was
+ * specified as default.
+ */
+ uvd->frame[frmx].request = uvd->videosize;
+ uvd->frame[frmx].palette = uvd->defaultPalette;
+ uvd->frame[frmx].frameState = FrameState_Ready;
+ usbvideo_NewFrame(uvd, frmx);
+ /* Now frame 0 is supposed to start filling... */
+ }
+
+ /*
+ * Get a pointer to the active frame. It is either previously
+ * completed frame or frame in progress but not completed yet.
+ */
+ frame = &uvd->frame[frmx];
+
+ /*
+ * Sit back & wait until the frame gets filled and postprocessed.
+ * If we fail to get the picture [in time] then return the error.
+ * In this call we specify that we want the frame to be waited for,
+ * postprocessed and switched into FrameState_Done_Hold state. This
+ * state is used to hold the frame as "fully completed" between
+ * subsequent partial reads of the same frame.
+ */
+ if (frame->frameState != FrameState_Done_Hold) {
+ long rv = -EFAULT;
+ if (uvd->flags & FLAGS_NO_DECODING)
+ rv = usbvideo_GetFrame(uvd, frmx);
+ else if (VALID_CALLBACK(uvd, getFrame))
+ rv = GET_CALLBACK(uvd, getFrame)(uvd, frmx);
+ else
+ err("getFrame is not set");
+ if ((rv != 0) || (frame->frameState != FrameState_Done_Hold)) {
+ count = rv;
+ goto read_done;
+ }
+ }
+
+ /*
+ * Copy bytes to user space. We allow for partial reads, which
+ * means that the user application can request read less than
+ * the full frame size. It is up to the application to issue
+ * subsequent calls until entire frame is read.
+ *
+ * First things first, make sure we don't copy more than we
+ * have - even if the application wants more. That would be
+ * a big security embarassment!
+ */
+ if ((count + frame->seqRead_Index) > frame->seqRead_Length)
+ count = frame->seqRead_Length - frame->seqRead_Index;
+
+ /*
+ * Copy requested amount of data to user space. We start
+ * copying from the position where we last left it, which
+ * will be zero for a new frame (not read before).
+ */
+ if (copy_to_user(buf, frame->data + frame->seqRead_Index, count)) {
+ count = -EFAULT;
+ goto read_done;
+ }
+
+ /* Update last read position */
+ frame->seqRead_Index += count;
+ if (uvd->debug >= 1) {
+ err("%s: {copy} count used=%Zd, new seqRead_Index=%ld",
+ __FUNCTION__, count, frame->seqRead_Index);
+ }
+
+ /* Finally check if the frame is done with and "release" it */
+ if (frame->seqRead_Index >= frame->seqRead_Length) {
+ /* All data has been read */
+ frame->seqRead_Index = 0;
+
+ /* Mark it as available to be used again. */
+ uvd->frame[frmx].frameState = FrameState_Unused;
+ if (usbvideo_NewFrame(uvd, (frmx + 1) % USBVIDEO_NUMFRAMES)) {
+ err("%s: usbvideo_NewFrame failed.", __FUNCTION__);
+ }
+ }
+read_done:
+ mutex_unlock(&uvd->lock);
+ return count;
+}
+
+/*
+ * Make all of the blocks of data contiguous
+ */
+static int usbvideo_CompressIsochronous(struct uvd *uvd, struct urb *urb)
+{
+ char *cdata;
+ int i, totlen = 0;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int n = urb->iso_frame_desc[i].actual_length;
+ int st = urb->iso_frame_desc[i].status;
+
+ cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ /* Detect and ignore errored packets */
+ if (st < 0) {
+ if (uvd->debug >= 1)
+ err("Data error: packet=%d. len=%d. status=%d.", i, n, st);
+ uvd->stats.iso_err_count++;
+ continue;
+ }
+
+ /* Detect and ignore empty packets */
+ if (n <= 0) {
+ uvd->stats.iso_skip_count++;
+ continue;
+ }
+ totlen += n; /* Little local accounting */
+ RingQueue_Enqueue(&uvd->dp, cdata, n);
+ }
+ return totlen;
+}
+
+static void usbvideo_IsocIrq(struct urb *urb, struct pt_regs *regs)
+{
+ int i, ret, len;
+ struct uvd *uvd = urb->context;
+
+ /* We don't want to do anything if we are about to be removed! */
+ if (!CAMERA_IS_OPERATIONAL(uvd))
+ return;
+#if 0
+ if (urb->actual_length > 0) {
+ info("urb=$%p status=%d. errcount=%d. length=%d.",
+ urb, urb->status, urb->error_count, urb->actual_length);
+ } else {
+ static int c = 0;
+ if (c++ % 100 == 0)
+ info("No Isoc data");
+ }
+#endif
+
+ if (!uvd->streaming) {
+ if (uvd->debug >= 1)
+ info("Not streaming, but interrupt!");
+ return;
+ }
+
+ uvd->stats.urb_count++;
+ if (urb->actual_length <= 0)
+ goto urb_done_with;
+
+ /* Copy the data received into ring queue */
+ len = usbvideo_CompressIsochronous(uvd, urb);
+ uvd->stats.urb_length = len;
+ if (len <= 0)
+ goto urb_done_with;
+
+ /* Here we got some data */
+ uvd->stats.data_count += len;
+ RingQueue_WakeUpInterruptible(&uvd->dp);
+
+urb_done_with:
+ for (i = 0; i < FRAMES_PER_DESC; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+ urb->status = 0;
+ urb->dev = uvd->dev;
+ ret = usb_submit_urb (urb, GFP_KERNEL);
+ if(ret)
+ err("usb_submit_urb error (%d)", ret);
+ return;
+}
+
+/*
+ * usbvideo_StartDataPump()
+ *
+ * History:
+ * 27-Jan-2000 Used ibmcam->iface, ibmcam->ifaceAltActive instead
+ * of hardcoded values. Simplified by using for loop,
+ * allowed any number of URBs.
+ */
+static int usbvideo_StartDataPump(struct uvd *uvd)
+{
+ struct usb_device *dev = uvd->dev;
+ int i, errFlag;
+
+ if (uvd->debug > 1)
+ info("%s($%p)", __FUNCTION__, uvd);
+
+ if (!CAMERA_IS_OPERATIONAL(uvd)) {
+ err("%s: Camera is not operational", __FUNCTION__);
+ return -EFAULT;
+ }
+ uvd->curframe = -1;
+
+ /* Alternate interface 1 is is the biggest frame size */
+ i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive);
+ if (i < 0) {
+ err("%s: usb_set_interface error", __FUNCTION__);
+ uvd->last_error = i;
+ return -EBUSY;
+ }
+ if (VALID_CALLBACK(uvd, videoStart))
+ GET_CALLBACK(uvd, videoStart)(uvd);
+ else
+ err("%s: videoStart not set", __FUNCTION__);
+
+ /* We double buffer the Iso lists */
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ int j, k;
+ struct urb *urb = uvd->sbuf[i].urb;
+ urb->dev = dev;
+ urb->context = uvd;
+ urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp);
+ urb->interval = 1;
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = uvd->sbuf[i].data;
+ urb->complete = usbvideo_IsocIrq;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC;
+ for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length = uvd->iso_packet_len;
+ }
+ }
+
+ /* Submit all URBs */
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL);
+ if (errFlag)
+ err("%s: usb_submit_isoc(%d) ret %d", __FUNCTION__, i, errFlag);
+ }
+
+ uvd->streaming = 1;
+ if (uvd->debug > 1)
+ info("%s: streaming=1 video_endp=$%02x", __FUNCTION__, uvd->video_endp);
+ return 0;
+}
+
+/*
+ * usbvideo_StopDataPump()
+ *
+ * This procedure stops streaming and deallocates URBs. Then it
+ * activates zero-bandwidth alt. setting of the video interface.
+ *
+ * History:
+ * 22-Jan-2000 Corrected order of actions to work after surprise removal.
+ * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values.
+ */
+static void usbvideo_StopDataPump(struct uvd *uvd)
+{
+ int i, j;
+
+ if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL))
+ return;
+
+ if (uvd->debug > 1)
+ info("%s($%p)", __FUNCTION__, uvd);
+
+ /* Unschedule all of the iso td's */
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ usb_kill_urb(uvd->sbuf[i].urb);
+ }
+ if (uvd->debug > 1)
+ info("%s: streaming=0", __FUNCTION__);
+ uvd->streaming = 0;
+
+ if (!uvd->remove_pending) {
+ /* Invoke minidriver's magic to stop the camera */
+ if (VALID_CALLBACK(uvd, videoStop))
+ GET_CALLBACK(uvd, videoStop)(uvd);
+ else
+ err("%s: videoStop not set", __FUNCTION__);
+
+ /* Set packet size to 0 */
+ j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive);
+ if (j < 0) {
+ err("%s: usb_set_interface() error %d.", __FUNCTION__, j);
+ uvd->last_error = j;
+ }
+ }
+}
+
+/*
+ * usbvideo_NewFrame()
+ *
+ * History:
+ * 29-Mar-00 Added copying of previous frame into the current one.
+ * 6-Aug-00 Added model 3 video sizes, removed redundant width, height.
+ */
+static int usbvideo_NewFrame(struct uvd *uvd, int framenum)
+{
+ struct usbvideo_frame *frame;
+ int n;
+
+ if (uvd->debug > 1)
+ info("usbvideo_NewFrame($%p,%d.)", uvd, framenum);
+
+ /* If we're not grabbing a frame right now and the other frame is */
+ /* ready to be grabbed into, then use it instead */
+ if (uvd->curframe != -1)
+ return 0;
+
+ /* If necessary we adjust picture settings between frames */
+ if (!uvd->settingsAdjusted) {
+ if (VALID_CALLBACK(uvd, adjustPicture))
+ GET_CALLBACK(uvd, adjustPicture)(uvd);
+ uvd->settingsAdjusted = 1;
+ }
+
+ n = (framenum + 1) % USBVIDEO_NUMFRAMES;
+ if (uvd->frame[n].frameState == FrameState_Ready)
+ framenum = n;
+
+ frame = &uvd->frame[framenum];
+
+ frame->frameState = FrameState_Grabbing;
+ frame->scanstate = ScanState_Scanning;
+ frame->seqRead_Length = 0; /* Accumulated in xxx_parse_data() */
+ frame->deinterlace = Deinterlace_None;
+ frame->flags = 0; /* No flags yet, up to minidriver (or us) to set them */
+ uvd->curframe = framenum;
+
+ /*
+ * Normally we would want to copy previous frame into the current one
+ * before we even start filling it with data; this allows us to stop
+ * filling at any moment; top portion of the frame will be new and
+ * bottom portion will stay as it was in previous frame. If we don't
+ * do that then missing chunks of video stream will result in flickering
+ * portions of old data whatever it was before.
+ *
+ * If we choose not to copy previous frame (to, for example, save few
+ * bus cycles - the frame can be pretty large!) then we have an option
+ * to clear the frame before using. If we experience losses in this
+ * mode then missing picture will be black (no flickering).
+ *
+ * Finally, if user chooses not to clean the current frame before
+ * filling it with data then the old data will be visible if we fail
+ * to refill entire frame with new data.
+ */
+ if (!(uvd->flags & FLAGS_SEPARATE_FRAMES)) {
+ /* This copies previous frame into this one to mask losses */
+ int prev = (framenum - 1 + USBVIDEO_NUMFRAMES) % USBVIDEO_NUMFRAMES;
+ memmove(frame->data, uvd->frame[prev].data, uvd->max_frame_size);
+ } else {
+ if (uvd->flags & FLAGS_CLEAN_FRAMES) {
+ /* This provides a "clean" frame but slows things down */
+ memset(frame->data, 0, uvd->max_frame_size);
+ }
+ }
+ return 0;
+}
+
+/*
+ * usbvideo_CollectRawData()
+ *
+ * This procedure can be used instead of 'processData' callback if you
+ * only want to dump the raw data from the camera into the output
+ * device (frame buffer). You can look at it with V4L client, but the
+ * image will be unwatchable. The main purpose of this code and of the
+ * mode FLAGS_NO_DECODING is debugging and capturing of datastreams from
+ * new, unknown cameras. This procedure will be automatically invoked
+ * instead of the specified callback handler when uvd->flags has bit
+ * FLAGS_NO_DECODING set. Therefore, any regular build of any driver
+ * based on usbvideo can use this feature at any time.
+ */
+static void usbvideo_CollectRawData(struct uvd *uvd, struct usbvideo_frame *frame)
+{
+ int n;
+
+ assert(uvd != NULL);
+ assert(frame != NULL);
+
+ /* Try to move data from queue into frame buffer */
+ n = RingQueue_GetLength(&uvd->dp);
+ if (n > 0) {
+ int m;
+ /* See how much space we have left */
+ m = uvd->max_frame_size - frame->seqRead_Length;
+ if (n > m)
+ n = m;
+ /* Now move that much data into frame buffer */
+ RingQueue_Dequeue(
+ &uvd->dp,
+ frame->data + frame->seqRead_Length,
+ m);
+ frame->seqRead_Length += m;
+ }
+ /* See if we filled the frame */
+ if (frame->seqRead_Length >= uvd->max_frame_size) {
+ frame->frameState = FrameState_Done;
+ uvd->curframe = -1;
+ uvd->stats.frame_num++;
+ }
+}
+
+static int usbvideo_GetFrame(struct uvd *uvd, int frameNum)
+{
+ struct usbvideo_frame *frame = &uvd->frame[frameNum];
+
+ if (uvd->debug >= 2)
+ info("%s($%p,%d.)", __FUNCTION__, uvd, frameNum);
+
+ switch (frame->frameState) {
+ case FrameState_Unused:
+ if (uvd->debug >= 2)
+ info("%s: FrameState_Unused", __FUNCTION__);
+ return -EINVAL;
+ case FrameState_Ready:
+ case FrameState_Grabbing:
+ case FrameState_Error:
+ {
+ int ntries, signalPending;
+ redo:
+ if (!CAMERA_IS_OPERATIONAL(uvd)) {
+ if (uvd->debug >= 2)
+ info("%s: Camera is not operational (1)", __FUNCTION__);
+ return -EIO;
+ }
+ ntries = 0;
+ do {
+ RingQueue_InterruptibleSleepOn(&uvd->dp);
+ signalPending = signal_pending(current);
+ if (!CAMERA_IS_OPERATIONAL(uvd)) {
+ if (uvd->debug >= 2)
+ info("%s: Camera is not operational (2)", __FUNCTION__);
+ return -EIO;
+ }
+ assert(uvd->fbuf != NULL);
+ if (signalPending) {
+ if (uvd->debug >= 2)
+ info("%s: Signal=$%08x", __FUNCTION__, signalPending);
+ if (uvd->flags & FLAGS_RETRY_VIDIOCSYNC) {
+ usbvideo_TestPattern(uvd, 1, 0);
+ uvd->curframe = -1;
+ uvd->stats.frame_num++;
+ if (uvd->debug >= 2)
+ info("%s: Forced test pattern screen", __FUNCTION__);
+ return 0;
+ } else {
+ /* Standard answer: Interrupted! */
+ if (uvd->debug >= 2)
+ info("%s: Interrupted!", __FUNCTION__);
+ return -EINTR;
+ }
+ } else {
+ /* No signals - we just got new data in dp queue */
+ if (uvd->flags & FLAGS_NO_DECODING)
+ usbvideo_CollectRawData(uvd, frame);
+ else if (VALID_CALLBACK(uvd, processData))
+ GET_CALLBACK(uvd, processData)(uvd, frame);
+ else
+ err("%s: processData not set", __FUNCTION__);
+ }
+ } while (frame->frameState == FrameState_Grabbing);
+ if (uvd->debug >= 2) {
+ info("%s: Grabbing done; state=%d. (%lu. bytes)",
+ __FUNCTION__, frame->frameState, frame->seqRead_Length);
+ }
+ if (frame->frameState == FrameState_Error) {
+ int ret = usbvideo_NewFrame(uvd, frameNum);
+ if (ret < 0) {
+ err("%s: usbvideo_NewFrame() failed (%d.)", __FUNCTION__, ret);
+ return ret;
+ }
+ goto redo;
+ }
+ /* Note that we fall through to meet our destiny below */
+ }
+ case FrameState_Done:
+ /*
+ * Do all necessary postprocessing of data prepared in
+ * "interrupt" code and the collecting code above. The
+ * frame gets marked as FrameState_Done by queue parsing code.
+ * This status means that we collected enough data and
+ * most likely processed it as we went through. However
+ * the data may need postprocessing, such as deinterlacing
+ * or picture adjustments implemented in software (horror!)
+ *
+ * As soon as the frame becomes "final" it gets promoted to
+ * FrameState_Done_Hold status where it will remain until the
+ * caller consumed all the video data from the frame. Then
+ * the empty shell of ex-frame is thrown out for dogs to eat.
+ * But we, worried about pets, will recycle the frame!
+ */
+ uvd->stats.frame_num++;
+ if ((uvd->flags & FLAGS_NO_DECODING) == 0) {
+ if (VALID_CALLBACK(uvd, postProcess))
+ GET_CALLBACK(uvd, postProcess)(uvd, frame);
+ if (frame->flags & USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST)
+ usbvideo_SoftwareContrastAdjustment(uvd, frame);
+ }
+ frame->frameState = FrameState_Done_Hold;
+ if (uvd->debug >= 2)
+ info("%s: Entered FrameState_Done_Hold state.", __FUNCTION__);
+ return 0;
+
+ case FrameState_Done_Hold:
+ /*
+ * We stay in this state indefinitely until someone external,
+ * like ioctl() or read() call finishes digesting the frame
+ * data. Then it will mark the frame as FrameState_Unused and
+ * it will be released back into the wild to roam freely.
+ */
+ if (uvd->debug >= 2)
+ info("%s: FrameState_Done_Hold state.", __FUNCTION__);
+ return 0;
+ }
+
+ /* Catch-all for other cases. We shall not be here. */
+ err("%s: Invalid state %d.", __FUNCTION__, frame->frameState);
+ frame->frameState = FrameState_Unused;
+ return 0;
+}
+
+/*
+ * usbvideo_DeinterlaceFrame()
+ *
+ * This procedure deinterlaces the given frame. Some cameras produce
+ * only half of scanlines - sometimes only even lines, sometimes only
+ * odd lines. The deinterlacing method is stored in frame->deinterlace
+ * variable.
+ *
+ * Here we scan the frame vertically and replace missing scanlines with
+ * average between surrounding ones - before and after. If we have no
+ * line above then we just copy next line. Similarly, if we need to
+ * create a last line then preceding line is used.
+ */
+void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame)
+{
+ if ((uvd == NULL) || (frame == NULL))
+ return;
+
+ if ((frame->deinterlace == Deinterlace_FillEvenLines) ||
+ (frame->deinterlace == Deinterlace_FillOddLines))
+ {
+ const int v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL;
+ int i = (frame->deinterlace == Deinterlace_FillEvenLines) ? 0 : 1;
+
+ for (; i < VIDEOSIZE_Y(frame->request); i += 2) {
+ const unsigned char *fs1, *fs2;
+ unsigned char *fd;
+ int ip, in, j; /* Previous and next lines */
+
+ /*
+ * Need to average lines before and after 'i'.
+ * If we go out of bounds seeking those lines then
+ * we point back to existing line.
+ */
+ ip = i - 1; /* First, get rough numbers */
+ in = i + 1;
+
+ /* Now validate */
+ if (ip < 0)
+ ip = in;
+ if (in >= VIDEOSIZE_Y(frame->request))
+ in = ip;
+
+ /* Sanity check */
+ if ((ip < 0) || (in < 0) ||
+ (ip >= VIDEOSIZE_Y(frame->request)) ||
+ (in >= VIDEOSIZE_Y(frame->request)))
+ {
+ err("Error: ip=%d. in=%d. req.height=%ld.",
+ ip, in, VIDEOSIZE_Y(frame->request));
+ break;
+ }
+
+ /* Now we need to average lines 'ip' and 'in' to produce line 'i' */
+ fs1 = frame->data + (v4l_linesize * ip);
+ fs2 = frame->data + (v4l_linesize * in);
+ fd = frame->data + (v4l_linesize * i);
+
+ /* Average lines around destination */
+ for (j=0; j < v4l_linesize; j++) {
+ fd[j] = (unsigned char)((((unsigned) fs1[j]) +
+ ((unsigned)fs2[j])) >> 1);
+ }
+ }
+ }
+
+ /* Optionally display statistics on the screen */
+ if (uvd->flags & FLAGS_OVERLAY_STATS)
+ usbvideo_OverlayStats(uvd, frame);
+}
+
+EXPORT_SYMBOL(usbvideo_DeinterlaceFrame);
+
+/*
+ * usbvideo_SoftwareContrastAdjustment()
+ *
+ * This code adjusts the contrast of the frame, assuming RGB24 format.
+ * As most software image processing, this job is CPU-intensive.
+ * Get a camera that supports hardware adjustment!
+ *
+ * History:
+ * 09-Feb-2001 Created.
+ */
+static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd,
+ struct usbvideo_frame *frame)
+{
+ int i, j, v4l_linesize;
+ signed long adj;
+ const int ccm = 128; /* Color correction median - see below */
+
+ if ((uvd == NULL) || (frame == NULL)) {
+ err("%s: Illegal call.", __FUNCTION__);
+ return;
+ }
+ adj = (uvd->vpic.contrast - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/
+ RESTRICT_TO_RANGE(adj, -ccm, ccm+1);
+ if (adj == 0) {
+ /* In rare case of no adjustment */
+ return;
+ }
+ v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL;
+ for (i=0; i < VIDEOSIZE_Y(frame->request); i++) {
+ unsigned char *fd = frame->data + (v4l_linesize * i);
+ for (j=0; j < v4l_linesize; j++) {
+ signed long v = (signed long) fd[j];
+ /* Magnify up to 2 times, reduce down to zero */
+ v = 128 + ((ccm + adj) * (v - 128)) / ccm;
+ RESTRICT_TO_RANGE(v, 0, 0xFF); /* Must flatten tails */
+ fd[j] = (unsigned char) v;
+ }
+ }
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/usbvideo/usbvideo.h b/drivers/media/video/usbvideo/usbvideo.h
new file mode 100644
index 000000000000..135433c2680a
--- /dev/null
+++ b/drivers/media/video/usbvideo/usbvideo.h
@@ -0,0 +1,394 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef usbvideo_h
+#define usbvideo_h
+
+#include <linux/config.h>
+#include <linux/videodev.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+
+/* Most helpful debugging aid */
+#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))
+
+#define USBVIDEO_REPORT_STATS 1 /* Set to 0 to block statistics on close */
+
+/* Bit flags (options) */
+#define FLAGS_RETRY_VIDIOCSYNC (1 << 0)
+#define FLAGS_MONOCHROME (1 << 1)
+#define FLAGS_DISPLAY_HINTS (1 << 2)
+#define FLAGS_OVERLAY_STATS (1 << 3)
+#define FLAGS_FORCE_TESTPATTERN (1 << 4)
+#define FLAGS_SEPARATE_FRAMES (1 << 5)
+#define FLAGS_CLEAN_FRAMES (1 << 6)
+#define FLAGS_NO_DECODING (1 << 7)
+
+/* Bit flags for frames (apply to the frame where they are specified) */
+#define USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST (1 << 0)
+
+/* Camera capabilities (maximum) */
+#define CAMERA_URB_FRAMES 32
+#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */
+#define FRAMES_PER_DESC (CAMERA_URB_FRAMES)
+#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET)
+
+/* This macro restricts an int variable to an inclusive range */
+#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); }
+
+#define V4L_BYTES_PER_PIXEL 3 /* Because we produce RGB24 */
+
+/*
+ * Use this macro to construct constants for different video sizes.
+ * We have to deal with different video sizes that have to be
+ * configured in the device or compared against when we receive
+ * a data. Normally one would define a bunch of VIDEOSIZE_x_by_y
+ * #defines and that's the end of story. However this solution
+ * does not allow to convert between real pixel sizes and the
+ * constant (integer) value that may be used to tag a frame or
+ * whatever. The set of macros below constructs videosize constants
+ * from the pixel size and allows to reconstruct the pixel size
+ * from the combined value later.
+ */
+#define VIDEOSIZE(x,y) (((x) & 0xFFFFL) | (((y) & 0xFFFFL) << 16))
+#define VIDEOSIZE_X(vs) ((vs) & 0xFFFFL)
+#define VIDEOSIZE_Y(vs) (((vs) >> 16) & 0xFFFFL)
+typedef unsigned long videosize_t;
+
+/*
+ * This macro checks if the camera is still operational. The 'uvd'
+ * pointer must be valid, uvd->dev must be valid, we are not
+ * removing the device and the device has not erred on us.
+ */
+#define CAMERA_IS_OPERATIONAL(uvd) (\
+ (uvd != NULL) && \
+ ((uvd)->dev != NULL) && \
+ ((uvd)->last_error == 0) && \
+ (!(uvd)->remove_pending))
+
+/*
+ * We use macros to do YUV -> RGB conversion because this is
+ * very important for speed and totally unimportant for size.
+ *
+ * YUV -> RGB Conversion
+ * ---------------------
+ *
+ * B = 1.164*(Y-16) + 2.018*(V-128)
+ * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128)
+ * R = 1.164*(Y-16) + 1.596*(U-128)
+ *
+ * If you fancy integer arithmetics (as you should), hear this:
+ *
+ * 65536*B = 76284*(Y-16) + 132252*(V-128)
+ * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128)
+ * 65536*R = 76284*(Y-16) + 104595*(U-128)
+ *
+ * Make sure the output values are within [0..255] range.
+ */
+#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x)))
+#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \
+ int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \
+ mm_y = (my) - 16; \
+ mm_u = (mu) - 128; \
+ mm_v = (mv) - 128; \
+ mm_yc= mm_y * 76284; \
+ mm_b = (mm_yc + 132252*mm_v ) >> 16; \
+ mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \
+ mm_r = (mm_yc + 104595*mm_u ) >> 16; \
+ mb = LIMIT_RGB(mm_b); \
+ mg = LIMIT_RGB(mm_g); \
+ mr = LIMIT_RGB(mm_r); \
+}
+
+#define RING_QUEUE_SIZE (128*1024) /* Must be a power of 2 */
+#define RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) & ((rq)->length-1)
+#define RING_QUEUE_DEQUEUE_BYTES(rq,n) RING_QUEUE_ADVANCE_INDEX(rq,ri,n)
+#define RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri) & ((rq)->length-1)])
+
+struct RingQueue {
+ unsigned char *queue; /* Data from the Isoc data pump */
+ int length; /* How many bytes allocated for the queue */
+ int wi; /* That's where we write */
+ int ri; /* Read from here until you hit write index */
+ wait_queue_head_t wqh; /* Processes waiting */
+};
+
+enum ScanState {
+ ScanState_Scanning, /* Scanning for header */
+ ScanState_Lines /* Parsing lines */
+};
+
+/* Completion states of the data parser */
+enum ParseState {
+ scan_Continue, /* Just parse next item */
+ scan_NextFrame, /* Frame done, send it to V4L */
+ scan_Out, /* Not enough data for frame */
+ scan_EndParse /* End parsing */
+};
+
+enum FrameState {
+ FrameState_Unused, /* Unused (no MCAPTURE) */
+ FrameState_Ready, /* Ready to start grabbing */
+ FrameState_Grabbing, /* In the process of being grabbed into */
+ FrameState_Done, /* Finished grabbing, but not been synced yet */
+ FrameState_Done_Hold, /* Are syncing or reading */
+ FrameState_Error, /* Something bad happened while processing */
+};
+
+/*
+ * Some frames may contain only even or odd lines. This type
+ * specifies what type of deinterlacing is required.
+ */
+enum Deinterlace {
+ Deinterlace_None=0,
+ Deinterlace_FillOddLines,
+ Deinterlace_FillEvenLines
+};
+
+#define USBVIDEO_NUMFRAMES 2 /* How many frames we work with */
+#define USBVIDEO_NUMSBUF 2 /* How many URBs linked in a ring */
+
+/* This structure represents one Isoc request - URB and buffer */
+struct usbvideo_sbuf {
+ char *data;
+ struct urb *urb;
+};
+
+struct usbvideo_frame {
+ char *data; /* Frame buffer */
+ unsigned long header; /* Significant bits from the header */
+
+ videosize_t canvas; /* The canvas (max. image) allocated */
+ videosize_t request; /* That's what the application asked for */
+ unsigned short palette; /* The desired format */
+
+ enum FrameState frameState;/* State of grabbing */
+ enum ScanState scanstate; /* State of scanning */
+ enum Deinterlace deinterlace;
+ int flags; /* USBVIDEO_FRAME_FLAG_xxx bit flags */
+
+ int curline; /* Line of frame we're working on */
+
+ long seqRead_Length; /* Raw data length of frame */
+ long seqRead_Index; /* Amount of data that has been already read */
+
+ void *user; /* Additional data that user may need */
+};
+
+/* Statistics that can be overlaid on screen */
+struct usbvideo_statistics {
+ unsigned long frame_num; /* Sequential number of the frame */
+ unsigned long urb_count; /* How many URBs we received so far */
+ unsigned long urb_length; /* Length of last URB */
+ unsigned long data_count; /* How many bytes we received */
+ unsigned long header_count; /* How many frame headers we found */
+ unsigned long iso_skip_count; /* How many empty ISO packets received */
+ unsigned long iso_err_count; /* How many bad ISO packets received */
+};
+
+struct usbvideo;
+
+struct uvd {
+ struct video_device vdev; /* Must be the first field! */
+ struct usb_device *dev;
+ struct usbvideo *handle; /* Points back to the struct usbvideo */
+ void *user_data; /* Camera-dependent data */
+ int user_size; /* Size of that camera-dependent data */
+ int debug; /* Debug level for usbvideo */
+ unsigned char iface; /* Video interface number */
+ unsigned char video_endp;
+ unsigned char ifaceAltActive;
+ unsigned char ifaceAltInactive; /* Alt settings */
+ unsigned long flags; /* FLAGS_USBVIDEO_xxx */
+ unsigned long paletteBits; /* Which palettes we accept? */
+ unsigned short defaultPalette; /* What palette to use for read() */
+ struct mutex lock;
+ int user; /* user count for exclusive use */
+
+ videosize_t videosize; /* Current setting */
+ videosize_t canvas; /* This is the width,height of the V4L canvas */
+ int max_frame_size; /* Bytes in one video frame */
+
+ int uvd_used; /* Is this structure in use? */
+ int streaming; /* Are we streaming Isochronous? */
+ int grabbing; /* Are we grabbing? */
+ int settingsAdjusted; /* Have we adjusted contrast etc.? */
+ int last_error; /* What calamity struck us? */
+
+ char *fbuf; /* Videodev buffer area */
+ int fbuf_size; /* Videodev buffer size */
+
+ int curframe;
+ int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */
+
+ struct RingQueue dp; /* Isoc data pump */
+ struct usbvideo_frame frame[USBVIDEO_NUMFRAMES];
+ struct usbvideo_sbuf sbuf[USBVIDEO_NUMSBUF];
+
+ volatile int remove_pending; /* If set then about to exit */
+
+ struct video_picture vpic, vpic_old; /* Picture settings */
+ struct video_capability vcap; /* Video capabilities */
+ struct video_channel vchan; /* May be used for tuner support */
+ struct usbvideo_statistics stats;
+ char videoName[32]; /* Holds name like "video7" */
+};
+
+/*
+ * usbvideo callbacks (virtual methods). They are set when usbvideo
+ * services are registered. All of these default to NULL, except those
+ * that default to usbvideo-provided methods.
+ */
+struct usbvideo_cb {
+ int (*probe)(struct usb_interface *, const struct usb_device_id *);
+ void (*userFree)(struct uvd *);
+ void (*disconnect)(struct usb_interface *);
+ int (*setupOnOpen)(struct uvd *);
+ void (*videoStart)(struct uvd *);
+ void (*videoStop)(struct uvd *);
+ void (*processData)(struct uvd *, struct usbvideo_frame *);
+ void (*postProcess)(struct uvd *, struct usbvideo_frame *);
+ void (*adjustPicture)(struct uvd *);
+ int (*getFPS)(struct uvd *);
+ int (*overlayHook)(struct uvd *, struct usbvideo_frame *);
+ int (*getFrame)(struct uvd *, int);
+ int (*startDataPump)(struct uvd *uvd);
+ void (*stopDataPump)(struct uvd *uvd);
+ int (*setVideoMode)(struct uvd *uvd, struct video_window *vw);
+};
+
+struct usbvideo {
+ int num_cameras; /* As allocated */
+ struct usb_driver usbdrv; /* Interface to the USB stack */
+ char drvName[80]; /* Driver name */
+ struct mutex lock; /* Mutex protecting camera structures */
+ struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */
+ struct video_device vdt; /* Video device template */
+ struct uvd *cam; /* Array of camera structures */
+ struct module *md_module; /* Minidriver module */
+};
+
+
+/*
+ * This macro retrieves callback address from the struct uvd object.
+ * No validity checks are done here, so be sure to check the
+ * callback beforehand with VALID_CALLBACK.
+ */
+#define GET_CALLBACK(uvd,cbName) ((uvd)->handle->cb.cbName)
+
+/*
+ * This macro returns either callback pointer or NULL. This is safe
+ * macro, meaning that most of components of data structures involved
+ * may be NULL - this only results in NULL being returned. You may
+ * wish to use this macro to make sure that the callback is callable.
+ * However keep in mind that those checks take time.
+ */
+#define VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \
+ ((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL)
+
+int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len);
+int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n);
+void RingQueue_WakeUpInterruptible(struct RingQueue *rq);
+void RingQueue_Flush(struct RingQueue *rq);
+
+static inline int RingQueue_GetLength(const struct RingQueue *rq)
+{
+ return (rq->wi - rq->ri + rq->length) & (rq->length-1);
+}
+
+static inline int RingQueue_GetFreeSpace(const struct RingQueue *rq)
+{
+ return rq->length - RingQueue_GetLength(rq);
+}
+
+void usbvideo_DrawLine(
+ struct usbvideo_frame *frame,
+ int x1, int y1,
+ int x2, int y2,
+ unsigned char cr, unsigned char cg, unsigned char cb);
+void usbvideo_HexDump(const unsigned char *data, int len);
+void usbvideo_SayAndWait(const char *what);
+void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode);
+
+/* Memory allocation routines */
+unsigned long usbvideo_kvirt_to_pa(unsigned long adr);
+
+int usbvideo_register(
+ struct usbvideo **pCams,
+ const int num_cams,
+ const int num_extra,
+ const char *driverName,
+ const struct usbvideo_cb *cbTable,
+ struct module *md,
+ const struct usb_device_id *id_table);
+struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams);
+int usbvideo_RegisterVideoDevice(struct uvd *uvd);
+void usbvideo_Deregister(struct usbvideo **uvt);
+
+int usbvideo_v4l_initialize(struct video_device *dev);
+
+void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame);
+
+/*
+ * This code performs bounds checking - use it when working with
+ * new formats, or else you may get oopses all over the place.
+ * If pixel falls out of bounds then it gets shoved back (as close
+ * to place of offence as possible) and is painted bright red.
+ *
+ * There are two important concepts: frame width, height and
+ * V4L canvas width, height. The former is the area requested by
+ * the application -for this very frame-. The latter is the largest
+ * possible frame that we can serve (we advertise that via V4L ioctl).
+ * The frame data is expected to be formatted as lines of length
+ * VIDEOSIZE_X(fr->request), total VIDEOSIZE_Y(frame->request) lines.
+ */
+static inline void RGB24_PUTPIXEL(
+ struct usbvideo_frame *fr,
+ int ix, int iy,
+ unsigned char vr,
+ unsigned char vg,
+ unsigned char vb)
+{
+ register unsigned char *pf;
+ int limiter = 0, mx, my;
+ mx = ix;
+ my = iy;
+ if (mx < 0) {
+ mx=0;
+ limiter++;
+ } else if (mx >= VIDEOSIZE_X((fr)->request)) {
+ mx= VIDEOSIZE_X((fr)->request) - 1;
+ limiter++;
+ }
+ if (my < 0) {
+ my = 0;
+ limiter++;
+ } else if (my >= VIDEOSIZE_Y((fr)->request)) {
+ my = VIDEOSIZE_Y((fr)->request) - 1;
+ limiter++;
+ }
+ pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*VIDEOSIZE_X((fr)->request) + (ix));
+ if (limiter) {
+ *pf++ = 0;
+ *pf++ = 0;
+ *pf++ = 0xFF;
+ } else {
+ *pf++ = (vb);
+ *pf++ = (vg);
+ *pf++ = (vr);
+ }
+}
+
+#endif /* usbvideo_h */
diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c
new file mode 100644
index 000000000000..1d06e53ec7c5
--- /dev/null
+++ b/drivers/media/video/usbvideo/vicam.c
@@ -0,0 +1,1411 @@
+/*
+ * USB ViCam WebCam driver
+ * Copyright (c) 2002 Joe Burks (jburks@wavicle.org),
+ * Christopher L Cheney (ccheney@cheney.cx),
+ * Pavel Machek (pavel@suse.cz),
+ * John Tyner (jtyner@cs.ucr.edu),
+ * Monroe Williams (monroe@pobox.com)
+ *
+ * Supports 3COM HomeConnect PC Digital WebCam
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This source code is based heavily on the CPiA webcam driver which was
+ * written by Peter Pregler, Scott J. Bertin and Johannes Erdfelt
+ *
+ * Portions of this code were also copied from usbvideo.c
+ *
+ * Special thanks to the the whole team at Sourceforge for help making
+ * this driver become a reality. Notably:
+ * Andy Armstrong who reverse engineered the color encoding and
+ * Pavel Machek and Chris Cheney who worked on reverse engineering the
+ * camera controls and wrote the first generation driver.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/videodev.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/mutex.h>
+#include "usbvideo.h"
+
+// #define VICAM_DEBUG
+
+#ifdef VICAM_DEBUG
+#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __FUNCTION__, lineno, ##args)
+#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args)
+#else
+#define DBG(fmn,args...) do {} while(0)
+#endif
+
+#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org"
+#define DRIVER_DESC "ViCam WebCam Driver"
+
+/* Define these values to match your device */
+#define USB_VICAM_VENDOR_ID 0x04c1
+#define USB_VICAM_PRODUCT_ID 0x009d
+
+#define VICAM_BYTES_PER_PIXEL 3
+#define VICAM_MAX_READ_SIZE (512*242+128)
+#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240)
+#define VICAM_FRAMES 2
+
+#define VICAM_HEADER_SIZE 64
+
+#define clamp( x, l, h ) max_t( __typeof__( x ), \
+ ( l ), \
+ min_t( __typeof__( x ), \
+ ( h ), \
+ ( x ) ) )
+
+/* Not sure what all the bytes in these char
+ * arrays do, but they're necessary to make
+ * the camera work.
+ */
+
+static unsigned char 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 unsigned char setup2[] = {
+ 0xB6, 0xC3, 0x03, 0x00, 0x03, 0x64, 0x18, 0x00,
+ 0x00, 0x00
+};
+
+static unsigned char setup3[] = {
+ 0xB6, 0xC3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00
+};
+
+static unsigned char 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 unsigned char 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
+};
+
+/* rvmalloc / rvfree copied from usbvideo.c
+ *
+ * Not sure why these are not yet non-statics which I can reference through
+ * usbvideo.h the same as it is in 2.4.20. I bet this will get fixed sometime
+ * in the future.
+ *
+*/
+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 (size > 0) {
+ SetPageReserved(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;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
+struct vicam_camera {
+ u16 shutter_speed; // capture shutter speed
+ u16 gain; // capture gain
+
+ u8 *raw_image; // raw data captured from the camera
+ u8 *framebuf; // processed data in RGB24 format
+ u8 *cntrlbuf; // area used to send control msgs
+
+ struct video_device vdev; // v4l video device
+ struct usb_device *udev; // usb device
+
+ /* guard against simultaneous accesses to the camera */
+ struct mutex cam_lock;
+
+ int is_initialized;
+ u8 open_count;
+ u8 bulkEndpoint;
+ int needsDummyRead;
+
+#if defined(CONFIG_VIDEO_PROC_FS)
+ struct proc_dir_entry *proc_dir;
+#endif
+
+};
+
+static int vicam_probe( struct usb_interface *intf, const struct usb_device_id *id);
+static void vicam_disconnect(struct usb_interface *intf);
+static void read_frame(struct vicam_camera *cam, int framenum);
+static void vicam_decode_color(const u8 *, u8 *);
+
+static int __send_control_msg(struct vicam_camera *cam,
+ u8 request,
+ u16 value,
+ u16 index,
+ unsigned char *cp,
+ u16 size)
+{
+ int status;
+
+ /* cp must be memory that has been allocated by kmalloc */
+
+ status = usb_control_msg(cam->udev,
+ usb_sndctrlpipe(cam->udev, 0),
+ request,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, value, index,
+ cp, size, 1000);
+
+ status = min(status, 0);
+
+ if (status < 0) {
+ printk(KERN_INFO "Failed sending control message, error %d.\n",
+ status);
+ }
+
+ return status;
+}
+
+static int send_control_msg(struct vicam_camera *cam,
+ u8 request,
+ u16 value,
+ u16 index,
+ unsigned char *cp,
+ u16 size)
+{
+ int status = -ENODEV;
+ mutex_lock(&cam->cam_lock);
+ if (cam->udev) {
+ status = __send_control_msg(cam, request, value,
+ index, cp, size);
+ }
+ mutex_unlock(&cam->cam_lock);
+ return status;
+}
+static int
+initialize_camera(struct vicam_camera *cam)
+{
+ const struct {
+ u8 *data;
+ u32 size;
+ } firmware[] = {
+ { .data = setup1, .size = sizeof(setup1) },
+ { .data = setup2, .size = sizeof(setup2) },
+ { .data = setup3, .size = sizeof(setup3) },
+ { .data = setup4, .size = sizeof(setup4) },
+ { .data = setup5, .size = sizeof(setup5) },
+ { .data = setup3, .size = sizeof(setup3) },
+ { .data = NULL, .size = 0 }
+ };
+
+ int err, i;
+
+ for (i = 0, err = 0; firmware[i].data && !err; i++) {
+ memcpy(cam->cntrlbuf, firmware[i].data, firmware[i].size);
+
+ err = send_control_msg(cam, 0xff, 0, 0,
+ cam->cntrlbuf, firmware[i].size);
+ }
+
+ return err;
+}
+
+static int
+set_camera_power(struct vicam_camera *cam, int state)
+{
+ int status;
+
+ if ((status = send_control_msg(cam, 0x50, state, 0, NULL, 0)) < 0)
+ return status;
+
+ if (state) {
+ send_control_msg(cam, 0x55, 1, 0, NULL, 0);
+ }
+
+ return 0;
+}
+
+static int
+vicam_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsigned long arg)
+{
+ void __user *user_arg = (void __user *)arg;
+ struct vicam_camera *cam = file->private_data;
+ int retval = 0;
+
+ if (!cam)
+ return -ENODEV;
+
+ switch (ioctlnr) {
+ /* query capabilities */
+ case VIDIOCGCAP:
+ {
+ struct video_capability b;
+
+ DBG("VIDIOCGCAP\n");
+ memset(&b, 0, sizeof(b));
+ strcpy(b.name, "ViCam-based Camera");
+ b.type = VID_TYPE_CAPTURE;
+ b.channels = 1;
+ b.audios = 0;
+ b.maxwidth = 320; /* VIDEOSIZE_CIF */
+ b.maxheight = 240;
+ b.minwidth = 320; /* VIDEOSIZE_48_48 */
+ b.minheight = 240;
+
+ if (copy_to_user(user_arg, &b, sizeof(b)))
+ retval = -EFAULT;
+
+ break;
+ }
+ /* get/set video source - we are a camera and nothing else */
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
+
+ DBG("VIDIOCGCHAN\n");
+ if (copy_from_user(&v, user_arg, sizeof(v))) {
+ retval = -EFAULT;
+ break;
+ }
+ if (v.channel != 0) {
+ retval = -EINVAL;
+ break;
+ }
+
+ v.channel = 0;
+ strcpy(v.name, "Camera");
+ v.tuners = 0;
+ v.flags = 0;
+ v.type = VIDEO_TYPE_CAMERA;
+ v.norm = 0;
+
+ if (copy_to_user(user_arg, &v, sizeof(v)))
+ retval = -EFAULT;
+ break;
+ }
+
+ case VIDIOCSCHAN:
+ {
+ int v;
+
+ if (copy_from_user(&v, user_arg, sizeof(v)))
+ retval = -EFAULT;
+ DBG("VIDIOCSCHAN %d\n", v);
+
+ if (retval == 0 && v != 0)
+ retval = -EINVAL;
+
+ break;
+ }
+
+ /* image properties */
+ case VIDIOCGPICT:
+ {
+ struct video_picture vp;
+ DBG("VIDIOCGPICT\n");
+ memset(&vp, 0, sizeof (struct video_picture));
+ vp.brightness = cam->gain << 8;
+ vp.depth = 24;
+ vp.palette = VIDEO_PALETTE_RGB24;
+ if (copy_to_user(user_arg, &vp, sizeof (struct video_picture)))
+ retval = -EFAULT;
+ break;
+ }
+
+ case VIDIOCSPICT:
+ {
+ struct video_picture vp;
+
+ if (copy_from_user(&vp, user_arg, sizeof(vp))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ DBG("VIDIOCSPICT depth = %d, pal = %d\n", vp.depth,
+ vp.palette);
+
+ cam->gain = vp.brightness >> 8;
+
+ if (vp.depth != 24
+ || vp.palette != VIDEO_PALETTE_RGB24)
+ retval = -EINVAL;
+
+ break;
+ }
+
+ /* get/set capture window */
+ case VIDIOCGWIN:
+ {
+ struct video_window vw;
+ vw.x = 0;
+ vw.y = 0;
+ vw.width = 320;
+ vw.height = 240;
+ vw.chromakey = 0;
+ vw.flags = 0;
+ vw.clips = NULL;
+ vw.clipcount = 0;
+
+ DBG("VIDIOCGWIN\n");
+
+ if (copy_to_user(user_arg, (void *)&vw, sizeof(vw)))
+ retval = -EFAULT;
+
+ // I'm not sure what the deal with a capture window is, it is very poorly described
+ // in the doc. So I won't support it now.
+ break;
+ }
+
+ case VIDIOCSWIN:
+ {
+
+ struct video_window vw;
+
+ if (copy_from_user(&vw, user_arg, sizeof(vw))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ DBG("VIDIOCSWIN %d x %d\n", vw.width, vw.height);
+
+ if ( vw.width != 320 || vw.height != 240 )
+ retval = -EFAULT;
+
+ break;
+ }
+
+ /* mmap interface */
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf vm;
+ int i;
+
+ DBG("VIDIOCGMBUF\n");
+ memset(&vm, 0, sizeof (vm));
+ vm.size =
+ VICAM_MAX_FRAME_SIZE * VICAM_FRAMES;
+ vm.frames = VICAM_FRAMES;
+ for (i = 0; i < VICAM_FRAMES; i++)
+ vm.offsets[i] = VICAM_MAX_FRAME_SIZE * i;
+
+ if (copy_to_user(user_arg, (void *)&vm, sizeof(vm)))
+ retval = -EFAULT;
+
+ break;
+ }
+
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap vm;
+ // int video_size;
+
+ if (copy_from_user((void *)&vm, user_arg, sizeof(vm))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ DBG("VIDIOCMCAPTURE frame=%d, height=%d, width=%d, format=%d.\n",vm.frame,vm.width,vm.height,vm.format);
+
+ if ( vm.frame >= VICAM_FRAMES || vm.format != VIDEO_PALETTE_RGB24 )
+ retval = -EINVAL;
+
+ // in theory right here we'd start the image capturing
+ // (fill in a bulk urb and submit it asynchronously)
+ //
+ // Instead we're going to do a total hack job for now and
+ // retrieve the frame in VIDIOCSYNC
+
+ break;
+ }
+
+ case VIDIOCSYNC:
+ {
+ int frame;
+
+ if (copy_from_user((void *)&frame, user_arg, sizeof(int))) {
+ retval = -EFAULT;
+ break;
+ }
+ DBG("VIDIOCSYNC: %d\n", frame);
+
+ read_frame(cam, frame);
+ vicam_decode_color(cam->raw_image,
+ cam->framebuf +
+ frame * VICAM_MAX_FRAME_SIZE );
+
+ break;
+ }
+
+ /* pointless to implement overlay with this camera */
+ case VIDIOCCAPTURE:
+ case VIDIOCGFBUF:
+ case VIDIOCSFBUF:
+ case VIDIOCKEY:
+ retval = -EINVAL;
+ break;
+
+ /* tuner interface - we have none */
+ case VIDIOCGTUNER:
+ case VIDIOCSTUNER:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ retval = -EINVAL;
+ break;
+
+ /* audio interface - we have none */
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ retval = -EINVAL;
+ break;
+ default:
+ retval = -ENOIOCTLCMD;
+ break;
+ }
+
+ return retval;
+}
+
+static int
+vicam_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct vicam_camera *cam =
+ (struct vicam_camera *) dev->priv;
+ DBG("open\n");
+
+ if (!cam) {
+ printk(KERN_ERR
+ "vicam video_device improperly initialized");
+ return -EINVAL;
+ }
+
+ /* the videodev_lock held above us protects us from
+ * simultaneous opens...for now. we probably shouldn't
+ * rely on this fact forever.
+ */
+
+ if (cam->open_count > 0) {
+ printk(KERN_INFO
+ "vicam_open called on already opened camera");
+ return -EBUSY;
+ }
+
+ cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL);
+ if (!cam->raw_image) {
+ return -ENOMEM;
+ }
+
+ cam->framebuf = rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
+ if (!cam->framebuf) {
+ kfree(cam->raw_image);
+ return -ENOMEM;
+ }
+
+ cam->cntrlbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!cam->cntrlbuf) {
+ kfree(cam->raw_image);
+ rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
+ return -ENOMEM;
+ }
+
+ // First upload firmware, then turn the camera on
+
+ if (!cam->is_initialized) {
+ initialize_camera(cam);
+
+ cam->is_initialized = 1;
+ }
+
+ set_camera_power(cam, 1);
+
+ cam->needsDummyRead = 1;
+ cam->open_count++;
+
+ file->private_data = cam;
+
+ return 0;
+}
+
+static int
+vicam_close(struct inode *inode, struct file *file)
+{
+ struct vicam_camera *cam = file->private_data;
+ int open_count;
+ struct usb_device *udev;
+
+ DBG("close\n");
+
+ /* it's not the end of the world if
+ * we fail to turn the camera off.
+ */
+
+ set_camera_power(cam, 0);
+
+ kfree(cam->raw_image);
+ rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
+ kfree(cam->cntrlbuf);
+
+ mutex_lock(&cam->cam_lock);
+
+ cam->open_count--;
+ open_count = cam->open_count;
+ udev = cam->udev;
+
+ mutex_unlock(&cam->cam_lock);
+
+ if (!open_count && !udev) {
+ kfree(cam);
+ }
+
+ return 0;
+}
+
+static void vicam_decode_color(const u8 *data, u8 *rgb)
+{
+ /* vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB
+ * Copyright (C) 2002 Monroe Williams (monroe@pobox.com)
+ */
+
+ int i, prevY, nextY;
+
+ prevY = 512;
+ nextY = 512;
+
+ data += VICAM_HEADER_SIZE;
+
+ for( i = 0; i < 240; i++, data += 512 ) {
+ const int y = ( i * 242 ) / 240;
+
+ int j, prevX, nextX;
+ int Y, Cr, Cb;
+
+ if ( y == 242 - 1 ) {
+ nextY = -512;
+ }
+
+ prevX = 1;
+ nextX = 1;
+
+ for ( j = 0; j < 320; j++, rgb += 3 ) {
+ const int x = ( j * 512 ) / 320;
+ const u8 * const src = &data[x];
+
+ if ( x == 512 - 1 ) {
+ nextX = -1;
+ }
+
+ Cr = ( src[prevX] - src[0] ) +
+ ( src[nextX] - src[0] );
+ Cr /= 2;
+
+ Cb = ( src[prevY] - src[prevX + prevY] ) +
+ ( src[prevY] - src[nextX + prevY] ) +
+ ( src[nextY] - src[prevX + nextY] ) +
+ ( src[nextY] - src[nextX + nextY] );
+ Cb /= 4;
+
+ Y = 1160 * ( src[0] + ( Cr / 2 ) - 16 );
+
+ if ( i & 1 ) {
+ int Ct = Cr;
+ Cr = Cb;
+ Cb = Ct;
+ }
+
+ if ( ( x ^ i ) & 1 ) {
+ Cr = -Cr;
+ Cb = -Cb;
+ }
+
+ rgb[0] = clamp( ( ( Y + ( 2017 * Cb ) ) +
+ 500 ) / 900, 0, 255 );
+ rgb[1] = clamp( ( ( Y - ( 392 * Cb ) -
+ ( 813 * Cr ) ) +
+ 500 ) / 1000, 0, 255 );
+ rgb[2] = clamp( ( ( Y + ( 1594 * Cr ) ) +
+ 500 ) / 1300, 0, 255 );
+
+ prevX = -1;
+ }
+
+ prevY = -512;
+ }
+}
+
+static void
+read_frame(struct vicam_camera *cam, int framenum)
+{
+ unsigned char *request = cam->cntrlbuf;
+ int realShutter;
+ int n;
+ int actual_length;
+
+ if (cam->needsDummyRead) {
+ cam->needsDummyRead = 0;
+ read_frame(cam, framenum);
+ }
+
+ memset(request, 0, 16);
+ request[0] = cam->gain; // 0 = 0% gain, FF = 100% gain
+
+ request[1] = 0; // 512x242 capture
+
+ request[2] = 0x90; // the function of these two bytes
+ request[3] = 0x07; // is not yet understood
+
+ if (cam->shutter_speed > 60) {
+ // Short exposure
+ realShutter =
+ ((-15631900 / cam->shutter_speed) + 260533) / 1000;
+ request[4] = realShutter & 0xFF;
+ request[5] = (realShutter >> 8) & 0xFF;
+ request[6] = 0x03;
+ request[7] = 0x01;
+ } else {
+ // Long exposure
+ realShutter = 15600 / cam->shutter_speed - 1;
+ request[4] = 0;
+ request[5] = 0;
+ request[6] = realShutter & 0xFF;
+ request[7] = realShutter >> 8;
+ }
+
+ // Per John Markus Bjørndalen, byte at index 8 causes problems if it isn't 0
+ request[8] = 0;
+ // bytes 9-15 do not seem to affect exposure or image quality
+
+ mutex_lock(&cam->cam_lock);
+
+ if (!cam->udev) {
+ goto done;
+ }
+
+ n = __send_control_msg(cam, 0x51, 0x80, 0, request, 16);
+
+ if (n < 0) {
+ printk(KERN_ERR
+ " Problem sending frame capture control message");
+ goto done;
+ }
+
+ n = usb_bulk_msg(cam->udev,
+ usb_rcvbulkpipe(cam->udev, cam->bulkEndpoint),
+ cam->raw_image,
+ 512 * 242 + 128, &actual_length, 10000);
+
+ if (n < 0) {
+ printk(KERN_ERR "Problem during bulk read of frame data: %d\n",
+ n);
+ }
+
+ done:
+ mutex_unlock(&cam->cam_lock);
+}
+
+static ssize_t
+vicam_read( struct file *file, char __user *buf, size_t count, loff_t *ppos )
+{
+ struct vicam_camera *cam = file->private_data;
+
+ DBG("read %d bytes.\n", (int) count);
+
+ if (*ppos >= VICAM_MAX_FRAME_SIZE) {
+ *ppos = 0;
+ return 0;
+ }
+
+ if (*ppos == 0) {
+ read_frame(cam, 0);
+ vicam_decode_color(cam->raw_image,
+ cam->framebuf +
+ 0 * VICAM_MAX_FRAME_SIZE);
+ }
+
+ count = min_t(size_t, count, VICAM_MAX_FRAME_SIZE - *ppos);
+
+ if (copy_to_user(buf, &cam->framebuf[*ppos], count)) {
+ count = -EFAULT;
+ } else {
+ *ppos += count;
+ }
+
+ if (count == VICAM_MAX_FRAME_SIZE) {
+ *ppos = 0;
+ }
+
+ return count;
+}
+
+
+static int
+vicam_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ // TODO: allocate the raw frame buffer if necessary
+ unsigned long page, pos;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end-vma->vm_start;
+ struct vicam_camera *cam = file->private_data;
+
+ if (!cam)
+ return -ENODEV;
+
+ DBG("vicam_mmap: %ld\n", size);
+
+ /* We let mmap allocate as much as it wants because Linux was adding 2048 bytes
+ * to the size the application requested for mmap and it was screwing apps up.
+ if (size > VICAM_FRAMES*VICAM_MAX_FRAME_SIZE)
+ return -EINVAL;
+ */
+
+ pos = (unsigned long)cam->framebuf;
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_VIDEO_PROC_FS)
+
+static struct proc_dir_entry *vicam_proc_root = NULL;
+
+static int vicam_read_helper(char *page, char **start, off_t off,
+ int count, int *eof, int value)
+{
+ char *out = page;
+ int len;
+
+ out += sprintf(out, "%d",value);
+
+ len = out - page;
+ len -= off;
+ if (len < count) {
+ *eof = 1;
+ if (len <= 0)
+ return 0;
+ } else
+ len = count;
+
+ *start = page + off;
+ return len;
+}
+
+static int vicam_read_proc_shutter(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ return vicam_read_helper(page,start,off,count,eof,
+ ((struct vicam_camera *)data)->shutter_speed);
+}
+
+static int vicam_read_proc_gain(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ return vicam_read_helper(page,start,off,count,eof,
+ ((struct vicam_camera *)data)->gain);
+}
+
+static int
+vicam_write_proc_shutter(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ u16 stmp;
+ char kbuf[8];
+ struct vicam_camera *cam = (struct vicam_camera *) data;
+
+ if (count > 6)
+ return -EINVAL;
+
+ if (copy_from_user(kbuf, buffer, count))
+ return -EFAULT;
+
+ stmp = (u16) simple_strtoul(kbuf, NULL, 10);
+ if (stmp < 4 || stmp > 32000)
+ return -EINVAL;
+
+ cam->shutter_speed = stmp;
+
+ return count;
+}
+
+static int
+vicam_write_proc_gain(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ u16 gtmp;
+ char kbuf[8];
+
+ struct vicam_camera *cam = (struct vicam_camera *) data;
+
+ if (count > 4)
+ return -EINVAL;
+
+ if (copy_from_user(kbuf, buffer, count))
+ return -EFAULT;
+
+ gtmp = (u16) simple_strtoul(kbuf, NULL, 10);
+ if (gtmp > 255)
+ return -EINVAL;
+ cam->gain = gtmp;
+
+ return count;
+}
+
+static void
+vicam_create_proc_root(void)
+{
+ vicam_proc_root = proc_mkdir("video/vicam", NULL);
+
+ if (vicam_proc_root)
+ vicam_proc_root->owner = THIS_MODULE;
+ else
+ printk(KERN_ERR
+ "could not create /proc entry for vicam!");
+}
+
+static void
+vicam_destroy_proc_root(void)
+{
+ if (vicam_proc_root)
+ remove_proc_entry("video/vicam", 0);
+}
+
+static void
+vicam_create_proc_entry(struct vicam_camera *cam)
+{
+ char name[64];
+ struct proc_dir_entry *ent;
+
+ DBG(KERN_INFO "vicam: creating proc entry\n");
+
+ if (!vicam_proc_root || !cam) {
+ printk(KERN_INFO
+ "vicam: could not create proc entry, %s pointer is null.\n",
+ (!cam ? "camera" : "root"));
+ return;
+ }
+
+ sprintf(name, "video%d", cam->vdev.minor);
+
+ cam->proc_dir = proc_mkdir(name, vicam_proc_root);
+
+ if ( !cam->proc_dir )
+ return; // FIXME: We should probably return an error here
+
+ ent = create_proc_entry("shutter", S_IFREG | S_IRUGO | S_IWUSR,
+ cam->proc_dir);
+ if (ent) {
+ ent->data = cam;
+ ent->read_proc = vicam_read_proc_shutter;
+ ent->write_proc = vicam_write_proc_shutter;
+ ent->size = 64;
+ }
+
+ ent = create_proc_entry("gain", S_IFREG | S_IRUGO | S_IWUSR,
+ cam->proc_dir);
+ if (ent) {
+ ent->data = cam;
+ ent->read_proc = vicam_read_proc_gain;
+ ent->write_proc = vicam_write_proc_gain;
+ ent->size = 64;
+ }
+}
+
+static void
+vicam_destroy_proc_entry(void *ptr)
+{
+ struct vicam_camera *cam = (struct vicam_camera *) ptr;
+ char name[16];
+
+ if ( !cam->proc_dir )
+ return;
+
+ sprintf(name, "video%d", cam->vdev.minor);
+ remove_proc_entry("shutter", cam->proc_dir);
+ remove_proc_entry("gain", cam->proc_dir);
+ remove_proc_entry(name,vicam_proc_root);
+ cam->proc_dir = NULL;
+
+}
+
+#else
+static inline void vicam_create_proc_root(void) { }
+static inline void vicam_destroy_proc_root(void) { }
+static inline void vicam_create_proc_entry(struct vicam_camera *cam) { }
+static inline void vicam_destroy_proc_entry(void *ptr) { }
+#endif
+
+static struct file_operations vicam_fops = {
+ .owner = THIS_MODULE,
+ .open = vicam_open,
+ .release = vicam_close,
+ .read = vicam_read,
+ .mmap = vicam_mmap,
+ .ioctl = vicam_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device vicam_template = {
+ .owner = THIS_MODULE,
+ .name = "ViCam-based USB Camera",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_VICAM,
+ .fops = &vicam_fops,
+ .minor = -1,
+};
+
+/* table of devices that work with this driver */
+static struct usb_device_id vicam_table[] = {
+ {USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, vicam_table);
+
+static struct usb_driver vicam_driver = {
+ .name = "vicam",
+ .probe = vicam_probe,
+ .disconnect = vicam_disconnect,
+ .id_table = vicam_table
+};
+
+/**
+ * vicam_probe
+ * @intf: the interface
+ * @id: the device id
+ *
+ * Called by the usb core when a new device is connected that it thinks
+ * this driver might be interested in.
+ */
+static int
+vicam_probe( struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ int bulkEndpoint = 0;
+ const struct usb_host_interface *interface;
+ const struct usb_endpoint_descriptor *endpoint;
+ struct vicam_camera *cam;
+
+ printk(KERN_INFO "ViCam based webcam connected\n");
+
+ interface = intf->cur_altsetting;
+
+ DBG(KERN_DEBUG "Interface %d. has %u. endpoints!\n",
+ interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints));
+ endpoint = &interface->endpoint[0].desc;
+
+ if ((endpoint->bEndpointAddress & 0x80) &&
+ ((endpoint->bmAttributes & 3) == 0x02)) {
+ /* we found a bulk in endpoint */
+ bulkEndpoint = endpoint->bEndpointAddress;
+ } else {
+ printk(KERN_ERR
+ "No bulk in endpoint was found ?! (this is bad)\n");
+ }
+
+ if ((cam =
+ kmalloc(sizeof (struct vicam_camera), GFP_KERNEL)) == NULL) {
+ printk(KERN_WARNING
+ "could not allocate kernel memory for vicam_camera struct\n");
+ return -ENOMEM;
+ }
+
+ memset(cam, 0, sizeof (struct vicam_camera));
+
+ cam->shutter_speed = 15;
+
+ mutex_init(&cam->cam_lock);
+
+ memcpy(&cam->vdev, &vicam_template,
+ sizeof (vicam_template));
+ cam->vdev.priv = cam; // sort of a reverse mapping for those functions that get vdev only
+
+ cam->udev = dev;
+ cam->bulkEndpoint = bulkEndpoint;
+
+ if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) == -1) {
+ kfree(cam);
+ printk(KERN_WARNING "video_register_device failed\n");
+ return -EIO;
+ }
+
+ vicam_create_proc_entry(cam);
+
+ printk(KERN_INFO "ViCam webcam driver now controlling video device %d\n",cam->vdev.minor);
+
+ usb_set_intfdata (intf, cam);
+
+ return 0;
+}
+
+static void
+vicam_disconnect(struct usb_interface *intf)
+{
+ int open_count;
+ struct vicam_camera *cam = usb_get_intfdata (intf);
+ usb_set_intfdata (intf, NULL);
+
+ /* we must unregister the device before taking its
+ * cam_lock. This is because the video open call
+ * holds the same lock as video unregister. if we
+ * unregister inside of the cam_lock and open also
+ * uses the cam_lock, we get deadlock.
+ */
+
+ video_unregister_device(&cam->vdev);
+
+ /* stop the camera from being used */
+
+ mutex_lock(&cam->cam_lock);
+
+ /* mark the camera as gone */
+
+ cam->udev = NULL;
+
+ vicam_destroy_proc_entry(cam);
+
+ /* the only thing left to do is synchronize with
+ * our close/release function on who should release
+ * the camera memory. if there are any users using the
+ * camera, it's their job. if there are no users,
+ * it's ours.
+ */
+
+ open_count = cam->open_count;
+
+ mutex_unlock(&cam->cam_lock);
+
+ if (!open_count) {
+ kfree(cam);
+ }
+
+ printk(KERN_DEBUG "ViCam-based WebCam disconnected\n");
+}
+
+/*
+ */
+static int __init
+usb_vicam_init(void)
+{
+ int retval;
+ DBG(KERN_INFO "ViCam-based WebCam driver startup\n");
+ vicam_create_proc_root();
+ retval = usb_register(&vicam_driver);
+ if (retval)
+ printk(KERN_WARNING "usb_register failed!\n");
+ return retval;
+}
+
+static void __exit
+usb_vicam_exit(void)
+{
+ DBG(KERN_INFO
+ "ViCam-based WebCam driver shutdown\n");
+
+ usb_deregister(&vicam_driver);
+ vicam_destroy_proc_root();
+}
+
+module_init(usb_vicam_init);
+module_exit(usb_vicam_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c
new file mode 100644
index 000000000000..b57dec3782e0
--- /dev/null
+++ b/drivers/media/video/w9968cf.c
@@ -0,0 +1,3691 @@
+/***************************************************************************
+ * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. *
+ * *
+ * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * - Memory management code from bttv driver by Ralph Metzler, *
+ * Marcus Metzler and Gerd Knorr. *
+ * - I2C interface to kernel, high-level image sensor control routines and *
+ * some symbolic names from OV511 driver by Mark W. McClelland. *
+ * - Low-level I2C fast write function by Piotr Czerczak. *
+ * - Low-level I2C read function by Frederic Jouault. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/stddef.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+#include <linux/page-flags.h>
+#include <linux/moduleparam.h>
+
+#include "w9968cf.h"
+#include "w9968cf_decoder.h"
+
+static struct w9968cf_vpp_t* w9968cf_vpp;
+static DECLARE_WAIT_QUEUE_HEAD(w9968cf_vppmod_wait);
+
+static LIST_HEAD(w9968cf_dev_list); /* head of V4L registered cameras list */
+static DEFINE_MUTEX(w9968cf_devlist_mutex); /* semaphore for list traversal */
+
+static DECLARE_RWSEM(w9968cf_disconnect); /* prevent races with open() */
+
+
+/****************************************************************************
+ * Module macros and parameters *
+ ****************************************************************************/
+
+MODULE_DEVICE_TABLE(usb, winbond_id_table);
+
+MODULE_AUTHOR(W9968CF_MODULE_AUTHOR" "W9968CF_AUTHOR_EMAIL);
+MODULE_DESCRIPTION(W9968CF_MODULE_NAME);
+MODULE_VERSION(W9968CF_MODULE_VERSION);
+MODULE_LICENSE(W9968CF_MODULE_LICENSE);
+MODULE_SUPPORTED_DEVICE("Video");
+
+static int ovmod_load = W9968CF_OVMOD_LOAD;
+static unsigned short simcams = W9968CF_SIMCAMS;
+static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/
+static unsigned int packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_PACKET_SIZE};
+static unsigned short max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_BUFFERS};
+static int double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_DOUBLE_BUFFER};
+static int clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING};
+static unsigned short filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_FILTER_TYPE};
+static int largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW};
+static unsigned short decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_DECOMPRESSION};
+static int upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING};
+static unsigned short force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0};
+static int force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB};
+static int autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT};
+static int autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP};
+static unsigned short lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_LIGHTFREQ};
+static int bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]=
+ W9968CF_BANDINGFILTER};
+static short clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV};
+static int backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT};
+static int mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR};
+static int monochrome[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_MONOCHROME};
+static unsigned int brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_BRIGHTNESS};
+static unsigned int hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE};
+static unsigned int colour[]={[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR};
+static unsigned int contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_CONTRAST};
+static unsigned int whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_WHITENESS};
+#ifdef W9968CF_DEBUG
+static unsigned short debug = W9968CF_DEBUG_LEVEL;
+static int specific_debug = W9968CF_SPECIFIC_DEBUG;
+#endif
+
+static unsigned int param_nv[24]; /* number of values per parameter */
+
+#ifdef CONFIG_KMOD
+module_param(ovmod_load, bool, 0644);
+#endif
+module_param(simcams, ushort, 0644);
+module_param_array(video_nr, short, &param_nv[0], 0444);
+module_param_array(packet_size, uint, &param_nv[1], 0444);
+module_param_array(max_buffers, ushort, &param_nv[2], 0444);
+module_param_array(double_buffer, bool, &param_nv[3], 0444);
+module_param_array(clamping, bool, &param_nv[4], 0444);
+module_param_array(filter_type, ushort, &param_nv[5], 0444);
+module_param_array(largeview, bool, &param_nv[6], 0444);
+module_param_array(decompression, ushort, &param_nv[7], 0444);
+module_param_array(upscaling, bool, &param_nv[8], 0444);
+module_param_array(force_palette, ushort, &param_nv[9], 0444);
+module_param_array(force_rgb, ushort, &param_nv[10], 0444);
+module_param_array(autobright, bool, &param_nv[11], 0444);
+module_param_array(autoexp, bool, &param_nv[12], 0444);
+module_param_array(lightfreq, ushort, &param_nv[13], 0444);
+module_param_array(bandingfilter, bool, &param_nv[14], 0444);
+module_param_array(clockdiv, short, &param_nv[15], 0444);
+module_param_array(backlight, bool, &param_nv[16], 0444);
+module_param_array(mirror, bool, &param_nv[17], 0444);
+module_param_array(monochrome, bool, &param_nv[18], 0444);
+module_param_array(brightness, uint, &param_nv[19], 0444);
+module_param_array(hue, uint, &param_nv[20], 0444);
+module_param_array(colour, uint, &param_nv[21], 0444);
+module_param_array(contrast, uint, &param_nv[22], 0444);
+module_param_array(whiteness, uint, &param_nv[23], 0444);
+#ifdef W9968CF_DEBUG
+module_param(debug, ushort, 0644);
+module_param(specific_debug, bool, 0644);
+#endif
+
+#ifdef CONFIG_KMOD
+MODULE_PARM_DESC(ovmod_load,
+ "\n<0|1> Automatic 'ovcamchip' module loading."
+ "\n0 disabled, 1 enabled."
+ "\nIf enabled,'insmod' searches for the required 'ovcamchip'"
+ "\nmodule in the system, according to its configuration, and"
+ "\nattempts to load that module automatically. This action is"
+ "\nperformed once as soon as the 'w9968cf' module is loaded"
+ "\ninto memory."
+ "\nDefault value is "__MODULE_STRING(W9968CF_OVMOD_LOAD)"."
+ "\n");
+#endif
+MODULE_PARM_DESC(simcams,
+ "\n<n> Number of cameras allowed to stream simultaneously."
+ "\nn may vary from 0 to "
+ __MODULE_STRING(W9968CF_MAX_DEVICES)"."
+ "\nDefault value is "__MODULE_STRING(W9968CF_SIMCAMS)"."
+ "\n");
+MODULE_PARM_DESC(video_nr,
+ "\n<-1|n[,...]> Specify V4L minor mode number."
+ "\n -1 = use next available (default)"
+ "\n n = use minor number n (integer >= 0)"
+ "\nYou can specify up to "__MODULE_STRING(W9968CF_MAX_DEVICES)
+ " cameras this way."
+ "\nFor example:"
+ "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+ "\nthe second camera and use auto for the first"
+ "\none and for every other camera."
+ "\n");
+MODULE_PARM_DESC(packet_size,
+ "\n<n[,...]> Specify the maximum data payload"
+ "\nsize in bytes for alternate settings, for each device."
+ "\nn is scaled between 63 and 1023 "
+ "(default is "__MODULE_STRING(W9968CF_PACKET_SIZE)")."
+ "\n");
+MODULE_PARM_DESC(max_buffers,
+ "\n<n[,...]> For advanced users."
+ "\nSpecify the maximum number of video frame buffers"
+ "\nto allocate for each device, from 2 to "
+ __MODULE_STRING(W9968CF_MAX_BUFFERS)
+ ". (default is "__MODULE_STRING(W9968CF_BUFFERS)")."
+ "\n");
+MODULE_PARM_DESC(double_buffer,
+ "\n<0|1[,...]> "
+ "Hardware double buffering: 0 disabled, 1 enabled."
+ "\nIt should be enabled if you want smooth video output: if"
+ "\nyou obtain out of sync. video, disable it, or try to"
+ "\ndecrease the 'clockdiv' module parameter value."
+ "\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(clamping,
+ "\n<0|1[,...]> Video data clamping: 0 disabled, 1 enabled."
+ "\nDefault value is "__MODULE_STRING(W9968CF_CLAMPING)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(filter_type,
+ "\n<0|1|2[,...]> Video filter type."
+ "\n0 none, 1 (1-2-1) 3-tap filter, "
+ "2 (2-3-6-3-2) 5-tap filter."
+ "\nDefault value is "__MODULE_STRING(W9968CF_FILTER_TYPE)
+ " for every device."
+ "\nThe filter is used to reduce noise and aliasing artifacts"
+ "\nproduced by the CCD or CMOS image sensor, and the scaling"
+ " process."
+ "\n");
+MODULE_PARM_DESC(largeview,
+ "\n<0|1[,...]> Large view: 0 disabled, 1 enabled."
+ "\nDefault value is "__MODULE_STRING(W9968CF_LARGEVIEW)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(upscaling,
+ "\n<0|1[,...]> Software scaling (for non-compressed video):"
+ "\n0 disabled, 1 enabled."
+ "\nDisable it if you have a slow CPU or you don't have"
+ " enough memory."
+ "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING)
+ " for every device."
+ "\nIf 'w9968cf-vpp' is not present, this parameter is"
+ " set to 0."
+ "\n");
+MODULE_PARM_DESC(decompression,
+ "\n<0|1|2[,...]> Software video decompression:"
+ "\n- 0 disables decompression (doesn't allow formats needing"
+ " decompression)"
+ "\n- 1 forces decompression (allows formats needing"
+ " decompression only);"
+ "\n- 2 allows any permitted formats."
+ "\nFormats supporting compressed video are YUV422P and"
+ " YUV420P/YUV420 "
+ "\nin any resolutions where both width and height are "
+ "a multiple of 16."
+ "\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION)
+ " for every device."
+ "\nIf 'w9968cf-vpp' is not present, forcing decompression is "
+ "\nnot allowed; in this case this parameter is set to 2."
+ "\n");
+MODULE_PARM_DESC(force_palette,
+ "\n<0"
+ "|" __MODULE_STRING(VIDEO_PALETTE_UYVY)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUV420)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUV422P)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUV420P)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUYV)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUV422)
+ "|" __MODULE_STRING(VIDEO_PALETTE_GREY)
+ "|" __MODULE_STRING(VIDEO_PALETTE_RGB555)
+ "|" __MODULE_STRING(VIDEO_PALETTE_RGB565)
+ "|" __MODULE_STRING(VIDEO_PALETTE_RGB24)
+ "|" __MODULE_STRING(VIDEO_PALETTE_RGB32)
+ "[,...]>"
+ " Force picture palette."
+ "\nIn order:"
+ "\n- 0 allows any of the following formats:"
+ "\n- UYVY 16 bpp - Original video, compression disabled"
+ "\n- YUV420 12 bpp - Original video, compression enabled"
+ "\n- YUV422P 16 bpp - Original video, compression enabled"
+ "\n- YUV420P 12 bpp - Original video, compression enabled"
+ "\n- YUVY 16 bpp - Software conversion from UYVY"
+ "\n- YUV422 16 bpp - Software conversion from UYVY"
+ "\n- GREY 8 bpp - Software conversion from UYVY"
+ "\n- RGB555 16 bpp - Software conversion from UYVY"
+ "\n- RGB565 16 bpp - Software conversion from UYVY"
+ "\n- RGB24 24 bpp - Software conversion from UYVY"
+ "\n- RGB32 32 bpp - Software conversion from UYVY"
+ "\nWhen not 0, this parameter will override 'decompression'."
+ "\nDefault value is 0 for every device."
+ "\nInitial palette is "
+ __MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"."
+ "\nIf 'w9968cf-vpp' is not present, this parameter is"
+ " set to 9 (UYVY)."
+ "\n");
+MODULE_PARM_DESC(force_rgb,
+ "\n<0|1[,...]> Read RGB video data instead of BGR:"
+ "\n 1 = use RGB component ordering."
+ "\n 0 = use BGR component ordering."
+ "\nThis parameter has effect when using RGBX palettes only."
+ "\nDefault value is "__MODULE_STRING(W9968CF_FORCE_RGB)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(autobright,
+ "\n<0|1[,...]> Image sensor automatically changes brightness:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_AUTOBRIGHT)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(autoexp,
+ "\n<0|1[,...]> Image sensor automatically changes exposure:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(lightfreq,
+ "\n<50|60[,...]> Light frequency in Hz:"
+ "\n 50 for European and Asian lighting,"
+ " 60 for American lighting."
+ "\nDefault value is "__MODULE_STRING(W9968CF_LIGHTFREQ)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(bandingfilter,
+ "\n<0|1[,...]> Banding filter to reduce effects of"
+ " fluorescent lighting:"
+ "\n 0 disabled, 1 enabled."
+ "\nThis filter tries to reduce the pattern of horizontal"
+ "\nlight/dark bands caused by some (usually fluorescent)"
+ " lighting."
+ "\nDefault value is "__MODULE_STRING(W9968CF_BANDINGFILTER)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(clockdiv,
+ "\n<-1|n[,...]> "
+ "Force pixel clock divisor to a specific value (for experts):"
+ "\n n may vary from 0 to 127."
+ "\n -1 for automatic value."
+ "\nSee also the 'double_buffer' module parameter."
+ "\nDefault value is "__MODULE_STRING(W9968CF_CLOCKDIV)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(backlight,
+ "\n<0|1[,...]> Objects are lit from behind:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_BACKLIGHT)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(mirror,
+ "\n<0|1[,...]> Reverse image horizontally:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_MIRROR)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(monochrome,
+ "\n<0|1[,...]> Use image sensor as monochrome sensor:"
+ "\n 0 = no, 1 = yes"
+ "\nNot all the sensors support monochrome color."
+ "\nDefault value is "__MODULE_STRING(W9968CF_MONOCHROME)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(brightness,
+ "\n<n[,...]> Set picture brightness (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_BRIGHTNESS)
+ " for every device."
+ "\nThis parameter has no effect if 'autobright' is enabled."
+ "\n");
+MODULE_PARM_DESC(hue,
+ "\n<n[,...]> Set picture hue (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_HUE)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(colour,
+ "\n<n[,...]> Set picture saturation (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_COLOUR)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(contrast,
+ "\n<n[,...]> Set picture contrast (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_CONTRAST)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(whiteness,
+ "\n<n[,...]> Set picture whiteness (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_WHITENESS)
+ " for every device."
+ "\n");
+#ifdef W9968CF_DEBUG
+MODULE_PARM_DESC(debug,
+ "\n<n> Debugging information level, from 0 to 6:"
+ "\n0 = none (use carefully)"
+ "\n1 = critical errors"
+ "\n2 = significant informations"
+ "\n3 = configuration or general messages"
+ "\n4 = warnings"
+ "\n5 = called functions"
+ "\n6 = function internals"
+ "\nLevel 5 and 6 are useful for testing only, when only "
+ "one device is used."
+ "\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"."
+ "\n");
+MODULE_PARM_DESC(specific_debug,
+ "\n<0|1> Enable or disable specific debugging messages:"
+ "\n0 = print messages concerning every level"
+ " <= 'debug' level."
+ "\n1 = print messages concerning the level"
+ " indicated by 'debug'."
+ "\nDefault value is "
+ __MODULE_STRING(W9968CF_SPECIFIC_DEBUG)"."
+ "\n");
+#endif /* W9968CF_DEBUG */
+
+
+
+/****************************************************************************
+ * Some prototypes *
+ ****************************************************************************/
+
+/* Video4linux interface */
+static struct file_operations w9968cf_fops;
+static int w9968cf_open(struct inode*, struct file*);
+static int w9968cf_release(struct inode*, struct file*);
+static int w9968cf_mmap(struct file*, struct vm_area_struct*);
+static int w9968cf_ioctl(struct inode*, struct file*, unsigned, unsigned long);
+static ssize_t w9968cf_read(struct file*, char __user *, size_t, loff_t*);
+static int w9968cf_v4l_ioctl(struct inode*, struct file*, unsigned int,
+ void __user *);
+
+/* USB-specific */
+static int w9968cf_start_transfer(struct w9968cf_device*);
+static int w9968cf_stop_transfer(struct w9968cf_device*);
+static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index);
+static int w9968cf_read_reg(struct w9968cf_device*, u16 index);
+static int w9968cf_write_fsb(struct w9968cf_device*, u16* data);
+static int w9968cf_write_sb(struct w9968cf_device*, u16 value);
+static int w9968cf_read_sb(struct w9968cf_device*);
+static int w9968cf_upload_quantizationtables(struct w9968cf_device*);
+static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs);
+
+/* Low-level I2C (SMBus) I/O */
+static int w9968cf_smbus_start(struct w9968cf_device*);
+static int w9968cf_smbus_stop(struct w9968cf_device*);
+static int w9968cf_smbus_write_byte(struct w9968cf_device*, u8 v);
+static int w9968cf_smbus_read_byte(struct w9968cf_device*, u8* v);
+static int w9968cf_smbus_write_ack(struct w9968cf_device*);
+static int w9968cf_smbus_read_ack(struct w9968cf_device*);
+static int w9968cf_smbus_refresh_bus(struct w9968cf_device*);
+static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam,
+ u16 address, u8* value);
+static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device*, u16 address,
+ u8 subaddress, u8* value);
+static int w9968cf_i2c_adap_write_byte(struct w9968cf_device*,
+ u16 address, u8 subaddress);
+static int w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device*,
+ u16 address, u8 subaddress,
+ u8 value);
+
+/* I2C interface to kernel */
+static int w9968cf_i2c_init(struct w9968cf_device*);
+static int w9968cf_i2c_smbus_xfer(struct i2c_adapter*, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data*);
+static u32 w9968cf_i2c_func(struct i2c_adapter*);
+static int w9968cf_i2c_attach_inform(struct i2c_client*);
+static int w9968cf_i2c_detach_inform(struct i2c_client*);
+static int w9968cf_i2c_control(struct i2c_adapter*, unsigned int cmd,
+ unsigned long arg);
+
+/* Memory management */
+static void* rvmalloc(unsigned long size);
+static void rvfree(void *mem, unsigned long size);
+static void w9968cf_deallocate_memory(struct w9968cf_device*);
+static int w9968cf_allocate_memory(struct w9968cf_device*);
+
+/* High-level image sensor control functions */
+static int w9968cf_sensor_set_control(struct w9968cf_device*,int cid,int val);
+static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val);
+static int w9968cf_sensor_cmd(struct w9968cf_device*,
+ unsigned int cmd, void *arg);
+static int w9968cf_sensor_init(struct w9968cf_device*);
+static int w9968cf_sensor_update_settings(struct w9968cf_device*);
+static int w9968cf_sensor_get_picture(struct w9968cf_device*);
+static int w9968cf_sensor_update_picture(struct w9968cf_device*,
+ struct video_picture pict);
+
+/* Other helper functions */
+static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*,
+ enum w9968cf_model_id,
+ const unsigned short dev_nr);
+static void w9968cf_adjust_configuration(struct w9968cf_device*);
+static int w9968cf_turn_on_led(struct w9968cf_device*);
+static int w9968cf_init_chip(struct w9968cf_device*);
+static inline u16 w9968cf_valid_palette(u16 palette);
+static inline u16 w9968cf_valid_depth(u16 palette);
+static inline u8 w9968cf_need_decompression(u16 palette);
+static int w9968cf_set_picture(struct w9968cf_device*, struct video_picture);
+static int w9968cf_set_window(struct w9968cf_device*, struct video_window);
+static int w9968cf_postprocess_frame(struct w9968cf_device*,
+ struct w9968cf_frame_t*);
+static int w9968cf_adjust_window_size(struct w9968cf_device*, u16* w, u16* h);
+static void w9968cf_init_framelist(struct w9968cf_device*);
+static void w9968cf_push_frame(struct w9968cf_device*, u8 f_num);
+static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**);
+static void w9968cf_release_resources(struct w9968cf_device*);
+
+
+
+/****************************************************************************
+ * Symbolic names *
+ ****************************************************************************/
+
+/* Used to represent a list of values and their respective symbolic names */
+struct w9968cf_symbolic_list {
+ const int num;
+ const char *name;
+};
+
+/*--------------------------------------------------------------------------
+ Returns the name of the matching element in the symbolic_list array. The
+ end of the list must be marked with an element that has a NULL name.
+ --------------------------------------------------------------------------*/
+static inline const char *
+symbolic(struct w9968cf_symbolic_list list[], const int num)
+{
+ int i;
+
+ for (i = 0; list[i].name != NULL; i++)
+ if (list[i].num == num)
+ return (list[i].name);
+
+ return "Unknown";
+}
+
+static struct w9968cf_symbolic_list camlist[] = {
+ { W9968CF_MOD_GENERIC, "W996[87]CF JPEG USB Dual Mode Camera" },
+ { W9968CF_MOD_CLVBWGP, "Creative Labs Video Blaster WebCam Go Plus" },
+
+ /* Other cameras (having the same descriptors as Generic W996[87]CF) */
+ { W9968CF_MOD_ADPVDMA, "Aroma Digi Pen VGA Dual Mode ADG-5000" },
+ { W9986CF_MOD_AAU, "AVerMedia AVerTV USB" },
+ { W9968CF_MOD_CLVBWG, "Creative Labs Video Blaster WebCam Go" },
+ { W9968CF_MOD_LL, "Lebon LDC-035A" },
+ { W9968CF_MOD_EEEMC, "Ezonics EZ-802 EZMega Cam" },
+ { W9968CF_MOD_OOE, "OmniVision OV8610-EDE" },
+ { W9968CF_MOD_ODPVDMPC, "OPCOM Digi Pen VGA Dual Mode Pen Camera" },
+ { W9968CF_MOD_PDPII, "Pretec Digi Pen-II" },
+ { W9968CF_MOD_PDP480, "Pretec DigiPen-480" },
+
+ { -1, NULL }
+};
+
+static struct w9968cf_symbolic_list senlist[] = {
+ { CC_OV76BE, "OV76BE" },
+ { CC_OV7610, "OV7610" },
+ { CC_OV7620, "OV7620" },
+ { CC_OV7620AE, "OV7620AE" },
+ { CC_OV6620, "OV6620" },
+ { CC_OV6630, "OV6630" },
+ { CC_OV6630AE, "OV6630AE" },
+ { CC_OV6630AF, "OV6630AF" },
+ { -1, NULL }
+};
+
+/* Video4Linux1 palettes */
+static struct w9968cf_symbolic_list v4l1_plist[] = {
+ { VIDEO_PALETTE_GREY, "GREY" },
+ { VIDEO_PALETTE_HI240, "HI240" },
+ { VIDEO_PALETTE_RGB565, "RGB565" },
+ { VIDEO_PALETTE_RGB24, "RGB24" },
+ { VIDEO_PALETTE_RGB32, "RGB32" },
+ { VIDEO_PALETTE_RGB555, "RGB555" },
+ { VIDEO_PALETTE_YUV422, "YUV422" },
+ { VIDEO_PALETTE_YUYV, "YUYV" },
+ { VIDEO_PALETTE_UYVY, "UYVY" },
+ { VIDEO_PALETTE_YUV420, "YUV420" },
+ { VIDEO_PALETTE_YUV411, "YUV411" },
+ { VIDEO_PALETTE_RAW, "RAW" },
+ { VIDEO_PALETTE_YUV422P, "YUV422P" },
+ { VIDEO_PALETTE_YUV411P, "YUV411P" },
+ { VIDEO_PALETTE_YUV420P, "YUV420P" },
+ { VIDEO_PALETTE_YUV410P, "YUV410P" },
+ { -1, NULL }
+};
+
+/* Decoder error codes: */
+static struct w9968cf_symbolic_list decoder_errlist[] = {
+ { W9968CF_DEC_ERR_CORRUPTED_DATA, "Corrupted data" },
+ { W9968CF_DEC_ERR_BUF_OVERFLOW, "Buffer overflow" },
+ { W9968CF_DEC_ERR_NO_SOI, "SOI marker not found" },
+ { W9968CF_DEC_ERR_NO_SOF0, "SOF0 marker not found" },
+ { W9968CF_DEC_ERR_NO_SOS, "SOS marker not found" },
+ { W9968CF_DEC_ERR_NO_EOI, "EOI marker not found" },
+ { -1, NULL }
+};
+
+/* URB error codes: */
+static struct w9968cf_symbolic_list urb_errlist[] = {
+ { -ENOMEM, "No memory for allocation of internal structures" },
+ { -ENOSPC, "The host controller's bandwidth is already consumed" },
+ { -ENOENT, "URB was canceled by unlink_urb" },
+ { -EXDEV, "ISO transfer only partially completed" },
+ { -EAGAIN, "Too match scheduled for the future" },
+ { -ENXIO, "URB already queued" },
+ { -EFBIG, "Too much ISO frames requested" },
+ { -ENOSR, "Buffer error (overrun)" },
+ { -EPIPE, "Specified endpoint is stalled (device not responding)"},
+ { -EOVERFLOW, "Babble (bad cable?)" },
+ { -EPROTO, "Bit-stuff error (bad cable?)" },
+ { -EILSEQ, "CRC/Timeout" },
+ { -ETIMEDOUT, "NAK (device does not respond)" },
+ { -1, NULL }
+};
+
+
+
+/****************************************************************************
+ * Memory management functions *
+ ****************************************************************************/
+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 (size > 0) {
+ SetPageReserved(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;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
+
+/*--------------------------------------------------------------------------
+ Deallocate previously allocated memory.
+ --------------------------------------------------------------------------*/
+static void w9968cf_deallocate_memory(struct w9968cf_device* cam)
+{
+ u8 i;
+
+ /* Free the isochronous transfer buffers */
+ for (i = 0; i < W9968CF_URBS; i++) {
+ kfree(cam->transfer_buffer[i]);
+ cam->transfer_buffer[i] = NULL;
+ }
+
+ /* Free temporary frame buffer */
+ if (cam->frame_tmp.buffer) {
+ rvfree(cam->frame_tmp.buffer, cam->frame_tmp.size);
+ cam->frame_tmp.buffer = NULL;
+ }
+
+ /* Free helper buffer */
+ if (cam->frame_vpp.buffer) {
+ rvfree(cam->frame_vpp.buffer, cam->frame_vpp.size);
+ cam->frame_vpp.buffer = NULL;
+ }
+
+ /* Free video frame buffers */
+ if (cam->frame[0].buffer) {
+ rvfree(cam->frame[0].buffer, cam->nbuffers*cam->frame[0].size);
+ cam->frame[0].buffer = NULL;
+ }
+
+ cam->nbuffers = 0;
+
+ DBG(5, "Memory successfully deallocated")
+}
+
+
+/*--------------------------------------------------------------------------
+ Allocate memory buffers for USB transfers and video frames.
+ This function is called by open() only.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_allocate_memory(struct w9968cf_device* cam)
+{
+ const u16 p_size = wMaxPacketSize[cam->altsetting-1];
+ void* buff = NULL;
+ unsigned long hw_bufsize, vpp_bufsize;
+ u8 i, bpp;
+
+ /* NOTE: Deallocation is done elsewhere in case of error */
+
+ /* Calculate the max amount of raw data per frame from the device */
+ hw_bufsize = cam->maxwidth*cam->maxheight*2;
+
+ /* Calculate the max buf. size needed for post-processing routines */
+ bpp = (w9968cf_vpp) ? 4 : 2;
+ if (cam->upscaling)
+ vpp_bufsize = max(W9968CF_MAX_WIDTH*W9968CF_MAX_HEIGHT*bpp,
+ cam->maxwidth*cam->maxheight*bpp);
+ else
+ vpp_bufsize = cam->maxwidth*cam->maxheight*bpp;
+
+ /* Allocate memory for the isochronous transfer buffers */
+ for (i = 0; i < W9968CF_URBS; i++) {
+ if (!(cam->transfer_buffer[i] =
+ kzalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) {
+ DBG(1, "Couldn't allocate memory for the isochronous "
+ "transfer buffers (%u bytes)",
+ p_size * W9968CF_ISO_PACKETS)
+ return -ENOMEM;
+ }
+ }
+
+ /* Allocate memory for the temporary frame buffer */
+ if (!(cam->frame_tmp.buffer = rvmalloc(hw_bufsize))) {
+ DBG(1, "Couldn't allocate memory for the temporary "
+ "video frame buffer (%lu bytes)", hw_bufsize)
+ return -ENOMEM;
+ }
+ cam->frame_tmp.size = hw_bufsize;
+ cam->frame_tmp.number = -1;
+
+ /* Allocate memory for the helper buffer */
+ if (w9968cf_vpp) {
+ if (!(cam->frame_vpp.buffer = rvmalloc(vpp_bufsize))) {
+ DBG(1, "Couldn't allocate memory for the helper buffer"
+ " (%lu bytes)", vpp_bufsize)
+ return -ENOMEM;
+ }
+ cam->frame_vpp.size = vpp_bufsize;
+ } else
+ cam->frame_vpp.buffer = NULL;
+
+ /* Allocate memory for video frame buffers */
+ cam->nbuffers = cam->max_buffers;
+ while (cam->nbuffers >= 2) {
+ if ((buff = rvmalloc(cam->nbuffers * vpp_bufsize)))
+ break;
+ else
+ cam->nbuffers--;
+ }
+
+ if (!buff) {
+ DBG(1, "Couldn't allocate memory for the video frame buffers")
+ cam->nbuffers = 0;
+ return -ENOMEM;
+ }
+
+ if (cam->nbuffers != cam->max_buffers)
+ DBG(2, "Couldn't allocate memory for %u video frame buffers. "
+ "Only memory for %u buffers has been allocated",
+ cam->max_buffers, cam->nbuffers)
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->frame[i].buffer = buff + i*vpp_bufsize;
+ cam->frame[i].size = vpp_bufsize;
+ cam->frame[i].number = i;
+ /* Circular list */
+ if (i != cam->nbuffers-1)
+ cam->frame[i].next = &cam->frame[i+1];
+ else
+ cam->frame[i].next = &cam->frame[0];
+ cam->frame[i].status = F_UNUSED;
+ }
+
+ DBG(5, "Memory successfully allocated")
+ return 0;
+}
+
+
+
+/****************************************************************************
+ * USB-specific functions *
+ ****************************************************************************/
+
+/*--------------------------------------------------------------------------
+ This is an handler function which is called after the URBs are completed.
+ It collects multiple data packets coming from the camera by putting them
+ into frame buffers: one or more zero data length data packets are used to
+ mark the end of a video frame; the first non-zero data packet is the start
+ of the next video frame; if an error is encountered in a packet, the entire
+ video frame is discarded and grabbed again.
+ If there are no requested frames in the FIFO list, packets are collected into
+ a temporary buffer.
+ --------------------------------------------------------------------------*/
+static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct w9968cf_device* cam = (struct w9968cf_device*)urb->context;
+ struct w9968cf_frame_t** f;
+ unsigned int len, status;
+ void* pos;
+ u8 i;
+ int err = 0;
+
+ if ((!cam->streaming) || cam->disconnected) {
+ DBG(4, "Got interrupt, but not streaming")
+ return;
+ }
+
+ /* "(*f)" will be used instead of "cam->frame_current" */
+ f = &cam->frame_current;
+
+ /* If a frame has been requested and we are grabbing into
+ the temporary frame, we'll switch to that requested frame */
+ if ((*f) == &cam->frame_tmp && *cam->requested_frame) {
+ if (cam->frame_tmp.status == F_GRABBING) {
+ w9968cf_pop_frame(cam, &cam->frame_current);
+ (*f)->status = F_GRABBING;
+ (*f)->length = cam->frame_tmp.length;
+ memcpy((*f)->buffer, cam->frame_tmp.buffer,
+ (*f)->length);
+ DBG(6, "Switched from temp. frame to frame #%d",
+ (*f)->number)
+ }
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ len = urb->iso_frame_desc[i].actual_length;
+ status = urb->iso_frame_desc[i].status;
+ pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+ if (status && len != 0) {
+ DBG(4, "URB failed, error in data packet "
+ "(error #%u, %s)",
+ status, symbolic(urb_errlist, status))
+ (*f)->status = F_ERROR;
+ continue;
+ }
+
+ if (len) { /* start of frame */
+
+ if ((*f)->status == F_UNUSED) {
+ (*f)->status = F_GRABBING;
+ (*f)->length = 0;
+ }
+
+ /* Buffer overflows shouldn't happen, however...*/
+ if ((*f)->length + len > (*f)->size) {
+ DBG(4, "Buffer overflow: bad data packets")
+ (*f)->status = F_ERROR;
+ }
+
+ if ((*f)->status == F_GRABBING) {
+ memcpy((*f)->buffer + (*f)->length, pos, len);
+ (*f)->length += len;
+ }
+
+ } else if ((*f)->status == F_GRABBING) { /* end of frame */
+
+ DBG(6, "Frame #%d successfully grabbed", (*f)->number)
+
+ if (cam->vpp_flag & VPP_DECOMPRESSION) {
+ err = w9968cf_vpp->check_headers((*f)->buffer,
+ (*f)->length);
+ if (err) {
+ DBG(4, "Skip corrupted frame: %s",
+ symbolic(decoder_errlist, err))
+ (*f)->status = F_UNUSED;
+ continue; /* grab this frame again */
+ }
+ }
+
+ (*f)->status = F_READY;
+ (*f)->queued = 0;
+
+ /* Take a pointer to the new frame from the FIFO list.
+ If the list is empty,we'll use the temporary frame*/
+ if (*cam->requested_frame)
+ w9968cf_pop_frame(cam, &cam->frame_current);
+ else {
+ cam->frame_current = &cam->frame_tmp;
+ (*f)->status = F_UNUSED;
+ }
+
+ } else if ((*f)->status == F_ERROR)
+ (*f)->status = F_UNUSED; /* grab it again */
+
+ PDBGG("Frame length %lu | pack.#%u | pack.len. %u | state %d",
+ (unsigned long)(*f)->length, i, len, (*f)->status)
+
+ } /* end for */
+
+ /* Resubmit this URB */
+ urb->dev = cam->usbdev;
+ urb->status = 0;
+ spin_lock(&cam->urb_lock);
+ if (cam->streaming)
+ if ((err = usb_submit_urb(urb, GFP_ATOMIC))) {
+ cam->misconfigured = 1;
+ DBG(1, "Couldn't resubmit the URB: error %d, %s",
+ err, symbolic(urb_errlist, err))
+ }
+ spin_unlock(&cam->urb_lock);
+
+ /* Wake up the user process */
+ wake_up_interruptible(&cam->wait_queue);
+}
+
+
+/*---------------------------------------------------------------------------
+ Setup the URB structures for the isochronous transfer.
+ Submit the URBs so that the data transfer begins.
+ Return 0 on success, a negative number otherwise.
+ ---------------------------------------------------------------------------*/
+static int w9968cf_start_transfer(struct w9968cf_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ struct urb* urb;
+ const u16 p_size = wMaxPacketSize[cam->altsetting-1];
+ u16 w, h, d;
+ int vidcapt;
+ u32 t_size;
+ int err = 0;
+ s8 i, j;
+
+ for (i = 0; i < W9968CF_URBS; i++) {
+ urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL);
+ cam->urb[i] = urb;
+ if (!urb) {
+ for (j = 0; j < i; j++)
+ usb_free_urb(cam->urb[j]);
+ DBG(1, "Couldn't allocate the URB structures")
+ return -ENOMEM;
+ }
+
+ urb->dev = udev;
+ urb->context = (void*)cam;
+ urb->pipe = usb_rcvisocpipe(udev, 1);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->number_of_packets = W9968CF_ISO_PACKETS;
+ urb->complete = w9968cf_urb_complete;
+ urb->transfer_buffer = cam->transfer_buffer[i];
+ urb->transfer_buffer_length = p_size*W9968CF_ISO_PACKETS;
+ urb->interval = 1;
+ for (j = 0; j < W9968CF_ISO_PACKETS; j++) {
+ urb->iso_frame_desc[j].offset = p_size*j;
+ urb->iso_frame_desc[j].length = p_size;
+ }
+ }
+
+ /* Transfer size per frame, in WORD ! */
+ d = cam->hw_depth;
+ w = cam->hw_width;
+ h = cam->hw_height;
+
+ t_size = (w*h*d)/16;
+
+ err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */
+ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */
+
+ /* Transfer size */
+ err += w9968cf_write_reg(cam, t_size & 0xffff, 0x3d); /* low bits */
+ err += w9968cf_write_reg(cam, t_size >> 16, 0x3e); /* high bits */
+
+ if (cam->vpp_flag & VPP_DECOMPRESSION)
+ err += w9968cf_upload_quantizationtables(cam);
+
+ vidcapt = w9968cf_read_reg(cam, 0x16); /* read picture settings */
+ err += w9968cf_write_reg(cam, vidcapt|0x8000, 0x16); /* capt. enable */
+
+ err += usb_set_interface(udev, 0, cam->altsetting);
+ err += w9968cf_write_reg(cam, 0x8a05, 0x3c); /* USB FIFO enable */
+
+ if (err || (vidcapt < 0)) {
+ for (i = 0; i < W9968CF_URBS; i++)
+ usb_free_urb(cam->urb[i]);
+ DBG(1, "Couldn't tell the camera to start the data transfer")
+ return err;
+ }
+
+ w9968cf_init_framelist(cam);
+
+ /* Begin to grab into the temporary buffer */
+ cam->frame_tmp.status = F_UNUSED;
+ cam->frame_tmp.queued = 0;
+ cam->frame_current = &cam->frame_tmp;
+
+ if (!(cam->vpp_flag & VPP_DECOMPRESSION))
+ DBG(5, "Isochronous transfer size: %lu bytes/frame",
+ (unsigned long)t_size*2)
+
+ DBG(5, "Starting the isochronous transfer...")
+
+ cam->streaming = 1;
+
+ /* Submit the URBs */
+ for (i = 0; i < W9968CF_URBS; i++) {
+ err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+ if (err) {
+ cam->streaming = 0;
+ for (j = i-1; j >= 0; j--) {
+ usb_kill_urb(cam->urb[j]);
+ usb_free_urb(cam->urb[j]);
+ }
+ DBG(1, "Couldn't send a transfer request to the "
+ "USB core (error #%d, %s)", err,
+ symbolic(urb_errlist, err))
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Stop the isochronous transfer and set alternate setting to 0 (0Mb/s).
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_stop_transfer(struct w9968cf_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ unsigned long lock_flags;
+ int err = 0;
+ s8 i;
+
+ if (!cam->streaming)
+ return 0;
+
+ /* This avoids race conditions with usb_submit_urb()
+ in the URB completition handler */
+ spin_lock_irqsave(&cam->urb_lock, lock_flags);
+ cam->streaming = 0;
+ spin_unlock_irqrestore(&cam->urb_lock, lock_flags);
+
+ for (i = W9968CF_URBS-1; i >= 0; i--)
+ if (cam->urb[i]) {
+ usb_kill_urb(cam->urb[i]);
+ usb_free_urb(cam->urb[i]);
+ cam->urb[i] = NULL;
+ }
+
+ if (cam->disconnected)
+ goto exit;
+
+ err = w9968cf_write_reg(cam, 0x0a05, 0x3c); /* stop USB transfer */
+ err += usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+ err += w9968cf_write_reg(cam, 0x0000, 0x39); /* disable JPEG encoder */
+ err += w9968cf_write_reg(cam, 0x0000, 0x16); /* stop video capture */
+
+ if (err) {
+ DBG(2, "Failed to tell the camera to stop the isochronous "
+ "transfer. However this is not a critical error.")
+ return -EIO;
+ }
+
+exit:
+ DBG(5, "Isochronous transfer stopped")
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Write a W9968CF register.
+ Return 0 on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_write_reg(struct w9968cf_device* cam, u16 value, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ int res;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+ value, index, NULL, 0, W9968CF_USB_CTRL_TIMEOUT);
+
+ if (res < 0)
+ DBG(4, "Failed to write a register "
+ "(value 0x%04X, index 0x%02X, error #%d, %s)",
+ value, index, res, symbolic(urb_errlist, res))
+
+ return (res >= 0) ? 0 : -1;
+}
+
+
+/*--------------------------------------------------------------------------
+ Read a W9968CF register.
+ Return the register value on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u16* buff = cam->control_buffer;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, buff, 2, W9968CF_USB_CTRL_TIMEOUT);
+
+ if (res < 0)
+ DBG(4, "Failed to read a register "
+ "(index 0x%02X, error #%d, %s)",
+ index, res, symbolic(urb_errlist, res))
+
+ return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+/*--------------------------------------------------------------------------
+ Write 64-bit data to the fast serial bus registers.
+ Return 0 on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data)
+{
+ struct usb_device* udev = cam->usbdev;
+ u16 value;
+ int res;
+
+ value = *data++;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+ value, 0x06, data, 6, W9968CF_USB_CTRL_TIMEOUT);
+
+ if (res < 0)
+ DBG(4, "Failed to write the FSB registers "
+ "(error #%d, %s)", res, symbolic(urb_errlist, res))
+
+ return (res >= 0) ? 0 : -1;
+}
+
+
+/*--------------------------------------------------------------------------
+ Write data to the serial bus control register.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_write_sb(struct w9968cf_device* cam, u16 value)
+{
+ int err = 0;
+
+ err = w9968cf_write_reg(cam, value, 0x01);
+ udelay(W9968CF_I2C_BUS_DELAY);
+
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Read data from the serial bus control register.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_read_sb(struct w9968cf_device* cam)
+{
+ int v = 0;
+
+ v = w9968cf_read_reg(cam, 0x01);
+ udelay(W9968CF_I2C_BUS_DELAY);
+
+ return v;
+}
+
+
+/*--------------------------------------------------------------------------
+ Upload quantization tables for the JPEG compression.
+ This function is called by w9968cf_start_transfer().
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_upload_quantizationtables(struct w9968cf_device* cam)
+{
+ u16 a, b;
+ int err = 0, i, j;
+
+ err += w9968cf_write_reg(cam, 0x0010, 0x39); /* JPEG clock enable */
+
+ for (i = 0, j = 0; i < 32; i++, j += 2) {
+ a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8);
+ b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8);
+ err += w9968cf_write_reg(cam, a, 0x40+i);
+ err += w9968cf_write_reg(cam, b, 0x60+i);
+ }
+ err += w9968cf_write_reg(cam, 0x0012, 0x39); /* JPEG encoder enable */
+
+ return err;
+}
+
+
+
+/****************************************************************************
+ * Low-level I2C I/O functions. *
+ * The adapter supports the following I2C transfer functions: *
+ * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) *
+ * i2c_adap_read_byte_data() *
+ * i2c_adap_read_byte() *
+ ****************************************************************************/
+
+static int w9968cf_smbus_start(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+
+ return err;
+}
+
+
+static int w9968cf_smbus_stop(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+
+ return err;
+}
+
+
+static int w9968cf_smbus_write_byte(struct w9968cf_device* cam, u8 v)
+{
+ u8 bit;
+ int err = 0, sda;
+
+ for (bit = 0 ; bit < 8 ; bit++) {
+ sda = (v & 0x80) ? 2 : 0;
+ v <<= 1;
+ /* SDE=1, SDA=sda, SCL=0 */
+ err += w9968cf_write_sb(cam, 0x10 | sda);
+ /* SDE=1, SDA=sda, SCL=1 */
+ err += w9968cf_write_sb(cam, 0x11 | sda);
+ /* SDE=1, SDA=sda, SCL=0 */
+ err += w9968cf_write_sb(cam, 0x10 | sda);
+ }
+
+ return err;
+}
+
+
+static int w9968cf_smbus_read_byte(struct w9968cf_device* cam, u8* v)
+{
+ u8 bit;
+ int err = 0;
+
+ *v = 0;
+ for (bit = 0 ; bit < 8 ; bit++) {
+ *v <<= 1;
+ err += w9968cf_write_sb(cam, 0x0013);
+ *v |= (w9968cf_read_sb(cam) & 0x0008) ? 1 : 0;
+ err += w9968cf_write_sb(cam, 0x0012);
+ }
+
+ return err;
+}
+
+
+static int w9968cf_smbus_write_ack(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+ err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+
+ return err;
+}
+
+
+static int w9968cf_smbus_read_ack(struct w9968cf_device* cam)
+{
+ int err = 0, sda;
+
+ err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+ sda = (w9968cf_read_sb(cam) & 0x08) ? 1 : 0; /* sda = SDA */
+ err += w9968cf_write_sb(cam, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+ if (sda < 0)
+ err += sda;
+ if (sda == 1) {
+ DBG(6, "Couldn't receive the ACK")
+ err += -1;
+ }
+
+ return err;
+}
+
+
+/* This seems to refresh the communication through the serial bus */
+static int w9968cf_smbus_refresh_bus(struct w9968cf_device* cam)
+{
+ int err = 0, j;
+
+ for (j = 1; j <= 10; j++) {
+ err = w9968cf_write_reg(cam, 0x0020, 0x01);
+ err += w9968cf_write_reg(cam, 0x0000, 0x01);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */
+static int
+w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam,
+ u16 address, u8 subaddress,u8 value)
+{
+ u16* data = cam->data_buffer;
+ int err = 0;
+
+ err += w9968cf_smbus_refresh_bus(cam);
+
+ /* Enable SBUS outputs */
+ err += w9968cf_write_sb(cam, 0x0020);
+
+ data[0] = 0x082f | ((address & 0x80) ? 0x1500 : 0x0);
+ data[0] |= (address & 0x40) ? 0x4000 : 0x0;
+ data[1] = 0x2082 | ((address & 0x40) ? 0x0005 : 0x0);
+ data[1] |= (address & 0x20) ? 0x0150 : 0x0;
+ data[1] |= (address & 0x10) ? 0x5400 : 0x0;
+ data[2] = 0x8208 | ((address & 0x08) ? 0x0015 : 0x0);
+ data[2] |= (address & 0x04) ? 0x0540 : 0x0;
+ data[2] |= (address & 0x02) ? 0x5000 : 0x0;
+ data[3] = 0x1d20 | ((address & 0x02) ? 0x0001 : 0x0);
+ data[3] |= (address & 0x01) ? 0x0054 : 0x0;
+
+ err += w9968cf_write_fsb(cam, data);
+
+ data[0] = 0x8208 | ((subaddress & 0x80) ? 0x0015 : 0x0);
+ data[0] |= (subaddress & 0x40) ? 0x0540 : 0x0;
+ data[0] |= (subaddress & 0x20) ? 0x5000 : 0x0;
+ data[1] = 0x0820 | ((subaddress & 0x20) ? 0x0001 : 0x0);
+ data[1] |= (subaddress & 0x10) ? 0x0054 : 0x0;
+ data[1] |= (subaddress & 0x08) ? 0x1500 : 0x0;
+ data[1] |= (subaddress & 0x04) ? 0x4000 : 0x0;
+ data[2] = 0x2082 | ((subaddress & 0x04) ? 0x0005 : 0x0);
+ data[2] |= (subaddress & 0x02) ? 0x0150 : 0x0;
+ data[2] |= (subaddress & 0x01) ? 0x5400 : 0x0;
+ data[3] = 0x001d;
+
+ err += w9968cf_write_fsb(cam, data);
+
+ data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0);
+ data[0] |= (value & 0x40) ? 0x0540 : 0x0;
+ data[0] |= (value & 0x20) ? 0x5000 : 0x0;
+ data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0);
+ data[1] |= (value & 0x10) ? 0x0054 : 0x0;
+ data[1] |= (value & 0x08) ? 0x1500 : 0x0;
+ data[1] |= (value & 0x04) ? 0x4000 : 0x0;
+ data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0);
+ data[2] |= (value & 0x02) ? 0x0150 : 0x0;
+ data[2] |= (value & 0x01) ? 0x5400 : 0x0;
+ data[3] = 0xfe1d;
+
+ err += w9968cf_write_fsb(cam, data);
+
+ /* Disable SBUS outputs */
+ err += w9968cf_write_sb(cam, 0x0000);
+
+ if (!err)
+ DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X "
+ "value 0x%02X", address, subaddress, value)
+ else
+ DBG(5, "I2C write byte data failed, addr.0x%04X, "
+ "subaddr.0x%02X, value 0x%02X",
+ address, subaddress, value)
+
+ return err;
+}
+
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */
+static int
+w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam,
+ u16 address, u8 subaddress,
+ u8* value)
+{
+ int err = 0;
+
+ /* Serial data enable */
+ err += w9968cf_write_sb(cam, 0x0013); /* don't change ! */
+
+ err += w9968cf_smbus_start(cam);
+ err += w9968cf_smbus_write_byte(cam, address);
+ err += w9968cf_smbus_read_ack(cam);
+ err += w9968cf_smbus_write_byte(cam, subaddress);
+ err += w9968cf_smbus_read_ack(cam);
+ err += w9968cf_smbus_stop(cam);
+ err += w9968cf_smbus_start(cam);
+ err += w9968cf_smbus_write_byte(cam, address + 1);
+ err += w9968cf_smbus_read_ack(cam);
+ err += w9968cf_smbus_read_byte(cam, value);
+ err += w9968cf_smbus_write_ack(cam);
+ err += w9968cf_smbus_stop(cam);
+
+ /* Serial data disable */
+ err += w9968cf_write_sb(cam, 0x0000);
+
+ if (!err)
+ DBG(5, "I2C read byte data done, addr.0x%04X, "
+ "subaddr.0x%02X, value 0x%02X",
+ address, subaddress, *value)
+ else
+ DBG(5, "I2C read byte data failed, addr.0x%04X, "
+ "subaddr.0x%02X, wrong value 0x%02X",
+ address, subaddress, *value)
+
+ return err;
+}
+
+
+/* SMBus protocol: S Addr+1 Rd [A] [Value] NA P */
+static int
+w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam,
+ u16 address, u8* value)
+{
+ int err = 0;
+
+ /* Serial data enable */
+ err += w9968cf_write_sb(cam, 0x0013);
+
+ err += w9968cf_smbus_start(cam);
+ err += w9968cf_smbus_write_byte(cam, address + 1);
+ err += w9968cf_smbus_read_ack(cam);
+ err += w9968cf_smbus_read_byte(cam, value);
+ err += w9968cf_smbus_write_ack(cam);
+ err += w9968cf_smbus_stop(cam);
+
+ /* Serial data disable */
+ err += w9968cf_write_sb(cam, 0x0000);
+
+ if (!err)
+ DBG(5, "I2C read byte done, addr.0x%04X, "
+ "value 0x%02X", address, *value)
+ else
+ DBG(5, "I2C read byte failed, addr.0x%04X, "
+ "wrong value 0x%02X", address, *value)
+
+ return err;
+}
+
+
+/* SMBus protocol: S Addr Wr [A] Value [A] P */
+static int
+w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam,
+ u16 address, u8 value)
+{
+ DBG(4, "i2c_write_byte() is an unsupported transfer mode")
+ return -EINVAL;
+}
+
+
+
+/****************************************************************************
+ * I2C interface to kernel *
+ ****************************************************************************/
+
+static int
+w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char read_write, u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct w9968cf_device* cam = i2c_get_adapdata(adapter);
+ u8 i;
+ int err = 0;
+
+ switch (addr) {
+ case OV6xx0_SID:
+ case OV7xx0_SID:
+ break;
+ default:
+ DBG(4, "Rejected slave ID 0x%04X", addr)
+ return -EINVAL;
+ }
+
+ if (size == I2C_SMBUS_BYTE) {
+ /* Why addr <<= 1? See OVXXX0_SID defines in ovcamchip.h */
+ addr <<= 1;
+
+ if (read_write == I2C_SMBUS_WRITE)
+ err = w9968cf_i2c_adap_write_byte(cam, addr, command);
+ else if (read_write == I2C_SMBUS_READ)
+ err = w9968cf_i2c_adap_read_byte(cam,addr,&data->byte);
+
+ } else if (size == I2C_SMBUS_BYTE_DATA) {
+ addr <<= 1;
+
+ if (read_write == I2C_SMBUS_WRITE)
+ err = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr,
+ command, data->byte);
+ else if (read_write == I2C_SMBUS_READ) {
+ for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) {
+ err = w9968cf_i2c_adap_read_byte_data(cam,addr,
+ command, &data->byte);
+ if (err) {
+ if (w9968cf_smbus_refresh_bus(cam)) {
+ err = -EIO;
+ break;
+ }
+ } else
+ break;
+ }
+
+ } else
+ return -EINVAL;
+
+ } else {
+ DBG(4, "Unsupported I2C transfer mode (%d)", size)
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+
+static u32 w9968cf_i2c_func(struct i2c_adapter* adap)
+{
+ return I2C_FUNC_SMBUS_READ_BYTE |
+ I2C_FUNC_SMBUS_READ_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+
+static int w9968cf_i2c_attach_inform(struct i2c_client* client)
+{
+ struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
+ int id = client->driver->id, err = 0;
+
+ if (id == I2C_DRIVERID_OVCAMCHIP) {
+ cam->sensor_client = client;
+ err = w9968cf_sensor_init(cam);
+ if (err) {
+ cam->sensor_client = NULL;
+ return err;
+ }
+ } else {
+ DBG(4, "Rejected client [%s] with driver [%s]",
+ client->name, client->driver->driver.name)
+ return -EINVAL;
+ }
+
+ DBG(5, "I2C attach client [%s] with driver [%s]",
+ client->name, client->driver->driver.name)
+
+ return 0;
+}
+
+
+static int w9968cf_i2c_detach_inform(struct i2c_client* client)
+{
+ struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
+
+ if (cam->sensor_client == client)
+ cam->sensor_client = NULL;
+
+ DBG(5, "I2C detach client [%s]", client->name)
+
+ return 0;
+}
+
+
+static int
+w9968cf_i2c_control(struct i2c_adapter* adapter, unsigned int cmd,
+ unsigned long arg)
+{
+ return 0;
+}
+
+
+static int w9968cf_i2c_init(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ static struct i2c_algorithm algo = {
+ .smbus_xfer = w9968cf_i2c_smbus_xfer,
+ .algo_control = w9968cf_i2c_control,
+ .functionality = w9968cf_i2c_func,
+ };
+
+ static struct i2c_adapter adap = {
+ .id = I2C_HW_SMBUS_W9968CF,
+ .class = I2C_CLASS_CAM_DIGITAL,
+ .owner = THIS_MODULE,
+ .client_register = w9968cf_i2c_attach_inform,
+ .client_unregister = w9968cf_i2c_detach_inform,
+ .algo = &algo,
+ };
+
+ memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter));
+ strcpy(cam->i2c_adapter.name, "w9968cf");
+ i2c_set_adapdata(&cam->i2c_adapter, cam);
+
+ DBG(6, "Registering I2C adapter with kernel...")
+
+ err = i2c_add_adapter(&cam->i2c_adapter);
+ if (err)
+ DBG(1, "Failed to register the I2C adapter")
+ else
+ DBG(5, "I2C adapter registered")
+
+ return err;
+}
+
+
+
+/****************************************************************************
+ * Helper functions *
+ ****************************************************************************/
+
+/*--------------------------------------------------------------------------
+ Turn on the LED on some webcams. A beep should be heard too.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_turn_on_led(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power-down */
+ err += w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */
+ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */
+ err += w9968cf_write_reg(cam, 0x0010, 0x01); /* serial bus, SDS high */
+ err += w9968cf_write_reg(cam, 0x0000, 0x01); /* serial bus, SDS low */
+ err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */
+
+ if (err)
+ DBG(2, "Couldn't turn on the LED")
+
+ DBG(5, "LED turned on")
+
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Write some registers for the device initialization.
+ This function is called once on open().
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_init_chip(struct w9968cf_device* cam)
+{
+ unsigned long hw_bufsize = cam->maxwidth*cam->maxheight*2,
+ y0 = 0x0000,
+ u0 = y0 + hw_bufsize/2,
+ v0 = u0 + hw_bufsize/4,
+ y1 = v0 + hw_bufsize/4,
+ u1 = y1 + hw_bufsize/2,
+ v1 = u1 + hw_bufsize/4;
+ int err = 0;
+
+ err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */
+ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* power on */
+
+ err += w9968cf_write_reg(cam, 0x405d, 0x03); /* DRAM timings */
+ err += w9968cf_write_reg(cam, 0x0030, 0x04); /* SDRAM timings */
+
+ err += w9968cf_write_reg(cam, y0 & 0xffff, 0x20); /* Y buf.0, low */
+ err += w9968cf_write_reg(cam, y0 >> 16, 0x21); /* Y buf.0, high */
+ err += w9968cf_write_reg(cam, u0 & 0xffff, 0x24); /* U buf.0, low */
+ err += w9968cf_write_reg(cam, u0 >> 16, 0x25); /* U buf.0, high */
+ err += w9968cf_write_reg(cam, v0 & 0xffff, 0x28); /* V buf.0, low */
+ err += w9968cf_write_reg(cam, v0 >> 16, 0x29); /* V buf.0, high */
+
+ err += w9968cf_write_reg(cam, y1 & 0xffff, 0x22); /* Y buf.1, low */
+ err += w9968cf_write_reg(cam, y1 >> 16, 0x23); /* Y buf.1, high */
+ err += w9968cf_write_reg(cam, u1 & 0xffff, 0x26); /* U buf.1, low */
+ err += w9968cf_write_reg(cam, u1 >> 16, 0x27); /* U buf.1, high */
+ err += w9968cf_write_reg(cam, v1 & 0xffff, 0x2a); /* V buf.1, low */
+ err += w9968cf_write_reg(cam, v1 >> 16, 0x2b); /* V buf.1, high */
+
+ err += w9968cf_write_reg(cam, y1 & 0xffff, 0x32); /* JPEG buf 0 low */
+ err += w9968cf_write_reg(cam, y1 >> 16, 0x33); /* JPEG buf 0 high */
+
+ err += w9968cf_write_reg(cam, y1 & 0xffff, 0x34); /* JPEG buf 1 low */
+ err += w9968cf_write_reg(cam, y1 >> 16, 0x35); /* JPEG bug 1 high */
+
+ err += w9968cf_write_reg(cam, 0x0000, 0x36);/* JPEG restart interval */
+ err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/
+ err += w9968cf_write_reg(cam, 0x0000, 0x38);/* disable hw up-scaling */
+ err += w9968cf_write_reg(cam, 0x0000, 0x3f); /* JPEG/MCTL test data */
+
+ err += w9968cf_set_picture(cam, cam->picture); /* this before */
+ err += w9968cf_set_window(cam, cam->window);
+
+ if (err)
+ DBG(1, "Chip initialization failed")
+ else
+ DBG(5, "Chip successfully initialized")
+
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Return non-zero if the palette is supported, 0 otherwise.
+ --------------------------------------------------------------------------*/
+static inline u16 w9968cf_valid_palette(u16 palette)
+{
+ u8 i = 0;
+ while (w9968cf_formatlist[i].palette != 0) {
+ if (palette == w9968cf_formatlist[i].palette)
+ return palette;
+ i++;
+ }
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Return the depth corresponding to the given palette.
+ Palette _must_ be supported !
+ --------------------------------------------------------------------------*/
+static inline u16 w9968cf_valid_depth(u16 palette)
+{
+ u8 i=0;
+ while (w9968cf_formatlist[i].palette != palette)
+ i++;
+
+ return w9968cf_formatlist[i].depth;
+}
+
+
+/*--------------------------------------------------------------------------
+ Return non-zero if the format requires decompression, 0 otherwise.
+ --------------------------------------------------------------------------*/
+static inline u8 w9968cf_need_decompression(u16 palette)
+{
+ u8 i = 0;
+ while (w9968cf_formatlist[i].palette != 0) {
+ if (palette == w9968cf_formatlist[i].palette)
+ return w9968cf_formatlist[i].compression;
+ i++;
+ }
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Change the picture settings of the camera.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict)
+{
+ u16 fmt, hw_depth, hw_palette, reg_v = 0x0000;
+ int err = 0;
+
+ /* Make sure we are using a valid depth */
+ pict.depth = w9968cf_valid_depth(pict.palette);
+
+ fmt = pict.palette;
+
+ hw_depth = pict.depth; /* depth used by the winbond chip */
+ hw_palette = pict.palette; /* palette used by the winbond chip */
+
+ /* VS & HS polarities */
+ reg_v = (cam->vs_polarity << 12) | (cam->hs_polarity << 11);
+
+ switch (fmt)
+ {
+ case VIDEO_PALETTE_UYVY:
+ reg_v |= 0x0000;
+ cam->vpp_flag = VPP_NONE;
+ break;
+ case VIDEO_PALETTE_YUV422P:
+ reg_v |= 0x0002;
+ cam->vpp_flag = VPP_DECOMPRESSION;
+ break;
+ case VIDEO_PALETTE_YUV420:
+ case VIDEO_PALETTE_YUV420P:
+ reg_v |= 0x0003;
+ cam->vpp_flag = VPP_DECOMPRESSION;
+ break;
+ case VIDEO_PALETTE_YUYV:
+ case VIDEO_PALETTE_YUV422:
+ reg_v |= 0x0000;
+ cam->vpp_flag = VPP_SWAP_YUV_BYTES;
+ hw_palette = VIDEO_PALETTE_UYVY;
+ break;
+ /* Original video is used instead of RGBX palettes.
+ Software conversion later. */
+ case VIDEO_PALETTE_GREY:
+ case VIDEO_PALETTE_RGB555:
+ case VIDEO_PALETTE_RGB565:
+ case VIDEO_PALETTE_RGB24:
+ case VIDEO_PALETTE_RGB32:
+ reg_v |= 0x0000; /* UYVY 16 bit is used */
+ hw_depth = 16;
+ hw_palette = VIDEO_PALETTE_UYVY;
+ cam->vpp_flag = VPP_UYVY_TO_RGBX;
+ break;
+ }
+
+ /* NOTE: due to memory issues, it is better to disable the hardware
+ double buffering during compression */
+ if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION))
+ reg_v |= 0x0080;
+
+ if (cam->clamping)
+ reg_v |= 0x0020;
+
+ if (cam->filter_type == 1)
+ reg_v |= 0x0008;
+ else if (cam->filter_type == 2)
+ reg_v |= 0x000c;
+
+ if ((err = w9968cf_write_reg(cam, reg_v, 0x16)))
+ goto error;
+
+ if ((err = w9968cf_sensor_update_picture(cam, pict)))
+ goto error;
+
+ /* If all went well, update the device data structure */
+ memcpy(&cam->picture, &pict, sizeof(pict));
+ cam->hw_depth = hw_depth;
+ cam->hw_palette = hw_palette;
+
+ /* Settings changed, so we clear the frame buffers */
+ memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size);
+
+ DBG(4, "Palette is %s, depth is %u bpp",
+ symbolic(v4l1_plist, pict.palette), pict.depth)
+
+ return 0;
+
+error:
+ DBG(1, "Failed to change picture settings")
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Change the capture area size of the camera.
+ This function _must_ be called _after_ w9968cf_set_picture().
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
+{
+ u16 x, y, w, h, scx, scy, cw, ch, ax, ay;
+ unsigned long fw, fh;
+ struct ovcamchip_window s_win;
+ int err = 0;
+
+ /* Work around to avoid FP arithmetics */
+ #define __SC(x) ((x) << 10)
+ #define __UNSC(x) ((x) >> 10)
+
+ /* Make sure we are using a supported resolution */
+ if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width,
+ (u16*)&win.height)))
+ goto error;
+
+ /* Scaling factors */
+ fw = __SC(win.width) / cam->maxwidth;
+ fh = __SC(win.height) / cam->maxheight;
+
+ /* Set up the width and height values used by the chip */
+ if ((win.width > cam->maxwidth) || (win.height > cam->maxheight)) {
+ cam->vpp_flag |= VPP_UPSCALE;
+ /* Calculate largest w,h mantaining the same w/h ratio */
+ w = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh;
+ h = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight;
+ if (w < cam->minwidth) /* just in case */
+ w = cam->minwidth;
+ if (h < cam->minheight) /* just in case */
+ h = cam->minheight;
+ } else {
+ cam->vpp_flag &= ~VPP_UPSCALE;
+ w = win.width;
+ h = win.height;
+ }
+
+ /* x,y offsets of the cropped area */
+ scx = cam->start_cropx;
+ scy = cam->start_cropy;
+
+ /* Calculate cropped area manteining the right w/h ratio */
+ if (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) {
+ cw = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh;
+ ch = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight;
+ } else {
+ cw = w;
+ ch = h;
+ }
+
+ /* Setup the window of the sensor */
+ s_win.format = VIDEO_PALETTE_UYVY;
+ s_win.width = cam->maxwidth;
+ s_win.height = cam->maxheight;
+ s_win.quarter = 0; /* full progressive video */
+
+ /* Center it */
+ s_win.x = (s_win.width - cw) / 2;
+ s_win.y = (s_win.height - ch) / 2;
+
+ /* Clock divisor */
+ if (cam->clockdiv >= 0)
+ s_win.clockdiv = cam->clockdiv; /* manual override */
+ else
+ switch (cam->sensor) {
+ case CC_OV6620:
+ s_win.clockdiv = 0;
+ break;
+ case CC_OV6630:
+ s_win.clockdiv = 0;
+ break;
+ case CC_OV76BE:
+ case CC_OV7610:
+ case CC_OV7620:
+ s_win.clockdiv = 0;
+ break;
+ default:
+ s_win.clockdiv = W9968CF_DEF_CLOCKDIVISOR;
+ }
+
+ /* We have to scale win.x and win.y offsets */
+ if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE))
+ || (cam->vpp_flag & VPP_UPSCALE) ) {
+ ax = __SC(win.x)/fw;
+ ay = __SC(win.y)/fh;
+ } else {
+ ax = win.x;
+ ay = win.y;
+ }
+
+ if ((ax + cw) > cam->maxwidth)
+ ax = cam->maxwidth - cw;
+
+ if ((ay + ch) > cam->maxheight)
+ ay = cam->maxheight - ch;
+
+ /* Adjust win.x, win.y */
+ if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE))
+ || (cam->vpp_flag & VPP_UPSCALE) ) {
+ win.x = __UNSC(ax*fw);
+ win.y = __UNSC(ay*fh);
+ } else {
+ win.x = ax;
+ win.y = ay;
+ }
+
+ /* Offsets used by the chip */
+ x = ax + s_win.x;
+ y = ay + s_win.y;
+
+ /* Go ! */
+ if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win)))
+ goto error;
+
+ err += w9968cf_write_reg(cam, scx + x, 0x10);
+ err += w9968cf_write_reg(cam, scy + y, 0x11);
+ err += w9968cf_write_reg(cam, scx + x + cw, 0x12);
+ err += w9968cf_write_reg(cam, scy + y + ch, 0x13);
+ err += w9968cf_write_reg(cam, w, 0x14);
+ err += w9968cf_write_reg(cam, h, 0x15);
+
+ /* JPEG width & height */
+ err += w9968cf_write_reg(cam, w, 0x30);
+ err += w9968cf_write_reg(cam, h, 0x31);
+
+ /* Y & UV frame buffer strides (in WORD) */
+ if (cam->vpp_flag & VPP_DECOMPRESSION) {
+ err += w9968cf_write_reg(cam, w/2, 0x2c);
+ err += w9968cf_write_reg(cam, w/4, 0x2d);
+ } else
+ err += w9968cf_write_reg(cam, w, 0x2c);
+
+ if (err)
+ goto error;
+
+ /* If all went well, update the device data structure */
+ memcpy(&cam->window, &win, sizeof(win));
+ cam->hw_width = w;
+ cam->hw_height = h;
+
+ /* Settings changed, so we clear the frame buffers */
+ memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size);
+
+ DBG(4, "The capture area is %dx%d, Offset (x,y)=(%u,%u)",
+ win.width, win.height, win.x, win.y)
+
+ PDBGG("x=%u ,y=%u, w=%u, h=%u, ax=%u, ay=%u, s_win.x=%u, s_win.y=%u, "
+ "cw=%u, ch=%u, win.x=%u, win.y=%u, win.width=%u, win.height=%u",
+ x, y, w, h, ax, ay, s_win.x, s_win.y, cw, ch, win.x, win.y,
+ win.width, win.height)
+
+ return 0;
+
+error:
+ DBG(1, "Failed to change the capture area size")
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Adjust the asked values for window width and height.
+ Return 0 on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height)
+{
+ u16 maxw, maxh;
+
+ if ((*width < cam->minwidth) || (*height < cam->minheight))
+ return -ERANGE;
+
+ maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) &&
+ w9968cf_vpp ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth)
+ : cam->maxwidth;
+ maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) &&
+ w9968cf_vpp ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight)
+ : cam->maxheight;
+
+ if (*width > maxw)
+ *width = maxw;
+ if (*height > maxh)
+ *height = maxh;
+
+ if (cam->vpp_flag & VPP_DECOMPRESSION) {
+ *width &= ~15L; /* multiple of 16 */
+ *height &= ~15L;
+ }
+
+ PDBGG("Window size adjusted w=%u, h=%u ", *width, *height)
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Initialize the FIFO list of requested frames.
+ --------------------------------------------------------------------------*/
+static void w9968cf_init_framelist(struct w9968cf_device* cam)
+{
+ u8 i;
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->requested_frame[i] = NULL;
+ cam->frame[i].queued = 0;
+ cam->frame[i].status = F_UNUSED;
+ }
+}
+
+
+/*--------------------------------------------------------------------------
+ Add a frame in the FIFO list of requested frames.
+ This function is called in process context.
+ --------------------------------------------------------------------------*/
+static void w9968cf_push_frame(struct w9968cf_device* cam, u8 f_num)
+{
+ u8 f;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&cam->flist_lock, lock_flags);
+
+ for (f=0; cam->requested_frame[f] != NULL; f++);
+ cam->requested_frame[f] = &cam->frame[f_num];
+ cam->frame[f_num].queued = 1;
+ cam->frame[f_num].status = F_UNUSED; /* clear the status */
+
+ spin_unlock_irqrestore(&cam->flist_lock, lock_flags);
+
+ DBG(6, "Frame #%u pushed into the FIFO list. Position %u", f_num, f)
+}
+
+
+/*--------------------------------------------------------------------------
+ Read, store and remove the first pointer in the FIFO list of requested
+ frames. This function is called in interrupt context.
+ --------------------------------------------------------------------------*/
+static void
+w9968cf_pop_frame(struct w9968cf_device* cam, struct w9968cf_frame_t** framep)
+{
+ u8 i;
+
+ spin_lock(&cam->flist_lock);
+
+ *framep = cam->requested_frame[0];
+
+ /* Shift the list of pointers */
+ for (i = 0; i < cam->nbuffers-1; i++)
+ cam->requested_frame[i] = cam->requested_frame[i+1];
+ cam->requested_frame[i] = NULL;
+
+ spin_unlock(&cam->flist_lock);
+
+ DBG(6,"Popped frame #%d from the list", (*framep)->number)
+}
+
+
+/*--------------------------------------------------------------------------
+ High-level video post-processing routine on grabbed frames.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_postprocess_frame(struct w9968cf_device* cam,
+ struct w9968cf_frame_t* fr)
+{
+ void *pIn = fr->buffer, *pOut = cam->frame_vpp.buffer, *tmp;
+ u16 w = cam->window.width,
+ h = cam->window.height,
+ d = cam->picture.depth,
+ fmt = cam->picture.palette,
+ rgb = cam->force_rgb,
+ hw_w = cam->hw_width,
+ hw_h = cam->hw_height,
+ hw_d = cam->hw_depth;
+ int err = 0;
+
+ #define _PSWAP(pIn, pOut) {tmp = (pIn); (pIn) = (pOut); (pOut) = tmp;}
+
+ if (cam->vpp_flag & VPP_DECOMPRESSION) {
+ memcpy(pOut, pIn, fr->length);
+ _PSWAP(pIn, pOut)
+ err = w9968cf_vpp->decode(pIn, fr->length, hw_w, hw_h, pOut);
+ PDBGG("Compressed frame length: %lu",(unsigned long)fr->length)
+ fr->length = (hw_w*hw_h*hw_d)/8;
+ _PSWAP(pIn, pOut)
+ if (err) {
+ DBG(4, "An error occurred while decoding the frame: "
+ "%s", symbolic(decoder_errlist, err))
+ return err;
+ } else
+ DBG(6, "Frame decoded")
+ }
+
+ if (cam->vpp_flag & VPP_SWAP_YUV_BYTES) {
+ w9968cf_vpp->swap_yuvbytes(pIn, fr->length);
+ DBG(6, "Original UYVY component ordering changed")
+ }
+
+ if (cam->vpp_flag & VPP_UPSCALE) {
+ w9968cf_vpp->scale_up(pIn, pOut, hw_w, hw_h, hw_d, w, h);
+ fr->length = (w*h*hw_d)/8;
+ _PSWAP(pIn, pOut)
+ DBG(6, "Vertical up-scaling done: %u,%u,%ubpp->%u,%u",
+ hw_w, hw_h, hw_d, w, h)
+ }
+
+ if (cam->vpp_flag & VPP_UYVY_TO_RGBX) {
+ w9968cf_vpp->uyvy_to_rgbx(pIn, fr->length, pOut, fmt, rgb);
+ fr->length = (w*h*d)/8;
+ _PSWAP(pIn, pOut)
+ DBG(6, "UYVY-16bit to %s conversion done",
+ symbolic(v4l1_plist, fmt))
+ }
+
+ if (pOut == fr->buffer)
+ memcpy(fr->buffer, cam->frame_vpp.buffer, fr->length);
+
+ return 0;
+}
+
+
+
+/****************************************************************************
+ * Image sensor control routines *
+ ****************************************************************************/
+
+static int
+w9968cf_sensor_set_control(struct w9968cf_device* cam, int cid, int val)
+{
+ struct ovcamchip_control ctl;
+ int err;
+
+ ctl.id = cid;
+ ctl.value = val;
+
+ err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl);
+
+ return err;
+}
+
+
+static int
+w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int* val)
+{
+ struct ovcamchip_control ctl;
+ int err;
+
+ ctl.id = cid;
+
+ err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl);
+ if (!err)
+ *val = ctl.value;
+
+ return err;
+}
+
+
+static int
+w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void* arg)
+{
+ struct i2c_client* c = cam->sensor_client;
+ int rc = 0;
+
+ if (!c || !c->driver || !c->driver->command)
+ return -EINVAL;
+
+ rc = c->driver->command(c, cmd, arg);
+ /* The I2C driver returns -EPERM on non-supported controls */
+ return (rc < 0 && rc != -EPERM) ? rc : 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Update some settings of the image sensor.
+ Returns: 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_sensor_update_settings(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ /* Auto brightness */
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT,
+ cam->auto_brt);
+ if (err)
+ return err;
+
+ /* Auto exposure */
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP,
+ cam->auto_exp);
+ if (err)
+ return err;
+
+ /* Banding filter */
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT,
+ cam->bandfilt);
+ if (err)
+ return err;
+
+ /* Light frequency */
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ,
+ cam->lightfreq);
+ if (err)
+ return err;
+
+ /* Back light */
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT,
+ cam->backlight);
+ if (err)
+ return err;
+
+ /* Mirror */
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR,
+ cam->mirror);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Get some current picture settings from the image sensor and update the
+ internal 'picture' structure of the camera.
+ Returns: 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_sensor_get_picture(struct w9968cf_device* cam)
+{
+ int err, v;
+
+ err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v);
+ if (err)
+ return err;
+ cam->picture.contrast = v;
+
+ err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v);
+ if (err)
+ return err;
+ cam->picture.brightness = v;
+
+ err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v);
+ if (err)
+ return err;
+ cam->picture.colour = v;
+
+ err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v);
+ if (err)
+ return err;
+ cam->picture.hue = v;
+
+ DBG(5, "Got picture settings from the image sensor")
+
+ PDBGG("Brightness, contrast, hue, colour, whiteness are "
+ "%u,%u,%u,%u,%u", cam->picture.brightness,cam->picture.contrast,
+ cam->picture.hue, cam->picture.colour, cam->picture.whiteness)
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Update picture settings of the image sensor.
+ Returns: 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_sensor_update_picture(struct w9968cf_device* cam,
+ struct video_picture pict)
+{
+ int err = 0;
+
+ if ((!cam->sensor_initialized)
+ || pict.contrast != cam->picture.contrast) {
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_CONT,
+ pict.contrast);
+ if (err)
+ goto fail;
+ DBG(4, "Contrast changed from %u to %u",
+ cam->picture.contrast, pict.contrast)
+ cam->picture.contrast = pict.contrast;
+ }
+
+ if (((!cam->sensor_initialized) ||
+ pict.brightness != cam->picture.brightness) && (!cam->auto_brt)) {
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT,
+ pict.brightness);
+ if (err)
+ goto fail;
+ DBG(4, "Brightness changed from %u to %u",
+ cam->picture.brightness, pict.brightness)
+ cam->picture.brightness = pict.brightness;
+ }
+
+ if ((!cam->sensor_initialized) || pict.colour != cam->picture.colour) {
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT,
+ pict.colour);
+ if (err)
+ goto fail;
+ DBG(4, "Colour changed from %u to %u",
+ cam->picture.colour, pict.colour)
+ cam->picture.colour = pict.colour;
+ }
+
+ if ((!cam->sensor_initialized) || pict.hue != cam->picture.hue) {
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE,
+ pict.hue);
+ if (err)
+ goto fail;
+ DBG(4, "Hue changed from %u to %u",
+ cam->picture.hue, pict.hue)
+ cam->picture.hue = pict.hue;
+ }
+
+ return 0;
+
+fail:
+ DBG(4, "Failed to change sensor picture setting")
+ return err;
+}
+
+
+
+/****************************************************************************
+ * Camera configuration *
+ ****************************************************************************/
+
+/*--------------------------------------------------------------------------
+ This function is called when a supported image sensor is detected.
+ Return 0 if the initialization succeeds, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_sensor_init(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE,
+ &cam->monochrome)))
+ goto error;
+
+ if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE,
+ &cam->sensor)))
+ goto error;
+
+ /* NOTE: Make sure width and height are a multiple of 16 */
+ switch (cam->sensor_client->addr) {
+ case OV6xx0_SID:
+ cam->maxwidth = 352;
+ cam->maxheight = 288;
+ cam->minwidth = 64;
+ cam->minheight = 48;
+ break;
+ case OV7xx0_SID:
+ cam->maxwidth = 640;
+ cam->maxheight = 480;
+ cam->minwidth = 64;
+ cam->minheight = 48;
+ break;
+ default:
+ DBG(1, "Not supported image sensor detected for %s",
+ symbolic(camlist, cam->id))
+ return -EINVAL;
+ }
+
+ /* These values depend on the ones in the ovxxx0.c sources */
+ switch (cam->sensor) {
+ case CC_OV7620:
+ cam->start_cropx = 287;
+ cam->start_cropy = 35;
+ /* Seems to work around a bug in the image sensor */
+ cam->vs_polarity = 1;
+ cam->hs_polarity = 1;
+ break;
+ default:
+ cam->start_cropx = 320;
+ cam->start_cropy = 35;
+ cam->vs_polarity = 1;
+ cam->hs_polarity = 0;
+ }
+
+ if ((err = w9968cf_sensor_update_settings(cam)))
+ goto error;
+
+ if ((err = w9968cf_sensor_update_picture(cam, cam->picture)))
+ goto error;
+
+ cam->sensor_initialized = 1;
+
+ DBG(2, "%s image sensor initialized", symbolic(senlist, cam->sensor))
+ return 0;
+
+error:
+ cam->sensor_initialized = 0;
+ cam->sensor = CC_UNKNOWN;
+ DBG(1, "Image sensor initialization failed for %s (/dev/video%d). "
+ "Try to detach and attach this device again",
+ symbolic(camlist, cam->id), cam->v4ldev->minor)
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Fill some basic fields in the main device data structure.
+ This function is called once on w9968cf_usb_probe() for each recognized
+ camera.
+ --------------------------------------------------------------------------*/
+static void
+w9968cf_configure_camera(struct w9968cf_device* cam,
+ struct usb_device* udev,
+ enum w9968cf_model_id mod_id,
+ const unsigned short dev_nr)
+{
+ mutex_init(&cam->fileop_mutex);
+ init_waitqueue_head(&cam->open);
+ spin_lock_init(&cam->urb_lock);
+ spin_lock_init(&cam->flist_lock);
+
+ cam->users = 0;
+ cam->disconnected = 0;
+ cam->id = mod_id;
+ cam->sensor = CC_UNKNOWN;
+ cam->sensor_initialized = 0;
+
+ /* Calculate the alternate setting number (from 1 to 16)
+ according to the 'packet_size' module parameter */
+ if (packet_size[dev_nr] < W9968CF_MIN_PACKET_SIZE)
+ packet_size[dev_nr] = W9968CF_MIN_PACKET_SIZE;
+ for (cam->altsetting = 1;
+ packet_size[dev_nr] < wMaxPacketSize[cam->altsetting-1];
+ cam->altsetting++);
+
+ cam->max_buffers = (max_buffers[dev_nr] < 2 ||
+ max_buffers[dev_nr] > W9968CF_MAX_BUFFERS)
+ ? W9968CF_BUFFERS : (u8)max_buffers[dev_nr];
+
+ cam->double_buffer = (double_buffer[dev_nr] == 0 ||
+ double_buffer[dev_nr] == 1)
+ ? (u8)double_buffer[dev_nr]:W9968CF_DOUBLE_BUFFER;
+
+ cam->clamping = (clamping[dev_nr] == 0 || clamping[dev_nr] == 1)
+ ? (u8)clamping[dev_nr] : W9968CF_CLAMPING;
+
+ cam->filter_type = (filter_type[dev_nr] == 0 ||
+ filter_type[dev_nr] == 1 ||
+ filter_type[dev_nr] == 2)
+ ? (u8)filter_type[dev_nr] : W9968CF_FILTER_TYPE;
+
+ cam->capture = 1;
+
+ cam->largeview = (largeview[dev_nr] == 0 || largeview[dev_nr] == 1)
+ ? (u8)largeview[dev_nr] : W9968CF_LARGEVIEW;
+
+ cam->decompression = (decompression[dev_nr] == 0 ||
+ decompression[dev_nr] == 1 ||
+ decompression[dev_nr] == 2)
+ ? (u8)decompression[dev_nr]:W9968CF_DECOMPRESSION;
+
+ cam->upscaling = (upscaling[dev_nr] == 0 ||
+ upscaling[dev_nr] == 1)
+ ? (u8)upscaling[dev_nr] : W9968CF_UPSCALING;
+
+ cam->auto_brt = (autobright[dev_nr] == 0 || autobright[dev_nr] == 1)
+ ? (u8)autobright[dev_nr] : W9968CF_AUTOBRIGHT;
+
+ cam->auto_exp = (autoexp[dev_nr] == 0 || autoexp[dev_nr] == 1)
+ ? (u8)autoexp[dev_nr] : W9968CF_AUTOEXP;
+
+ cam->lightfreq = (lightfreq[dev_nr] == 50 || lightfreq[dev_nr] == 60)
+ ? (u8)lightfreq[dev_nr] : W9968CF_LIGHTFREQ;
+
+ cam->bandfilt = (bandingfilter[dev_nr] == 0 ||
+ bandingfilter[dev_nr] == 1)
+ ? (u8)bandingfilter[dev_nr] : W9968CF_BANDINGFILTER;
+
+ cam->backlight = (backlight[dev_nr] == 0 || backlight[dev_nr] == 1)
+ ? (u8)backlight[dev_nr] : W9968CF_BACKLIGHT;
+
+ cam->clockdiv = (clockdiv[dev_nr] == -1 || clockdiv[dev_nr] >= 0)
+ ? (s8)clockdiv[dev_nr] : W9968CF_CLOCKDIV;
+
+ cam->mirror = (mirror[dev_nr] == 0 || mirror[dev_nr] == 1)
+ ? (u8)mirror[dev_nr] : W9968CF_MIRROR;
+
+ cam->monochrome = (monochrome[dev_nr] == 0 || monochrome[dev_nr] == 1)
+ ? monochrome[dev_nr] : W9968CF_MONOCHROME;
+
+ cam->picture.brightness = (u16)brightness[dev_nr];
+ cam->picture.hue = (u16)hue[dev_nr];
+ cam->picture.colour = (u16)colour[dev_nr];
+ cam->picture.contrast = (u16)contrast[dev_nr];
+ cam->picture.whiteness = (u16)whiteness[dev_nr];
+ if (w9968cf_valid_palette((u16)force_palette[dev_nr])) {
+ cam->picture.palette = (u16)force_palette[dev_nr];
+ cam->force_palette = 1;
+ } else {
+ cam->force_palette = 0;
+ if (cam->decompression == 0)
+ cam->picture.palette = W9968CF_PALETTE_DECOMP_OFF;
+ else if (cam->decompression == 1)
+ cam->picture.palette = W9968CF_PALETTE_DECOMP_FORCE;
+ else
+ cam->picture.palette = W9968CF_PALETTE_DECOMP_ON;
+ }
+ cam->picture.depth = w9968cf_valid_depth(cam->picture.palette);
+
+ cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1)
+ ? (u8)force_rgb[dev_nr] : W9968CF_FORCE_RGB;
+
+ cam->window.x = 0;
+ cam->window.y = 0;
+ cam->window.width = W9968CF_WIDTH;
+ cam->window.height = W9968CF_HEIGHT;
+ cam->window.chromakey = 0;
+ cam->window.clipcount = 0;
+ cam->window.flags = 0;
+
+ DBG(3, "%s configured with settings #%u:",
+ symbolic(camlist, cam->id), dev_nr)
+
+ DBG(3, "- Data packet size for USB isochrnous transfer: %u bytes",
+ wMaxPacketSize[cam->altsetting-1])
+
+ DBG(3, "- Number of requested video frame buffers: %u",
+ cam->max_buffers)
+
+ if (cam->double_buffer)
+ DBG(3, "- Hardware double buffering enabled")
+ else
+ DBG(3, "- Hardware double buffering disabled")
+
+ if (cam->filter_type == 0)
+ DBG(3, "- Video filtering disabled")
+ else if (cam->filter_type == 1)
+ DBG(3, "- Video filtering enabled: type 1-2-1")
+ else if (cam->filter_type == 2)
+ DBG(3, "- Video filtering enabled: type 2-3-6-3-2")
+
+ if (cam->clamping)
+ DBG(3, "- Video data clamping (CCIR-601 format) enabled")
+ else
+ DBG(3, "- Video data clamping (CCIR-601 format) disabled")
+
+ if (cam->largeview)
+ DBG(3, "- Large view enabled")
+ else
+ DBG(3, "- Large view disabled")
+
+ if ((cam->decompression) == 0 && (!cam->force_palette))
+ DBG(3, "- Decompression disabled")
+ else if ((cam->decompression) == 1 && (!cam->force_palette))
+ DBG(3, "- Decompression forced")
+ else if ((cam->decompression) == 2 && (!cam->force_palette))
+ DBG(3, "- Decompression allowed")
+
+ if (cam->upscaling)
+ DBG(3, "- Software image scaling enabled")
+ else
+ DBG(3, "- Software image scaling disabled")
+
+ if (cam->force_palette)
+ DBG(3, "- Image palette forced to %s",
+ symbolic(v4l1_plist, cam->picture.palette))
+
+ if (cam->force_rgb)
+ DBG(3, "- RGB component ordering will be used instead of BGR")
+
+ if (cam->auto_brt)
+ DBG(3, "- Auto brightness enabled")
+ else
+ DBG(3, "- Auto brightness disabled")
+
+ if (cam->auto_exp)
+ DBG(3, "- Auto exposure enabled")
+ else
+ DBG(3, "- Auto exposure disabled")
+
+ if (cam->backlight)
+ DBG(3, "- Backlight exposure algorithm enabled")
+ else
+ DBG(3, "- Backlight exposure algorithm disabled")
+
+ if (cam->mirror)
+ DBG(3, "- Mirror enabled")
+ else
+ DBG(3, "- Mirror disabled")
+
+ if (cam->bandfilt)
+ DBG(3, "- Banding filter enabled")
+ else
+ DBG(3, "- Banding filter disabled")
+
+ DBG(3, "- Power lighting frequency: %u", cam->lightfreq)
+
+ if (cam->clockdiv == -1)
+ DBG(3, "- Automatic clock divisor enabled")
+ else
+ DBG(3, "- Clock divisor: %d", cam->clockdiv)
+
+ if (cam->monochrome)
+ DBG(3, "- Image sensor used as monochrome")
+ else
+ DBG(3, "- Image sensor not used as monochrome")
+}
+
+
+/*--------------------------------------------------------------------------
+ If the video post-processing module is not loaded, some parameters
+ must be overridden.
+ --------------------------------------------------------------------------*/
+static void w9968cf_adjust_configuration(struct w9968cf_device* cam)
+{
+ if (!w9968cf_vpp) {
+ if (cam->decompression == 1) {
+ cam->decompression = 2;
+ DBG(2, "Video post-processing module not found: "
+ "'decompression' parameter forced to 2")
+ }
+ if (cam->upscaling) {
+ cam->upscaling = 0;
+ DBG(2, "Video post-processing module not found: "
+ "'upscaling' parameter forced to 0")
+ }
+ if (cam->picture.palette != VIDEO_PALETTE_UYVY) {
+ cam->force_palette = 0;
+ DBG(2, "Video post-processing module not found: "
+ "'force_palette' parameter forced to 0")
+ }
+ cam->picture.palette = VIDEO_PALETTE_UYVY;
+ cam->picture.depth = w9968cf_valid_depth(cam->picture.palette);
+ }
+}
+
+
+/*--------------------------------------------------------------------------
+ Release the resources used by the driver.
+ This function is called on disconnect
+ (or on close if deallocation has been deferred)
+ --------------------------------------------------------------------------*/
+static void w9968cf_release_resources(struct w9968cf_device* cam)
+{
+ mutex_lock(&w9968cf_devlist_mutex);
+
+ DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev->minor)
+
+ video_unregister_device(cam->v4ldev);
+ list_del(&cam->v4llist);
+ i2c_del_adapter(&cam->i2c_adapter);
+ w9968cf_deallocate_memory(cam);
+ kfree(cam->control_buffer);
+ kfree(cam->data_buffer);
+
+ mutex_unlock(&w9968cf_devlist_mutex);
+}
+
+
+
+/****************************************************************************
+ * Video4Linux interface *
+ ****************************************************************************/
+
+static int w9968cf_open(struct inode* inode, struct file* filp)
+{
+ struct w9968cf_device* cam;
+ int err;
+
+ /* This the only safe way to prevent race conditions with disconnect */
+ if (!down_read_trylock(&w9968cf_disconnect))
+ return -ERESTARTSYS;
+
+ cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
+
+ mutex_lock(&cam->dev_mutex);
+
+ if (cam->sensor == CC_UNKNOWN) {
+ DBG(2, "No supported image sensor has been detected by the "
+ "'ovcamchip' module for the %s (/dev/video%d). Make "
+ "sure it is loaded *before* (re)connecting the camera.",
+ symbolic(camlist, cam->id), cam->v4ldev->minor)
+ mutex_unlock(&cam->dev_mutex);
+ up_read(&w9968cf_disconnect);
+ return -ENODEV;
+ }
+
+ if (cam->users) {
+ DBG(2, "%s (/dev/video%d) has been already occupied by '%s'",
+ symbolic(camlist, cam->id),cam->v4ldev->minor,cam->command)
+ if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) {
+ mutex_unlock(&cam->dev_mutex);
+ up_read(&w9968cf_disconnect);
+ return -EWOULDBLOCK;
+ }
+ mutex_unlock(&cam->dev_mutex);
+ err = wait_event_interruptible_exclusive(cam->open,
+ cam->disconnected ||
+ !cam->users);
+ if (err) {
+ up_read(&w9968cf_disconnect);
+ return err;
+ }
+ if (cam->disconnected) {
+ up_read(&w9968cf_disconnect);
+ return -ENODEV;
+ }
+ mutex_lock(&cam->dev_mutex);
+ }
+
+ DBG(5, "Opening '%s', /dev/video%d ...",
+ symbolic(camlist, cam->id), cam->v4ldev->minor)
+
+ cam->streaming = 0;
+ cam->misconfigured = 0;
+
+ w9968cf_adjust_configuration(cam);
+
+ if ((err = w9968cf_allocate_memory(cam)))
+ goto deallocate_memory;
+
+ if ((err = w9968cf_init_chip(cam)))
+ goto deallocate_memory;
+
+ if ((err = w9968cf_start_transfer(cam)))
+ goto deallocate_memory;
+
+ filp->private_data = cam;
+
+ cam->users++;
+ strcpy(cam->command, current->comm);
+
+ init_waitqueue_head(&cam->wait_queue);
+
+ DBG(5, "Video device is open")
+
+ mutex_unlock(&cam->dev_mutex);
+ up_read(&w9968cf_disconnect);
+
+ return 0;
+
+deallocate_memory:
+ w9968cf_deallocate_memory(cam);
+ DBG(2, "Failed to open the video device")
+ mutex_unlock(&cam->dev_mutex);
+ up_read(&w9968cf_disconnect);
+ return err;
+}
+
+
+static int w9968cf_release(struct inode* inode, struct file* filp)
+{
+ struct w9968cf_device* cam;
+
+ cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
+
+ mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */
+
+ w9968cf_stop_transfer(cam);
+
+ if (cam->disconnected) {
+ w9968cf_release_resources(cam);
+ mutex_unlock(&cam->dev_mutex);
+ kfree(cam);
+ return 0;
+ }
+
+ cam->users--;
+ w9968cf_deallocate_memory(cam);
+ wake_up_interruptible_nr(&cam->open, 1);
+
+ DBG(5, "Video device closed")
+ mutex_unlock(&cam->dev_mutex);
+ return 0;
+}
+
+
+static ssize_t
+w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
+{
+ struct w9968cf_device* cam;
+ struct w9968cf_frame_t* fr;
+ int err = 0;
+
+ cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
+
+ if (filp->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->disconnected) {
+ DBG(2, "Device not present")
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->misconfigured) {
+ DBG(2, "The camera is misconfigured. Close and open it again.")
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (!cam->frame[0].queued)
+ w9968cf_push_frame(cam, 0);
+
+ if (!cam->frame[1].queued)
+ w9968cf_push_frame(cam, 1);
+
+ err = wait_event_interruptible(cam->wait_queue,
+ cam->frame[0].status == F_READY ||
+ cam->frame[1].status == F_READY ||
+ cam->disconnected);
+ if (err) {
+ mutex_unlock(&cam->fileop_mutex);
+ return err;
+ }
+ if (cam->disconnected) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ fr = (cam->frame[0].status == F_READY) ? &cam->frame[0]:&cam->frame[1];
+
+ if (w9968cf_vpp)
+ w9968cf_postprocess_frame(cam, fr);
+
+ if (count > fr->length)
+ count = fr->length;
+
+ if (copy_to_user(buf, fr->buffer, count)) {
+ fr->status = F_UNUSED;
+ mutex_unlock(&cam->fileop_mutex);
+ return -EFAULT;
+ }
+ *f_pos += count;
+
+ fr->status = F_UNUSED;
+
+ DBG(5, "%zu bytes read", count)
+
+ mutex_unlock(&cam->fileop_mutex);
+ return count;
+}
+
+
+static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+ struct w9968cf_device* cam = (struct w9968cf_device*)
+ video_get_drvdata(video_devdata(filp));
+ unsigned long vsize = vma->vm_end - vma->vm_start,
+ psize = cam->nbuffers * cam->frame[0].size,
+ start = vma->vm_start,
+ pos = (unsigned long)cam->frame[0].buffer,
+ page;
+
+ if (cam->disconnected) {
+ DBG(2, "Device not present")
+ return -ENODEV;
+ }
+
+ if (cam->misconfigured) {
+ DBG(2, "The camera is misconfigured. Close and open it again")
+ return -EIO;
+ }
+
+ PDBGG("mmapping %lu bytes...", vsize)
+
+ if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT))
+ return -EINVAL;
+
+ while (vsize > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page + vma->vm_pgoff,
+ PAGE_SIZE, vma->vm_page_prot))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ vsize -= PAGE_SIZE;
+ }
+
+ DBG(5, "mmap method successfully called")
+ return 0;
+}
+
+
+static int
+w9968cf_ioctl(struct inode* inode, struct file* filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct w9968cf_device* cam;
+ int err;
+
+ cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->disconnected) {
+ DBG(2, "Device not present")
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->misconfigured) {
+ DBG(2, "The camera is misconfigured. Close and open it again.")
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ err = w9968cf_v4l_ioctl(inode, filp, cmd, (void __user *)arg);
+
+ mutex_unlock(&cam->fileop_mutex);
+ return err;
+}
+
+
+static int w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
+ unsigned int cmd, void __user * arg)
+{
+ struct w9968cf_device* cam;
+ const char* v4l1_ioctls[] = {
+ "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER",
+ "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF",
+ "SFBUF", "KEY", "GFREQ", "SFREQ", "GAUDIO", "SAUDIO",
+ "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE",
+ "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE",
+ "GVBIFMT", "SVBIFMT"
+ };
+
+ #define V4L1_IOCTL(cmd) \
+ ((_IOC_NR((cmd)) < ARRAY_SIZE(v4l1_ioctls)) ? \
+ v4l1_ioctls[_IOC_NR((cmd))] : "?")
+
+ cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
+
+ switch (cmd) {
+
+ case VIDIOCGCAP: /* get video capability */
+ {
+ struct video_capability cap = {
+ .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES,
+ .channels = 1,
+ .audios = 0,
+ .minwidth = cam->minwidth,
+ .minheight = cam->minheight,
+ };
+ sprintf(cap.name, "W996[87]CF USB Camera #%d",
+ cam->v4ldev->minor);
+ cap.maxwidth = (cam->upscaling && w9968cf_vpp)
+ ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth)
+ : cam->maxwidth;
+ cap.maxheight = (cam->upscaling && w9968cf_vpp)
+ ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight)
+ : cam->maxheight;
+
+ if (copy_to_user(arg, &cap, sizeof(cap)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGCAP successfully called")
+ return 0;
+ }
+
+ case VIDIOCGCHAN: /* get video channel informations */
+ {
+ struct video_channel chan;
+ if (copy_from_user(&chan, arg, sizeof(chan)))
+ return -EFAULT;
+
+ if (chan.channel != 0)
+ return -EINVAL;
+
+ strcpy(chan.name, "Camera");
+ chan.tuners = 0;
+ chan.flags = 0;
+ chan.type = VIDEO_TYPE_CAMERA;
+ chan.norm = VIDEO_MODE_AUTO;
+
+ if (copy_to_user(arg, &chan, sizeof(chan)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGCHAN successfully called")
+ return 0;
+ }
+
+ case VIDIOCSCHAN: /* set active channel */
+ {
+ struct video_channel chan;
+
+ if (copy_from_user(&chan, arg, sizeof(chan)))
+ return -EFAULT;
+
+ if (chan.channel != 0)
+ return -EINVAL;
+
+ DBG(5, "VIDIOCSCHAN successfully called")
+ return 0;
+ }
+
+ case VIDIOCGPICT: /* get image properties of the picture */
+ {
+ if (w9968cf_sensor_get_picture(cam))
+ return -EIO;
+
+ if (copy_to_user(arg, &cam->picture, sizeof(cam->picture)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGPICT successfully called")
+ return 0;
+ }
+
+ case VIDIOCSPICT: /* change picture settings */
+ {
+ struct video_picture pict;
+ int err = 0;
+
+ if (copy_from_user(&pict, arg, sizeof(pict)))
+ return -EFAULT;
+
+ if ( (cam->force_palette || !w9968cf_vpp)
+ && pict.palette != cam->picture.palette ) {
+ DBG(4, "Palette %s rejected: only %s is allowed",
+ symbolic(v4l1_plist, pict.palette),
+ symbolic(v4l1_plist, cam->picture.palette))
+ return -EINVAL;
+ }
+
+ if (!w9968cf_valid_palette(pict.palette)) {
+ DBG(4, "Palette %s not supported. VIDIOCSPICT failed",
+ symbolic(v4l1_plist, pict.palette))
+ return -EINVAL;
+ }
+
+ if (!cam->force_palette) {
+ if (cam->decompression == 0) {
+ if (w9968cf_need_decompression(pict.palette)) {
+ DBG(4, "Decompression disabled: palette %s is not "
+ "allowed. VIDIOCSPICT failed",
+ symbolic(v4l1_plist, pict.palette))
+ return -EINVAL;
+ }
+ } else if (cam->decompression == 1) {
+ if (!w9968cf_need_decompression(pict.palette)) {
+ DBG(4, "Decompression forced: palette %s is not "
+ "allowed. VIDIOCSPICT failed",
+ symbolic(v4l1_plist, pict.palette))
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (pict.depth != w9968cf_valid_depth(pict.palette)) {
+ DBG(4, "Requested depth %u bpp is not valid for %s "
+ "palette: ignored and changed to %u bpp",
+ pict.depth, symbolic(v4l1_plist, pict.palette),
+ w9968cf_valid_depth(pict.palette))
+ pict.depth = w9968cf_valid_depth(pict.palette);
+ }
+
+ if (pict.palette != cam->picture.palette) {
+ if(*cam->requested_frame
+ || cam->frame_current->queued) {
+ err = wait_event_interruptible
+ ( cam->wait_queue,
+ cam->disconnected ||
+ (!*cam->requested_frame &&
+ !cam->frame_current->queued) );
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ }
+
+ if (w9968cf_stop_transfer(cam))
+ goto ioctl_fail;
+
+ if (w9968cf_set_picture(cam, pict))
+ goto ioctl_fail;
+
+ if (w9968cf_start_transfer(cam))
+ goto ioctl_fail;
+
+ } else if (w9968cf_sensor_update_picture(cam, pict))
+ return -EIO;
+
+
+ DBG(5, "VIDIOCSPICT successfully called")
+ return 0;
+ }
+
+ case VIDIOCSWIN: /* set capture area */
+ {
+ struct video_window win;
+ int err = 0;
+
+ if (copy_from_user(&win, arg, sizeof(win)))
+ return -EFAULT;
+
+ DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%u, "
+ "x=%u, y=%u, %ux%u", win.clipcount, win.flags,
+ win.x, win.y, win.width, win.height)
+
+ if (win.clipcount != 0 || win.flags != 0)
+ return -EINVAL;
+
+ if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width,
+ (u16*)&win.height))) {
+ DBG(4, "Resolution not supported (%ux%u). "
+ "VIDIOCSWIN failed", win.width, win.height)
+ return err;
+ }
+
+ if (win.x != cam->window.x ||
+ win.y != cam->window.y ||
+ win.width != cam->window.width ||
+ win.height != cam->window.height) {
+ if(*cam->requested_frame
+ || cam->frame_current->queued) {
+ err = wait_event_interruptible
+ ( cam->wait_queue,
+ cam->disconnected ||
+ (!*cam->requested_frame &&
+ !cam->frame_current->queued) );
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ }
+
+ if (w9968cf_stop_transfer(cam))
+ goto ioctl_fail;
+
+ /* This _must_ be called before set_window() */
+ if (w9968cf_set_picture(cam, cam->picture))
+ goto ioctl_fail;
+
+ if (w9968cf_set_window(cam, win))
+ goto ioctl_fail;
+
+ if (w9968cf_start_transfer(cam))
+ goto ioctl_fail;
+ }
+
+ DBG(5, "VIDIOCSWIN successfully called. ")
+ return 0;
+ }
+
+ case VIDIOCGWIN: /* get current window properties */
+ {
+ if (copy_to_user(arg,&cam->window,sizeof(struct video_window)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGWIN successfully called")
+ return 0;
+ }
+
+ case VIDIOCGMBUF: /* request for memory (mapped) buffer */
+ {
+ struct video_mbuf mbuf;
+ u8 i;
+
+ mbuf.size = cam->nbuffers * cam->frame[0].size;
+ mbuf.frames = cam->nbuffers;
+ for (i = 0; i < cam->nbuffers; i++)
+ mbuf.offsets[i] = (unsigned long)cam->frame[i].buffer -
+ (unsigned long)cam->frame[0].buffer;
+
+ if (copy_to_user(arg, &mbuf, sizeof(mbuf)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGMBUF successfully called")
+ return 0;
+ }
+
+ case VIDIOCMCAPTURE: /* start the capture to a frame */
+ {
+ struct video_mmap mmap;
+ struct w9968cf_frame_t* fr;
+ int err = 0;
+
+ if (copy_from_user(&mmap, arg, sizeof(mmap)))
+ return -EFAULT;
+
+ DBG(6, "VIDIOCMCAPTURE called: frame #%u, format=%s, %dx%d",
+ mmap.frame, symbolic(v4l1_plist, mmap.format),
+ mmap.width, mmap.height)
+
+ if (mmap.frame >= cam->nbuffers) {
+ DBG(4, "Invalid frame number (%u). "
+ "VIDIOCMCAPTURE failed", mmap.frame)
+ return -EINVAL;
+ }
+
+ if (mmap.format!=cam->picture.palette &&
+ (cam->force_palette || !w9968cf_vpp)) {
+ DBG(4, "Palette %s rejected: only %s is allowed",
+ symbolic(v4l1_plist, mmap.format),
+ symbolic(v4l1_plist, cam->picture.palette))
+ return -EINVAL;
+ }
+
+ if (!w9968cf_valid_palette(mmap.format)) {
+ DBG(4, "Palette %s not supported. "
+ "VIDIOCMCAPTURE failed",
+ symbolic(v4l1_plist, mmap.format))
+ return -EINVAL;
+ }
+
+ if (!cam->force_palette) {
+ if (cam->decompression == 0) {
+ if (w9968cf_need_decompression(mmap.format)) {
+ DBG(4, "Decompression disabled: palette %s is not "
+ "allowed. VIDIOCSPICT failed",
+ symbolic(v4l1_plist, mmap.format))
+ return -EINVAL;
+ }
+ } else if (cam->decompression == 1) {
+ if (!w9968cf_need_decompression(mmap.format)) {
+ DBG(4, "Decompression forced: palette %s is not "
+ "allowed. VIDIOCSPICT failed",
+ symbolic(v4l1_plist, mmap.format))
+ return -EINVAL;
+ }
+ }
+ }
+
+ if ((err = w9968cf_adjust_window_size(cam, (u16*)&mmap.width,
+ (u16*)&mmap.height))) {
+ DBG(4, "Resolution not supported (%dx%d). "
+ "VIDIOCMCAPTURE failed",
+ mmap.width, mmap.height)
+ return err;
+ }
+
+ fr = &cam->frame[mmap.frame];
+
+ if (mmap.width != cam->window.width ||
+ mmap.height != cam->window.height ||
+ mmap.format != cam->picture.palette) {
+
+ struct video_window win;
+ struct video_picture pict;
+
+ if(*cam->requested_frame
+ || cam->frame_current->queued) {
+ DBG(6, "VIDIOCMCAPTURE. Change settings for "
+ "frame #%u: %dx%d, format %s. Wait...",
+ mmap.frame, mmap.width, mmap.height,
+ symbolic(v4l1_plist, mmap.format))
+ err = wait_event_interruptible
+ ( cam->wait_queue,
+ cam->disconnected ||
+ (!*cam->requested_frame &&
+ !cam->frame_current->queued) );
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ }
+
+ memcpy(&win, &cam->window, sizeof(win));
+ memcpy(&pict, &cam->picture, sizeof(pict));
+ win.width = mmap.width;
+ win.height = mmap.height;
+ pict.palette = mmap.format;
+
+ if (w9968cf_stop_transfer(cam))
+ goto ioctl_fail;
+
+ /* This before set_window */
+ if (w9968cf_set_picture(cam, pict))
+ goto ioctl_fail;
+
+ if (w9968cf_set_window(cam, win))
+ goto ioctl_fail;
+
+ if (w9968cf_start_transfer(cam))
+ goto ioctl_fail;
+
+ } else if (fr->queued) {
+
+ DBG(6, "Wait until frame #%u is free", mmap.frame)
+
+ err = wait_event_interruptible(cam->wait_queue,
+ cam->disconnected ||
+ (!fr->queued));
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ }
+
+ w9968cf_push_frame(cam, mmap.frame);
+ DBG(5, "VIDIOCMCAPTURE(%u): successfully called", mmap.frame)
+ return 0;
+ }
+
+ case VIDIOCSYNC: /* wait until the capture of a frame is finished */
+ {
+ unsigned int f_num;
+ struct w9968cf_frame_t* fr;
+ int err = 0;
+
+ if (copy_from_user(&f_num, arg, sizeof(f_num)))
+ return -EFAULT;
+
+ if (f_num >= cam->nbuffers) {
+ DBG(4, "Invalid frame number (%u). "
+ "VIDIOCMCAPTURE failed", f_num)
+ return -EINVAL;
+ }
+
+ DBG(6, "VIDIOCSYNC called for frame #%u", f_num)
+
+ fr = &cam->frame[f_num];
+
+ switch (fr->status) {
+ case F_UNUSED:
+ if (!fr->queued) {
+ DBG(4, "VIDIOSYNC: Frame #%u not requested!",
+ f_num)
+ return -EFAULT;
+ }
+ case F_ERROR:
+ case F_GRABBING:
+ err = wait_event_interruptible(cam->wait_queue,
+ (fr->status == F_READY)
+ || cam->disconnected);
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ break;
+ case F_READY:
+ break;
+ }
+
+ if (w9968cf_vpp)
+ w9968cf_postprocess_frame(cam, fr);
+
+ fr->status = F_UNUSED;
+
+ DBG(5, "VIDIOCSYNC(%u) successfully called", f_num)
+ return 0;
+ }
+
+ case VIDIOCGUNIT:/* report the unit numbers of the associated devices*/
+ {
+ struct video_unit unit = {
+ .video = cam->v4ldev->minor,
+ .vbi = VIDEO_NO_UNIT,
+ .radio = VIDEO_NO_UNIT,
+ .audio = VIDEO_NO_UNIT,
+ .teletext = VIDEO_NO_UNIT,
+ };
+
+ if (copy_to_user(arg, &unit, sizeof(unit)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGUNIT successfully called")
+ return 0;
+ }
+
+ case VIDIOCKEY:
+ return 0;
+
+ case VIDIOCGFBUF:
+ {
+ if (clear_user(arg, sizeof(struct video_buffer)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGFBUF successfully called")
+ return 0;
+ }
+
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner tuner;
+ if (copy_from_user(&tuner, arg, sizeof(tuner)))
+ return -EFAULT;
+
+ if (tuner.tuner != 0)
+ return -EINVAL;
+
+ strcpy(tuner.name, "no_tuner");
+ tuner.rangelow = 0;
+ tuner.rangehigh = 0;
+ tuner.flags = VIDEO_TUNER_NORM;
+ tuner.mode = VIDEO_MODE_AUTO;
+ tuner.signal = 0xffff;
+
+ if (copy_to_user(arg, &tuner, sizeof(tuner)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGTUNER successfully called")
+ return 0;
+ }
+
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner tuner;
+ if (copy_from_user(&tuner, arg, sizeof(tuner)))
+ return -EFAULT;
+
+ if (tuner.tuner != 0)
+ return -EINVAL;
+
+ if (tuner.mode != VIDEO_MODE_AUTO)
+ return -EINVAL;
+
+ DBG(5, "VIDIOCSTUNER successfully called")
+ return 0;
+ }
+
+ case VIDIOCSFBUF:
+ case VIDIOCCAPTURE:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ case VIDIOCSPLAYMODE:
+ case VIDIOCSWRITEMODE:
+ case VIDIOCGPLAYINFO:
+ case VIDIOCSMICROCODE:
+ case VIDIOCGVBIFMT:
+ case VIDIOCSVBIFMT:
+ DBG(4, "Unsupported V4L1 IOCtl: VIDIOC%s "
+ "(type 0x%01X, "
+ "n. 0x%01X, "
+ "dir. 0x%01X, "
+ "size 0x%02X)",
+ V4L1_IOCTL(cmd),
+ _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd))
+
+ return -EINVAL;
+
+ default:
+ DBG(4, "Invalid V4L1 IOCtl: VIDIOC%s "
+ "type 0x%01X, "
+ "n. 0x%01X, "
+ "dir. 0x%01X, "
+ "size 0x%02X",
+ V4L1_IOCTL(cmd),
+ _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd))
+
+ return -ENOIOCTLCMD;
+
+ } /* end of switch */
+
+ioctl_fail:
+ cam->misconfigured = 1;
+ DBG(1, "VIDIOC%s failed because of hardware problems. "
+ "To use the camera, close and open it again.", V4L1_IOCTL(cmd))
+ return -EFAULT;
+}
+
+
+static struct file_operations w9968cf_fops = {
+ .owner = THIS_MODULE,
+ .open = w9968cf_open,
+ .release = w9968cf_release,
+ .read = w9968cf_read,
+ .ioctl = w9968cf_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .mmap = w9968cf_mmap,
+ .llseek = no_llseek,
+};
+
+
+
+/****************************************************************************
+ * USB probe and V4L registration, disconnect and id_table[] definition *
+ ****************************************************************************/
+
+static int
+w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct w9968cf_device* cam;
+ int err = 0;
+ enum w9968cf_model_id mod_id;
+ struct list_head* ptr;
+ u8 sc = 0; /* number of simultaneous cameras */
+ static unsigned short dev_nr = 0; /* we are handling device number n */
+
+ if (le16_to_cpu(udev->descriptor.idVendor) == winbond_id_table[0].idVendor &&
+ le16_to_cpu(udev->descriptor.idProduct) == winbond_id_table[0].idProduct)
+ mod_id = W9968CF_MOD_CLVBWGP; /* see camlist[] table */
+ else if (le16_to_cpu(udev->descriptor.idVendor) == winbond_id_table[1].idVendor &&
+ le16_to_cpu(udev->descriptor.idProduct) == winbond_id_table[1].idProduct)
+ mod_id = W9968CF_MOD_GENERIC; /* see camlist[] table */
+ else
+ return -ENODEV;
+
+ cam = (struct w9968cf_device*)
+ kzalloc(sizeof(struct w9968cf_device), GFP_KERNEL);
+ if (!cam)
+ return -ENOMEM;
+
+ mutex_init(&cam->dev_mutex);
+ mutex_lock(&cam->dev_mutex);
+
+ cam->usbdev = udev;
+ /* NOTE: a local copy is used to avoid possible race conditions */
+ memcpy(&cam->dev, &udev->dev, sizeof(struct device));
+
+ DBG(2, "%s detected", symbolic(camlist, mod_id))
+
+ if (simcams > W9968CF_MAX_DEVICES)
+ simcams = W9968CF_SIMCAMS;
+
+ /* How many cameras are connected ? */
+ mutex_lock(&w9968cf_devlist_mutex);
+ list_for_each(ptr, &w9968cf_dev_list)
+ sc++;
+ mutex_unlock(&w9968cf_devlist_mutex);
+
+ if (sc >= simcams) {
+ DBG(2, "Device rejected: too many connected cameras "
+ "(max. %u)", simcams)
+ err = -EPERM;
+ goto fail;
+ }
+
+
+ /* Allocate 2 bytes of memory for camera control USB transfers */
+ if (!(cam->control_buffer = kzalloc(2, GFP_KERNEL))) {
+ DBG(1,"Couldn't allocate memory for camera control transfers")
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* Allocate 8 bytes of memory for USB data transfers to the FSB */
+ if (!(cam->data_buffer = kzalloc(8, GFP_KERNEL))) {
+ DBG(1, "Couldn't allocate memory for data "
+ "transfers to the FSB")
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* Register the V4L device */
+ cam->v4ldev = video_device_alloc();
+ if (!cam->v4ldev) {
+ DBG(1, "Could not allocate memory for a V4L structure")
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ strcpy(cam->v4ldev->name, symbolic(camlist, mod_id));
+ cam->v4ldev->owner = THIS_MODULE;
+ cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
+ cam->v4ldev->hardware = VID_HARDWARE_W9968CF;
+ cam->v4ldev->fops = &w9968cf_fops;
+ cam->v4ldev->minor = video_nr[dev_nr];
+ cam->v4ldev->release = video_device_release;
+ video_set_drvdata(cam->v4ldev, cam);
+ cam->v4ldev->dev = &cam->dev;
+
+ err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
+ video_nr[dev_nr]);
+ if (err) {
+ DBG(1, "V4L device registration failed")
+ if (err == -ENFILE && video_nr[dev_nr] == -1)
+ DBG(2, "Couldn't find a free /dev/videoX node")
+ video_nr[dev_nr] = -1;
+ dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
+ goto fail;
+ }
+
+ DBG(2, "V4L device registered as /dev/video%d", cam->v4ldev->minor)
+
+ /* Set some basic constants */
+ w9968cf_configure_camera(cam, udev, mod_id, dev_nr);
+
+ /* Add a new entry into the list of V4L registered devices */
+ mutex_lock(&w9968cf_devlist_mutex);
+ list_add(&cam->v4llist, &w9968cf_dev_list);
+ mutex_unlock(&w9968cf_devlist_mutex);
+ dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
+ w9968cf_turn_on_led(cam);
+
+ w9968cf_i2c_init(cam);
+
+ usb_set_intfdata(intf, cam);
+ mutex_unlock(&cam->dev_mutex);
+ return 0;
+
+fail: /* Free unused memory */
+ kfree(cam->control_buffer);
+ kfree(cam->data_buffer);
+ if (cam->v4ldev)
+ video_device_release(cam->v4ldev);
+ mutex_unlock(&cam->dev_mutex);
+ kfree(cam);
+ return err;
+}
+
+
+static void w9968cf_usb_disconnect(struct usb_interface* intf)
+{
+ struct w9968cf_device* cam =
+ (struct w9968cf_device*)usb_get_intfdata(intf);
+
+ down_write(&w9968cf_disconnect);
+
+ if (cam) {
+ /* Prevent concurrent accesses to data */
+ mutex_lock(&cam->dev_mutex);
+
+ cam->disconnected = 1;
+
+ DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id))
+
+ wake_up_interruptible_all(&cam->open);
+
+ if (cam->users) {
+ DBG(2, "The device is open (/dev/video%d)! "
+ "Process name: %s. Deregistration and memory "
+ "deallocation are deferred on close.",
+ cam->v4ldev->minor, cam->command)
+ cam->misconfigured = 1;
+ w9968cf_stop_transfer(cam);
+ wake_up_interruptible(&cam->wait_queue);
+ } else
+ w9968cf_release_resources(cam);
+
+ mutex_unlock(&cam->dev_mutex);
+
+ if (!cam->users)
+ kfree(cam);
+ }
+
+ up_write(&w9968cf_disconnect);
+}
+
+
+static struct usb_driver w9968cf_usb_driver = {
+ .name = "w9968cf",
+ .id_table = winbond_id_table,
+ .probe = w9968cf_usb_probe,
+ .disconnect = w9968cf_usb_disconnect,
+};
+
+
+
+/****************************************************************************
+ * Module init, exit and intermodule communication *
+ ****************************************************************************/
+
+static int __init w9968cf_module_init(void)
+{
+ int err;
+
+ KDBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION)
+ KDBG(3, W9968CF_MODULE_AUTHOR)
+
+ if (ovmod_load)
+ request_module("ovcamchip");
+
+ if ((err = usb_register(&w9968cf_usb_driver)))
+ return err;
+
+ return 0;
+}
+
+
+static void __exit w9968cf_module_exit(void)
+{
+ /* w9968cf_usb_disconnect() will be called */
+ usb_deregister(&w9968cf_usb_driver);
+
+ KDBG(2, W9968CF_MODULE_NAME" deregistered")
+}
+
+
+module_init(w9968cf_module_init);
+module_exit(w9968cf_module_exit);
+
diff --git a/drivers/media/video/w9968cf.h b/drivers/media/video/w9968cf.h
new file mode 100644
index 000000000000..a87be719a281
--- /dev/null
+++ b/drivers/media/video/w9968cf.h
@@ -0,0 +1,330 @@
+/***************************************************************************
+ * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. *
+ * *
+ * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _W9968CF_H_
+#define _W9968CF_H_
+
+#include <linux/videodev.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/config.h>
+#include <linux/param.h>
+#include <linux/types.h>
+#include <linux/rwsem.h>
+#include <linux/mutex.h>
+
+#include <media/ovcamchip.h>
+
+#include "w9968cf_vpp.h"
+
+
+/****************************************************************************
+ * Default values *
+ ****************************************************************************/
+
+#define W9968CF_OVMOD_LOAD 1 /* automatic 'ovcamchip' module loading */
+#define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */
+
+/* Comment/uncomment the following line to enable/disable debugging messages */
+#define W9968CF_DEBUG
+
+/* These have effect only if W9968CF_DEBUG is defined */
+#define W9968CF_DEBUG_LEVEL 2 /* from 0 to 6. 0 for no debug informations */
+#define W9968CF_SPECIFIC_DEBUG 0 /* 0 or 1 */
+
+#define W9968CF_MAX_DEVICES 32
+#define W9968CF_SIMCAMS W9968CF_MAX_DEVICES /* simultaneous cameras */
+
+#define W9968CF_MAX_BUFFERS 32
+#define W9968CF_BUFFERS 2 /* n. of frame buffers from 2 to MAX_BUFFERS */
+
+/* Maximum data payload sizes in bytes for alternate settings */
+static const u16 wMaxPacketSize[] = {1023, 959, 895, 831, 767, 703, 639, 575,
+ 511, 447, 383, 319, 255, 191, 127, 63};
+#define W9968CF_PACKET_SIZE 1023 /* according to wMaxPacketSizes[] */
+#define W9968CF_MIN_PACKET_SIZE 63 /* minimum value */
+#define W9968CF_ISO_PACKETS 5 /* n.of packets for isochronous transfers */
+#define W9968CF_USB_CTRL_TIMEOUT 1000 /* timeout (ms) for usb control commands */
+#define W9968CF_URBS 2 /* n. of scheduled URBs for ISO transfer */
+
+#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */
+#define W9968CF_I2C_RW_RETRIES 15 /* number of max I2C r/w retries */
+
+/* Available video formats */
+struct w9968cf_format {
+ const u16 palette;
+ const u16 depth;
+ const u8 compression;
+};
+
+static const struct w9968cf_format w9968cf_formatlist[] = {
+ { VIDEO_PALETTE_UYVY, 16, 0 }, /* original video */
+ { VIDEO_PALETTE_YUV422P, 16, 1 }, /* with JPEG compression */
+ { VIDEO_PALETTE_YUV420P, 12, 1 }, /* with JPEG compression */
+ { VIDEO_PALETTE_YUV420, 12, 1 }, /* same as YUV420P */
+ { VIDEO_PALETTE_YUYV, 16, 0 }, /* software conversion */
+ { VIDEO_PALETTE_YUV422, 16, 0 }, /* software conversion */
+ { VIDEO_PALETTE_GREY, 8, 0 }, /* software conversion */
+ { VIDEO_PALETTE_RGB555, 16, 0 }, /* software conversion */
+ { VIDEO_PALETTE_RGB565, 16, 0 }, /* software conversion */
+ { VIDEO_PALETTE_RGB24, 24, 0 }, /* software conversion */
+ { VIDEO_PALETTE_RGB32, 32, 0 }, /* software conversion */
+ { 0, 0, 0 } /* 0 is a terminating entry */
+};
+
+#define W9968CF_DECOMPRESSION 2 /* decomp:0=disable,1=force,2=any formats */
+#define W9968CF_PALETTE_DECOMP_OFF VIDEO_PALETTE_UYVY /* when decomp=0 */
+#define W9968CF_PALETTE_DECOMP_FORCE VIDEO_PALETTE_YUV420P /* when decomp=1 */
+#define W9968CF_PALETTE_DECOMP_ON VIDEO_PALETTE_UYVY /* when decomp=2 */
+
+#define W9968CF_FORCE_RGB 0 /* read RGB instead of BGR, yes=1/no=0 */
+
+#define W9968CF_MAX_WIDTH 800 /* Has effect if up-scaling is on */
+#define W9968CF_MAX_HEIGHT 600 /* Has effect if up-scaling is on */
+#define W9968CF_WIDTH 320 /* from 128 to 352, multiple of 16 */
+#define W9968CF_HEIGHT 240 /* from 96 to 288, multiple of 16 */
+
+#define W9968CF_CLAMPING 0 /* 0 disable, 1 enable video data clamping */
+#define W9968CF_FILTER_TYPE 0 /* 0 disable 1 (1-2-1), 2 (2-3-6-3-2) */
+#define W9968CF_DOUBLE_BUFFER 1 /* 0 disable, 1 enable double buffer */
+#define W9968CF_LARGEVIEW 1 /* 0 disable, 1 enable */
+#define W9968CF_UPSCALING 0 /* 0 disable, 1 enable */
+
+#define W9968CF_MONOCHROME 0 /* 0 not monochrome, 1 monochrome sensor */
+#define W9968CF_BRIGHTNESS 31000 /* from 0 to 65535 */
+#define W9968CF_HUE 32768 /* from 0 to 65535 */
+#define W9968CF_COLOUR 32768 /* from 0 to 65535 */
+#define W9968CF_CONTRAST 50000 /* from 0 to 65535 */
+#define W9968CF_WHITENESS 32768 /* from 0 to 65535 */
+
+#define W9968CF_AUTOBRIGHT 0 /* 0 disable, 1 enable automatic brightness */
+#define W9968CF_AUTOEXP 1 /* 0 disable, 1 enable automatic exposure */
+#define W9968CF_LIGHTFREQ 50 /* light frequency. 50Hz (Europe) or 60Hz */
+#define W9968CF_BANDINGFILTER 0 /* 0 disable, 1 enable banding filter */
+#define W9968CF_BACKLIGHT 0 /* 0 or 1, 1=object is lit from behind */
+#define W9968CF_MIRROR 0 /* 0 or 1 [don't] reverse image horizontally*/
+
+#define W9968CF_CLOCKDIV -1 /* -1 = automatic clock divisor */
+#define W9968CF_DEF_CLOCKDIVISOR 0 /* default sensor clock divisor value */
+
+
+/****************************************************************************
+ * Globals *
+ ****************************************************************************/
+
+#define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \
+ "Dual Mode Camera Chip"
+#define W9968CF_MODULE_VERSION "1:1.33-basic"
+#define W9968CF_MODULE_AUTHOR "(C) 2002-2004 Luca Risolia"
+#define W9968CF_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
+#define W9968CF_MODULE_LICENSE "GPL"
+
+static const struct usb_device_id winbond_id_table[] = {
+ {
+ /* Creative Labs Video Blaster WebCam Go Plus */
+ USB_DEVICE(0x041e, 0x4003),
+ .driver_info = (unsigned long)"w9968cf",
+ },
+ {
+ /* Generic W996[87]CF JPEG USB Dual Mode Camera */
+ USB_DEVICE(0x1046, 0x9967),
+ .driver_info = (unsigned long)"w9968cf",
+ },
+ { } /* terminating entry */
+};
+
+/* W996[87]CF camera models, internal ids: */
+enum w9968cf_model_id {
+ W9968CF_MOD_GENERIC = 1, /* Generic W996[87]CF based device */
+ W9968CF_MOD_CLVBWGP = 11,/*Creative Labs Video Blaster WebCam Go Plus*/
+ W9968CF_MOD_ADPVDMA = 21, /* Aroma Digi Pen VGA Dual Mode ADG-5000 */
+ W9986CF_MOD_AAU = 31, /* AVerMedia AVerTV USB */
+ W9968CF_MOD_CLVBWG = 34, /* Creative Labs Video Blaster WebCam Go */
+ W9968CF_MOD_LL = 37, /* Lebon LDC-035A */
+ W9968CF_MOD_EEEMC = 40, /* Ezonics EZ-802 EZMega Cam */
+ W9968CF_MOD_OOE = 42, /* OmniVision OV8610-EDE */
+ W9968CF_MOD_ODPVDMPC = 43,/* OPCOM Digi Pen VGA Dual Mode Pen Camera */
+ W9968CF_MOD_PDPII = 46, /* Pretec Digi Pen-II */
+ W9968CF_MOD_PDP480 = 49, /* Pretec DigiPen-480 */
+};
+
+enum w9968cf_frame_status {
+ F_READY, /* finished grabbing & ready to be read/synced */
+ F_GRABBING, /* in the process of being grabbed into */
+ F_ERROR, /* something bad happened while processing */
+ F_UNUSED /* unused (no VIDIOCMCAPTURE) */
+};
+
+struct w9968cf_frame_t {
+ void* buffer;
+ unsigned long size;
+ u32 length;
+ int number;
+ enum w9968cf_frame_status status;
+ struct w9968cf_frame_t* next;
+ u8 queued;
+};
+
+enum w9968cf_vpp_flag {
+ VPP_NONE = 0x00,
+ VPP_UPSCALE = 0x01,
+ VPP_SWAP_YUV_BYTES = 0x02,
+ VPP_DECOMPRESSION = 0x04,
+ VPP_UYVY_TO_RGBX = 0x08,
+};
+
+/* Main device driver structure */
+struct w9968cf_device {
+ struct device dev; /* device structure */
+
+ enum w9968cf_model_id id; /* private device identifier */
+
+ struct video_device* v4ldev; /* -> V4L structure */
+ struct list_head v4llist; /* entry of the list of V4L cameras */
+
+ struct usb_device* usbdev; /* -> main USB structure */
+ struct urb* urb[W9968CF_URBS]; /* -> USB request block structs */
+ void* transfer_buffer[W9968CF_URBS]; /* -> ISO transfer buffers */
+ u16* control_buffer; /* -> buffer for control req.*/
+ u16* data_buffer; /* -> data to send to the FSB */
+
+ struct w9968cf_frame_t frame[W9968CF_MAX_BUFFERS];
+ struct w9968cf_frame_t frame_tmp; /* temporary frame */
+ struct w9968cf_frame_t frame_vpp; /* helper frame.*/
+ struct w9968cf_frame_t* frame_current; /* -> frame being grabbed */
+ struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS];
+
+ u8 max_buffers, /* number of requested buffers */
+ force_palette, /* yes=1/no=0 */
+ force_rgb, /* read RGB instead of BGR, yes=1, no=0 */
+ double_buffer, /* hardware double buffering yes=1/no=0 */
+ clamping, /* video data clamping yes=1/no=0 */
+ filter_type, /* 0=disabled, 1=3 tap, 2=5 tap filter */
+ capture, /* 0=disabled, 1=enabled */
+ largeview, /* 0=disabled, 1=enabled */
+ decompression, /* 0=disabled, 1=forced, 2=allowed */
+ upscaling; /* software image scaling, 0=enabled, 1=disabled */
+
+ struct video_picture picture; /* current picture settings */
+ struct video_window window; /* current window settings */
+
+ u16 hw_depth, /* depth (used by the chip) */
+ hw_palette, /* palette (used by the chip) */
+ hw_width, /* width (used by the chip) */
+ hw_height, /* height (used by the chip) */
+ hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */
+ vs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */
+ start_cropx, /* pixels from HS inactive edge to 1st cropped pixel*/
+ start_cropy; /* pixels from VS inactive edge to 1st cropped pixel*/
+
+ enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */
+
+ u8 nbuffers, /* number of allocated frame buffers */
+ altsetting, /* camera alternate setting */
+ disconnected, /* flag: yes=1, no=0 */
+ misconfigured, /* flag: yes=1, no=0 */
+ users, /* flag: number of users holding the device */
+ streaming; /* flag: yes=1, no=0 */
+
+ u8 sensor_initialized; /* flag: yes=1, no=0 */
+
+ /* Determined by the image sensor type: */
+ int sensor, /* type of image sensor chip (CC_*) */
+ monochrome; /* image sensor is (probably) monochrome */
+ u16 maxwidth, /* maximum width supported by the image sensor */
+ maxheight, /* maximum height supported by the image sensor */
+ minwidth, /* minimum width supported by the image sensor */
+ minheight; /* minimum height supported by the image sensor */
+ u8 auto_brt, /* auto brightness enabled flag */
+ auto_exp, /* auto exposure enabled flag */
+ backlight, /* backlight exposure algorithm flag */
+ mirror, /* image is reversed horizontally */
+ lightfreq, /* power (lighting) frequency */
+ bandfilt; /* banding filter enabled flag */
+ s8 clockdiv; /* clock divisor */
+
+ /* I2C interface to kernel */
+ struct i2c_adapter i2c_adapter;
+ struct i2c_client* sensor_client;
+
+ /* Locks */
+ struct mutex dev_mutex, /* for probe, disconnect,open and close */
+ fileop_mutex; /* for read and ioctl */
+ spinlock_t urb_lock, /* for submit_urb() and unlink_urb() */
+ flist_lock; /* for requested frame list accesses */
+ wait_queue_head_t open, wait_queue;
+
+ char command[16]; /* name of the program holding the device */
+};
+
+
+/****************************************************************************
+ * Macros for debugging *
+ ****************************************************************************/
+
+#undef DBG
+#undef KDBG
+#ifdef W9968CF_DEBUG
+/* For device specific debugging messages */
+# define DBG(level, fmt, args...) \
+{ \
+ if ( ((specific_debug) && (debug == (level))) || \
+ ((!specific_debug) && (debug >= (level))) ) { \
+ if ((level) == 1) \
+ dev_err(&cam->dev, fmt "\n", ## args); \
+ else if ((level) == 2 || (level) == 3) \
+ dev_info(&cam->dev, fmt "\n", ## args); \
+ else if ((level) == 4) \
+ dev_warn(&cam->dev, fmt "\n", ## args); \
+ else if ((level) >= 5) \
+ dev_info(&cam->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args); \
+ } \
+}
+/* For generic kernel (not device specific) messages */
+# define KDBG(level, fmt, args...) \
+{ \
+ if ( ((specific_debug) && (debug == (level))) || \
+ ((!specific_debug) && (debug >= (level))) ) { \
+ if ((level) >= 1 && (level) <= 4) \
+ pr_info("w9968cf: " fmt "\n", ## args); \
+ else if ((level) >= 5) \
+ pr_debug("w9968cf: [%s:%d] " fmt "\n", __FUNCTION__, \
+ __LINE__ , ## args); \
+ } \
+}
+#else
+ /* Not debugging: nothing */
+# define DBG(level, fmt, args...) do {;} while(0);
+# define KDBG(level, fmt, args...) do {;} while(0);
+#endif
+
+#undef PDBG
+#define PDBG(fmt, args...) \
+dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args);
+
+#undef PDBGG
+#define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */
+
+#endif /* _W9968CF_H_ */
diff --git a/drivers/media/video/w9968cf_decoder.h b/drivers/media/video/w9968cf_decoder.h
new file mode 100644
index 000000000000..31faccbe8f03
--- /dev/null
+++ b/drivers/media/video/w9968cf_decoder.h
@@ -0,0 +1,86 @@
+/***************************************************************************
+ * Video decoder for the W996[87]CF driver for Linux. *
+ * *
+ * Copyright (C) 2003 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _W9968CF_DECODER_H_
+#define _W9968CF_DECODER_H_
+
+/* Comment/uncomment this for high/low quality of compressed video */
+#define W9968CF_DEC_FAST_LOWQUALITY_VIDEO
+
+#ifdef W9968CF_DEC_FAST_LOWQUALITY_VIDEO
+static const unsigned char Y_QUANTABLE[64] = {
+ 16, 11, 10, 16, 24, 40, 51, 61,
+ 12, 12, 14, 19, 26, 58, 60, 55,
+ 14, 13, 16, 24, 40, 57, 69, 56,
+ 14, 17, 22, 29, 51, 87, 80, 62,
+ 18, 22, 37, 56, 68, 109, 103, 77,
+ 24, 35, 55, 64, 81, 104, 113, 92,
+ 49, 64, 78, 87, 103, 121, 120, 101,
+ 72, 92, 95, 98, 112, 100, 103, 99
+};
+
+static const unsigned char UV_QUANTABLE[64] = {
+ 17, 18, 24, 47, 99, 99, 99, 99,
+ 18, 21, 26, 66, 99, 99, 99, 99,
+ 24, 26, 56, 99, 99, 99, 99, 99,
+ 47, 66, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99
+};
+#else
+static const unsigned char Y_QUANTABLE[64] = {
+ 8, 5, 5, 8, 12, 20, 25, 30,
+ 6, 6, 7, 9, 13, 29, 30, 27,
+ 7, 6, 8, 12, 20, 28, 34, 28,
+ 7, 8, 11, 14, 25, 43, 40, 31,
+ 9, 11, 18, 28, 34, 54, 51, 38,
+ 12, 17, 27, 32, 40, 52, 56, 46,
+ 24, 32, 39, 43, 51, 60, 60, 50,
+ 36, 46, 47, 49, 56, 50, 51, 49
+};
+
+static const unsigned char UV_QUANTABLE[64] = {
+ 8, 9, 12, 23, 49, 49, 49, 49,
+ 9, 10, 13, 33, 49, 49, 49, 49,
+ 12, 13, 28, 49, 49, 49, 49, 49,
+ 23, 33, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49
+};
+#endif
+
+#define W9968CF_DEC_ERR_CORRUPTED_DATA -1
+#define W9968CF_DEC_ERR_BUF_OVERFLOW -2
+#define W9968CF_DEC_ERR_NO_SOI -3
+#define W9968CF_DEC_ERR_NO_SOF0 -4
+#define W9968CF_DEC_ERR_NO_SOS -5
+#define W9968CF_DEC_ERR_NO_EOI -6
+
+extern void w9968cf_init_decoder(void);
+extern int w9968cf_check_headers(const unsigned char* Pin,
+ const unsigned long BUF_SIZE);
+extern int w9968cf_decode(const char* Pin, const unsigned long BUF_SIZE,
+ const unsigned W, const unsigned H, char* Pout);
+
+#endif /* _W9968CF_DECODER_H_ */
diff --git a/drivers/media/video/w9968cf_vpp.h b/drivers/media/video/w9968cf_vpp.h
new file mode 100644
index 000000000000..f3b91b782671
--- /dev/null
+++ b/drivers/media/video/w9968cf_vpp.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * Interface for video post-processing functions for the W996[87]CF driver *
+ * for Linux. *
+ * *
+ * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _W9968CF_VPP_H_
+#define _W9968CF_VPP_H_
+
+#include <linux/module.h>
+#include <asm/types.h>
+
+struct w9968cf_vpp_t {
+ struct module* owner;
+ int (*check_headers)(const unsigned char*, const unsigned long);
+ int (*decode)(const char*, const unsigned long, const unsigned,
+ const unsigned, char*);
+ void (*swap_yuvbytes)(void*, unsigned long);
+ void (*uyvy_to_rgbx)(u8*, unsigned long, u8*, u16, u8);
+ void (*scale_up)(u8*, u8*, u16, u16, u16, u16, u16);
+
+ u8 busy; /* read-only flag: module is/is not in use */
+};
+
+#endif /* _W9968CF_VPP_H_ */
diff --git a/drivers/media/video/zc0301/Makefile b/drivers/media/video/zc0301/Makefile
new file mode 100644
index 000000000000..d749199d8f06
--- /dev/null
+++ b/drivers/media/video/zc0301/Makefile
@@ -0,0 +1,3 @@
+zc0301-objs := zc0301_core.o zc0301_pas202bcb.o
+
+obj-$(CONFIG_USB_ZC0301) += zc0301.o
diff --git a/drivers/media/video/zc0301/zc0301.h b/drivers/media/video/zc0301/zc0301.h
new file mode 100644
index 000000000000..8e0655140e60
--- /dev/null
+++ b/drivers/media/video/zc0301/zc0301.h
@@ -0,0 +1,192 @@
+/***************************************************************************
+ * V4L2 driver for ZC0301 Image Processor and Control Chip *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _ZC0301_H_
+#define _ZC0301_H_
+
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+
+#include "zc0301_sensor.h"
+
+/*****************************************************************************/
+
+#define ZC0301_DEBUG
+#define ZC0301_DEBUG_LEVEL 2
+#define ZC0301_MAX_DEVICES 64
+#define ZC0301_FORCE_MUNMAP 0
+#define ZC0301_MAX_FRAMES 32
+#define ZC0301_COMPRESSION_QUALITY 0
+#define ZC0301_URBS 2
+#define ZC0301_ISO_PACKETS 7
+#define ZC0301_ALTERNATE_SETTING 7
+#define ZC0301_URB_TIMEOUT msecs_to_jiffies(2 * ZC0301_ISO_PACKETS)
+#define ZC0301_CTRL_TIMEOUT 100
+#define ZC0301_FRAME_TIMEOUT 2
+
+/*****************************************************************************/
+
+ZC0301_ID_TABLE
+ZC0301_SENSOR_TABLE
+
+enum zc0301_frame_state {
+ F_UNUSED,
+ F_QUEUED,
+ F_GRABBING,
+ F_DONE,
+ F_ERROR,
+};
+
+struct zc0301_frame_t {
+ void* bufmem;
+ struct v4l2_buffer buf;
+ enum zc0301_frame_state state;
+ struct list_head frame;
+ unsigned long vma_use_count;
+};
+
+enum zc0301_dev_state {
+ DEV_INITIALIZED = 0x01,
+ DEV_DISCONNECTED = 0x02,
+ DEV_MISCONFIGURED = 0x04,
+};
+
+enum zc0301_io_method {
+ IO_NONE,
+ IO_READ,
+ IO_MMAP,
+};
+
+enum zc0301_stream_state {
+ STREAM_OFF,
+ STREAM_INTERRUPT,
+ STREAM_ON,
+};
+
+struct zc0301_module_param {
+ u8 force_munmap;
+ u16 frame_timeout;
+};
+
+static DECLARE_RWSEM(zc0301_disconnect);
+
+struct zc0301_device {
+ struct video_device* v4ldev;
+
+ struct zc0301_sensor sensor;
+
+ struct usb_device* usbdev;
+ struct urb* urb[ZC0301_URBS];
+ void* transfer_buffer[ZC0301_URBS];
+ u8* control_buffer;
+
+ struct zc0301_frame_t *frame_current, frame[ZC0301_MAX_FRAMES];
+ struct list_head inqueue, outqueue;
+ u32 frame_count, nbuffers, nreadbuffers;
+
+ enum zc0301_io_method io;
+ enum zc0301_stream_state stream;
+
+ struct v4l2_jpegcompression compression;
+
+ struct zc0301_module_param module_param;
+
+ enum zc0301_dev_state state;
+ u8 users;
+
+ struct mutex dev_mutex, fileop_mutex;
+ spinlock_t queue_lock;
+ wait_queue_head_t open, wait_frame, wait_stream;
+};
+
+/*****************************************************************************/
+
+struct zc0301_device*
+zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id)
+{
+ return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL;
+}
+
+void
+zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor)
+{
+ memcpy(&cam->sensor, sensor, sizeof(struct zc0301_sensor));
+}
+
+/*****************************************************************************/
+
+#undef DBG
+#undef KDBG
+#ifdef ZC0301_DEBUG
+# define DBG(level, fmt, args...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1) \
+ dev_err(&cam->usbdev->dev, fmt "\n", ## args); \
+ else if ((level) == 2) \
+ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \
+ else if ((level) >= 3) \
+ dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args); \
+ } \
+} while (0)
+# define KDBG(level, fmt, args...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1 || (level) == 2) \
+ pr_info("zc0301: " fmt "\n", ## args); \
+ else if ((level) == 3) \
+ pr_debug("zc0301: [%s:%d] " fmt "\n", __FUNCTION__, \
+ __LINE__ , ## args); \
+ } \
+} while (0)
+# define V4LDBG(level, name, cmd) \
+do { \
+ if (debug >= (level)) \
+ v4l_print_ioctl(name, cmd); \
+} while (0)
+#else
+# define DBG(level, fmt, args...) do {;} while(0)
+# define KDBG(level, fmt, args...) do {;} while(0)
+# define V4LDBG(level, name, cmd) do {;} while(0)
+#endif
+
+#undef PDBG
+#define PDBG(fmt, args...) \
+dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args)
+
+#undef PDBGG
+#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
+
+#endif /* _ZC0301_H_ */
diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c
new file mode 100644
index 000000000000..4036c6268bff
--- /dev/null
+++ b/drivers/media/video/zc0301/zc0301_core.c
@@ -0,0 +1,2055 @@
+/***************************************************************************
+ * Video4Linux2 driver for ZC0301 Image Processor and Control Chip *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * Informations about the chip internals needed to enable the I2C protocol *
+ * have been taken from the documentation of the ZC030x Video4Linux1 *
+ * driver written by Andrew Birkett <andy@nobugs.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/compiler.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/page-flags.h>
+#include <linux/byteorder/generic.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "zc0301.h"
+
+/*****************************************************************************/
+
+#define ZC0301_MODULE_NAME "V4L2 driver for ZC0301 " \
+ "Image Processor and Control Chip"
+#define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia"
+#define ZC0301_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
+#define ZC0301_MODULE_LICENSE "GPL"
+#define ZC0301_MODULE_VERSION "1:1.03"
+#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 3)
+
+/*****************************************************************************/
+
+MODULE_DEVICE_TABLE(usb, zc0301_id_table);
+
+MODULE_AUTHOR(ZC0301_MODULE_AUTHOR " " ZC0301_AUTHOR_EMAIL);
+MODULE_DESCRIPTION(ZC0301_MODULE_NAME);
+MODULE_VERSION(ZC0301_MODULE_VERSION);
+MODULE_LICENSE(ZC0301_MODULE_LICENSE);
+
+static short video_nr[] = {[0 ... ZC0301_MAX_DEVICES-1] = -1};
+module_param_array(video_nr, short, NULL, 0444);
+MODULE_PARM_DESC(video_nr,
+ "\n<-1|n[,...]> Specify V4L2 minor mode number."
+ "\n -1 = use next available (default)"
+ "\n n = use minor number n (integer >= 0)"
+ "\nYou can specify up to "
+ __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way."
+ "\nFor example:"
+ "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+ "\nthe second registered camera and use auto for the first"
+ "\none and for every other camera."
+ "\n");
+
+static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] =
+ ZC0301_FORCE_MUNMAP};
+module_param_array(force_munmap, bool, NULL, 0444);
+MODULE_PARM_DESC(force_munmap,
+ "\n<0|1[,...]> Force the application to unmap previously"
+ "\nmapped buffer memory before calling any VIDIOC_S_CROP or"
+ "\nVIDIOC_S_FMT ioctl's. Not all the applications support"
+ "\nthis feature. This parameter is specific for each"
+ "\ndetected camera."
+ "\n 0 = do not force memory unmapping"
+ "\n 1 = force memory unmapping (save memory)"
+ "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
+ "\n");
+
+static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] =
+ ZC0301_FRAME_TIMEOUT};
+module_param_array(frame_timeout, uint, NULL, 0644);
+MODULE_PARM_DESC(frame_timeout,
+ "\n<n[,...]> Timeout for a video frame in seconds."
+ "\nThis parameter is specific for each detected camera."
+ "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"."
+ "\n");
+
+#ifdef ZC0301_DEBUG
+static unsigned short debug = ZC0301_DEBUG_LEVEL;
+module_param(debug, ushort, 0644);
+MODULE_PARM_DESC(debug,
+ "\n<n> Debugging information level, from 0 to 3:"
+ "\n0 = none (use carefully)"
+ "\n1 = critical errors"
+ "\n2 = significant informations"
+ "\n3 = more verbose messages"
+ "\nLevel 3 is useful for testing only, when only "
+ "one device is used."
+ "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"."
+ "\n");
+#endif
+
+/*****************************************************************************/
+
+static u32
+zc0301_request_buffers(struct zc0301_device* cam, u32 count,
+ enum zc0301_io_method io)
+{
+ struct v4l2_pix_format* p = &(cam->sensor.pix_format);
+ struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
+ const size_t imagesize = cam->module_param.force_munmap ||
+ io == IO_READ ?
+ (p->width * p->height * p->priv) / 8 :
+ (r->width * r->height * p->priv) / 8;
+ void* buff = NULL;
+ u32 i;
+
+ if (count > ZC0301_MAX_FRAMES)
+ count = ZC0301_MAX_FRAMES;
+
+ cam->nbuffers = count;
+ while (cam->nbuffers > 0) {
+ if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize))))
+ break;
+ cam->nbuffers--;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
+ cam->frame[i].buf.index = i;
+ cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
+ cam->frame[i].buf.length = imagesize;
+ cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->frame[i].buf.sequence = 0;
+ cam->frame[i].buf.field = V4L2_FIELD_NONE;
+ cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
+ cam->frame[i].buf.flags = 0;
+ }
+
+ return cam->nbuffers;
+}
+
+
+static void zc0301_release_buffers(struct zc0301_device* cam)
+{
+ if (cam->nbuffers) {
+ vfree(cam->frame[0].bufmem);
+ cam->nbuffers = 0;
+ }
+ cam->frame_current = NULL;
+}
+
+
+static void zc0301_empty_framequeues(struct zc0301_device* cam)
+{
+ u32 i;
+
+ INIT_LIST_HEAD(&cam->inqueue);
+ INIT_LIST_HEAD(&cam->outqueue);
+
+ for (i = 0; i < ZC0301_MAX_FRAMES; i++) {
+ cam->frame[i].state = F_UNUSED;
+ cam->frame[i].buf.bytesused = 0;
+ }
+}
+
+
+static void zc0301_requeue_outqueue(struct zc0301_device* cam)
+{
+ struct zc0301_frame_t *i;
+
+ list_for_each_entry(i, &cam->outqueue, frame) {
+ i->state = F_QUEUED;
+ list_add(&i->frame, &cam->inqueue);
+ }
+
+ INIT_LIST_HEAD(&cam->outqueue);
+}
+
+
+static void zc0301_queue_unusedframes(struct zc0301_device* cam)
+{
+ unsigned long lock_flags;
+ u32 i;
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].state == F_UNUSED) {
+ cam->frame[i].state = F_QUEUED;
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[i].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ }
+}
+
+/*****************************************************************************/
+
+int zc0301_write_reg(struct zc0301_device* cam, u16 index, u16 value)
+{
+ struct usb_device* udev = cam->usbdev;
+ int res;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, 0x40,
+ value, index, NULL, 0, ZC0301_CTRL_TIMEOUT);
+ if (res < 0) {
+ DBG(3, "Failed to write a register (index 0x%04X, "
+ "value 0x%02X, error %d)",index, value, res);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int zc0301_read_reg(struct zc0301_device* cam, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0xa1, 0xc0,
+ 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT);
+ if (res < 0)
+ DBG(3, "Failed to read a register (index 0x%04X, error %d)",
+ index, res);
+
+ PDBGG("Read: index 0x%04X, value: 0x%04X", index, (int)(*buff));
+
+ return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+int zc0301_i2c_read(struct zc0301_device* cam, u16 address, u8 length)
+{
+ int err = 0, res, r0, r1;
+
+ err += zc0301_write_reg(cam, 0x0092, address);
+ err += zc0301_write_reg(cam, 0x0090, 0x02);
+
+ msleep(1);
+
+ res = zc0301_read_reg(cam, 0x0091);
+ if (res < 0)
+ err += res;
+ r0 = zc0301_read_reg(cam, 0x0095);
+ if (r0 < 0)
+ err += r0;
+ r1 = zc0301_read_reg(cam, 0x0096);
+ if (r1 < 0)
+ err += r1;
+
+ res = (length <= 1) ? r0 : r0 | (r1 << 8);
+
+ if (err)
+ DBG(3, "I2C read failed at address 0x%04X, value: 0x%04X",
+ address, res);
+
+
+ PDBGG("I2C read: address 0x%04X, value: 0x%04X", address, res);
+
+ return err ? -1 : res;
+}
+
+
+int zc0301_i2c_write(struct zc0301_device* cam, u16 address, u16 value)
+{
+ int err = 0, res;
+
+ err += zc0301_write_reg(cam, 0x0092, address);
+ err += zc0301_write_reg(cam, 0x0093, value & 0xff);
+ err += zc0301_write_reg(cam, 0x0094, value >> 8);
+ err += zc0301_write_reg(cam, 0x0090, 0x01);
+
+ msleep(1);
+
+ res = zc0301_read_reg(cam, 0x0091);
+ if (res < 0)
+ err += res;
+
+ if (err)
+ DBG(3, "I2C write failed at address 0x%04X, value: 0x%04X",
+ address, value);
+
+ PDBGG("I2C write: address 0x%04X, value: 0x%04X", address, value);
+
+ return err ? -1 : 0;
+}
+
+/*****************************************************************************/
+
+static void zc0301_urb_complete(struct urb *urb, struct pt_regs* regs)
+{
+ struct zc0301_device* cam = urb->context;
+ struct zc0301_frame_t** f;
+ size_t imagesize;
+ u8 i;
+ int err = 0;
+
+ if (urb->status == -ENOENT)
+ return;
+
+ f = &cam->frame_current;
+
+ if (cam->stream == STREAM_INTERRUPT) {
+ cam->stream = STREAM_OFF;
+ if ((*f))
+ (*f)->state = F_QUEUED;
+ DBG(3, "Stream interrupted");
+ wake_up(&cam->wait_stream);
+ }
+
+ if (cam->state & DEV_DISCONNECTED)
+ return;
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ wake_up_interruptible(&cam->wait_frame);
+ return;
+ }
+
+ if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
+ goto resubmit_urb;
+
+ if (!(*f))
+ (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t,
+ frame);
+
+ imagesize = (cam->sensor.pix_format.width *
+ cam->sensor.pix_format.height *
+ cam->sensor.pix_format.priv) / 8;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int len, status;
+ void *pos;
+ u16* soi;
+ u8 sof;
+
+ len = urb->iso_frame_desc[i].actual_length;
+ status = urb->iso_frame_desc[i].status;
+ pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+ if (status) {
+ DBG(3, "Error in isochronous frame");
+ (*f)->state = F_ERROR;
+ continue;
+ }
+
+ sof = (*(soi = pos) == 0xd8ff);
+
+ PDBGG("Isochrnous frame: length %u, #%u i,", len, i);
+
+ if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR)
+start_of_frame:
+ if (sof) {
+ (*f)->state = F_GRABBING;
+ (*f)->buf.bytesused = 0;
+ do_gettimeofday(&(*f)->buf.timestamp);
+ DBG(3, "SOF detected: new video frame");
+ }
+
+ if ((*f)->state == F_GRABBING) {
+ if (sof && (*f)->buf.bytesused)
+ goto end_of_frame;
+
+ if ((*f)->buf.bytesused + len > imagesize) {
+ DBG(3, "Video frame size exceeded");
+ (*f)->state = F_ERROR;
+ continue;
+ }
+
+ memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, len);
+ (*f)->buf.bytesused += len;
+
+ if ((*f)->buf.bytesused == imagesize) {
+ u32 b;
+end_of_frame:
+ b = (*f)->buf.bytesused;
+ (*f)->state = F_DONE;
+ (*f)->buf.sequence= ++cam->frame_count;
+ spin_lock(&cam->queue_lock);
+ list_move_tail(&(*f)->frame, &cam->outqueue);
+ if (!list_empty(&cam->inqueue))
+ (*f) = list_entry(cam->inqueue.next,
+ struct zc0301_frame_t,
+ frame);
+ else
+ (*f) = NULL;
+ spin_unlock(&cam->queue_lock);
+ DBG(3, "Video frame captured: : %lu bytes",
+ (unsigned long)(b));
+
+ if (!(*f))
+ goto resubmit_urb;
+
+ if (sof)
+ goto start_of_frame;
+ }
+ }
+ }
+
+resubmit_urb:
+ urb->dev = cam->usbdev;
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0 && err != -EPERM) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "usb_submit_urb() failed");
+ }
+
+ wake_up_interruptible(&cam->wait_frame);
+}
+
+
+static int zc0301_start_transfer(struct zc0301_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ struct urb* urb;
+ const unsigned int wMaxPacketSize[] = {0, 128, 192, 256, 384,
+ 512, 768, 1023};
+ const unsigned int psz = wMaxPacketSize[ZC0301_ALTERNATE_SETTING];
+ s8 i, j;
+ int err = 0;
+
+ for (i = 0; i < ZC0301_URBS; i++) {
+ cam->transfer_buffer[i] = kzalloc(ZC0301_ISO_PACKETS * psz,
+ GFP_KERNEL);
+ if (!cam->transfer_buffer[i]) {
+ err = -ENOMEM;
+ DBG(1, "Not enough memory");
+ goto free_buffers;
+ }
+ }
+
+ for (i = 0; i < ZC0301_URBS; i++) {
+ urb = usb_alloc_urb(ZC0301_ISO_PACKETS, GFP_KERNEL);
+ cam->urb[i] = urb;
+ if (!urb) {
+ err = -ENOMEM;
+ DBG(1, "usb_alloc_urb() failed");
+ goto free_urbs;
+ }
+ urb->dev = udev;
+ urb->context = cam;
+ urb->pipe = usb_rcvisocpipe(udev, 1);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->number_of_packets = ZC0301_ISO_PACKETS;
+ urb->complete = zc0301_urb_complete;
+ urb->transfer_buffer = cam->transfer_buffer[i];
+ urb->transfer_buffer_length = psz * ZC0301_ISO_PACKETS;
+ urb->interval = 1;
+ for (j = 0; j < ZC0301_ISO_PACKETS; j++) {
+ urb->iso_frame_desc[j].offset = psz * j;
+ urb->iso_frame_desc[j].length = psz;
+ }
+ }
+
+ err = usb_set_interface(udev, 0, ZC0301_ALTERNATE_SETTING);
+ if (err) {
+ DBG(1, "usb_set_interface() failed");
+ goto free_urbs;
+ }
+
+ cam->frame_current = NULL;
+
+ for (i = 0; i < ZC0301_URBS; i++) {
+ err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+ if (err) {
+ for (j = i-1; j >= 0; j--)
+ usb_kill_urb(cam->urb[j]);
+ DBG(1, "usb_submit_urb() failed, error %d", err);
+ goto free_urbs;
+ }
+ }
+
+ return 0;
+
+free_urbs:
+ for (i = 0; (i < ZC0301_URBS) && cam->urb[i]; i++)
+ usb_free_urb(cam->urb[i]);
+
+free_buffers:
+ for (i = 0; (i < ZC0301_URBS) && cam->transfer_buffer[i]; i++)
+ kfree(cam->transfer_buffer[i]);
+
+ return err;
+}
+
+
+static int zc0301_stop_transfer(struct zc0301_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ s8 i;
+ int err = 0;
+
+ if (cam->state & DEV_DISCONNECTED)
+ return 0;
+
+ for (i = ZC0301_URBS-1; i >= 0; i--) {
+ usb_kill_urb(cam->urb[i]);
+ usb_free_urb(cam->urb[i]);
+ kfree(cam->transfer_buffer[i]);
+ }
+
+ err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+ if (err)
+ DBG(3, "usb_set_interface() failed");
+
+ return err;
+}
+
+
+static int zc0301_stream_interrupt(struct zc0301_device* cam)
+{
+ long timeout;
+
+ cam->stream = STREAM_INTERRUPT;
+ timeout = wait_event_timeout(cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED),
+ ZC0301_URB_TIMEOUT);
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ else if (cam->stream != STREAM_OFF) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "URB timeout reached. The camera is misconfigured. To "
+ "use it, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+static int
+zc0301_set_compression(struct zc0301_device* cam,
+ struct v4l2_jpegcompression* compression)
+{
+ int r, err = 0;
+
+ if ((r = zc0301_read_reg(cam, 0x0008)) < 0)
+ err += r;
+ err += zc0301_write_reg(cam, 0x0008, r | 0x11 | compression->quality);
+
+ return err ? -EIO : 0;
+}
+
+
+static int zc0301_init(struct zc0301_device* cam)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ struct v4l2_queryctrl *qctrl;
+ struct v4l2_rect* rect;
+ u8 i = 0;
+ int err = 0;
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ init_waitqueue_head(&cam->open);
+ qctrl = s->qctrl;
+ rect = &(s->cropcap.defrect);
+ cam->compression.quality = ZC0301_COMPRESSION_QUALITY;
+ } else { /* use current values */
+ qctrl = s->_qctrl;
+ rect = &(s->_rect);
+ }
+
+ if (s->init) {
+ err = s->init(cam);
+ if (err) {
+ DBG(3, "Sensor initialization failed");
+ return err;
+ }
+ }
+
+ if ((err = zc0301_set_compression(cam, &cam->compression))) {
+ DBG(3, "set_compression() failed");
+ return err;
+ }
+
+ if (s->set_crop)
+ if ((err = s->set_crop(cam, rect))) {
+ DBG(3, "set_crop() failed");
+ return err;
+ }
+
+ if (s->set_ctrl) {
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (s->qctrl[i].id != 0 &&
+ !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) {
+ ctrl.id = s->qctrl[i].id;
+ ctrl.value = qctrl[i].default_value;
+ err = s->set_ctrl(cam, &ctrl);
+ if (err) {
+ DBG(3, "Set %s control failed",
+ s->qctrl[i].name);
+ return err;
+ }
+ DBG(3, "Image sensor supports '%s' control",
+ s->qctrl[i].name);
+ }
+ }
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ mutex_init(&cam->fileop_mutex);
+ spin_lock_init(&cam->queue_lock);
+ init_waitqueue_head(&cam->wait_frame);
+ init_waitqueue_head(&cam->wait_stream);
+ cam->nreadbuffers = 2;
+ memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
+ memcpy(&(s->_rect), &(s->cropcap.defrect),
+ sizeof(struct v4l2_rect));
+ cam->state |= DEV_INITIALIZED;
+ }
+
+ DBG(2, "Initialization succeeded");
+ return 0;
+}
+
+
+static void zc0301_release_resources(struct zc0301_device* cam)
+{
+ DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
+ video_set_drvdata(cam->v4ldev, NULL);
+ video_unregister_device(cam->v4ldev);
+ kfree(cam->control_buffer);
+}
+
+/*****************************************************************************/
+
+static int zc0301_open(struct inode* inode, struct file* filp)
+{
+ struct zc0301_device* cam;
+ int err = 0;
+
+ /*
+ This is the only safe way to prevent race conditions with
+ disconnect
+ */
+ if (!down_read_trylock(&zc0301_disconnect))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(video_devdata(filp));
+
+ if (mutex_lock_interruptible(&cam->dev_mutex)) {
+ up_read(&zc0301_disconnect);
+ return -ERESTARTSYS;
+ }
+
+ if (cam->users) {
+ DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor);
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (filp->f_flags & O_NDELAY)) {
+ err = -EWOULDBLOCK;
+ goto out;
+ }
+ mutex_unlock(&cam->dev_mutex);
+ err = wait_event_interruptible_exclusive(cam->open,
+ cam->state & DEV_DISCONNECTED
+ || !cam->users);
+ if (err) {
+ up_read(&zc0301_disconnect);
+ return err;
+ }
+ if (cam->state & DEV_DISCONNECTED) {
+ up_read(&zc0301_disconnect);
+ return -ENODEV;
+ }
+ mutex_lock(&cam->dev_mutex);
+ }
+
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ err = zc0301_init(cam);
+ if (err) {
+ DBG(1, "Initialization failed again. "
+ "I will retry on next open().");
+ goto out;
+ }
+ cam->state &= ~DEV_MISCONFIGURED;
+ }
+
+ if ((err = zc0301_start_transfer(cam)))
+ goto out;
+
+ filp->private_data = cam;
+ cam->users++;
+ cam->io = IO_NONE;
+ cam->stream = STREAM_OFF;
+ cam->nbuffers = 0;
+ cam->frame_count = 0;
+ zc0301_empty_framequeues(cam);
+
+ DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
+
+out:
+ mutex_unlock(&cam->dev_mutex);
+ up_read(&zc0301_disconnect);
+ return err;
+}
+
+
+static int zc0301_release(struct inode* inode, struct file* filp)
+{
+ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+
+ mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */
+
+ zc0301_stop_transfer(cam);
+
+ zc0301_release_buffers(cam);
+
+ if (cam->state & DEV_DISCONNECTED) {
+ zc0301_release_resources(cam);
+ usb_put_dev(cam->usbdev);
+ mutex_unlock(&cam->dev_mutex);
+ kfree(cam);
+ return 0;
+ }
+
+ cam->users--;
+ wake_up_interruptible_nr(&cam->open, 1);
+
+ DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);
+
+ mutex_unlock(&cam->dev_mutex);
+
+ return 0;
+}
+
+
+static ssize_t
+zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
+{
+ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ struct zc0301_frame_t* f, * i;
+ unsigned long lock_flags;
+ long timeout;
+ int err = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (cam->io == IO_MMAP) {
+ DBG(3, "Close and open the device again to choose the read "
+ "method");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) {
+ DBG(1, "read() failed, not enough memory");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENOMEM;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (list_empty(&cam->inqueue)) {
+ if (!list_empty(&cam->outqueue))
+ zc0301_empty_framequeues(cam);
+ zc0301_queue_unusedframes(cam);
+ }
+
+ if (!count) {
+ mutex_unlock(&cam->fileop_mutex);
+ return 0;
+ }
+
+ if (list_empty(&cam->outqueue)) {
+ if (filp->f_flags & O_NONBLOCK) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EAGAIN;
+ }
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0) {
+ mutex_unlock(&cam->fileop_mutex);
+ return timeout;
+ }
+ if (cam->state & DEV_DISCONNECTED) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+ if (!timeout || (cam->state & DEV_MISCONFIGURED)) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+ }
+
+ f = list_entry(cam->outqueue.prev, struct zc0301_frame_t, frame);
+
+ if (count > f->buf.bytesused)
+ count = f->buf.bytesused;
+
+ if (copy_to_user(buf, f->bufmem, count)) {
+ err = -EFAULT;
+ goto exit;
+ }
+ *f_pos += count;
+
+exit:
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(i, &cam->outqueue, frame)
+ i->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ zc0301_queue_unusedframes(cam);
+
+ PDBGG("Frame #%lu, bytes read: %zu",
+ (unsigned long)f->buf.index, count);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return err ? err : count;
+}
+
+
+static unsigned int zc0301_poll(struct file *filp, poll_table *wait)
+{
+ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ struct zc0301_frame_t* f;
+ unsigned long lock_flags;
+ unsigned int mask = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return POLLERR;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ goto error;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ goto error;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) {
+ DBG(1, "poll() failed, not enough memory");
+ goto error;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (cam->io == IO_READ) {
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(f, &cam->outqueue, frame)
+ f->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ zc0301_queue_unusedframes(cam);
+ }
+
+ poll_wait(filp, &cam->wait_frame, wait);
+
+ if (!list_empty(&cam->outqueue))
+ mask |= POLLIN | POLLRDNORM;
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return mask;
+
+error:
+ mutex_unlock(&cam->fileop_mutex);
+ return POLLERR;
+}
+
+
+static void zc0301_vm_open(struct vm_area_struct* vma)
+{
+ struct zc0301_frame_t* f = vma->vm_private_data;
+ f->vma_use_count++;
+}
+
+
+static void zc0301_vm_close(struct vm_area_struct* vma)
+{
+ /* NOTE: buffers are not freed here */
+ struct zc0301_frame_t* f = vma->vm_private_data;
+ f->vma_use_count--;
+}
+
+
+static struct vm_operations_struct zc0301_vm_ops = {
+ .open = zc0301_vm_open,
+ .close = zc0301_vm_close,
+};
+
+
+static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ unsigned long size = vma->vm_end - vma->vm_start,
+ start = vma->vm_start;
+ void *pos;
+ u32 i;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
+ size != PAGE_ALIGN(cam->frame[0].buf.length)) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+ if (i == cam->nbuffers) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED;
+
+ pos = cam->frame[i].bufmem;
+ while (size > 0) { /* size is page-aligned */
+ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vma->vm_ops = &zc0301_vm_ops;
+ vma->vm_private_data = &cam->frame[i];
+
+ zc0301_vm_open(vma);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+static int
+zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_capability cap = {
+ .driver = "zc0301",
+ .version = ZC0301_MODULE_VERSION_CODE,
+ .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING,
+ };
+
+ strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
+ if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
+ strlcpy(cap.bus_info, cam->usbdev->dev.bus_id,
+ sizeof(cap.bus_info));
+
+ if (copy_to_user(arg, &cap, sizeof(cap)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_enuminput(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_input i;
+
+ if (copy_from_user(&i, arg, sizeof(i)))
+ return -EFAULT;
+
+ if (i.index)
+ return -EINVAL;
+
+ memset(&i, 0, sizeof(i));
+ strcpy(i.name, "Camera");
+ i.type = V4L2_INPUT_TYPE_CAMERA;
+
+ if (copy_to_user(arg, &i, sizeof(i)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_g_input(struct zc0301_device* cam, void __user * arg)
+{
+ int index = 0;
+
+ if (copy_to_user(arg, &index, sizeof(index)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_s_input(struct zc0301_device* cam, void __user * arg)
+{
+ int index;
+
+ if (copy_from_user(&index, arg, sizeof(index)))
+ return -EFAULT;
+
+ if (index != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_query_ctrl(struct zc0301_device* cam, void __user * arg)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_queryctrl qc;
+ u8 i;
+
+ if (copy_from_user(&qc, arg, sizeof(qc)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (qc.id && qc.id == s->qctrl[i].id) {
+ memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
+ if (copy_to_user(arg, &qc, sizeof(qc)))
+ return -EFAULT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+
+static int
+zc0301_vidioc_g_ctrl(struct zc0301_device* cam, void __user * arg)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ int err = 0;
+ u8 i;
+
+ if (!s->get_ctrl && !s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ if (!s->get_ctrl) {
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (ctrl.id == s->qctrl[i].id) {
+ ctrl.value = s->_qctrl[i].default_value;
+ goto exit;
+ }
+ return -EINVAL;
+ } else
+ err = s->get_ctrl(cam, &ctrl);
+
+exit:
+ if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
+ return -EFAULT;
+
+ return err;
+}
+
+
+static int
+zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ u8 i;
+ int err = 0;
+
+ if (!s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (ctrl.id == s->qctrl[i].id) {
+ if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+ if (ctrl.value < s->qctrl[i].minimum ||
+ ctrl.value > s->qctrl[i].maximum)
+ return -ERANGE;
+ ctrl.value -= ctrl.value % s->qctrl[i].step;
+ break;
+ }
+
+ if ((err = s->set_ctrl(cam, &ctrl)))
+ return err;
+
+ s->_qctrl[i].default_value = ctrl.value;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_cropcap(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_cropcap* cc = &(cam->sensor.cropcap);
+
+ cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cc->pixelaspect.numerator = 1;
+ cc->pixelaspect.denominator = 1;
+
+ if (copy_to_user(arg, cc, sizeof(*cc)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_g_crop(struct zc0301_device* cam, void __user * arg)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_crop crop = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ };
+
+ memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
+
+ if (copy_to_user(arg, &crop, sizeof(crop)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_crop crop;
+ struct v4l2_rect* rect;
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ const enum zc0301_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&crop, arg, sizeof(crop)))
+ return -EFAULT;
+
+ rect = &(crop.c);
+
+ if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (cam->module_param.force_munmap)
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_CROP failed. "
+ "Unmap the buffers first.");
+ return -EINVAL;
+ }
+
+ if (!s->set_crop) {
+ memcpy(rect, &(s->_rect), sizeof(*rect));
+ if (copy_to_user(arg, &crop, sizeof(crop)))
+ return -EFAULT;
+ return 0;
+ }
+
+ rect->left &= ~7L;
+ rect->top &= ~7L;
+ if (rect->width < 8)
+ rect->width = 8;
+ if (rect->height < 8)
+ rect->height = 8;
+ if (rect->width > bounds->width)
+ rect->width = bounds->width;
+ if (rect->height > bounds->height)
+ rect->height = bounds->height;
+ if (rect->left < bounds->left)
+ rect->left = bounds->left;
+ if (rect->top < bounds->top)
+ rect->top = bounds->top;
+ if (rect->left + rect->width > bounds->left + bounds->width)
+ rect->left = bounds->left+bounds->width - rect->width;
+ if (rect->top + rect->height > bounds->top + bounds->height)
+ rect->top = bounds->top+bounds->height - rect->height;
+ rect->width &= ~7L;
+ rect->height &= ~7L;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = zc0301_stream_interrupt(cam)))
+ return err;
+
+ if (copy_to_user(arg, &crop, sizeof(crop))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ if (cam->module_param.force_munmap || cam->io == IO_READ)
+ zc0301_release_buffers(cam);
+
+ if (s->set_crop)
+ err += s->set_crop(cam, rect);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ s->pix_format.width = rect->width;
+ s->pix_format.height = rect->height;
+ memcpy(&(s->_rect), rect, sizeof(*rect));
+
+ if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+ nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -ENOMEM;
+ }
+
+ if (cam->io == IO_READ)
+ zc0301_empty_framequeues(cam);
+ else if (cam->module_param.force_munmap)
+ zc0301_requeue_outqueue(cam);
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_enum_fmt(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_fmtdesc fmtd;
+
+ if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
+ return -EFAULT;
+
+ if (fmtd.index == 0) {
+ strcpy(fmtd.description, "JPEG");
+ fmtd.pixelformat = V4L2_PIX_FMT_JPEG;
+ fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
+ } else
+ return -EINVAL;
+
+ fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
+
+ if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_g_fmt(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_format format;
+ struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format);
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ pfmt->bytesperline = 0;
+ pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
+ pfmt->field = V4L2_FIELD_NONE;
+ memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
+
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd,
+ void __user * arg)
+{
+ struct zc0301_sensor* s = &cam->sensor;
+ struct v4l2_format format;
+ struct v4l2_pix_format* pix;
+ struct v4l2_pix_format* pfmt = &(s->pix_format);
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ struct v4l2_rect rect;
+ const enum zc0301_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ pix = &(format.fmt.pix);
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memcpy(&rect, &(s->_rect), sizeof(rect));
+
+ if (!s->set_crop) {
+ pix->width = rect.width;
+ pix->height = rect.height;
+ } else {
+ rect.width = pix->width;
+ rect.height = pix->height;
+ }
+
+ if (rect.width < 8)
+ rect.width = 8;
+ if (rect.height < 8)
+ rect.height = 8;
+ if (rect.width > bounds->left + bounds->width - rect.left)
+ rect.width = bounds->left + bounds->width - rect.left;
+ if (rect.height > bounds->top + bounds->height - rect.top)
+ rect.height = bounds->top + bounds->height - rect.top;
+ rect.width &= ~7L;
+ rect.height &= ~7L;
+
+ pix->width = rect.width;
+ pix->height = rect.height;
+ pix->pixelformat = pfmt->pixelformat;
+ pix->priv = pfmt->priv;
+ pix->colorspace = pfmt->colorspace;
+ pix->bytesperline = 0;
+ pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8);
+ pix->field = V4L2_FIELD_NONE;
+
+ if (cmd == VIDIOC_TRY_FMT) {
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+ return 0;
+ }
+
+ if (cam->module_param.force_munmap)
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_FMT failed. "
+ "Unmap the buffers first.");
+ return -EINVAL;
+ }
+
+ if (cam->stream == STREAM_ON)
+ if ((err = zc0301_stream_interrupt(cam)))
+ return err;
+
+ if (copy_to_user(arg, &format, sizeof(format))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ if (cam->module_param.force_munmap || cam->io == IO_READ)
+ zc0301_release_buffers(cam);
+
+ if (s->set_crop)
+ err += s->set_crop(cam, &rect);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ memcpy(pfmt, pix, sizeof(*pix));
+ memcpy(&(s->_rect), &rect, sizeof(rect));
+
+ if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+ nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
+ "use the camera, close and open /dev/video%d again.",
+ cam->v4ldev->minor);
+ return -ENOMEM;
+ }
+
+ if (cam->io == IO_READ)
+ zc0301_empty_framequeues(cam);
+ else if (cam->module_param.force_munmap)
+ zc0301_requeue_outqueue(cam);
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_g_jpegcomp(struct zc0301_device* cam, void __user * arg)
+{
+ if (copy_to_user(arg, &cam->compression, sizeof(cam->compression)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_s_jpegcomp(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_jpegcompression jc;
+ const enum zc0301_stream_state stream = cam->stream;
+ int err = 0;
+
+ if (copy_from_user(&jc, arg, sizeof(jc)))
+ return -EFAULT;
+
+ if (jc.quality != 0)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = zc0301_stream_interrupt(cam)))
+ return err;
+
+ err += zc0301_set_compression(cam, &jc);
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
+ "problems. To use the camera, close and open "
+ "/dev/video%d again.", cam->v4ldev->minor);
+ return -EIO;
+ }
+
+ cam->compression.quality = jc.quality;
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_reqbufs(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_requestbuffers rb;
+ u32 i;
+ int err;
+
+ if (copy_from_user(&rb, arg, sizeof(rb)))
+ return -EFAULT;
+
+ if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ rb.memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (cam->io == IO_READ) {
+ DBG(3, "Close and open the device again to choose the mmap "
+ "I/O method");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_REQBUFS failed. "
+ "Previous buffers are still mapped.");
+ return -EINVAL;
+ }
+
+ if (cam->stream == STREAM_ON)
+ if ((err = zc0301_stream_interrupt(cam)))
+ return err;
+
+ zc0301_empty_framequeues(cam);
+
+ zc0301_release_buffers(cam);
+ if (rb.count)
+ rb.count = zc0301_request_buffers(cam, rb.count, IO_MMAP);
+
+ if (copy_to_user(arg, &rb, sizeof(rb))) {
+ zc0301_release_buffers(cam);
+ cam->io = IO_NONE;
+ return -EFAULT;
+ }
+
+ cam->io = rb.count ? IO_MMAP : IO_NONE;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_querybuf(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_buffer b;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
+
+ if (cam->frame[b.index].vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (cam->frame[b.index].state == F_DONE)
+ b.flags |= V4L2_BUF_FLAG_DONE;
+ else if (cam->frame[b.index].state != F_UNUSED)
+ b.flags |= V4L2_BUF_FLAG_QUEUED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_qbuf(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_buffer b;
+ unsigned long lock_flags;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->frame[b.index].state != F_UNUSED)
+ return -EINVAL;
+
+ cam->frame[b.index].state = F_QUEUED;
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ PDBGG("Frame #%lu queued", (unsigned long)b.index);
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp,
+ void __user * arg)
+{
+ struct v4l2_buffer b;
+ struct zc0301_frame_t *f;
+ unsigned long lock_flags;
+ long timeout;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&cam->outqueue)) {
+ if (cam->stream == STREAM_OFF)
+ return -EINVAL;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0)
+ return timeout;
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ if (!timeout || (cam->state & DEV_MISCONFIGURED))
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ f = list_entry(cam->outqueue.next, struct zc0301_frame_t, frame);
+ list_del(cam->outqueue.next);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ f->state = F_UNUSED;
+
+ memcpy(&b, &f->buf, sizeof(b));
+ if (f->vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index);
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_streamon(struct zc0301_device* cam, void __user * arg)
+{
+ int type;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&cam->inqueue))
+ return -EINVAL;
+
+ cam->stream = STREAM_ON;
+
+ DBG(3, "Stream on");
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_streamoff(struct zc0301_device* cam, void __user * arg)
+{
+ int type, err;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = zc0301_stream_interrupt(cam)))
+ return err;
+
+ zc0301_empty_framequeues(cam);
+
+ DBG(3, "Stream off");
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_g_parm(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_streamparm sp;
+
+ if (copy_from_user(&sp, arg, sizeof(sp)))
+ return -EFAULT;
+
+ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp.parm.capture.extendedmode = 0;
+ sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+zc0301_vidioc_s_parm(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_streamparm sp;
+
+ if (copy_from_user(&sp, arg, sizeof(sp)))
+ return -EFAULT;
+
+ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp.parm.capture.extendedmode = 0;
+
+ if (sp.parm.capture.readbuffers == 0)
+ sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+ if (sp.parm.capture.readbuffers > ZC0301_MAX_FRAMES)
+ sp.parm.capture.readbuffers = ZC0301_MAX_FRAMES;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ cam->nreadbuffers = sp.parm.capture.readbuffers;
+
+ return 0;
+}
+
+
+static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp,
+ unsigned int cmd, void __user * arg)
+{
+ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+
+ switch (cmd) {
+
+ case VIDIOC_QUERYCAP:
+ return zc0301_vidioc_querycap(cam, arg);
+
+ case VIDIOC_ENUMINPUT:
+ return zc0301_vidioc_enuminput(cam, arg);
+
+ case VIDIOC_G_INPUT:
+ return zc0301_vidioc_g_input(cam, arg);
+
+ case VIDIOC_S_INPUT:
+ return zc0301_vidioc_s_input(cam, arg);
+
+ case VIDIOC_QUERYCTRL:
+ return zc0301_vidioc_query_ctrl(cam, arg);
+
+ case VIDIOC_G_CTRL:
+ return zc0301_vidioc_g_ctrl(cam, arg);
+
+ case VIDIOC_S_CTRL_OLD:
+ case VIDIOC_S_CTRL:
+ return zc0301_vidioc_s_ctrl(cam, arg);
+
+ case VIDIOC_CROPCAP_OLD:
+ case VIDIOC_CROPCAP:
+ return zc0301_vidioc_cropcap(cam, arg);
+
+ case VIDIOC_G_CROP:
+ return zc0301_vidioc_g_crop(cam, arg);
+
+ case VIDIOC_S_CROP:
+ return zc0301_vidioc_s_crop(cam, arg);
+
+ case VIDIOC_ENUM_FMT:
+ return zc0301_vidioc_enum_fmt(cam, arg);
+
+ case VIDIOC_G_FMT:
+ return zc0301_vidioc_g_fmt(cam, arg);
+
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT:
+ return zc0301_vidioc_try_s_fmt(cam, cmd, arg);
+
+ case VIDIOC_G_JPEGCOMP:
+ return zc0301_vidioc_g_jpegcomp(cam, arg);
+
+ case VIDIOC_S_JPEGCOMP:
+ return zc0301_vidioc_s_jpegcomp(cam, arg);
+
+ case VIDIOC_REQBUFS:
+ return zc0301_vidioc_reqbufs(cam, arg);
+
+ case VIDIOC_QUERYBUF:
+ return zc0301_vidioc_querybuf(cam, arg);
+
+ case VIDIOC_QBUF:
+ return zc0301_vidioc_qbuf(cam, arg);
+
+ case VIDIOC_DQBUF:
+ return zc0301_vidioc_dqbuf(cam, filp, arg);
+
+ case VIDIOC_STREAMON:
+ return zc0301_vidioc_streamon(cam, arg);
+
+ case VIDIOC_STREAMOFF:
+ return zc0301_vidioc_streamoff(cam, arg);
+
+ case VIDIOC_G_PARM:
+ return zc0301_vidioc_g_parm(cam, arg);
+
+ case VIDIOC_S_PARM_OLD:
+ case VIDIOC_S_PARM:
+ return zc0301_vidioc_s_parm(cam, arg);
+
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_QUERYSTD:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_QUERYMENU:
+ return -EINVAL;
+
+ default:
+ return -EINVAL;
+
+ }
+}
+
+
+static int zc0301_ioctl(struct inode* inode, struct file* filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ int err = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ V4LDBG(3, "zc0301", cmd);
+
+ err = zc0301_ioctl_v4l2(inode, filp, cmd, (void __user *)arg);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return err;
+}
+
+
+static struct file_operations zc0301_fops = {
+ .owner = THIS_MODULE,
+ .open = zc0301_open,
+ .release = zc0301_release,
+ .ioctl = zc0301_ioctl,
+ .read = zc0301_read,
+ .poll = zc0301_poll,
+ .mmap = zc0301_mmap,
+ .llseek = no_llseek,
+};
+
+/*****************************************************************************/
+
+static int
+zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct zc0301_device* cam;
+ static unsigned int dev_nr = 0;
+ unsigned int i;
+ int err = 0;
+
+ if (!(cam = kzalloc(sizeof(struct zc0301_device), GFP_KERNEL)))
+ return -ENOMEM;
+
+ cam->usbdev = udev;
+
+ if (!(cam->control_buffer = kzalloc(4, GFP_KERNEL))) {
+ DBG(1, "kmalloc() failed");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ if (!(cam->v4ldev = video_device_alloc())) {
+ DBG(1, "video_device_alloc() failed");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ mutex_init(&cam->dev_mutex);
+
+ DBG(2, "ZC0301 Image Processor and Control Chip detected "
+ "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct);
+
+ for (i = 0; zc0301_sensor_table[i]; i++) {
+ err = zc0301_sensor_table[i](cam);
+ if (!err)
+ break;
+ }
+
+ if (!err)
+ DBG(2, "%s image sensor detected", cam->sensor.name);
+ else {
+ DBG(1, "No supported image sensor detected");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (zc0301_init(cam)) {
+ DBG(1, "Initialization failed. I will retry on open().");
+ cam->state |= DEV_MISCONFIGURED;
+ }
+
+ strcpy(cam->v4ldev->name, "ZC0301 PC Camera");
+ cam->v4ldev->owner = THIS_MODULE;
+ cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
+ cam->v4ldev->hardware = 0;
+ cam->v4ldev->fops = &zc0301_fops;
+ cam->v4ldev->minor = video_nr[dev_nr];
+ cam->v4ldev->release = video_device_release;
+ video_set_drvdata(cam->v4ldev, cam);
+
+ mutex_lock(&cam->dev_mutex);
+
+ err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
+ video_nr[dev_nr]);
+ if (err) {
+ DBG(1, "V4L2 device registration failed");
+ if (err == -ENFILE && video_nr[dev_nr] == -1)
+ DBG(1, "Free /dev/videoX node not found");
+ video_nr[dev_nr] = -1;
+ dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0;
+ mutex_unlock(&cam->dev_mutex);
+ goto fail;
+ }
+
+ DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor);
+
+ cam->module_param.force_munmap = force_munmap[dev_nr];
+ cam->module_param.frame_timeout = frame_timeout[dev_nr];
+
+ dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
+ usb_set_intfdata(intf, cam);
+
+ mutex_unlock(&cam->dev_mutex);
+
+ return 0;
+
+fail:
+ if (cam) {
+ kfree(cam->control_buffer);
+ if (cam->v4ldev)
+ video_device_release(cam->v4ldev);
+ kfree(cam);
+ }
+ return err;
+}
+
+
+static void zc0301_usb_disconnect(struct usb_interface* intf)
+{
+ struct zc0301_device* cam = usb_get_intfdata(intf);
+
+ if (!cam)
+ return;
+
+ down_write(&zc0301_disconnect);
+
+ mutex_lock(&cam->dev_mutex);
+
+ DBG(2, "Disconnecting %s...", cam->v4ldev->name);
+
+ wake_up_interruptible_all(&cam->open);
+
+ if (cam->users) {
+ DBG(2, "Device /dev/video%d is open! Deregistration and "
+ "memory deallocation are deferred on close.",
+ cam->v4ldev->minor);
+ cam->state |= DEV_MISCONFIGURED;
+ zc0301_stop_transfer(cam);
+ cam->state |= DEV_DISCONNECTED;
+ wake_up_interruptible(&cam->wait_frame);
+ wake_up(&cam->wait_stream);
+ usb_get_dev(cam->usbdev);
+ } else {
+ cam->state |= DEV_DISCONNECTED;
+ zc0301_release_resources(cam);
+ }
+
+ mutex_unlock(&cam->dev_mutex);
+
+ if (!cam->users)
+ kfree(cam);
+
+ up_write(&zc0301_disconnect);
+}
+
+
+static struct usb_driver zc0301_usb_driver = {
+ .name = "zc0301",
+ .id_table = zc0301_id_table,
+ .probe = zc0301_usb_probe,
+ .disconnect = zc0301_usb_disconnect,
+};
+
+/*****************************************************************************/
+
+static int __init zc0301_module_init(void)
+{
+ int err = 0;
+
+ KDBG(2, ZC0301_MODULE_NAME " v" ZC0301_MODULE_VERSION);
+ KDBG(3, ZC0301_MODULE_AUTHOR);
+
+ if ((err = usb_register(&zc0301_usb_driver)))
+ KDBG(1, "usb_register() failed");
+
+ return err;
+}
+
+
+static void __exit zc0301_module_exit(void)
+{
+ usb_deregister(&zc0301_usb_driver);
+}
+
+
+module_init(zc0301_module_init);
+module_exit(zc0301_module_exit);
diff --git a/drivers/media/video/zc0301/zc0301_pas202bcb.c b/drivers/media/video/zc0301/zc0301_pas202bcb.c
new file mode 100644
index 000000000000..9d282a22c15f
--- /dev/null
+++ b/drivers/media/video/zc0301/zc0301_pas202bcb.c
@@ -0,0 +1,361 @@
+/***************************************************************************
+ * Plug-in for PAS202BCB image sensor connected to the ZC030! Image *
+ * Processor and Control Chip *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * Initialization values of the ZC0301 have been taken from the SPCA5XX *
+ * driver maintained by Michel Xhaard <mxhaard@magic.fr> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+/*
+ NOTE: Sensor controls are disabled for now, becouse changing them while
+ streaming sometimes results in out-of-sync video frames. We'll use
+ the default initialization, until we know how to stop and start video
+ in the chip. However, the image quality still looks good under various
+ light conditions.
+*/
+
+#include <linux/delay.h>
+#include "zc0301_sensor.h"
+
+
+static struct zc0301_sensor pas202bcb;
+
+
+static int pas202bcb_init(struct zc0301_device* cam)
+{
+ int err = 0;
+
+ err += zc0301_write_reg(cam, 0x0002, 0x00);
+ err += zc0301_write_reg(cam, 0x0003, 0x02);
+ err += zc0301_write_reg(cam, 0x0004, 0x80);
+ err += zc0301_write_reg(cam, 0x0005, 0x01);
+ err += zc0301_write_reg(cam, 0x0006, 0xE0);
+ err += zc0301_write_reg(cam, 0x0098, 0x00);
+ err += zc0301_write_reg(cam, 0x009A, 0x03);
+ err += zc0301_write_reg(cam, 0x011A, 0x00);
+ err += zc0301_write_reg(cam, 0x011C, 0x03);
+ err += zc0301_write_reg(cam, 0x009B, 0x01);
+ err += zc0301_write_reg(cam, 0x009C, 0xE6);
+ err += zc0301_write_reg(cam, 0x009D, 0x02);
+ err += zc0301_write_reg(cam, 0x009E, 0x86);
+
+ err += zc0301_i2c_write(cam, 0x02, 0x02);
+ err += zc0301_i2c_write(cam, 0x0A, 0x01);
+ err += zc0301_i2c_write(cam, 0x0B, 0x01);
+ err += zc0301_i2c_write(cam, 0x0D, 0x00);
+ err += zc0301_i2c_write(cam, 0x12, 0x05);
+ err += zc0301_i2c_write(cam, 0x13, 0x63);
+ err += zc0301_i2c_write(cam, 0x15, 0x70);
+
+ err += zc0301_write_reg(cam, 0x0101, 0xB7);
+ err += zc0301_write_reg(cam, 0x0100, 0x0D);
+ err += zc0301_write_reg(cam, 0x0189, 0x06);
+ err += zc0301_write_reg(cam, 0x01AD, 0x00);
+ err += zc0301_write_reg(cam, 0x01C5, 0x03);
+ err += zc0301_write_reg(cam, 0x01CB, 0x13);
+ err += zc0301_write_reg(cam, 0x0250, 0x08);
+ err += zc0301_write_reg(cam, 0x0301, 0x08);
+ err += zc0301_write_reg(cam, 0x018D, 0x70);
+ err += zc0301_write_reg(cam, 0x0008, 0x03);
+ err += zc0301_write_reg(cam, 0x01C6, 0x04);
+ err += zc0301_write_reg(cam, 0x01CB, 0x07);
+ err += zc0301_write_reg(cam, 0x0120, 0x11);
+ err += zc0301_write_reg(cam, 0x0121, 0x37);
+ err += zc0301_write_reg(cam, 0x0122, 0x58);
+ err += zc0301_write_reg(cam, 0x0123, 0x79);
+ err += zc0301_write_reg(cam, 0x0124, 0x91);
+ err += zc0301_write_reg(cam, 0x0125, 0xA6);
+ err += zc0301_write_reg(cam, 0x0126, 0xB8);
+ err += zc0301_write_reg(cam, 0x0127, 0xC7);
+ err += zc0301_write_reg(cam, 0x0128, 0xD3);
+ err += zc0301_write_reg(cam, 0x0129, 0xDE);
+ err += zc0301_write_reg(cam, 0x012A, 0xE6);
+ err += zc0301_write_reg(cam, 0x012B, 0xED);
+ err += zc0301_write_reg(cam, 0x012C, 0xF3);
+ err += zc0301_write_reg(cam, 0x012D, 0xF8);
+ err += zc0301_write_reg(cam, 0x012E, 0xFB);
+ err += zc0301_write_reg(cam, 0x012F, 0xFF);
+ err += zc0301_write_reg(cam, 0x0130, 0x26);
+ err += zc0301_write_reg(cam, 0x0131, 0x23);
+ err += zc0301_write_reg(cam, 0x0132, 0x20);
+ err += zc0301_write_reg(cam, 0x0133, 0x1C);
+ err += zc0301_write_reg(cam, 0x0134, 0x16);
+ err += zc0301_write_reg(cam, 0x0135, 0x13);
+ err += zc0301_write_reg(cam, 0x0136, 0x10);
+ err += zc0301_write_reg(cam, 0x0137, 0x0D);
+ err += zc0301_write_reg(cam, 0x0138, 0x0B);
+ err += zc0301_write_reg(cam, 0x0139, 0x09);
+ err += zc0301_write_reg(cam, 0x013A, 0x07);
+ err += zc0301_write_reg(cam, 0x013B, 0x06);
+ err += zc0301_write_reg(cam, 0x013C, 0x05);
+ err += zc0301_write_reg(cam, 0x013D, 0x04);
+ err += zc0301_write_reg(cam, 0x013E, 0x03);
+ err += zc0301_write_reg(cam, 0x013F, 0x02);
+ err += zc0301_write_reg(cam, 0x010A, 0x4C);
+ err += zc0301_write_reg(cam, 0x010B, 0xF5);
+ err += zc0301_write_reg(cam, 0x010C, 0xFF);
+ err += zc0301_write_reg(cam, 0x010D, 0xF9);
+ err += zc0301_write_reg(cam, 0x010E, 0x51);
+ err += zc0301_write_reg(cam, 0x010F, 0xF5);
+ err += zc0301_write_reg(cam, 0x0110, 0xFB);
+ err += zc0301_write_reg(cam, 0x0111, 0xED);
+ err += zc0301_write_reg(cam, 0x0112, 0x5F);
+ err += zc0301_write_reg(cam, 0x0180, 0x00);
+ err += zc0301_write_reg(cam, 0x0019, 0x00);
+ err += zc0301_write_reg(cam, 0x0087, 0x20);
+ err += zc0301_write_reg(cam, 0x0088, 0x21);
+
+ err += zc0301_i2c_write(cam, 0x20, 0x02);
+ err += zc0301_i2c_write(cam, 0x21, 0x1B);
+ err += zc0301_i2c_write(cam, 0x03, 0x44);
+ err += zc0301_i2c_write(cam, 0x0E, 0x01);
+ err += zc0301_i2c_write(cam, 0x0F, 0x00);
+
+ err += zc0301_write_reg(cam, 0x01A9, 0x14);
+ err += zc0301_write_reg(cam, 0x01AA, 0x24);
+ err += zc0301_write_reg(cam, 0x0190, 0x00);
+ err += zc0301_write_reg(cam, 0x0191, 0x02);
+ err += zc0301_write_reg(cam, 0x0192, 0x1B);
+ err += zc0301_write_reg(cam, 0x0195, 0x00);
+ err += zc0301_write_reg(cam, 0x0196, 0x00);
+ err += zc0301_write_reg(cam, 0x0197, 0x4D);
+ err += zc0301_write_reg(cam, 0x018C, 0x10);
+ err += zc0301_write_reg(cam, 0x018F, 0x20);
+ err += zc0301_write_reg(cam, 0x001D, 0x44);
+ err += zc0301_write_reg(cam, 0x001E, 0x6F);
+ err += zc0301_write_reg(cam, 0x001F, 0xAD);
+ err += zc0301_write_reg(cam, 0x0020, 0xEB);
+ err += zc0301_write_reg(cam, 0x0087, 0x0F);
+ err += zc0301_write_reg(cam, 0x0088, 0x0E);
+ err += zc0301_write_reg(cam, 0x0180, 0x40);
+ err += zc0301_write_reg(cam, 0x0192, 0x1B);
+ err += zc0301_write_reg(cam, 0x0191, 0x02);
+ err += zc0301_write_reg(cam, 0x0190, 0x00);
+ err += zc0301_write_reg(cam, 0x0116, 0x1D);
+ err += zc0301_write_reg(cam, 0x0117, 0x40);
+ err += zc0301_write_reg(cam, 0x0118, 0x99);
+ err += zc0301_write_reg(cam, 0x0180, 0x42);
+ err += zc0301_write_reg(cam, 0x0116, 0x1D);
+ err += zc0301_write_reg(cam, 0x0117, 0x40);
+ err += zc0301_write_reg(cam, 0x0118, 0x99);
+ err += zc0301_write_reg(cam, 0x0007, 0x00);
+
+ err += zc0301_i2c_write(cam, 0x11, 0x01);
+
+ msleep(100);
+
+ return err;
+}
+
+
+static int pas202bcb_get_ctrl(struct zc0301_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ {
+ int r1 = zc0301_i2c_read(cam, 0x04, 1),
+ r2 = zc0301_i2c_read(cam, 0x05, 1);
+ if (r1 < 0 || r2 < 0)
+ return -EIO;
+ ctrl->value = (r1 << 6) | (r2 & 0x3f);
+ }
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if ((ctrl->value = zc0301_i2c_read(cam, 0x09, 1)) < 0)
+ return -EIO;
+ ctrl->value &= 0x0f;
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ if ((ctrl->value = zc0301_i2c_read(cam, 0x07, 1)) < 0)
+ return -EIO;
+ ctrl->value &= 0x0f;
+ return 0;
+ case V4L2_CID_GAIN:
+ if ((ctrl->value = zc0301_i2c_read(cam, 0x10, 1)) < 0)
+ return -EIO;
+ ctrl->value &= 0x1f;
+ return 0;
+ case ZC0301_V4L2_CID_GREEN_BALANCE:
+ if ((ctrl->value = zc0301_i2c_read(cam, 0x08, 1)) < 0)
+ return -EIO;
+ ctrl->value &= 0x0f;
+ return 0;
+ case ZC0301_V4L2_CID_DAC_MAGNITUDE:
+ if ((ctrl->value = zc0301_i2c_read(cam, 0x0c, 1)) < 0)
+ return -EIO;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int pas202bcb_set_ctrl(struct zc0301_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += zc0301_i2c_write(cam, 0x04, ctrl->value >> 6);
+ err += zc0301_i2c_write(cam, 0x05, ctrl->value & 0x3f);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += zc0301_i2c_write(cam, 0x09, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += zc0301_i2c_write(cam, 0x07, ctrl->value);
+ break;
+ case V4L2_CID_GAIN:
+ err += zc0301_i2c_write(cam, 0x10, ctrl->value);
+ break;
+ case ZC0301_V4L2_CID_GREEN_BALANCE:
+ err += zc0301_i2c_write(cam, 0x08, ctrl->value);
+ break;
+ case ZC0301_V4L2_CID_DAC_MAGNITUDE:
+ err += zc0301_i2c_write(cam, 0x0c, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+ err += zc0301_i2c_write(cam, 0x11, 0x01);
+
+ return err ? -EIO : 0;
+}
+
+
+static struct zc0301_sensor pas202bcb = {
+ .name = "PAS202BCB",
+ .init = &pas202bcb_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x01e5,
+ .maximum = 0x3fff,
+ .step = 0x0001,
+ .default_value = 0x01e5,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x0c,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = ZC0301_V4L2_CID_DAC_MAGNITUDE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "DAC magnitude",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x01,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x05,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = ZC0301_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ },
+ .get_ctrl = &pas202bcb_get_ctrl,
+ .set_ctrl = &pas202bcb_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_JPEG,
+ .priv = 8,
+ },
+};
+
+
+int zc0301_probe_pas202bcb(struct zc0301_device* cam)
+{
+ int r0 = 0, r1 = 0, err = 0;
+ unsigned int pid = 0;
+
+ err += zc0301_write_reg(cam, 0x0000, 0x01);
+ err += zc0301_write_reg(cam, 0x0010, 0x0e);
+ err += zc0301_write_reg(cam, 0x0001, 0x01);
+ err += zc0301_write_reg(cam, 0x0012, 0x03);
+ err += zc0301_write_reg(cam, 0x0012, 0x01);
+ err += zc0301_write_reg(cam, 0x008d, 0x08);
+
+ msleep(10);
+
+ r0 = zc0301_i2c_read(cam, 0x00, 1);
+ r1 = zc0301_i2c_read(cam, 0x01, 1);
+
+ if (r0 < 0 || r1 < 0 || err)
+ return -EIO;
+
+ pid = (r0 << 4) | ((r1 & 0xf0) >> 4);
+ if (pid != 0x017)
+ return -ENODEV;
+
+ zc0301_attach_sensor(cam, &pas202bcb);
+
+ return 0;
+}
diff --git a/drivers/media/video/zc0301/zc0301_sensor.h b/drivers/media/video/zc0301/zc0301_sensor.h
new file mode 100644
index 000000000000..cf0965a81d01
--- /dev/null
+++ b/drivers/media/video/zc0301/zc0301_sensor.h
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * API for image sensors connected to the ZC030! Image Processor and *
+ * Control Chip *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _ZC0301_SENSOR_H_
+#define _ZC0301_SENSOR_H_
+
+#include <linux/usb.h>
+#include <linux/videodev.h>
+#include <linux/device.h>
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <asm/types.h>
+
+struct zc0301_device;
+struct zc0301_sensor;
+
+/*****************************************************************************/
+
+extern int zc0301_probe_pas202bcb(struct zc0301_device* cam);
+
+#define ZC0301_SENSOR_TABLE \
+/* Weak detections must go at the end of the list */ \
+static int (*zc0301_sensor_table[])(struct zc0301_device*) = { \
+ &zc0301_probe_pas202bcb, \
+ NULL, \
+};
+
+extern struct zc0301_device*
+zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id);
+
+extern void
+zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor);
+
+#define ZC0301_USB_DEVICE(vend, prod, intclass) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_CLASS, \
+ .idVendor = (vend), \
+ .idProduct = (prod), \
+ .bInterfaceClass = (intclass)
+
+#define ZC0301_ID_TABLE \
+static const struct usb_device_id zc0301_id_table[] = { \
+ { ZC0301_USB_DEVICE(0x041e, 0x4017, 0xff), }, \
+ { ZC0301_USB_DEVICE(0x041e, 0x401c, 0xff), }, /* PAS106 */ \
+ { ZC0301_USB_DEVICE(0x041e, 0x401e, 0xff), }, /* HV7131B */ \
+ { ZC0301_USB_DEVICE(0x041e, 0x4034, 0xff), }, /* PAS106 */ \
+ { ZC0301_USB_DEVICE(0x041e, 0x4035, 0xff), }, /* PAS106 */ \
+ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202BCB */ \
+ { ZC0301_USB_DEVICE(0x0ac8, 0x0301, 0xff), }, \
+ { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130D */ \
+ { } \
+};
+
+/*****************************************************************************/
+
+extern int zc0301_write_reg(struct zc0301_device*, u16 index, u16 value);
+extern int zc0301_read_reg(struct zc0301_device*, u16 index);
+extern int zc0301_i2c_write(struct zc0301_device*, u16 address, u16 value);
+extern int zc0301_i2c_read(struct zc0301_device*, u16 address, u8 length);
+
+/*****************************************************************************/
+
+#define ZC0301_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10
+#define ZC0301_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE
+#define ZC0301_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1
+
+struct zc0301_sensor {
+ char name[32];
+
+ struct v4l2_queryctrl qctrl[ZC0301_MAX_CTRLS];
+ struct v4l2_cropcap cropcap;
+ struct v4l2_pix_format pix_format;
+
+ int (*init)(struct zc0301_device*);
+ int (*get_ctrl)(struct zc0301_device*, struct v4l2_control* ctrl);
+ int (*set_ctrl)(struct zc0301_device*,
+ const struct v4l2_control* ctrl);
+ int (*set_crop)(struct zc0301_device*, const struct v4l2_rect* rect);
+
+ /* Private */
+ struct v4l2_queryctrl _qctrl[ZC0301_MAX_CTRLS];
+ struct v4l2_rect _rect;
+};
+
+#endif /* _ZC0301_SENSOR_H_ */