Changes in 4.9.269
net: usb: ax88179_178a: initialize local variables before use
iwlwifi: Fix softirq/hardirq disabling in iwl_pcie_enqueue_hcmd()
ALSA: usb-audio: Add MIDI quirk for Vox ToneLab EX
USB: Add LPM quirk for Lenovo ThinkPad USB-C Dock Gen2 Ethernet
USB: Add reset-resume quirk for WD19's Realtek Hub
platform/x86: thinkpad_acpi: Correct thermal sensor allocation
s390/disassembler: increase ebpf disasm buffer size
ACPI: custom_method: fix potential use-after-free issue
ACPI: custom_method: fix a possible memory leak
arm64: dts: mt8173: fix property typo of 'phys' in dsi node
ecryptfs: fix kernel panic with null dev_name
mmc: core: Do a power cycle when the CMD11 fails
mmc: core: Set read only for SD cards with permanent write protect bit
btrfs: fix metadata extent leak after failure to create subvolume
fbdev: zero-fill colormap in fbcmap.c
staging: wimax/i2400m: fix byte-order issue
usb: gadget: uvc: add bInterval checking for HS mode
usb: dwc3: gadget: Ignore EP queue requests during bus reset
usb: xhci: Fix port minor revision
PCI: PM: Do not read power state in pci_enable_device_flags()
x86/build: Propagate $(CLANG_FLAGS) to $(REALMODE_FLAGS)
spi: dln2: Fix reference leak to master
spi: omap-100k: Fix reference leak to master
intel_th: Consistency and off-by-one fix
phy: phy-twl4030-usb: Fix possible use-after-free in twl4030_usb_remove()
btrfs: convert logic BUG_ON()'s in replace_path to ASSERT()'s
scsi: target: pscsi: Fix warning in pscsi_complete_cmd()
media: ite-cir: check for receive overflow
extcon: arizona: Fix some issues when HPDET IRQ fires after the jack has been unplugged
media: media/saa7164: fix saa7164_encoder_register() memory leak bugs
media: gspca/sq905.c: fix uninitialized variable
power: supply: Use IRQF_ONESHOT
scsi: qla2xxx: Always check the return value of qla24xx_get_isp_stats()
scsi: scsi_dh_alua: Remove check for ASC 24h in alua_rtpg()
media: em28xx: fix memory leak
clk: socfpga: arria10: Fix memory leak of socfpga_clk on error return
power: supply: generic-adc-battery: fix possible use-after-free in gab_remove()
power: supply: s3c_adc_battery: fix possible use-after-free in s3c_adc_bat_remove()
media: adv7604: fix possible use-after-free in adv76xx_remove()
media: i2c: adv7511-v4l2: fix possible use-after-free in adv7511_remove()
media: i2c: adv7842: fix possible use-after-free in adv7842_remove()
media: dvb-usb: fix memory leak in dvb_usb_adapter_init
media: gscpa/stv06xx: fix memory leak
drm/msm/mdp5: Configure PP_SYNC_HEIGHT to double the vtotal
drm/amdgpu: fix NULL pointer dereference
scsi: lpfc: Fix crash when a REG_RPI mailbox fails triggering a LOGO response
scsi: libfc: Fix a format specifier
ALSA: emu8000: Fix a use after free in snd_emu8000_create_mixer
ALSA: sb: Fix two use after free in snd_sb_qsound_build
arm64/vdso: Discard .note.gnu.property sections in vDSO
openvswitch: fix stack OOB read while fragmenting IPv4 packets
NFSv4: Don't discard segments marked for return in _pnfs_return_layout()
jffs2: Fix kasan slab-out-of-bounds problem
powerpc/eeh: Fix EEH handling for hugepages in ioremap space.
powerpc: fix EDEADLOCK redefinition error in uapi/asm/errno.h
jffs2: check the validity of dstlen in jffs2_zlib_compress()
Revert 337f13046ff0 ("futex: Allow FUTEX_CLOCK_REALTIME with FUTEX_WAIT op")
ftrace: Handle commands when closing set_ftrace_filter file
ext4: fix check to prevent false positive report of incorrect used inodes
ext4: fix error code in ext4_commit_super
media: dvbdev: Fix memory leak in dvb_media_device_free()
usb: gadget: dummy_hcd: fix gpf in gadget_setup
usb: gadget: Fix double free of device descriptor pointers
usb: gadget/function/f_fs string table fix for multiple languages
dm persistent data: packed struct should have an aligned() attribute too
dm space map common: fix division bug in sm_ll_find_free_block()
dm rq: fix double free of blk_mq_tag_set in dev remove after table load fails
Bluetooth: verify AMP hci_chan before amp_destroy
hsr: use netdev_err() instead of WARN_ONCE()
bluetooth: eliminate the potential race condition when removing the HCI controller
net/nfc: fix use-after-free llcp_sock_bind/connect
FDDI: defxx: Bail out gracefully with unassigned PCI resource for CSR
misc: lis3lv02d: Fix false-positive WARN on various HP models
misc: vmw_vmci: explicitly initialize vmci_notify_bm_set_msg struct
misc: vmw_vmci: explicitly initialize vmci_datagram payload
tracing: Treat recording comm for idle task as a success
tracing: Use strlcpy() instead of strcpy() in __trace_find_cmdline()
tracing: Map all PIDs to command lines
tracing: Restructure trace_clock_global() to never block
md-cluster: fix use-after-free issue when removing rdev
md: factor out a mddev_find_locked helper from mddev_find
md: md_open returns -EBUSY when entering racing area
ipw2x00: potential buffer overflow in libipw_wx_set_encodeext()
cfg80211: scan: drop entry from hidden_list on overflow
drm/radeon: fix copy of uninitialized variable back to userspace
ALSA: hda/realtek: Re-order ALC882 Acer quirk table entries
ALSA: hda/realtek: Re-order ALC882 Sony quirk table entries
ALSA: hda/realtek: Re-order ALC269 Sony quirk table entries
ALSA: hda/realtek: Re-order ALC269 Lenovo quirk table entries
ALSA: hda/realtek: Remove redundant entry for ALC861 Haier/Uniwill devices
usb: gadget: pch_udc: Revert d3cb25a12138 completely
memory: gpmc: fix out of bounds read and dereference on gpmc_cs[]
ARM: dts: exynos: correct PMIC interrupt trigger level on SMDK5250
ARM: dts: exynos: correct PMIC interrupt trigger level on Snow
serial: stm32: fix incorrect characters on console
usb: gadget: pch_udc: Replace cpu_to_le32() by lower_32_bits()
usb: gadget: pch_udc: Check if driver is present before calling ->setup()
usb: gadget: pch_udc: Check for DMA mapping error
crypto: qat - don't release uninitialized resources
crypto: qat - ADF_STATUS_PF_RUNNING should be set after adf_dev_init
fotg210-udc: Fix DMA on EP0 for length > max packet size
fotg210-udc: Fix EP0 IN requests bigger than two packets
fotg210-udc: Remove a dubious condition leading to fotg210_done
fotg210-udc: Mask GRP2 interrupts we don't handle
fotg210-udc: Don't DMA more than the buffer can take
fotg210-udc: Complete OUT requests on short packets
mtd: require write permissions for locking and badblock ioctls
bus: qcom: Put child node before return
crypto: qat - fix error path in adf_isr_resource_alloc()
mtd: rawnand: gpmi: Fix a double free in gpmi_nand_init
staging: rtl8192u: Fix potential infinite loop
staging: greybus: uart: fix unprivileged TIOCCSERIAL
crypto: qat - Fix a double free in adf_create_ring
usb: gadget: r8a66597: Add missing null check on return from platform_get_resource
USB: cdc-acm: fix unprivileged TIOCCSERIAL
tty: actually undefine superseded ASYNC flags
tty: fix return value for unsupported ioctls
firmware: qcom-scm: Fix QCOM_SCM configuration
x86/platform/uv: Fix !KEXEC build failure
Drivers: hv: vmbus: Increase wait time for VMbus unload
ttyprintk: Add TTY hangup callback.
media: vivid: fix assignment of dev->fbuf_out_flags
media: omap4iss: return error code when omap4iss_get() failed
media: m88rs6000t: avoid potential out-of-bounds reads on arrays
pata_arasan_cf: fix IRQ check
pata_ipx4xx_cf: fix IRQ check
sata_mv: add IRQ checks
ata: libahci_platform: fix IRQ check
scsi: fcoe: Fix mismatched fcoe_wwn_from_mac declaration
media: dvb-usb-remote: fix dvb_usb_nec_rc_key_to_event type mismatch
clk: uniphier: Fix potential infinite loop
scsi: jazz_esp: Add IRQ check
scsi: sun3x_esp: Add IRQ check
scsi: sni_53c710: Add IRQ check
HSI: core: fix resource leaks in hsi_add_client_from_dt()
x86/events/amd/iommu: Fix sysfs type mismatch
HID: plantronics: Workaround for double volume key presses
perf symbols: Fix dso__fprintf_symbols_by_name() to return the number of printed chars
net: lapbether: Prevent racing when checking whether the netif is running
powerpc/prom: Mark identical_pvr_fixup as __init
ALSA: core: remove redundant spin_lock pair in snd_card_disconnect
nfc: pn533: prevent potential memory corruption
ALSA: usb-audio: Add error checks for usb_driver_claim_interface() calls
liquidio: Fix unintented sign extension of a left shift of a u16
powerpc/perf: Fix PMU constraint check for EBB events
powerpc: iommu: fix build when neither PCI or IBMVIO is set
mac80211: bail out if cipher schemes are invalid
mt7601u: fix always true expression
net: thunderx: Fix unintentional sign extension issue
i2c: cadence: add IRQ check
i2c: emev2: add IRQ check
i2c: jz4780: add IRQ check
i2c: sh7760: add IRQ check
MIPS: pci-legacy: stop using of_pci_range_to_resource
powerpc/pseries: extract host bridge from pci_bus prior to bus removal
i2c: sh7760: fix IRQ error path
mwl8k: Fix a double Free in mwl8k_probe_hw
vsock/vmci: log once the failed queue pair allocation
RDMA/i40iw: Fix error unwinding when i40iw_hmc_sd_one fails
net: davinci_emac: Fix incorrect masking of tx and rx error channel
ath9k: Fix error check in ath9k_hw_read_revisions() for PCI devices
powerpc/52xx: Fix an invalid ASM expression ('addi' used instead of 'add')
net:emac/emac-mac: Fix a use after free in emac_mac_tx_buf_send
net:nfc:digital: Fix a double free in digital_tg_recv_dep_req
kfifo: fix ternary sign extension bugs
Revert "net/sctp: fix race condition in sctp_destroy_sock"
sctp: delay auto_asconf init until binding the first addr
Revert "of/fdt: Make sure no-map does not remove already reserved regions"
Revert "fdt: Properly handle "no-map" field in the memory region"
fs: dlm: fix debugfs dump
tipc: convert dest node's address to network order
net: stmmac: Set FIFO sizes for ipq806x
ALSA: hdsp: don't disable if not enabled
ALSA: hdspm: don't disable if not enabled
ALSA: rme9652: don't disable if not enabled
Bluetooth: Set CONF_NOT_COMPLETE as l2cap_chan default
Bluetooth: initialize skb_queue_head at l2cap_chan_create()
ip6_vti: proper dev_{hold|put} in ndo_[un]init methods
mac80211: clear the beacon's CRC after channel switch
cuse: prevent clone
selftests: Set CC to clang in lib.mk if LLVM is set
kconfig: nconf: stop endless search loops
sctp: Fix out-of-bounds warning in sctp_process_asconf_param()
ASoC: rt286: Generalize support for ALC3263 codec
samples/bpf: Fix broken tracex1 due to kprobe argument change
powerpc/pseries: Stop calling printk in rtas_stop_self()
wl3501_cs: Fix out-of-bounds warnings in wl3501_send_pkt
wl3501_cs: Fix out-of-bounds warnings in wl3501_mgmt_join
powerpc/iommu: Annotate nested lock for lockdep
net: ethernet: mtk_eth_soc: fix RX VLAN offload
ASoC: rt286: Make RT286_SET_GPIO_* readable and writable
f2fs: fix a redundant call to f2fs_balance_fs if an error occurs
PCI: Release OF node in pci_scan_device()'s error path
ARM: 9064/1: hw_breakpoint: Do not directly check the event's overflow_handler hook
NFSv4.2: Always flush out writes in nfs42_proc_fallocate()
NFS: Deal correctly with attribute generation counter overflow
pNFS/flexfiles: fix incorrect size check in decode_nfs_fh()
NFSv4.2 fix handling of sr_eof in SEEK's reply
sctp: fix a SCTP_MIB_CURRESTAB leak in sctp_sf_do_dupcook_b
drm/radeon: Fix off-by-one power_state index heap overwrite
khugepaged: fix wrong result value for trace_mm_collapse_huge_page_isolate()
mm/hugeltb: handle the error case in hugetlb_fix_reserve_counts()
ksm: fix potential missing rmap_item for stable_node
kernel: kexec_file: fix error return code of kexec_calculate_store_digests()
ARC: entry: fix off-by-one error in syscall number validation
powerpc/64s: Fix crashes when toggling entry flush barrier
squashfs: fix divide error in calculate_skip()
iio: proximity: pulsedlight: Fix rumtime PM imbalance on error
usb: fotg210-hcd: Fix an error message
ACPI: scan: Fix a memory leak in an error handling path
usb: xhci: Increase timeout for HC halt
usb: dwc2: Fix gadget DMA unmap direction
usb: core: hub: fix race condition about TRSMRCY of resume
KVM: x86: Cancel pvclock_gtod_work on module removal
FDDI: defxx: Make MMIO the configuration default except for EISA
MIPS: Reinstate platform `__div64_32' handler
MIPS: Avoid DIVU in `__div64_32' is result would be zero
MIPS: Avoid handcoded DIVU in `__div64_32' altogether
thermal/core/fair share: Lock the thermal zone while looping over instances
dm ioctl: fix out of bounds array access when no devices
kobject_uevent: remove warning in init_uevent_argv()
netfilter: conntrack: Make global sysctls readonly in non-init netns
clk: exynos7: Mark aclk_fsys1_200 as critical
x86/msr: Fix wr/rdmsr_safe_regs_on_cpu() prototypes
extcon: adc-jack: Fix incompatible pointer type warning
kgdb: fix gcc-11 warning on indentation
usb: sl811-hcd: improve misleading indentation
cxgb4: Fix the -Wmisleading-indentation warning
isdn: capi: fix mismatched prototypes
ACPI / hotplug / PCI: Fix reference count leak in enable_slot()
Input: silead - add workaround for x86 BIOS-es which bring the chip up in a stuck state
um: Mark all kernel symbols as local
ceph: fix fscache invalidation
ALSA: hda: generic: change the DAC ctl name for LO+SPK or LO+HP
lib: stackdepot: turn depot_lock spinlock to raw_spinlock
sit: proper dev_{hold|put} in ndo_[un]init methods
ip6_tunnel: sit: proper dev_{hold|put} in ndo_[un]init methods
xhci: Do not use GFP_KERNEL in (potentially) atomic context
ipv6: remove extra dev_hold() for fallback tunnels
Linux 4.9.269
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: Ib994aef2c6746afa8dcbb237d8c0645ba2c6f7e1
666 lines
17 KiB
C
666 lines
17 KiB
C
/*
|
|
* Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
|
|
* Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
|
|
* Copyright (c) 2002, 2003 Tuukka Toivonen
|
|
* Copyright (c) 2008 Erik Andrén
|
|
*
|
|
* 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
|
|
*
|
|
* P/N 861037: Sensor HDCS1000 ASIC STV0600
|
|
* P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
|
|
* P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
|
|
* P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
|
|
* P/N 861075-0040: Sensor HDCS1000 ASIC
|
|
* P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
|
|
* P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/input.h>
|
|
#include "stv06xx_sensor.h"
|
|
|
|
MODULE_AUTHOR("Erik Andrén");
|
|
MODULE_DESCRIPTION("STV06XX USB Camera Driver");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
static bool dump_bridge;
|
|
static bool dump_sensor;
|
|
|
|
int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data)
|
|
{
|
|
int err;
|
|
struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
|
|
struct usb_device *udev = sd->gspca_dev.dev;
|
|
__u8 *buf = sd->gspca_dev.usb_buf;
|
|
|
|
u8 len = (i2c_data > 0xff) ? 2 : 1;
|
|
|
|
buf[0] = i2c_data & 0xff;
|
|
buf[1] = (i2c_data >> 8) & 0xff;
|
|
|
|
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
|
0x04, 0x40, address, 0, buf, len,
|
|
STV06XX_URB_MSG_TIMEOUT);
|
|
|
|
PDEBUG(D_CONF, "Written 0x%x to address 0x%x, status: %d",
|
|
i2c_data, address, err);
|
|
|
|
return (err < 0) ? err : 0;
|
|
}
|
|
|
|
int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data)
|
|
{
|
|
int err;
|
|
struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
|
|
struct usb_device *udev = sd->gspca_dev.dev;
|
|
__u8 *buf = sd->gspca_dev.usb_buf;
|
|
|
|
err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
|
|
0x04, 0xc0, address, 0, buf, 1,
|
|
STV06XX_URB_MSG_TIMEOUT);
|
|
|
|
*i2c_data = buf[0];
|
|
|
|
PDEBUG(D_CONF, "Reading 0x%x from address 0x%x, status %d",
|
|
*i2c_data, address, err);
|
|
|
|
return (err < 0) ? err : 0;
|
|
}
|
|
|
|
/* Wraps the normal write sensor bytes / words functions for writing a
|
|
single value */
|
|
int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value)
|
|
{
|
|
if (sd->sensor->i2c_len == 2) {
|
|
u16 data[2] = { address, value };
|
|
return stv06xx_write_sensor_words(sd, data, 1);
|
|
} else {
|
|
u8 data[2] = { address, value };
|
|
return stv06xx_write_sensor_bytes(sd, data, 1);
|
|
}
|
|
}
|
|
|
|
static int stv06xx_write_sensor_finish(struct sd *sd)
|
|
{
|
|
int err = 0;
|
|
|
|
if (sd->bridge == BRIDGE_STV610) {
|
|
struct usb_device *udev = sd->gspca_dev.dev;
|
|
__u8 *buf = sd->gspca_dev.usb_buf;
|
|
|
|
buf[0] = 0;
|
|
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
|
0x04, 0x40, 0x1704, 0, buf, 1,
|
|
STV06XX_URB_MSG_TIMEOUT);
|
|
}
|
|
|
|
return (err < 0) ? err : 0;
|
|
}
|
|
|
|
int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len)
|
|
{
|
|
int err, i, j;
|
|
struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
|
|
struct usb_device *udev = sd->gspca_dev.dev;
|
|
__u8 *buf = sd->gspca_dev.usb_buf;
|
|
|
|
PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len);
|
|
for (i = 0; i < len;) {
|
|
/* Build the command buffer */
|
|
memset(buf, 0, I2C_BUFFER_LENGTH);
|
|
for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) {
|
|
buf[j] = data[2*i];
|
|
buf[0x10 + j] = data[2*i+1];
|
|
PDEBUG(D_CONF, "I2C: Writing 0x%02x to reg 0x%02x",
|
|
data[2*i+1], data[2*i]);
|
|
}
|
|
buf[0x20] = sd->sensor->i2c_addr;
|
|
buf[0x21] = j - 1; /* Number of commands to send - 1 */
|
|
buf[0x22] = I2C_WRITE_CMD;
|
|
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
|
0x04, 0x40, 0x0400, 0, buf,
|
|
I2C_BUFFER_LENGTH,
|
|
STV06XX_URB_MSG_TIMEOUT);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
return stv06xx_write_sensor_finish(sd);
|
|
}
|
|
|
|
int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len)
|
|
{
|
|
int err, i, j;
|
|
struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
|
|
struct usb_device *udev = sd->gspca_dev.dev;
|
|
__u8 *buf = sd->gspca_dev.usb_buf;
|
|
|
|
PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len);
|
|
|
|
for (i = 0; i < len;) {
|
|
/* Build the command buffer */
|
|
memset(buf, 0, I2C_BUFFER_LENGTH);
|
|
for (j = 0; j < I2C_MAX_WORDS && i < len; j++, i++) {
|
|
buf[j] = data[2*i];
|
|
buf[0x10 + j * 2] = data[2*i+1];
|
|
buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8;
|
|
PDEBUG(D_CONF, "I2C: Writing 0x%04x to reg 0x%02x",
|
|
data[2*i+1], data[2*i]);
|
|
}
|
|
buf[0x20] = sd->sensor->i2c_addr;
|
|
buf[0x21] = j - 1; /* Number of commands to send - 1 */
|
|
buf[0x22] = I2C_WRITE_CMD;
|
|
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
|
0x04, 0x40, 0x0400, 0, buf,
|
|
I2C_BUFFER_LENGTH,
|
|
STV06XX_URB_MSG_TIMEOUT);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
return stv06xx_write_sensor_finish(sd);
|
|
}
|
|
|
|
int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value)
|
|
{
|
|
int err;
|
|
struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
|
|
struct usb_device *udev = sd->gspca_dev.dev;
|
|
__u8 *buf = sd->gspca_dev.usb_buf;
|
|
|
|
err = stv06xx_write_bridge(sd, STV_I2C_FLUSH, sd->sensor->i2c_flush);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
/* Clear mem */
|
|
memset(buf, 0, I2C_BUFFER_LENGTH);
|
|
|
|
buf[0] = address;
|
|
buf[0x20] = sd->sensor->i2c_addr;
|
|
buf[0x21] = 0;
|
|
|
|
/* Read I2C register */
|
|
buf[0x22] = I2C_READ_CMD;
|
|
|
|
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
|
0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH,
|
|
STV06XX_URB_MSG_TIMEOUT);
|
|
if (err < 0) {
|
|
pr_err("I2C: Read error writing address: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
|
|
0x04, 0xc0, 0x1410, 0, buf, sd->sensor->i2c_len,
|
|
STV06XX_URB_MSG_TIMEOUT);
|
|
if (sd->sensor->i2c_len == 2)
|
|
*value = buf[0] | (buf[1] << 8);
|
|
else
|
|
*value = buf[0];
|
|
|
|
PDEBUG(D_CONF, "I2C: Read 0x%x from address 0x%x, status: %d",
|
|
*value, address, err);
|
|
|
|
return (err < 0) ? err : 0;
|
|
}
|
|
|
|
/* Dumps all bridge registers */
|
|
static void stv06xx_dump_bridge(struct sd *sd)
|
|
{
|
|
int i;
|
|
u8 data, buf;
|
|
|
|
pr_info("Dumping all stv06xx bridge registers\n");
|
|
for (i = 0x1400; i < 0x160f; i++) {
|
|
stv06xx_read_bridge(sd, i, &data);
|
|
|
|
pr_info("Read 0x%x from address 0x%x\n", data, i);
|
|
}
|
|
|
|
pr_info("Testing stv06xx bridge registers for writability\n");
|
|
for (i = 0x1400; i < 0x160f; i++) {
|
|
stv06xx_read_bridge(sd, i, &data);
|
|
buf = data;
|
|
|
|
stv06xx_write_bridge(sd, i, 0xff);
|
|
stv06xx_read_bridge(sd, i, &data);
|
|
if (data == 0xff)
|
|
pr_info("Register 0x%x is read/write\n", i);
|
|
else if (data != buf)
|
|
pr_info("Register 0x%x is read/write, but only partially\n",
|
|
i);
|
|
else
|
|
pr_info("Register 0x%x is read-only\n", i);
|
|
|
|
stv06xx_write_bridge(sd, i, buf);
|
|
}
|
|
}
|
|
|
|
/* this function is called at probe and resume time */
|
|
static int stv06xx_init(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
int err;
|
|
|
|
PDEBUG(D_PROBE, "Initializing camera");
|
|
|
|
/* Let the usb init settle for a bit
|
|
before performing the initialization */
|
|
msleep(250);
|
|
|
|
err = sd->sensor->init(sd);
|
|
|
|
if (dump_sensor && sd->sensor->dump)
|
|
sd->sensor->dump(sd);
|
|
|
|
return (err < 0) ? err : 0;
|
|
}
|
|
|
|
/* this function is called at probe time */
|
|
static int stv06xx_init_controls(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
PDEBUG(D_PROBE, "Initializing controls");
|
|
|
|
gspca_dev->vdev.ctrl_handler = &gspca_dev->ctrl_handler;
|
|
return sd->sensor->init_controls(sd);
|
|
}
|
|
|
|
/* Start the camera */
|
|
static int stv06xx_start(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
struct usb_host_interface *alt;
|
|
struct usb_interface *intf;
|
|
int err, packet_size;
|
|
|
|
intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
|
|
alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
|
|
if (!alt) {
|
|
PERR("Couldn't get altsetting");
|
|
return -EIO;
|
|
}
|
|
|
|
if (alt->desc.bNumEndpoints < 1)
|
|
return -ENODEV;
|
|
|
|
packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
|
|
err = stv06xx_write_bridge(sd, STV_ISO_SIZE_L, packet_size);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
/* Prepare the sensor for start */
|
|
err = sd->sensor->start(sd);
|
|
if (err < 0)
|
|
goto out;
|
|
|
|
/* Start isochronous streaming */
|
|
err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 1);
|
|
|
|
out:
|
|
if (err < 0)
|
|
PDEBUG(D_STREAM, "Starting stream failed");
|
|
else
|
|
PDEBUG(D_STREAM, "Started streaming");
|
|
|
|
return (err < 0) ? err : 0;
|
|
}
|
|
|
|
static int stv06xx_isoc_init(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct usb_interface_cache *intfc;
|
|
struct usb_host_interface *alt;
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
intfc = gspca_dev->dev->actconfig->intf_cache[0];
|
|
|
|
if (intfc->num_altsetting < 2)
|
|
return -ENODEV;
|
|
|
|
alt = &intfc->altsetting[1];
|
|
|
|
if (alt->desc.bNumEndpoints < 1)
|
|
return -ENODEV;
|
|
|
|
/* Start isoc bandwidth "negotiation" at max isoc bandwidth */
|
|
alt->endpoint[0].desc.wMaxPacketSize =
|
|
cpu_to_le16(sd->sensor->max_packet_size[gspca_dev->curr_mode]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stv06xx_isoc_nego(struct gspca_dev *gspca_dev)
|
|
{
|
|
int ret, packet_size, min_packet_size;
|
|
struct usb_host_interface *alt;
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
/*
|
|
* Existence of altsetting and endpoint was verified in
|
|
* stv06xx_isoc_init()
|
|
*/
|
|
alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
|
|
packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
|
|
min_packet_size = sd->sensor->min_packet_size[gspca_dev->curr_mode];
|
|
if (packet_size <= min_packet_size)
|
|
return -EIO;
|
|
|
|
packet_size -= 100;
|
|
if (packet_size < min_packet_size)
|
|
packet_size = min_packet_size;
|
|
alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size);
|
|
|
|
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
|
|
if (ret < 0)
|
|
PERR("set alt 1 err %d", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void stv06xx_stopN(struct gspca_dev *gspca_dev)
|
|
{
|
|
int err;
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
/* stop ISO-streaming */
|
|
err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 0);
|
|
if (err < 0)
|
|
goto out;
|
|
|
|
err = sd->sensor->stop(sd);
|
|
|
|
out:
|
|
if (err < 0)
|
|
PDEBUG(D_STREAM, "Failed to stop stream");
|
|
else
|
|
PDEBUG(D_STREAM, "Stopped streaming");
|
|
}
|
|
|
|
/*
|
|
* Analyse an USB packet of the data stream and store it appropriately.
|
|
* Each packet contains an integral number of chunks. Each chunk has
|
|
* 2-bytes identification, followed by 2-bytes that describe the chunk
|
|
* length. Known/guessed chunk identifications are:
|
|
* 8001/8005/C001/C005 - Begin new frame
|
|
* 8002/8006/C002/C006 - End frame
|
|
* 0200/4200 - Contains actual image data, bayer or compressed
|
|
* 0005 - 11 bytes of unknown data
|
|
* 0100 - 2 bytes of unknown data
|
|
* The 0005 and 0100 chunks seem to appear only in compressed stream.
|
|
*/
|
|
static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev,
|
|
u8 *data, /* isoc packet */
|
|
int len) /* iso packet length */
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
PDEBUG(D_PACK, "Packet of length %d arrived", len);
|
|
|
|
/* A packet may contain several frames
|
|
loop until the whole packet is reached */
|
|
while (len) {
|
|
int id, chunk_len;
|
|
|
|
if (len < 4) {
|
|
PDEBUG(D_PACK, "Packet is smaller than 4 bytes");
|
|
return;
|
|
}
|
|
|
|
/* Capture the id */
|
|
id = (data[0] << 8) | data[1];
|
|
|
|
/* Capture the chunk length */
|
|
chunk_len = (data[2] << 8) | data[3];
|
|
PDEBUG(D_PACK, "Chunk id: %x, length: %d", id, chunk_len);
|
|
|
|
data += 4;
|
|
len -= 4;
|
|
|
|
if (len < chunk_len) {
|
|
PERR("URB packet length is smaller"
|
|
" than the specified chunk length");
|
|
gspca_dev->last_packet_type = DISCARD_PACKET;
|
|
return;
|
|
}
|
|
|
|
/* First byte seem to be 02=data 2nd byte is unknown??? */
|
|
if (sd->bridge == BRIDGE_ST6422 && (id & 0xff00) == 0x0200)
|
|
goto frame_data;
|
|
|
|
switch (id) {
|
|
case 0x0200:
|
|
case 0x4200:
|
|
frame_data:
|
|
PDEBUG(D_PACK, "Frame data packet detected");
|
|
|
|
if (sd->to_skip) {
|
|
int skip = (sd->to_skip < chunk_len) ?
|
|
sd->to_skip : chunk_len;
|
|
data += skip;
|
|
len -= skip;
|
|
chunk_len -= skip;
|
|
sd->to_skip -= skip;
|
|
}
|
|
|
|
gspca_frame_add(gspca_dev, INTER_PACKET,
|
|
data, chunk_len);
|
|
break;
|
|
|
|
case 0x8001:
|
|
case 0x8005:
|
|
case 0xc001:
|
|
case 0xc005:
|
|
PDEBUG(D_PACK, "Starting new frame");
|
|
|
|
/* Create a new frame, chunk length should be zero */
|
|
gspca_frame_add(gspca_dev, FIRST_PACKET,
|
|
NULL, 0);
|
|
|
|
if (sd->bridge == BRIDGE_ST6422)
|
|
sd->to_skip = gspca_dev->pixfmt.width * 4;
|
|
|
|
if (chunk_len)
|
|
PERR("Chunk length is "
|
|
"non-zero on a SOF");
|
|
break;
|
|
|
|
case 0x8002:
|
|
case 0x8006:
|
|
case 0xc002:
|
|
PDEBUG(D_PACK, "End of frame detected");
|
|
|
|
/* Complete the last frame (if any) */
|
|
gspca_frame_add(gspca_dev, LAST_PACKET,
|
|
NULL, 0);
|
|
|
|
if (chunk_len)
|
|
PERR("Chunk length is "
|
|
"non-zero on a EOF");
|
|
break;
|
|
|
|
case 0x0005:
|
|
PDEBUG(D_PACK, "Chunk 0x005 detected");
|
|
/* Unknown chunk with 11 bytes of data,
|
|
occurs just before end of each frame
|
|
in compressed mode */
|
|
break;
|
|
|
|
case 0x0100:
|
|
PDEBUG(D_PACK, "Chunk 0x0100 detected");
|
|
/* Unknown chunk with 2 bytes of data,
|
|
occurs 2-3 times per USB interrupt */
|
|
break;
|
|
case 0x42ff:
|
|
PDEBUG(D_PACK, "Chunk 0x42ff detected");
|
|
/* Special chunk seen sometimes on the ST6422 */
|
|
break;
|
|
default:
|
|
PDEBUG(D_PACK, "Unknown chunk 0x%04x detected", id);
|
|
/* Unknown chunk */
|
|
}
|
|
data += chunk_len;
|
|
len -= chunk_len;
|
|
}
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_INPUT)
|
|
static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
|
|
u8 *data, /* interrupt packet data */
|
|
int len) /* interrupt packet length */
|
|
{
|
|
int ret = -EINVAL;
|
|
|
|
if (len == 1 && (data[0] == 0x80 || data[0] == 0x10)) {
|
|
input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
|
|
input_sync(gspca_dev->input_dev);
|
|
ret = 0;
|
|
}
|
|
|
|
if (len == 1 && (data[0] == 0x88 || data[0] == 0x11)) {
|
|
input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
|
|
input_sync(gspca_dev->input_dev);
|
|
ret = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int stv06xx_config(struct gspca_dev *gspca_dev,
|
|
const struct usb_device_id *id);
|
|
|
|
static void stv06xx_probe_error(struct gspca_dev *gspca_dev)
|
|
{
|
|
struct sd *sd = (struct sd *)gspca_dev;
|
|
|
|
kfree(sd->sensor_priv);
|
|
sd->sensor_priv = NULL;
|
|
}
|
|
|
|
/* sub-driver description */
|
|
static const struct sd_desc sd_desc = {
|
|
.name = MODULE_NAME,
|
|
.config = stv06xx_config,
|
|
.init = stv06xx_init,
|
|
.init_controls = stv06xx_init_controls,
|
|
.probe_error = stv06xx_probe_error,
|
|
.start = stv06xx_start,
|
|
.stopN = stv06xx_stopN,
|
|
.pkt_scan = stv06xx_pkt_scan,
|
|
.isoc_init = stv06xx_isoc_init,
|
|
.isoc_nego = stv06xx_isoc_nego,
|
|
#if IS_ENABLED(CONFIG_INPUT)
|
|
.int_pkt_scan = sd_int_pkt_scan,
|
|
#endif
|
|
};
|
|
|
|
/* This function is called at probe time */
|
|
static int stv06xx_config(struct gspca_dev *gspca_dev,
|
|
const struct usb_device_id *id)
|
|
{
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
PDEBUG(D_PROBE, "Configuring camera");
|
|
|
|
sd->bridge = id->driver_info;
|
|
gspca_dev->sd_desc = &sd_desc;
|
|
|
|
if (dump_bridge)
|
|
stv06xx_dump_bridge(sd);
|
|
|
|
sd->sensor = &stv06xx_sensor_st6422;
|
|
if (!sd->sensor->probe(sd))
|
|
return 0;
|
|
|
|
sd->sensor = &stv06xx_sensor_vv6410;
|
|
if (!sd->sensor->probe(sd))
|
|
return 0;
|
|
|
|
sd->sensor = &stv06xx_sensor_hdcs1x00;
|
|
if (!sd->sensor->probe(sd))
|
|
return 0;
|
|
|
|
sd->sensor = &stv06xx_sensor_hdcs1020;
|
|
if (!sd->sensor->probe(sd))
|
|
return 0;
|
|
|
|
sd->sensor = &stv06xx_sensor_pb0100;
|
|
if (!sd->sensor->probe(sd))
|
|
return 0;
|
|
|
|
sd->sensor = NULL;
|
|
return -ENODEV;
|
|
}
|
|
|
|
|
|
|
|
/* -- module initialisation -- */
|
|
static const struct usb_device_id device_table[] = {
|
|
/* QuickCam Express */
|
|
{USB_DEVICE(0x046d, 0x0840), .driver_info = BRIDGE_STV600 },
|
|
/* LEGO cam / QuickCam Web */
|
|
{USB_DEVICE(0x046d, 0x0850), .driver_info = BRIDGE_STV610 },
|
|
/* Dexxa WebCam USB */
|
|
{USB_DEVICE(0x046d, 0x0870), .driver_info = BRIDGE_STV602 },
|
|
/* QuickCam Messenger */
|
|
{USB_DEVICE(0x046D, 0x08F0), .driver_info = BRIDGE_ST6422 },
|
|
/* QuickCam Communicate */
|
|
{USB_DEVICE(0x046D, 0x08F5), .driver_info = BRIDGE_ST6422 },
|
|
/* QuickCam Messenger (new) */
|
|
{USB_DEVICE(0x046D, 0x08F6), .driver_info = BRIDGE_ST6422 },
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(usb, device_table);
|
|
|
|
/* -- device connect -- */
|
|
static int sd_probe(struct usb_interface *intf,
|
|
const struct usb_device_id *id)
|
|
{
|
|
return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
|
|
THIS_MODULE);
|
|
}
|
|
|
|
static void sd_disconnect(struct usb_interface *intf)
|
|
{
|
|
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
void *priv = sd->sensor_priv;
|
|
PDEBUG(D_PROBE, "Disconnecting the stv06xx device");
|
|
|
|
sd->sensor = NULL;
|
|
gspca_disconnect(intf);
|
|
kfree(priv);
|
|
}
|
|
|
|
static struct usb_driver sd_driver = {
|
|
.name = MODULE_NAME,
|
|
.id_table = device_table,
|
|
.probe = sd_probe,
|
|
.disconnect = sd_disconnect,
|
|
#ifdef CONFIG_PM
|
|
.suspend = gspca_suspend,
|
|
.resume = gspca_resume,
|
|
.reset_resume = gspca_resume,
|
|
#endif
|
|
};
|
|
|
|
module_usb_driver(sd_driver);
|
|
|
|
module_param(dump_bridge, bool, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup");
|
|
|
|
module_param(dump_sensor, bool, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(dump_sensor, "Dumps all sensor registers at startup");
|