aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/usb/dvb-usb/dib0700_core.c
diff options
context:
space:
mode:
authorLibravatar Linus Torvalds <torvalds@linux-foundation.org>2023-02-21 18:24:12 -0800
committerLibravatar Linus Torvalds <torvalds@linux-foundation.org>2023-02-21 18:24:12 -0800
commit5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch)
treecc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/media/usb/dvb-usb/dib0700_core.c
downloadlinux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz
linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski: "Core: - Add dedicated kmem_cache for typical/small skb->head, avoid having to access struct page at kfree time, and improve memory use. - Introduce sysctl to set default RPS configuration for new netdevs. - Define Netlink protocol specification format which can be used to describe messages used by each family and auto-generate parsers. Add tools for generating kernel data structures and uAPI headers. - Expose all net/core sysctls inside netns. - Remove 4s sleep in netpoll if carrier is instantly detected on boot. - Add configurable limit of MDB entries per port, and port-vlan. - Continue populating drop reasons throughout the stack. - Retire a handful of legacy Qdiscs and classifiers. Protocols: - Support IPv4 big TCP (TSO frames larger than 64kB). - Add IP_LOCAL_PORT_RANGE socket option, to control local port range on socket by socket basis. - Track and report in procfs number of MPTCP sockets used. - Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path manager. - IPv6: don't check net.ipv6.route.max_size and rely on garbage collection to free memory (similarly to IPv4). - Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986). - ICMP: add per-rate limit counters. - Add support for user scanning requests in ieee802154. - Remove static WEP support. - Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate reporting. - WiFi 7 EHT channel puncturing support (client & AP). BPF: - Add a rbtree data structure following the "next-gen data structure" precedent set by recently added linked list, that is, by using kfunc + kptr instead of adding a new BPF map type. - Expose XDP hints via kfuncs with initial support for RX hash and timestamp metadata. - Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to better support decap on GRE tunnel devices not operating in collect metadata. - Improve x86 JIT's codegen for PROBE_MEM runtime error checks. - Remove the need for trace_printk_lock for bpf_trace_printk and bpf_trace_vprintk helpers. - Extend libbpf's bpf_tracing.h support for tracing arguments of kprobes/uprobes and syscall as a special case. - Significantly reduce the search time for module symbols by livepatch and BPF. - Enable cpumasks to be used as kptrs, which is useful for tracing programs tracking which tasks end up running on which CPUs in different time intervals. - Add support for BPF trampoline on s390x and riscv64. - Add capability to export the XDP features supported by the NIC. - Add __bpf_kfunc tag for marking kernel functions as kfuncs. - Add cgroup.memory=nobpf kernel parameter option to disable BPF memory accounting for container environments. Netfilter: - Remove the CLUSTERIP target. It has been marked as obsolete for years, and we still have WARN splats wrt races of the out-of-band /proc interface installed by this target. - Add 'destroy' commands to nf_tables. They are identical to the existing 'delete' commands, but do not return an error if the referenced object (set, chain, rule...) did not exist. Driver API: - Improve cpumask_local_spread() locality to help NICs set the right IRQ affinity on AMD platforms. - Separate C22 and C45 MDIO bus transactions more clearly. - Introduce new DCB table to control DSCP rewrite on egress. - Support configuration of Physical Layer Collision Avoidance (PLCA) Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of shared medium Ethernet. - Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing preemption of low priority frames by high priority frames. - Add support for controlling MACSec offload using netlink SET. - Rework devlink instance refcounts to allow registration and de-registration under the instance lock. Split the code into multiple files, drop some of the unnecessarily granular locks and factor out common parts of netlink operation handling. - Add TX frame aggregation parameters (for USB drivers). - Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning messages with notifications for debug. - Allow offloading of UDP NEW connections via act_ct. - Add support for per action HW stats in TC. - Support hardware miss to TC action (continue processing in SW from a specific point in the action chain). - Warn if old Wireless Extension user space interface is used with modern cfg80211/mac80211 drivers. Do not support Wireless Extensions for Wi-Fi 7 devices at all. Everyone should switch to using nl80211 interface instead. - Improve the CAN bit timing configuration. Use extack to return error messages directly to user space, update the SJW handling, including the definition of a new default value that will benefit CAN-FD controllers, by increasing their oscillator tolerance. New hardware / drivers: - Ethernet: - nVidia BlueField-3 support (control traffic driver) - Ethernet support for imx93 SoCs - Motorcomm yt8531 gigabit Ethernet PHY - onsemi NCN26000 10BASE-T1S PHY (with support for PLCA) - Microchip LAN8841 PHY (incl. cable diagnostics and PTP) - Amlogic gxl MDIO mux - WiFi: - RealTek RTL8188EU (rtl8xxxu) - Qualcomm Wi-Fi 7 devices (ath12k) - CAN: - Renesas R-Car V4H Drivers: - Bluetooth: - Set Per Platform Antenna Gain (PPAG) for Intel controllers. - Ethernet NICs: - Intel (1G, igc): - support TSN / Qbv / packet scheduling features of i226 model - Intel (100G, ice): - use GNSS subsystem instead of TTY - multi-buffer XDP support - extend support for GPIO pins to E823 devices - nVidia/Mellanox: - update the shared buffer configuration on PFC commands - implement PTP adjphase function for HW offset control - TC support for Geneve and GRE with VF tunnel offload - more efficient crypto key management method - multi-port eswitch support - Netronome/Corigine: - add DCB IEEE support - support IPsec offloading for NFP3800 - Freescale/NXP (enetc): - support XDP_REDIRECT for XDP non-linear buffers - improve reconfig, avoid link flap and waiting for idle - support MAC Merge layer - Other NICs: - sfc/ef100: add basic devlink support for ef100 - ionic: rx_push mode operation (writing descriptors via MMIO) - bnxt: use the auxiliary bus abstraction for RDMA - r8169: disable ASPM and reset bus in case of tx timeout - cpsw: support QSGMII mode for J721e CPSW9G - cpts: support pulse-per-second output - ngbe: add an mdio bus driver - usbnet: optimize usbnet_bh() by avoiding unnecessary queuing - r8152: handle devices with FW with NCM support - amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation - virtio-net: support multi buffer XDP - virtio/vsock: replace virtio_vsock_pkt with sk_buff - tsnep: XDP support - Ethernet high-speed switches: - nVidia/Mellanox (mlxsw): - add support for latency TLV (in FW control messages) - Microchip (sparx5): - separate explicit and implicit traffic forwarding rules, make the implicit rules always active - add support for egress DSCP rewrite - IS0 VCAP support (Ingress Classification) - IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS etc.) - ES2 VCAP support (Egress Access Control) - support for Per-Stream Filtering and Policing (802.1Q, 8.6.5.1) - Ethernet embedded switches: - Marvell (mv88e6xxx): - add MAB (port auth) offload support - enable PTP receive for mv88e6390 - NXP (ocelot): - support MAC Merge layer - support for the the vsc7512 internal copper phys - Microchip: - lan9303: convert to PHYLINK - lan966x: support TC flower filter statistics - lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x - lan937x: support Credit Based Shaper configuration - ksz9477: support Energy Efficient Ethernet - other: - qca8k: convert to regmap read/write API, use bulk operations - rswitch: Improve TX timestamp accuracy - Intel WiFi (iwlwifi): - EHT (Wi-Fi 7) rate reporting - STEP equalizer support: transfer some STEP (connection to radio on platforms with integrated wifi) related parameters from the BIOS to the firmware. - Qualcomm 802.11ax WiFi (ath11k): - IPQ5018 support - Fine Timing Measurement (FTM) responder role support - channel 177 support - MediaTek WiFi (mt76): - per-PHY LED support - mt7996: EHT (Wi-Fi 7) support - Wireless Ethernet Dispatch (WED) reset support - switch to using page pool allocator - RealTek WiFi (rtw89): - support new version of Bluetooth co-existance - Mobile: - rmnet: support TX aggregation" * tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits) page_pool: add a comment explaining the fragment counter usage net: ethtool: fix __ethtool_dev_mm_supported() implementation ethtool: pse-pd: Fix double word in comments xsk: add linux/vmalloc.h to xsk.c sefltests: netdevsim: wait for devlink instance after netns removal selftest: fib_tests: Always cleanup before exit net/mlx5e: Align IPsec ASO result memory to be as required by hardware net/mlx5e: TC, Set CT miss to the specific ct action instance net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG net/mlx5: Refactor tc miss handling to a single function net/mlx5: Kconfig: Make tc offload depend on tc skb extension net/sched: flower: Support hardware miss to tc action net/sched: flower: Move filter handle initialization earlier net/sched: cls_api: Support hardware miss to tc action net/sched: Rename user cookie and act cookie sfc: fix builds without CONFIG_RTC_LIB sfc: clean up some inconsistent indentings net/mlx4_en: Introduce flexible array to silence overflow warning net: lan966x: Fix possible deadlock inside PTP net/ulp: Remove redundant ->clone() test in inet_clone_ulp(). ...
Diffstat (limited to 'drivers/media/usb/dvb-usb/dib0700_core.c')
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_core.c948
1 files changed, 948 insertions, 0 deletions
diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
new file mode 100644
index 000000000..1caabb51e
--- /dev/null
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -0,0 +1,948 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Linux driver for devices based on the DiBcom DiB0700 USB bridge
+ *
+ * Copyright (C) 2005-6 DiBcom, SA
+ */
+#include "dib0700.h"
+
+/* debug */
+int dvb_usb_dib0700_debug;
+module_param_named(debug,dvb_usb_dib0700_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=fw,4=fwdata,8=data (or-able))." DVB_USB_DEBUG_STATUS);
+
+static int nb_packet_buffer_size = 21;
+module_param(nb_packet_buffer_size, int, 0644);
+MODULE_PARM_DESC(nb_packet_buffer_size,
+ "Set the dib0700 driver data buffer size. This parameter corresponds to the number of TS packets. The actual size of the data buffer corresponds to this parameter multiplied by 188 (default: 21)");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+
+int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
+ u32 *romversion, u32 *ramversion, u32 *fwtype)
+{
+ struct dib0700_state *st = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0),
+ REQUEST_GET_VERSION,
+ USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
+ st->buf, 16, USB_CTRL_GET_TIMEOUT);
+ if (hwversion != NULL)
+ *hwversion = (st->buf[0] << 24) | (st->buf[1] << 16) |
+ (st->buf[2] << 8) | st->buf[3];
+ if (romversion != NULL)
+ *romversion = (st->buf[4] << 24) | (st->buf[5] << 16) |
+ (st->buf[6] << 8) | st->buf[7];
+ if (ramversion != NULL)
+ *ramversion = (st->buf[8] << 24) | (st->buf[9] << 16) |
+ (st->buf[10] << 8) | st->buf[11];
+ if (fwtype != NULL)
+ *fwtype = (st->buf[12] << 24) | (st->buf[13] << 16) |
+ (st->buf[14] << 8) | st->buf[15];
+ mutex_unlock(&d->usb_mutex);
+ return ret;
+}
+
+/* expecting rx buffer: request data[0] data[1] ... data[2] */
+static int dib0700_ctrl_wr(struct dvb_usb_device *d, u8 *tx, u8 txlen)
+{
+ int status;
+
+ deb_data(">>> ");
+ debug_dump(tx, txlen, deb_data);
+
+ status = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev,0),
+ tx[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, tx, txlen,
+ USB_CTRL_GET_TIMEOUT);
+
+ if (status != txlen)
+ deb_data("ep 0 write error (status = %d, len: %d)\n",status,txlen);
+
+ return status < 0 ? status : 0;
+}
+
+/* expecting tx buffer: request data[0] ... data[n] (n <= 4) */
+int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen)
+{
+ u16 index, value;
+ int status;
+
+ if (txlen < 2) {
+ err("tx buffer length is smaller than 2. Makes no sense.");
+ return -EINVAL;
+ }
+ if (txlen > 4) {
+ err("tx buffer length is larger than 4. Not supported.");
+ return -EINVAL;
+ }
+
+ deb_data(">>> ");
+ debug_dump(tx,txlen,deb_data);
+
+ value = ((txlen - 2) << 8) | tx[1];
+ index = 0;
+ if (txlen > 2)
+ index |= (tx[2] << 8);
+ if (txlen > 3)
+ index |= tx[3];
+
+ status = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), tx[0],
+ USB_TYPE_VENDOR | USB_DIR_IN, value, index, rx, rxlen,
+ USB_CTRL_GET_TIMEOUT);
+
+ if (status < 0)
+ deb_info("ep 0 read error (status = %d)\n",status);
+
+ deb_data("<<< ");
+ debug_dump(rx, rxlen, deb_data);
+
+ return status; /* length in case of success */
+}
+
+int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val)
+{
+ struct dib0700_state *st = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ st->buf[0] = REQUEST_SET_GPIO;
+ st->buf[1] = gpio;
+ st->buf[2] = ((gpio_dir & 0x01) << 7) | ((gpio_val & 0x01) << 6);
+
+ ret = dib0700_ctrl_wr(d, st->buf, 3);
+
+ mutex_unlock(&d->usb_mutex);
+ return ret;
+}
+
+static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets)
+{
+ struct dib0700_state *st = d->priv;
+ int ret;
+
+ if (st->fw_version >= 0x10201) {
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ st->buf[0] = REQUEST_SET_USB_XFER_LEN;
+ st->buf[1] = (nb_ts_packets >> 8) & 0xff;
+ st->buf[2] = nb_ts_packets & 0xff;
+
+ deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets);
+
+ ret = dib0700_ctrl_wr(d, st->buf, 3);
+ mutex_unlock(&d->usb_mutex);
+ } else {
+ deb_info("this firmware does not allow to change the USB xfer len\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+/*
+ * I2C master xfer function (supported in 1.20 firmware)
+ */
+static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int num)
+{
+ /* The new i2c firmware messages are more reliable and in particular
+ properly support i2c read calls not preceded by a write */
+
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct dib0700_state *st = d->priv;
+ uint8_t bus_mode = 1; /* 0=eeprom bus, 1=frontend bus */
+ uint8_t gen_mode = 0; /* 0=master i2c, 1=gpio i2c */
+ uint8_t en_start = 0;
+ uint8_t en_stop = 0;
+ int result, i;
+
+ /* Ensure nobody else hits the i2c bus while we're sending our
+ sequence of messages, (such as the remote control thread) */
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EINTR;
+
+ for (i = 0; i < num; i++) {
+ if (i == 0) {
+ /* First message in the transaction */
+ en_start = 1;
+ } else if (!(msg[i].flags & I2C_M_NOSTART)) {
+ /* Device supports repeated-start */
+ en_start = 1;
+ } else {
+ /* Not the first packet and device doesn't support
+ repeated start */
+ en_start = 0;
+ }
+ if (i == (num - 1)) {
+ /* Last message in the transaction */
+ en_stop = 1;
+ }
+
+ if (msg[i].flags & I2C_M_RD) {
+ /* Read request */
+ u16 index, value;
+ uint8_t i2c_dest;
+
+ i2c_dest = (msg[i].addr << 1);
+ value = ((en_start << 7) | (en_stop << 6) |
+ (msg[i].len & 0x3F)) << 8 | i2c_dest;
+ /* I2C ctrl + FE bus; */
+ index = ((gen_mode << 6) & 0xC0) |
+ ((bus_mode << 4) & 0x30);
+
+ result = usb_control_msg(d->udev,
+ usb_rcvctrlpipe(d->udev, 0),
+ REQUEST_NEW_I2C_READ,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ value, index, st->buf,
+ msg[i].len,
+ USB_CTRL_GET_TIMEOUT);
+ if (result < 0) {
+ deb_info("i2c read error (status = %d)\n", result);
+ goto unlock;
+ }
+
+ if (msg[i].len > sizeof(st->buf)) {
+ deb_info("buffer too small to fit %d bytes\n",
+ msg[i].len);
+ result = -EIO;
+ goto unlock;
+ }
+
+ memcpy(msg[i].buf, st->buf, msg[i].len);
+
+ deb_data("<<< ");
+ debug_dump(msg[i].buf, msg[i].len, deb_data);
+
+ } else {
+ /* Write request */
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ result = -EINTR;
+ goto unlock;
+ }
+ st->buf[0] = REQUEST_NEW_I2C_WRITE;
+ st->buf[1] = msg[i].addr << 1;
+ st->buf[2] = (en_start << 7) | (en_stop << 6) |
+ (msg[i].len & 0x3F);
+ /* I2C ctrl + FE bus; */
+ st->buf[3] = ((gen_mode << 6) & 0xC0) |
+ ((bus_mode << 4) & 0x30);
+
+ if (msg[i].len > sizeof(st->buf) - 4) {
+ deb_info("i2c message to big: %d\n",
+ msg[i].len);
+ mutex_unlock(&d->usb_mutex);
+ result = -EIO;
+ goto unlock;
+ }
+
+ /* The Actual i2c payload */
+ memcpy(&st->buf[4], msg[i].buf, msg[i].len);
+
+ deb_data(">>> ");
+ debug_dump(st->buf, msg[i].len + 4, deb_data);
+
+ result = usb_control_msg(d->udev,
+ usb_sndctrlpipe(d->udev, 0),
+ REQUEST_NEW_I2C_WRITE,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ 0, 0, st->buf, msg[i].len + 4,
+ USB_CTRL_GET_TIMEOUT);
+ mutex_unlock(&d->usb_mutex);
+ if (result < 0) {
+ deb_info("i2c write error (status = %d)\n", result);
+ break;
+ }
+ }
+ }
+ result = i;
+
+unlock:
+ mutex_unlock(&d->i2c_mutex);
+ return result;
+}
+
+/*
+ * I2C master xfer function (pre-1.20 firmware)
+ */
+static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct dib0700_state *st = d->priv;
+ int i, len, result;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EINTR;
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ mutex_unlock(&d->i2c_mutex);
+ return -EINTR;
+ }
+
+ for (i = 0; i < num; i++) {
+ /* fill in the address */
+ st->buf[1] = msg[i].addr << 1;
+ /* fill the buffer */
+ if (msg[i].len > sizeof(st->buf) - 2) {
+ deb_info("i2c xfer to big: %d\n",
+ msg[i].len);
+ result = -EIO;
+ goto unlock;
+ }
+ memcpy(&st->buf[2], msg[i].buf, msg[i].len);
+
+ /* write/read request */
+ if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
+ st->buf[0] = REQUEST_I2C_READ;
+ st->buf[1] |= 1;
+
+ /* special thing in the current firmware: when length is zero the read-failed */
+ len = dib0700_ctrl_rd(d, st->buf, msg[i].len + 2,
+ st->buf, msg[i + 1].len);
+ if (len <= 0) {
+ deb_info("I2C read failed on address 0x%02x\n",
+ msg[i].addr);
+ result = -EIO;
+ goto unlock;
+ }
+
+ if (msg[i + 1].len > sizeof(st->buf)) {
+ deb_info("i2c xfer buffer to small for %d\n",
+ msg[i].len);
+ result = -EIO;
+ goto unlock;
+ }
+ memcpy(msg[i + 1].buf, st->buf, msg[i + 1].len);
+
+ msg[i+1].len = len;
+
+ i++;
+ } else {
+ st->buf[0] = REQUEST_I2C_WRITE;
+ result = dib0700_ctrl_wr(d, st->buf, msg[i].len + 2);
+ if (result < 0)
+ goto unlock;
+ }
+ }
+ result = i;
+unlock:
+ mutex_unlock(&d->usb_mutex);
+ mutex_unlock(&d->i2c_mutex);
+
+ return result;
+}
+
+static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct dib0700_state *st = d->priv;
+
+ if (st->fw_use_new_i2c_api == 1) {
+ /* User running at least fw 1.20 */
+ return dib0700_i2c_xfer_new(adap, msg, num);
+ } else {
+ /* Use legacy calls */
+ return dib0700_i2c_xfer_legacy(adap, msg, num);
+ }
+}
+
+static u32 dib0700_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+struct i2c_algorithm dib0700_i2c_algo = {
+ .master_xfer = dib0700_i2c_xfer,
+ .functionality = dib0700_i2c_func,
+};
+
+int dib0700_identify_state(struct usb_device *udev,
+ const struct dvb_usb_device_properties *props,
+ const struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ s16 ret;
+ u8 *b;
+
+ b = kmalloc(16, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, 16, USB_CTRL_GET_TIMEOUT);
+
+ deb_info("FW GET_VERSION length: %d\n",ret);
+
+ *cold = ret <= 0;
+ deb_info("cold: %d\n", *cold);
+
+ kfree(b);
+ return 0;
+}
+
+static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
+ u8 pll_src, u8 pll_range, u8 clock_gpio3, u16 pll_prediv,
+ u16 pll_loopdiv, u16 free_div, u16 dsuScaler)
+{
+ struct dib0700_state *st = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ st->buf[0] = REQUEST_SET_CLOCK;
+ st->buf[1] = (en_pll << 7) | (pll_src << 6) |
+ (pll_range << 5) | (clock_gpio3 << 4);
+ st->buf[2] = (pll_prediv >> 8) & 0xff; /* MSB */
+ st->buf[3] = pll_prediv & 0xff; /* LSB */
+ st->buf[4] = (pll_loopdiv >> 8) & 0xff; /* MSB */
+ st->buf[5] = pll_loopdiv & 0xff; /* LSB */
+ st->buf[6] = (free_div >> 8) & 0xff; /* MSB */
+ st->buf[7] = free_div & 0xff; /* LSB */
+ st->buf[8] = (dsuScaler >> 8) & 0xff; /* MSB */
+ st->buf[9] = dsuScaler & 0xff; /* LSB */
+
+ ret = dib0700_ctrl_wr(d, st->buf, 10);
+ mutex_unlock(&d->usb_mutex);
+
+ return ret;
+}
+
+int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz)
+{
+ struct dib0700_state *st = d->priv;
+ u16 divider;
+ int ret;
+
+ if (scl_kHz == 0)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ st->buf[0] = REQUEST_SET_I2C_PARAM;
+ divider = (u16) (30000 / scl_kHz);
+ st->buf[1] = 0;
+ st->buf[2] = (u8) (divider >> 8);
+ st->buf[3] = (u8) (divider & 0xff);
+ divider = (u16) (72000 / scl_kHz);
+ st->buf[4] = (u8) (divider >> 8);
+ st->buf[5] = (u8) (divider & 0xff);
+ divider = (u16) (72000 / scl_kHz); /* clock: 72MHz */
+ st->buf[6] = (u8) (divider >> 8);
+ st->buf[7] = (u8) (divider & 0xff);
+
+ deb_info("setting I2C speed: %04x %04x %04x (%d kHz).",
+ (st->buf[2] << 8) | (st->buf[3]), (st->buf[4] << 8) |
+ st->buf[5], (st->buf[6] << 8) | st->buf[7], scl_kHz);
+
+ ret = dib0700_ctrl_wr(d, st->buf, 8);
+ mutex_unlock(&d->usb_mutex);
+
+ return ret;
+}
+
+
+int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3)
+{
+ switch (clk_MHz) {
+ case 72: dib0700_set_clock(d, 1, 0, 1, clock_out_gp3, 2, 24, 0, 0x4c); break;
+ default: return -EINVAL;
+ }
+ return 0;
+}
+
+static int dib0700_jumpram(struct usb_device *udev, u32 address)
+{
+ int ret = 0, actlen;
+ u8 *buf;
+
+ buf = kmalloc(8, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ buf[0] = REQUEST_JUMPRAM;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = 0;
+ buf[4] = (address >> 24) & 0xff;
+ buf[5] = (address >> 16) & 0xff;
+ buf[6] = (address >> 8) & 0xff;
+ buf[7] = address & 0xff;
+
+ if ((ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x01),buf,8,&actlen,1000)) < 0) {
+ deb_fw("jumpram to 0x%x failed\n",address);
+ goto out;
+ }
+ if (actlen != 8) {
+ deb_fw("jumpram to 0x%x failed\n",address);
+ ret = -EIO;
+ goto out;
+ }
+out:
+ kfree(buf);
+ return ret;
+}
+
+int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw)
+{
+ struct hexline hx;
+ int pos = 0, ret, act_len, i, adap_num;
+ u8 *buf;
+ u32 fw_version;
+
+ buf = kmalloc(260, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ while ((ret = dvb_usb_get_hexline(fw, &hx, &pos)) > 0) {
+ deb_fwdata("writing to address 0x%08x (buffer: 0x%02x %02x)\n",
+ hx.addr, hx.len, hx.chk);
+
+ buf[0] = hx.len;
+ buf[1] = (hx.addr >> 8) & 0xff;
+ buf[2] = hx.addr & 0xff;
+ buf[3] = hx.type;
+ memcpy(&buf[4],hx.data,hx.len);
+ buf[4+hx.len] = hx.chk;
+
+ ret = usb_bulk_msg(udev,
+ usb_sndbulkpipe(udev, 0x01),
+ buf,
+ hx.len + 5,
+ &act_len,
+ 1000);
+
+ if (ret < 0) {
+ err("firmware download failed at %d with %d",pos,ret);
+ goto out;
+ }
+ }
+
+ if (ret == 0) {
+ /* start the firmware */
+ if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) {
+ info("firmware started successfully.");
+ msleep(500);
+ }
+ } else
+ ret = -EIO;
+
+ /* the number of ts packet has to be at least 1 */
+ if (nb_packet_buffer_size < 1)
+ nb_packet_buffer_size = 1;
+
+ /* get the firmware version */
+ usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ REQUEST_GET_VERSION,
+ USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
+ buf, 16, USB_CTRL_GET_TIMEOUT);
+ fw_version = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11];
+
+ /* set the buffer size - DVB-USB is allocating URB buffers
+ * only after the firwmare download was successful */
+ for (i = 0; i < dib0700_device_count; i++) {
+ for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters;
+ adap_num++) {
+ if (fw_version >= 0x10201) {
+ dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 188*nb_packet_buffer_size;
+ } else {
+ /* for fw version older than 1.20.1,
+ * the buffersize has to be n times 512 */
+ dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512;
+ if (dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize < 512)
+ dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 512;
+ }
+ }
+ }
+out:
+ kfree(buf);
+ return ret;
+}
+
+int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct dib0700_state *st = adap->dev->priv;
+ int ret, adapt_nr;
+
+ if ((onoff != 0) && (st->fw_version >= 0x10201)) {
+ /* for firmware later than 1.20.1,
+ * the USB xfer length can be set */
+ ret = dib0700_set_usb_xfer_len(adap->dev,
+ st->nb_packet_buffer_size);
+ if (ret < 0) {
+ deb_info("can not set the USB xfer len\n");
+ return ret;
+ }
+ }
+
+ mutex_lock(&adap->dev->usb_mutex);
+
+ st->buf[0] = REQUEST_ENABLE_VIDEO;
+ /* this bit gives a kind of command,
+ * rather than enabling something or not */
+ st->buf[1] = (onoff << 4) | 0x00;
+
+ if (st->disable_streaming_master_mode == 1)
+ st->buf[2] = 0x00;
+ else
+ st->buf[2] = 0x01 << 4; /* Master mode */
+
+ st->buf[3] = 0x00;
+
+ if ((adap->fe_adap[0].stream.props.endpoint != 2)
+ && (adap->fe_adap[0].stream.props.endpoint != 3)) {
+ deb_info("the endpoint number (%i) is not correct, use the adapter id instead\n",
+ adap->fe_adap[0].stream.props.endpoint);
+ adapt_nr = adap->id;
+ } else {
+ adapt_nr = adap->fe_adap[0].stream.props.endpoint - 2;
+ }
+
+ if (onoff)
+ st->channel_state |= 1 << adapt_nr;
+ else
+ st->channel_state &= ~(1 << adapt_nr);
+
+ st->buf[2] |= st->channel_state;
+
+ deb_info("adapter %d, streaming %s: %*ph\n",
+ adapt_nr, onoff ? "ON" : "OFF", 3, st->buf);
+
+ ret = dib0700_ctrl_wr(adap->dev, st->buf, 4);
+ mutex_unlock(&adap->dev->usb_mutex);
+
+ return ret;
+}
+
+int dib0700_change_protocol(struct rc_dev *rc, u64 *rc_proto)
+{
+ struct dvb_usb_device *d = rc->priv;
+ struct dib0700_state *st = d->priv;
+ int new_proto, ret;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ st->buf[0] = REQUEST_SET_RC;
+ st->buf[1] = 0;
+ st->buf[2] = 0;
+
+ /* Set the IR mode */
+ if (*rc_proto & RC_PROTO_BIT_RC5) {
+ new_proto = 1;
+ *rc_proto = RC_PROTO_BIT_RC5;
+ } else if (*rc_proto & RC_PROTO_BIT_NEC) {
+ new_proto = 0;
+ *rc_proto = RC_PROTO_BIT_NEC;
+ } else if (*rc_proto & RC_PROTO_BIT_RC6_MCE) {
+ if (st->fw_version < 0x10200) {
+ ret = -EINVAL;
+ goto out;
+ }
+ new_proto = 2;
+ *rc_proto = RC_PROTO_BIT_RC6_MCE;
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ st->buf[1] = new_proto;
+
+ ret = dib0700_ctrl_wr(d, st->buf, 3);
+ if (ret < 0) {
+ err("ir protocol setup failed");
+ goto out;
+ }
+
+ d->props.rc.core.protocol = *rc_proto;
+
+out:
+ mutex_unlock(&d->usb_mutex);
+ return ret;
+}
+
+/* This is the structure of the RC response packet starting in firmware 1.20 */
+struct dib0700_rc_response {
+ u8 report_id;
+ u8 data_state;
+ union {
+ struct {
+ u8 system;
+ u8 not_system;
+ u8 data;
+ u8 not_data;
+ } nec;
+ struct {
+ u8 not_used;
+ u8 system;
+ u8 data;
+ u8 not_data;
+ } rc5;
+ };
+};
+#define RC_MSG_SIZE_V1_20 6
+
+static void dib0700_rc_urb_completion(struct urb *purb)
+{
+ struct dvb_usb_device *d = purb->context;
+ struct dib0700_rc_response *poll_reply;
+ enum rc_proto protocol;
+ u32 keycode;
+ u8 toggle;
+
+ deb_info("%s()\n", __func__);
+ if (d->rc_dev == NULL) {
+ /* This will occur if disable_rc_polling=1 */
+ kfree(purb->transfer_buffer);
+ usb_free_urb(purb);
+ return;
+ }
+
+ poll_reply = purb->transfer_buffer;
+
+ if (purb->status < 0) {
+ deb_info("discontinuing polling\n");
+ kfree(purb->transfer_buffer);
+ usb_free_urb(purb);
+ return;
+ }
+
+ if (purb->actual_length != RC_MSG_SIZE_V1_20) {
+ deb_info("malformed rc msg size=%d\n", purb->actual_length);
+ goto resubmit;
+ }
+
+ deb_data("IR ID = %02X state = %02X System = %02X %02X Cmd = %02X %02X (len %d)\n",
+ poll_reply->report_id, poll_reply->data_state,
+ poll_reply->nec.system, poll_reply->nec.not_system,
+ poll_reply->nec.data, poll_reply->nec.not_data,
+ purb->actual_length);
+
+ switch (d->props.rc.core.protocol) {
+ case RC_PROTO_BIT_NEC:
+ toggle = 0;
+
+ /* NEC protocol sends repeat code as 0 0 0 FF */
+ if (poll_reply->nec.system == 0x00 &&
+ poll_reply->nec.not_system == 0x00 &&
+ poll_reply->nec.data == 0x00 &&
+ poll_reply->nec.not_data == 0xff) {
+ poll_reply->data_state = 2;
+ rc_repeat(d->rc_dev);
+ goto resubmit;
+ }
+
+ if ((poll_reply->nec.data ^ poll_reply->nec.not_data) != 0xff) {
+ deb_data("NEC32 protocol\n");
+ keycode = RC_SCANCODE_NEC32(poll_reply->nec.system << 24 |
+ poll_reply->nec.not_system << 16 |
+ poll_reply->nec.data << 8 |
+ poll_reply->nec.not_data);
+ protocol = RC_PROTO_NEC32;
+ } else if ((poll_reply->nec.system ^ poll_reply->nec.not_system) != 0xff) {
+ deb_data("NEC extended protocol\n");
+ keycode = RC_SCANCODE_NECX(poll_reply->nec.system << 8 |
+ poll_reply->nec.not_system,
+ poll_reply->nec.data);
+
+ protocol = RC_PROTO_NECX;
+ } else {
+ deb_data("NEC normal protocol\n");
+ keycode = RC_SCANCODE_NEC(poll_reply->nec.system,
+ poll_reply->nec.data);
+ protocol = RC_PROTO_NEC;
+ }
+
+ break;
+ default:
+ deb_data("RC5 protocol\n");
+ protocol = RC_PROTO_RC5;
+ toggle = poll_reply->report_id;
+ keycode = RC_SCANCODE_RC5(poll_reply->rc5.system, poll_reply->rc5.data);
+
+ if ((poll_reply->rc5.data ^ poll_reply->rc5.not_data) != 0xff) {
+ /* Key failed integrity check */
+ err("key failed integrity check: %02x %02x %02x %02x",
+ poll_reply->rc5.not_used, poll_reply->rc5.system,
+ poll_reply->rc5.data, poll_reply->rc5.not_data);
+ goto resubmit;
+ }
+
+ break;
+ }
+
+ rc_keydown(d->rc_dev, protocol, keycode, toggle);
+
+resubmit:
+ /* Clean the buffer before we requeue */
+ memset(purb->transfer_buffer, 0, RC_MSG_SIZE_V1_20);
+
+ /* Requeue URB */
+ usb_submit_urb(purb, GFP_ATOMIC);
+}
+
+int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf)
+{
+ struct dib0700_state *st = d->priv;
+ struct urb *purb;
+ const struct usb_endpoint_descriptor *e;
+ int ret, rc_ep = 1;
+ unsigned int pipe = 0;
+
+ /* Poll-based. Don't initialize bulk mode */
+ if (st->fw_version < 0x10200 || !intf)
+ return 0;
+
+ /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */
+
+ if (intf->cur_altsetting->desc.bNumEndpoints < rc_ep + 1)
+ return -ENODEV;
+
+ purb = usb_alloc_urb(0, GFP_KERNEL);
+ if (purb == NULL)
+ return -ENOMEM;
+
+ purb->transfer_buffer = kzalloc(RC_MSG_SIZE_V1_20, GFP_KERNEL);
+ if (purb->transfer_buffer == NULL) {
+ err("rc kzalloc failed");
+ usb_free_urb(purb);
+ return -ENOMEM;
+ }
+
+ purb->status = -EINPROGRESS;
+
+ /*
+ * Some devices like the Hauppauge NovaTD model 52009 use an interrupt
+ * endpoint, while others use a bulk one.
+ */
+ e = &intf->cur_altsetting->endpoint[rc_ep].desc;
+ if (usb_endpoint_dir_in(e)) {
+ if (usb_endpoint_xfer_bulk(e)) {
+ pipe = usb_rcvbulkpipe(d->udev, rc_ep);
+ usb_fill_bulk_urb(purb, d->udev, pipe,
+ purb->transfer_buffer,
+ RC_MSG_SIZE_V1_20,
+ dib0700_rc_urb_completion, d);
+
+ } else if (usb_endpoint_xfer_int(e)) {
+ pipe = usb_rcvintpipe(d->udev, rc_ep);
+ usb_fill_int_urb(purb, d->udev, pipe,
+ purb->transfer_buffer,
+ RC_MSG_SIZE_V1_20,
+ dib0700_rc_urb_completion, d, 1);
+ }
+ }
+
+ if (!pipe) {
+ err("There's no endpoint for remote controller");
+ kfree(purb->transfer_buffer);
+ usb_free_urb(purb);
+ return 0;
+ }
+
+ ret = usb_submit_urb(purb, GFP_ATOMIC);
+ if (ret) {
+ err("rc submit urb failed");
+ kfree(purb->transfer_buffer);
+ usb_free_urb(purb);
+ }
+
+ return ret;
+}
+
+static int dib0700_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int i;
+ struct dvb_usb_device *dev;
+
+ for (i = 0; i < dib0700_device_count; i++)
+ if (dvb_usb_device_init(intf, &dib0700_devices[i], THIS_MODULE,
+ &dev, adapter_nr) == 0) {
+ struct dib0700_state *st = dev->priv;
+ u32 hwversion, romversion, fw_version, fwtype;
+
+ dib0700_get_version(dev, &hwversion, &romversion,
+ &fw_version, &fwtype);
+
+ deb_info("Firmware version: %x, %d, 0x%x, %d\n",
+ hwversion, romversion, fw_version, fwtype);
+
+ st->fw_version = fw_version;
+ st->nb_packet_buffer_size = (u32)nb_packet_buffer_size;
+
+ /* Disable polling mode on newer firmwares */
+ if (st->fw_version >= 0x10200)
+ dev->props.rc.core.bulk_mode = true;
+ else
+ dev->props.rc.core.bulk_mode = false;
+
+ dib0700_rc_setup(dev, intf);
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static void dib0700_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+ struct dib0700_state *st = d->priv;
+ struct i2c_client *client;
+
+ /* remove I2C client for tuner */
+ client = st->i2c_client_tuner;
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
+
+ /* remove I2C client for demodulator */
+ client = st->i2c_client_demod;
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
+
+ dvb_usb_device_exit(intf);
+}
+
+
+static struct usb_driver dib0700_driver = {
+ .name = "dvb_usb_dib0700",
+ .probe = dib0700_probe,
+ .disconnect = dib0700_disconnect,
+ .id_table = dib0700_usb_id_table,
+};
+
+module_usb_driver(dib0700_driver);
+
+MODULE_FIRMWARE("dvb-usb-dib0700-1.20.fw");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
+MODULE_DESCRIPTION("Driver for devices based on DiBcom DiB0700 - USB bridge");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");