[
  {
    "path": "README-old.md",
    "content": "# synology - th0ma7\nSynology personnal hack, info, tools &amp; source code\n\nDonnations welcomed at: `0x522d164549E68681dfaC850A2cabdb95686C1fEC`\n\n# Hauppauge WinTV DualHD HWC 955D\nThe following allows building kernel modules for the Hauppauge WinTV DualHD HWC 955D media adapter allowing to use TVheadend natively within the NAS.\n* https://www.linuxtv.org/wiki/index.php/Hauppauge_WinTV-HVR-955Q\n\nIt was tested on the following hardware:\n* model: DS918+\n* OS: DSM 6.2.2 build #24922\n* kernel: 4.4.59+\n* arch: x86_64\n* core name: Apollo Lake\n\nFinding your running kernel:\n```\n$ uname -mvr\n4.4.59+ #24922 SMP PREEMPT Thu Mar 28 11:07:03 CST 2019 x86_64\n```\n\nFinding your CPU type:\n* https://en.wikichip.org/wiki/intel/celeron/j3455\n```\n$ cat /proc/cpuinfo | grep model.name | head -1\nmodel name\t: Intel(R) Celeron(R) CPU J3455 @ 1.50GHz\n```\n\n## Current status\nAvailable patches makes both tuner detected by the kernel using the `em28xx.ko` updated driver.  The `lgdt3306a.ko` demodulator driver providing the dvb-frontend devices now works and has a few DEBUG messages output.  It originally failed because I was also sharing the USB device with a Ubuntu VM running on-top and loading the kernel modules in the wrong order.\n\nWorking:\n- `em28xx`: both tuners detected & firmware loading OK\n- `lgdt3306a`: now functional\nEnd result:\n- `tvheadend`: fully detects both tuners\n\nWork is based on the backporting of the following upstream kernel patches:\n\nDemodulator (lgdt3306a):\n* https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=4f75189024f4186a7ff9d56f4a8cb690774412ec\n\nAdaptor (em28xx):\n* https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=11a2a949d05e9d2d9823f0c45fa476743d9e462b\n* https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=1586342e428d80e53f9a926b2e238d2175b9f5b5\n* https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=be7fd3c3a8c5e9acbc69f887ca961df5e68cf6f0\n\nAlong with backported patches from the media-tree that b-rad-NDi made available here:\n* https://github.com/b-rad-NDi/Ubuntu-media-tree-kernel-builder/tree/master/patches/fedora-22-4.4.0\n\n## Preparation\nUsing a Ubuntu 18.04 OS VM in order to build the updated modules.\n\nInstall a few essential packages:\n```\n$ sudo apt update\n$ sudo apt install build-essential ncurses-dev bc libssl-dev libc6-i386 curl\n```\n\nThe toolchain & kernel sources are located here:\nhttps://sourceforge.net/projects/dsgpl/files/\n\nI use a $HOME/synology directory to drop all the downloaded files and a $HOME/source as my working directory.  Later references make usage of theses both paths:\n```\n$ mkdir $HOME/synology\n$ mkdir $HOME/sources\n```\n\nDownload the toolchain from Home / DSM 6.2 Tool Chains / Intel x86 Linux 4.4.59 (Apollolake)\n```\n$ wget https://sourceforge.net/projects/dsgpl/files/DSM%206.2%20Tool%20Chains/Intel%20x86%20Linux%204.4.59%20%28Apollolake%29/apollolake-gcc493_glibc220_linaro_x86_64-GPL.txz/download -O apollolake-gcc493_glibc220_linaro_x86_64-GPL.txz -P $HOME/synology\n```\n\nDownload the synology kernel sources from Home / Synology NAS GPL Source / 22259branch / apollolake-source:\n```\n$ wget https://sourceforge.net/projects/dsgpl/files/Synology%20NAS%20GPL%20Source/22259branch/apollolake-source/linux-4.4.x.txz/download -O linux-4.4.x.txz -P $HOME/synology\n```\n\nExtract the files:\n* The toolset is decompressed into the `$HOME/synology` directory\n* Kernel sources are decompresed in the `$HOME/sources` directory\n```\n$ tar -xvf $HOME/synology/apollolake-gcc493_glibc220_linaro_x86_64-GPL.txz -C $HOME/synology\n$ tar -xvf $HOME/synology/linux-4.4.x.txz -C $HOME/sources\n```\n\nDownload the `lgdt3306a` and `em28xx` patches:\n```\n$ wget https://raw.githubusercontent.com/th0ma7/synology/master/hauppauge/001-Hauppauge955D-lgdt3306a-v3.patch -P $HOME/sources\n$ wget https://raw.githubusercontent.com/th0ma7/synology/master/hauppauge/002-Hauppauge955D-em28xx-Tuner1.patch -P $HOME/sources\n$ wget https://raw.githubusercontent.com/th0ma7/synology/master/hauppauge/003-Hauppauge955D-em28xx-Tuner2-v6.patch -P $HOME/sources\n```\n\n## Patching\nWe now have to prepare the kernel sources for compilation.  Move down to the `$HOME/sources` directory:\n```\n$ cd $HOME/sources/linux-4.4.x\n```\n\nFrist need to copy the apollolake synology kernel configuration\n```\n~/sources/linux-4.4.x$ synoconfigs/apollolake .config\n```\n\nSecondly, using a text editor you need to adjust a few variables in the Makefile such as:\n```\nEXTRAVERSION = +\nARCH            ?= x86_64\nCROSS_COMPILE   ?= /usr/local/x86_64-pc-linux-gnu/bin/x86_64-pc-linux-gnu-\n```\n\nLastly, apply the necessary patches:\n```\n~/sources/linux-4.4.x$ patch -p1 < ../001-Hauppauge955D-lgdt3306a-v3.patch\n~/sources/linux-4.4.x$ patch -p1 < ../002-Hauppauge955D-em28xx-Tuner1.patch\n~/sources/linux-4.4.x$ patch -p1 < ../003-Hauppauge955D-em28xx-Tuner2-v6.patch\n```\n\n# Compilation\nModify the original configuration:\n```\n~/sources/linux-4.4.x$ sudo make oldconfig\n~/sources/linux-4.4.x$ sudo make menuconfig\n    Device Drivers  --->\n    <M> Multimedia support  --->\n        [*]   Media Controller API\n        [*]   V4L2 sub-device userspace API\n```\nEverything should now all set for building the modules:\n```\n~/sources/linux-4.4.x$ make modules_prepare\n~/sources/linux-4.4.x$ make modules M=drivers/media -j4\n```\n\n# Installation\n\nUsing SSH login as admin on the synology NAS:\n```\n$ ssh admin@192.168.x.x\n```\n\nCreate a new local module directory (name will match kernel version):\n```\n$ sudo mkdir -p /usr/local/lib/modules/$(uname -r)\n$ cd /usr/local/lib/modules/$(uname -r)\n```\n\nCopy the updated media drivers modules over to the NAS (or download the one from my repository on github - Use at your own risks):\n```\n$ sudo scp \"username@192.168.x.x:~/sources/linux-4.4.x/drivers/media/usb/em28xx/*.ko\" .\n$ sudo scp \"username@192.168.x.x:~/sources/linux-4.4.x/drivers/media/dvb-frontends/lgdt3306a.ko\" .\n$ sudo scp \"username@192.168.x.x:~/sources/linux-4.4.x/drivers/media/dvb-frontends/media.ko\" .\n```\n\nCopy the load/unload script to the NAS as well (and make it executable):\n```\n$ wget https://raw.githubusercontent.com/th0ma7/synology/master/hauppauge/hauppauge-load.sh\n$ wget https://raw.githubusercontent.com/th0ma7/synology/master/hauppauge/hauppauge-unload.sh\n$ chmod 755 hauppauge-*.sh\n```\n\nCreate a local rc file locate at `/usr/local/etc/rc.d/hauppauge.sh` that will be executed at boot time (or copy `hauppauge-load.sh` script):\n```\n#!/bin/sh\n\n# Load mandatory modules\nsudo insmod /lib/modules/dvb-core.ko\nsudo insmod /lib/modules/rc-core.ko\nsudo insmod /lib/modules/dvb-usb.ko\nsudo insmod /lib/modules/videodev.ko\nsudo insmod /lib/modules/v4l2-common.ko\nsudo insmod /lib/modules/tveeprom.ko\n\n# Load Hauppauge updated drivers\nsudo insmod /lib/modules/si2157.ko\nsudo insmod /usr/local/lib/modules/$(uname -r)/media.ko\nsudo insmod /usr/local/lib/modules/$(uname -r)/lgdt3306a.ko\nsudo insmod /usr/local/lib/modules/$(uname -r)/em28xx.ko\nsudo insmod /usr/local/lib/modules/$(uname -r)/em28xx-dvb.ko\n```\n\nExecute manually the rc script to confirm all is ok:\n```\n$ sudo /usr/local/etc/rc.d/hauppauge.sh\n```\n\nNormally should see the following in kernel `dmesg` (added a few DEBUG to lgdt3306a):\n```\n[  557.806644] em28xx: New device HCW 955D @ 480 Mbps (2040:026d, interface 0, class 0)\n[  557.815308] em28xx: DVB interface 0 found: isoc\n[  557.820423] em28xx: chip ID is em28174\n[  558.939915] em28174 #0: EEPROM ID = 26 00 01 00, EEPROM hash = 0x3d790eca\n[  558.947531] em28174 #0: EEPROM info:\n[  558.951857] em28174 #0: \tmicrocode start address = 0x0004, boot configuration = 0x01\n[  558.966683] em28174 #0: \tAC97 audio (5 sample rates)\n[  558.972234] em28174 #0: \t500mA max power\n[  558.976620] em28174 #0: \tTable at offset 0x27, strings=0x0a72, 0x187c, 0x086a\n[  558.984753] em28174 #0: Identified as Hauppauge WinTV-dualHD 01595 ATSC/QAM (card=100)\n[  558.994647] tveeprom 8-0050: Hauppauge model 204101, rev B2I6, serial# 11584195\n[  559.002824] tveeprom 8-0050: tuner model is SiLabs Si2157 (idx 186, type 4)\n[  559.010649] tveeprom 8-0050: TV standards PAL(B/G) NTSC(M) PAL(I) SECAM(L/L') PAL(D/D1/K) ATSC/DVB Digital (eeprom 0xfc)\n[  559.023133] tveeprom 8-0050: audio processor is None (idx 0)\n[  559.029491] tveeprom 8-0050: has no radio, has IR receiver, has no IR transmitter\n[  559.038167] em28174 #0: dvb set to isoc mode.\n[  559.043177] em28xx: chip ID is em28174\n[  560.162726] em28174 #1: EEPROM ID = 26 00 01 00, EEPROM hash = 0x3d790eca\n[  560.170323] em28174 #1: EEPROM info:\n[  560.174326] em28174 #1: \tmicrocode start address = 0x0004, boot configuration = 0x01\n[  560.189064] em28174 #1: \tAC97 audio (5 sample rates)\n[  560.194613] em28174 #1: \t500mA max power\n[  560.199009] em28174 #1: \tTable at offset 0x27, strings=0x0a72, 0x187c, 0x086a\n[  560.207139] em28174 #1: Identified as Hauppauge WinTV-dualHD 01595 ATSC/QAM (card=100)\n[  560.216915] tveeprom 10-0050: Hauppauge model 204101, rev B2I6, serial# 11584195\n[  560.225192] tveeprom 10-0050: tuner model is SiLabs Si2157 (idx 186, type 4)\n[  560.233070] tveeprom 10-0050: TV standards PAL(B/G) NTSC(M) PAL(I) SECAM(L/L') PAL(D/D1/K) ATSC/DVB Digital (eeprom 0xfc)\n[  560.245327] tveeprom 10-0050: audio processor is None (idx 0)\n[  560.251757] tveeprom 10-0050: has no radio, has IR receiver, has no IR transmitter\n[  560.260220] em28xx: dvb ts2 set to isoc mode.\n[  560.465298] em28174 #0: Binding DVB extension\n[  560.476140] i2c i2c-8: Added multiplexed i2c bus 11\n[  560.481615] DEBUG: Passed lgdt3306a_probe 2351 \n[  560.486720] DEBUG: Passed lgdt3306a_probe 2353 \n[  560.491791] DEBUG: Passed lgdt3306a_probe 2355 \n[  560.496853] DEBUG: Passed lgdt3306a_probe 2357 \n[  560.501921] lgdt3306a 8-0059: LG Electronics LGDT3306A successfully identified\n[  560.509994] DEBUG: Passed lgdt3306a_probe 2360 \n[  560.517015] si2157 11-0060: Silicon Labs Si2147/2148/2157/2158 successfully attached\n[  560.525695] DVB: registering new adapter (em28174 #0)\n[  560.531352] usb 1-3: DVB: registering adapter 0 frontend 0 (LG Electronics LGDT3306A VSB/QAM Frontend)...\n[  560.544142] em28174 #0: DVB extension successfully initialized\n[  560.550672] em28174 #1: Binding DVB extension\n[  560.560027] i2c i2c-10: Added multiplexed i2c bus 12\n[  560.565578] DEBUG: Passed lgdt3306a_probe 2351 \n[  560.570650] DEBUG: Passed lgdt3306a_probe 2353 \n[  560.575800] DEBUG: Passed lgdt3306a_probe 2355 \n[  560.580886] DEBUG: Passed lgdt3306a_probe 2357 \n[  560.585962] lgdt3306a 10-000e: LG Electronics LGDT3306A successfully identified\n[  560.594141] DEBUG: Passed lgdt3306a_probe 2360 \n[  560.601410] si2157 12-0062: Silicon Labs Si2147/2148/2157/2158 successfully attached\n[  560.610075] DVB: registering new adapter (em28174 #1)\n[  560.615721] usb 1-3: DVB: registering adapter 1 frontend 0 (LG Electronics LGDT3306A VSB/QAM Frontend)...\n[  560.627161] em28174 #1: DVB extension successfully initialized\n[  562.882976] si2157 12-0062: found a 'Silicon Labs Si2157-A30'\n[  562.939717] si2157 12-0062: firmware version: 3.0.5\n[  562.945214] usb 1-3: DVB: adapter 1 frontend 0 frequency 0 out of range (55000000..858000000)\n```\n\nAnd the following USB devices with associated modules (ID may vary depending if connected using the front or back USB ports):\n```\n$ lsusb -Ic\n|__usb1          1d6b:0002:0404 09  2.00  480MBit/s 0mA 1IF  (Linux 4.4.59+ xhci-hcd xHCI Host Controller 0000:00:15.0)\n 1-0:1.0          (IF) 09:00:00 1EP  () hub \n  |__1-1         2040:026d:0100 00  2.00  480MBit/s 500mA 1IF  (HCW 955D 0011584195)\n  1-1:1.0         (IF) ff:00:00 2EPs () em28xx \n  |__1-4         f400:f400:0100 00  2.00  480MBit/s 200mA 1IF  (Synology DiskStation 6500794064E41636)\n  1-4:1.0         (IF) 08:06:50 2EPs () usb-storage host5 (synoboot)\n|__usb2          1d6b:0003:0404 09  3.00 5000MBit/s 0mA 1IF  (Linux 4.4.59+ xhci-hcd xHCI Host Controller 0000:00:15.0)\n 2-0:1.0          (IF) 09:00:00 1EP  () hub\n```\n\nNow reboot the NAS using the admin web page and confirm after reboot that the dmesg output and lsusb are still ok.\n\nIn case you run into issue where your NAS refuses to fully shutdown (and thus reboot) with the power button led blinking, it is most probably due to tainted modules still in memory.  Using the hauppauge-unload.sh script prior to shutdown/reboot will remove all the tainted modules from memory thus allowing the NAS to properly shutdown/reboot.\n"
  },
  {
    "path": "README.md",
    "content": "# synology - th0ma7\nSynology personnal hack, info, tools &amp; source code\n\nDonnations welcomed at: `0x522d164549E68681dfaC850A2cabdb95686C1fEC`\n\n# Hauppauge WinTV DualHD HWC 955D\nThe following allows building kernel modules for the Hauppauge WinTV DualHD HWC 955D media adapter allowing to use TVheadEnd (TVH) natively within the NAS.\n* https://www.linuxtv.org/wiki/index.php/Hauppauge_WinTV-HVR-955Q\n\nIn theory this procedure is also valid to build most supported DVB adaptors available from the Media Tree within the Linux Media Subsystem.\n* https://linuxtv.org/wiki/index.php/ATSC_USB_devices\n* https://linuxtv.org/wiki/index.php/DVB-C_USB_Devices\n\nTested on the following hardware:\n* model: DS918+\n* OS: DSM 6.2.2 build #24922\n* kernel: 4.4.59+\n* arch: x86_64\n* core name: Apollo Lake\n\nFinding your running kernel:\n```\n$ uname -mvr\n4.4.59+ #24922 SMP PREEMPT Thu Mar 28 11:07:03 CST 2019 x86_64\n```\n\nFinding your CPU type:\n* https://en.wikichip.org/wiki/intel/celeron/j3455\n```\n$ cat /proc/cpuinfo | grep model.name | head -1\nmodel name\t: Intel(R) Celeron(R) CPU J3455 @ 1.50GHz\n```\n\n## Current status\nI had backported patches to the Synology DSM 6.x 4.4.59+ kernel but there where a few pending issues.  Since then b-rad-NDi ended-up providing a backporting tool that allows rebuilding the media tree over the Synology DSM kernel.  This solution as been playing really nicely on my NAS over the last months.  _Big thanks to b-rad-NDi!!!_\n\nFor more details on b-rad-NDi project refer to:\n* https://github.com/b-rad-NDi/Embedded-MediaDrivers\n\nWorking:\n- `em28xx`: both tuners detected & firmware loading OK\n- `lgdt3306a`: fully functional\n\nEnd result:\n- `tvheadend`: fully detects both tuners\n\nInstead of building your own I've made available a pre-built module package for Hauppauge 955D USB DVB dongle to work on Synology NAS 6.2.2 kernel 4.4.59+ with Apollolake CPU (e.g. DS918+):\n* https://github.com/th0ma7/synology/raw/master/hauppauge/hauppauge955D-SYNOApollolake-DSM622_24922-Kernel_4.4.59-20190520.tar.bz2\n\n## Preparation\nUsing a Ubuntu 18.04 OS to build the updated modules install a few essential packages:\n```\n$ sudo apt update\n$ sudo apt install build-essential ncurses-dev bc libssl-dev libc6-i386 curl libproc-processtable-perl\n```\n\nClone b-rad-NDi git repository:\n```\n$ git clone https://github.com/b-rad-NDi/Embedded-MediaDrivers.git\n$ cd Embedded-MediaDrivers\n~/Embedded-MediaDrivers$\n```\n\nCreate a `SYNO-Apollolake` download directory:\n```\n$ mkdir dl/SYNO-Apollolake\n```\n\nDownload the toolchain\n* https://sourceforge.net/projects/dsgpl/files/DSM%206.2%20Tool%20Chains/\n```\n$ wget --content-disposition https://sourceforge.net/projects/dsgpl/files/DSM%206.2%20Tool%20Chains/Intel%20x86%20Linux%204.4.59%20%28Apollolake%29/apollolake-gcc493_glibc220_linaro_x86_64-GPL.txz/download -P dl/SYNO-Apollolake/\n```\n\nDownload the Synology DSM kernel sources:\n* https://sourceforge.net/projects/dsgpl/files/Synology%20NAS%20GPL%20Source/22259branch/\n```\n$ wget --content-disposition https://sourceforge.net/projects/dsgpl/files/Synology%20NAS%20GPL%20Source/22259branch/apollolake-source/linux-4.4.x.txz/download -P dl/SYNO-Apollolake/\n```\n\nInitialize the repository:\n```\n$ ./md_builder.sh -i -d SYNO-Apollolake\n```\n\nBuild a default Synology DSM kernel build (takes a while):\n```\n$ export MAKEOPTS=\"-j`nproc`\"\n$ ./md_builder.sh -B media -d SYNO-Apollolake\n```\n\nConfigure the media tree, get the latest media tree patches that applies over the default Synology DSM kernel and build the media drivers:\n```\n$ ./md_builder.sh -g -d SYNO-Apollolake\n$ cd build/SYNOAPOLLOLAKE/media_build\nbuild/SYNOAPOLLOLAKE/media_build$ ./build\n```\n\n## Installation\n\nUsing SSH login as admin on the synology NAS:\n```\n$ ssh admin@<my.syno.nas.ip>\n```\n\nCreate a new local module directory (name will match kernel version):\n```\n$ sudo mkdir -p /usr/local/lib/modules/$(uname -r)\n$ cd /usr/local/lib/modules/$(uname -r)\n```\n\nDownload the updated media drivers modules over to the NAS (the following downloads not only the mandatory modules for Hauppauge WinTV but rather all the media tree modules):\n```\n$ cd /usr/local/lib/modules/$(uname -r)\n$ sudo scp \"username@<my.ubuntu.linux.ip>:~/Embedded-MediaDrivers/build/SYNOAPOLLOLAKE/media_build/v4l/*.ko\" .\n```\n\nCopy the start/stop/load/reset script to the NAS (and make it executable):\n```\n$ cd /usr/local/lib/modules/$(uname -r)\n$ wget https://raw.githubusercontent.com/th0ma7/synology/master/hauppauge.sh\n$ chmod 755 hauppauge.sh\n```\n\nCreate a symbolic link to `/opt/bin/hauppauge.sh` for ease of use:\n```\n$ sudo ln -s -T -f /usr/local/lib/modules/$(uname -r)/hauppauge.sh /opt/bin/hauppauge.sh\n```\n\nCreate a local rc file locate at `/usr/local/etc/rc.d/media.sh` that will be executed at boot time:\n```\n$ cat << EOF | sudo tee /usr/local/etc/rc.d/media.sh\n#!/bin/sh\n/usr/local/lib/modules/$(uname -r)/hauppauge.sh load\nEOF\n$ sudo chmod 755 /usr/local/etc/rc.d/media.sh\n```\n\nExecute manually the rc script to confirm there is no error:\n```\n$ sudo /usr/local/etc/rc.d/media.sh\n```\n\nValidate the status:\n```\n$ sudo /opt/bin/hauppauge.sh status\nStatus pkgctl-tvheadend...            N/A\nkernel module status... \n\tem28xx_dvb                    OK\n\tem28xx                        OK\n\tlgdt3306a                     OK\n\tsi2157                        OK\n\ttveeprom                      OK\n\tv4l2_common                   OK\n\tdvb_usb                       OK\n\trc_core                       OK\n\tdvb_core                      OK\n\tvideobuf2_vmalloc             OK\n\tvideobuf2_memops              OK\n\tvideobuf2_v4l2                OK\n\tvideobuf2_common              OK\n\tvideodev                      OK\n\tmedia                         OK\nkernel USB (1-3) autosuspend values...\n\t(1-3)autosuspend_delay_ms     [-1000] -> OK\n\t(1-3)autosuspend              [   -1] -> OK\nkernel sysctl values... \n\tvm.dirty_expire_centisecs     [  300] -> OK\n\tvm.swappiness                 [    1] -> OK\n```\n\nNormally should see something similar in kernel `dmesg`:\n```\n[  557.806644] em28xx: New device HCW 955D @ 480 Mbps (2040:026d, interface 0, class 0)\n[  557.815308] em28xx: DVB interface 0 found: isoc\n[  557.820423] em28xx: chip ID is em28174\n[  558.939915] em28174 #0: EEPROM ID = 26 00 01 00, EEPROM hash = 0x3d790eca\n[  558.947531] em28174 #0: EEPROM info:\n[  558.951857] em28174 #0: \tmicrocode start address = 0x0004, boot configuration = 0x01\n[  558.966683] em28174 #0: \tAC97 audio (5 sample rates)\n[  558.972234] em28174 #0: \t500mA max power\n[  558.976620] em28174 #0: \tTable at offset 0x27, strings=0x0a72, 0x187c, 0x086a\n[  558.984753] em28174 #0: Identified as Hauppauge WinTV-dualHD 01595 ATSC/QAM (card=100)\n[  558.994647] tveeprom 8-0050: Hauppauge model 204101, rev B2I6, serial# 11584195\n[  559.002824] tveeprom 8-0050: tuner model is SiLabs Si2157 (idx 186, type 4)\n[  559.010649] tveeprom 8-0050: TV standards PAL(B/G) NTSC(M) PAL(I) SECAM(L/L') PAL(D/D1/K) ATSC/DVB Digital (eeprom 0xfc)\n[  559.023133] tveeprom 8-0050: audio processor is None (idx 0)\n[  559.029491] tveeprom 8-0050: has no radio, has IR receiver, has no IR transmitter\n[  559.038167] em28174 #0: dvb set to isoc mode.\n[  559.043177] em28xx: chip ID is em28174\n[  560.162726] em28174 #1: EEPROM ID = 26 00 01 00, EEPROM hash = 0x3d790eca\n[  560.170323] em28174 #1: EEPROM info:\n[  560.174326] em28174 #1: \tmicrocode start address = 0x0004, boot configuration = 0x01\n[  560.189064] em28174 #1: \tAC97 audio (5 sample rates)\n[  560.194613] em28174 #1: \t500mA max power\n[  560.199009] em28174 #1: \tTable at offset 0x27, strings=0x0a72, 0x187c, 0x086a\n[  560.207139] em28174 #1: Identified as Hauppauge WinTV-dualHD 01595 ATSC/QAM (card=100)\n[  560.216915] tveeprom 10-0050: Hauppauge model 204101, rev B2I6, serial# 11584195\n[  560.225192] tveeprom 10-0050: tuner model is SiLabs Si2157 (idx 186, type 4)\n[  560.233070] tveeprom 10-0050: TV standards PAL(B/G) NTSC(M) PAL(I) SECAM(L/L') PAL(D/D1/K) ATSC/DVB Digital (eeprom 0xfc)\n[  560.245327] tveeprom 10-0050: audio processor is None (idx 0)\n[  560.251757] tveeprom 10-0050: has no radio, has IR receiver, has no IR transmitter\n[  560.260220] em28xx: dvb ts2 set to isoc mode.\n[  560.465298] em28174 #0: Binding DVB extension\n[  560.476140] i2c i2c-8: Added multiplexed i2c bus 11\n[  560.501921] lgdt3306a 8-0059: LG Electronics LGDT3306A successfully identified\n[  560.509994] DEBUG: Passed lgdt3306a_probe 2360 \n[  560.517015] si2157 11-0060: Silicon Labs Si2147/2148/2157/2158 successfully attached\n[  560.525695] DVB: registering new adapter (em28174 #0)\n[  560.531352] usb 1-3: DVB: registering adapter 0 frontend 0 (LG Electronics LGDT3306A VSB/QAM Frontend)...\n[  560.544142] em28174 #0: DVB extension successfully initialized\n[  560.550672] em28174 #1: Binding DVB extension\n[  560.560027] i2c i2c-10: Added multiplexed i2c bus 12\n[  560.585962] lgdt3306a 10-000e: LG Electronics LGDT3306A successfully identified\n[  560.601410] si2157 12-0062: Silicon Labs Si2147/2148/2157/2158 successfully attached\n[  560.610075] DVB: registering new adapter (em28174 #1)\n[  560.615721] usb 1-3: DVB: registering adapter 1 frontend 0 (LG Electronics LGDT3306A VSB/QAM Frontend)...\n[  560.627161] em28174 #1: DVB extension successfully initialized\n[  562.882976] si2157 12-0062: found a 'Silicon Labs Si2157-A30'\n[  562.939717] si2157 12-0062: firmware version: 3.0.5\n[  562.945214] usb 1-3: DVB: adapter 1 frontend 0 frequency 0 out of range (55000000..858000000)\n```\n\nAnd the following USB devices with associated modules (ID may vary depending if connected using the front or back USB ports):\n```\n$ lsusb -Ic\n|__usb1          1d6b:0002:0404 09  2.00  480MBit/s 0mA 1IF  (Linux 4.4.59+ xhci-hcd xHCI Host Controller 0000:00:15.0)\n 1-0:1.0          (IF) 09:00:00 1EP  () hub \n  |__1-1         2040:026d:0100 00  2.00  480MBit/s 500mA 1IF  (HCW 955D 0011584195)\n  1-1:1.0         (IF) ff:00:00 2EPs () em28xx \n  |__1-4         f400:f400:0100 00  2.00  480MBit/s 200mA 1IF  (Synology DiskStation 6500794064E41636)\n  1-4:1.0         (IF) 08:06:50 2EPs () usb-storage host5 (synoboot)\n|__usb2          1d6b:0003:0404 09  3.00 5000MBit/s 0mA 1IF  (Linux 4.4.59+ xhci-hcd xHCI Host Controller 0000:00:15.0)\n 2-0:1.0          (IF) 09:00:00 1EP  () hub\n```\n\nNow reboot the NAS using the admin web page and confirm after reboot that the dmesg output and lsusb are still ok.\n\nIn case you run into issue where your NAS refuses to fully shutdown (and thus reboot) with the power button led blinking, it is most probably due to tainted modules still in memory.  Running `hauppauge.sh stop` prior to shutdown/reboot will remove all the tainted modules from memory thus allowing the NAS to properly shutdown/reboot.\n\n---\n\n# hauppauge.sh\nThis script is intended to provide a simple method to start|stop|restart the various perequesites into getting media modules loaded onto the Synology NAS.\n\nBasicaly what the script does:\n1. Load all the necessary modules\n2. Disable USB autosuspend over the Hauppauge USB ID\n3. Injects a few kernel `sysctl` for optmizations\n4. Starts TVH (service name `pkgctl-tvheadend`)\n\n## Modules\nThe script uses `insmod` to load|unload the modules into the appropriate order.  The `MODULES` parameter in the script can be adapted as needed for other DVB dongles than the Hauppauge WinTV 955D.\n\n|Order | Module                 | `rmmod`             |\n|:----:|:----------------------:|:-------------------:|\n| 1    | `media.ko`             | `media`             |\n| 2    | `videodev.ko`          | `videodev`          |\n| 3    | `videobuf2-common.ko`  | `videobuf2_common`  |\n| 4    | `videobuf2-v4l2.ko`    | `videobuf2_v4l2`    |\n| 5    | `videobuf2-memops.ko`  | `videobuf2_memops`  |\n| 6    | `videobuf2-vmalloc.ko` | `videobuf2_vmalloc` |\n| 7    | `dvb-core.ko`          | `dvb_core`          |\n| 8    | `rc-core.ko`           | `rc_core`           |\n| 9    | `dvb-usb.ko`           | `dvb_usb`           |\n| 10   | `v4l2-common.ko`       | `v4l2_common`       |\n| 11   | `tveeprom.ko`          | `tveeprom`          |\n| 12   | `si2157.ko`            | `si2157`            |\n| 13   | `lgdt3306a.ko`         | `lgdt3306a`         |\n| 14   | `em28xx.ko`            | `em28xx`            |\n| 15   | `em28xx-dvb.ko`        | `em28xx_dvb`        |\n\n## Options\n**start:** Does a full start including:\n1. loading all the modules\n2. disabling USB autosuspend\n3. `sysctl` adjustments\n4. TVH startup\n\n**stop:** Does a stop which basicaly is:\n1. TVH shutdown\n2. Unloading all the modules\n\n**restart:** Basically performs a `stop` then `start`.\n\n**reset:** This is usefull when hitting BUGS with TVH such as OOM killer where the tvheadend service is being killed by the system.  The `reset` option reduces to the minimal the impact over the already loaded modules by resetting only the DVB frontend module such as:\n1. TVH shutdown (forces it if needed)\n2. unload `em28xx_dvb`\n3. load of `em28xx-dvb.ko`\n4. TVH startup\n\n**load:** This is the option to be used at NAS startup.  It basically does all the same things as the `start` option without the TVH startup as it's being managed by the Synology DSM stack automatically.\n\n**status:** This provides a view on all things namely modules loaded into memory, USB autosuspend, `sysctl` adjustments and TVH service status including it's PID.\n"
  },
  {
    "path": "hauppauge/001-Hauppauge955D-lgdt3306a-v3.patch",
    "content": "diff -uprN linux-4.4.x-orig/drivers/media/dvb-frontends/lgdt3306a.c linux-4.4.x-new/drivers/media/dvb-frontends/lgdt3306a.c\n--- linux-4.4.x-orig/drivers/media/dvb-frontends/lgdt3306a.c\t2017-10-30 17:45:14.000000000 -0400\n+++ linux-4.4.x-new/drivers/media/dvb-frontends/lgdt3306a.c\t2019-05-13 20:41:32.123976946 -0400\n@@ -16,17 +16,31 @@\n  *    GNU General Public License for more details.\n  */\n \n+#undef pr_fmt\n #define pr_fmt(fmt) KBUILD_MODNAME \": \" fmt\n \n #include <asm/div64.h>\n+#include <linux/kernel.h>\n #include <linux/dvb/frontend.h>\n #include \"dvb_math.h\"\n #include \"lgdt3306a.h\"\n+#include <linux/i2c-mux.h>\n \n static int debug;\n module_param(debug, int, 0644);\n MODULE_PARM_DESC(debug, \"set debug level (info=1, reg=2 (or-able))\");\n \n+/*\n+ * Older drivers treated QAM64 and QAM256 the same; that is the HW always\n+ * used \"Auto\" mode during detection.  Setting \"forced_manual\"=1 allows\n+ * the user to treat these modes as separate.  For backwards compatibility,\n+ * it's off by default.  QAM_AUTO can now be specified to achive that\n+ * effect even if \"forced_manual\"=1\n+ */\n+static int forced_manual;\n+module_param(forced_manual, int, 0644);\n+MODULE_PARM_DESC(forced_manual, \"if set, QAM64 and QAM256 will only lock to modulation specified\");\n+\n #define DBG_INFO 1\n #define DBG_REG  2\n #define DBG_DUMP 4 /* FGR - comment out to remove dump code */\n@@ -55,6 +69,8 @@ MODULE_PARM_DESC(debug, \"set debug level\n \t__ret;\t\t\t\t\t\t\t\t\\\n })\n \n+#define MHz 1000000\n+\n struct lgdt3306a_state {\n \tstruct i2c_adapter *i2c_adap;\n \tconst struct lgdt3306a_config *cfg;\n@@ -558,7 +574,12 @@ static int lgdt3306a_set_qam(struct lgdt\n \t/* 3. : 64QAM/256QAM detection(manual, auto) */\n \tret = lgdt3306a_read_reg(state, 0x0009, &val);\n \tval &= 0xfc;\n-\tval |= 0x02; /* STDOPDETCMODE[1:0]=1=Manual 2=Auto */\n+\t/* Check for forced Manual modulation modes; otherwise always \"auto\" */\n+\tif(forced_manual && (modulation != QAM_AUTO)){\n+\t\tval |= 0x01; /* STDOPDETCMODE[1:0]= 1=Manual */\n+\t} else {\n+\t\tval |= 0x02; /* STDOPDETCMODE[1:0]= 2=Auto */\n+\t}\n \tret = lgdt3306a_write_reg(state, 0x0009, val);\n \tif (lg_chkerr(ret))\n \t\tgoto fail;\n@@ -590,6 +611,28 @@ static int lgdt3306a_set_qam(struct lgdt\n \tif (lg_chkerr(ret))\n \t\tgoto fail;\n \n+\t/* 5.1 V0.36 SRDCHKALWAYS : For better QAM detection */\n+\tret = lgdt3306a_read_reg(state, 0x000a, &val);\n+\tval &= 0xfd;\n+\tval |= 0x02;\n+\tret = lgdt3306a_write_reg(state, 0x000a, val);\n+\tif (lg_chkerr(ret))\n+\t\tgoto fail;\n+\n+\t/* 5.2 V0.36 Control of \"no signal\" detector function */\n+\tret = lgdt3306a_read_reg(state, 0x2849, &val);\n+\tval &= 0xdf;\n+\tret = lgdt3306a_write_reg(state, 0x2849, val);\n+\tif (lg_chkerr(ret))\n+\t\tgoto fail;\n+\n+\t/* 5.3 Fix for Blonder Tongue HDE-2H-QAM and AQM modulators */\n+\tret = lgdt3306a_read_reg(state, 0x302b, &val);\n+\tval &= 0x7f;  /* SELFSYNCFINDEN_CQS=0; disable auto reset */\n+\tret = lgdt3306a_write_reg(state, 0x302b, val);\n+\tif (lg_chkerr(ret))\n+\t\tgoto fail;\n+\n \t/* 6. Reset */\n \tret = lgdt3306a_soft_reset(state);\n \tif (lg_chkerr(ret))\n@@ -612,10 +655,9 @@ static int lgdt3306a_set_modulation(stru\n \t\tret = lgdt3306a_set_vsb(state);\n \t\tbreak;\n \tcase QAM_64:\n-\t\tret = lgdt3306a_set_qam(state, QAM_64);\n-\t\tbreak;\n \tcase QAM_256:\n-\t\tret = lgdt3306a_set_qam(state, QAM_256);\n+\tcase QAM_AUTO:\n+\t\tret = lgdt3306a_set_qam(state, p->modulation);\n \t\tbreak;\n \tdefault:\n \t\treturn -EINVAL;\n@@ -642,6 +684,7 @@ static int lgdt3306a_agc_setup(struct lg\n \t\tbreak;\n \tcase QAM_64:\n \tcase QAM_256:\n+\tcase QAM_AUTO:\n \t\tbreak;\n \tdefault:\n \t\treturn -EINVAL;\n@@ -696,6 +739,7 @@ static int lgdt3306a_spectral_inversion(\n \t\tbreak;\n \tcase QAM_64:\n \tcase QAM_256:\n+\tcase QAM_AUTO:\n \t\t/* Auto ok for QAM */\n \t\tret = lgdt3306a_set_inversion_auto(state, 1);\n \t\tbreak;\n@@ -719,6 +763,7 @@ static int lgdt3306a_set_if(struct lgdt3\n \t\tbreak;\n \tcase QAM_64:\n \tcase QAM_256:\n+\tcase QAM_AUTO:\n \t\tif_freq_khz = state->cfg->qam_if_khz;\n \t\tbreak;\n \tdefault:\n@@ -727,7 +772,7 @@ static int lgdt3306a_set_if(struct lgdt3\n \n \tswitch (if_freq_khz) {\n \tdefault:\n-\t\tpr_warn(\"IF=%d KHz is not supportted, 3250 assumed\\n\",\n+\t\tpr_warn(\"IF=%d KHz is not supported, 3250 assumed\\n\",\n \t\t\tif_freq_khz);\n \t\t/* fallthrough */\n \tcase 3250: /* 3.25Mhz */\n@@ -1577,6 +1622,7 @@ static int lgdt3306a_read_status(struct\n \t\tswitch (state->current_modulation) {\n \t\tcase QAM_256:\n \t\tcase QAM_64:\n+\t\tcase QAM_AUTO:\n \t\t\tif (lgdt3306a_qam_lock_poll(state) == LG3306_LOCK) {\n \t\t\t\t*status |= FE_HAS_VITERBI;\n \t\t\t\t*status |= FE_HAS_SYNC;\n@@ -1619,6 +1665,7 @@ static int lgdt3306a_read_signal_strengt\n \t * Calculate some sort of \"strength\" from SNR\n \t */\n \tstruct lgdt3306a_state *state = fe->demodulator_priv;\n+\tu8 val;\n \tu16 snr; /* snr_x10 */\n \tint ret;\n \tu32 ref_snr; /* snr*100 */\n@@ -1631,11 +1678,15 @@ static int lgdt3306a_read_signal_strengt\n \t\t ref_snr = 1600; /* 16dB */\n \t\t break;\n \tcase QAM_64:\n-\t\t ref_snr = 2200; /* 22dB */\n-\t\t break;\n \tcase QAM_256:\n-\t\t ref_snr = 2800; /* 28dB */\n-\t\t break;\n+\tcase QAM_AUTO:\n+\t\t/* need to know actual modulation to set proper SNR baseline */\n+\t\tlgdt3306a_read_reg(state, 0x00a6, &val);\n+\t\tif(val & 0x04)\n+\t\t\tref_snr = 2800; /* QAM-256 28dB */\n+\t\telse\n+\t\t\tref_snr = 2200; /* QAM-64  22dB */\n+\t\tbreak;\n \tdefault:\n \t\treturn -EINVAL;\n \t}\n@@ -1729,27 +1780,19 @@ static int lgdt3306a_get_tune_settings(s\n \treturn 0;\n }\n \n-static int lgdt3306a_search(struct dvb_frontend *fe)\n+static enum dvbfe_search lgdt3306a_search(struct dvb_frontend *fe)\n {\n \tenum fe_status status = 0;\n-\tint i, ret;\n+\tint ret;\n \n \t/* set frontend */\n \tret = lgdt3306a_set_parameters(fe);\n \tif (ret)\n \t\tgoto error;\n \n-\t/* wait frontend lock */\n-\tfor (i = 20; i > 0; i--) {\n-\t\tdbg_info(\": loop=%d\\n\", i);\n-\t\tmsleep(50);\n-\t\tret = lgdt3306a_read_status(fe, &status);\n-\t\tif (ret)\n-\t\t\tgoto error;\n-\n-\t\tif (status & FE_HAS_LOCK)\n-\t\t\tbreak;\n-\t}\n+\tret = lgdt3306a_read_status(fe, &status);\n+\tif (ret)\n+\t\tgoto error;\n \n \t/* check if we have a valid signal */\n \tif (status & FE_HAS_LOCK)\n@@ -1770,7 +1813,7 @@ static void lgdt3306a_release(struct dvb\n \tkfree(state);\n }\n \n-static struct dvb_frontend_ops lgdt3306a_ops;\n+static const struct dvb_frontend_ops lgdt3306a_ops;\n \n struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config,\n \t\t\t\t      struct i2c_adapter *i2c_adap)\n@@ -2072,7 +2115,7 @@ static const short regtab[] = {\n \t0x30aa, /* MPEGLOCK */\n };\n \n-#define numDumpRegs (sizeof(regtab)/sizeof(regtab[0]))\n+#define numDumpRegs (ARRAY_SIZE(regtab))\n static u8 regval1[numDumpRegs] = {0, };\n static u8 regval2[numDumpRegs] = {0, };\n \n@@ -2104,14 +2147,14 @@ static void lgdt3306a_DumpRegs(struct lg\n }\n #endif /* DBG_DUMP */\n \n-static struct dvb_frontend_ops lgdt3306a_ops = {\n+static const struct dvb_frontend_ops lgdt3306a_ops = {\n \t.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },\n \t.info = {\n \t\t.name = \"LG Electronics LGDT3306A VSB/QAM Frontend\",\n-\t\t.frequency_min      = 54000000,\n-\t\t.frequency_max      = 858000000,\n-\t\t.frequency_stepsize = 62500,\n-\t\t.caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB\n+\t\t.frequency_min          =  54 * MHz,\n+\t\t.frequency_max          = 858 * MHz,\n+\t\t.frequency_stepsize     = 62500,\n+\t\t.caps = FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB\n \t},\n \t.i2c_gate_ctrl        = lgdt3306a_i2c_gate_ctrl,\n \t.init                 = lgdt3306a_init,\n@@ -2132,6 +2175,234 @@ static struct dvb_frontend_ops lgdt3306a\n \t.search               = lgdt3306a_search,\n };\n \n+/*\n+ * I2C gate logic\n+ * We must use unlocked I2C I/O because I2C adapter lock is already taken\n+ * by the caller (usually tuner driver).\n+ * select/unselect are unlocked versions of lgdt3306a_i2c_gate_ctrl\n+ */\n+static int lgdt3306a_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)\n+{\n+\tstruct i2c_client *client = mux_priv;\n+\tint ret;\n+\tu8 val;\n+\tu8 buf[3];\n+\n+\tstruct i2c_msg read_msg_1 = {\n+\t\t.addr = client->addr,\n+\t\t.flags = 0,\n+\t\t.buf = \"\\x00\\x02\",\n+\t\t.len = 2,\n+\t};\n+\tstruct i2c_msg read_msg_2 = {\n+\t\t.addr = client->addr,\n+\t\t.flags = I2C_M_RD,\n+\t\t.buf = &val,\n+\t\t.len = 1,\n+\t};\n+\n+\tstruct i2c_msg write_msg = {\n+\t\t.addr = client->addr,\n+\t\t.flags = 0,\n+\t\t.len = 3,\n+\t\t.buf = buf,\n+\t};\n+\n+\tret = __i2c_transfer(client->adapter, &read_msg_1, 1);\n+\tif (ret != 1)\n+\t{\n+\t\tpr_err(\"error (addr %02x reg 0x002 error (ret == %i)\\n\",\n+\t\t       client->addr, ret);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t\telse\n+\t\t\treturn -EREMOTEIO;\n+\t}\n+\n+\tret = __i2c_transfer(client->adapter, &read_msg_2, 1);\n+\tif (ret != 1)\n+\t{\n+\t\tpr_err(\"error (addr %02x reg 0x002 error (ret == %i)\\n\",\n+\t\t       client->addr, ret);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t\telse\n+\t\t\treturn -EREMOTEIO;\n+\t}\n+\n+\tbuf[0] = 0x00;\n+\tbuf[1] = 0x02;\n+\tval &= 0x7F;\n+\tval |= LG3306_TUNERI2C_ON;\n+\tbuf[2] = val;\n+\tret = __i2c_transfer(client->adapter, &write_msg, 1);\n+\tif (ret != 1) {\n+\t\tpr_err(\"error (addr %02x %02x <- %02x, err = %i)\\n\",\n+\t\t       write_msg.buf[0], write_msg.buf[1], write_msg.buf[2], ret);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t\telse\n+\t\t\treturn -EREMOTEIO;\n+\t}\n+\treturn 0;\n+}\n+\n+static int lgdt3306a_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan)\n+{\n+\tstruct i2c_client *client = mux_priv;\n+\tint ret;\n+\tu8 val;\n+\tu8 buf[3];\n+\n+\tstruct i2c_msg read_msg_1 = {\n+\t\t.addr = client->addr,\n+\t\t.flags = 0,\n+\t\t.buf = \"\\x00\\x02\",\n+\t\t.len = 2,\n+\t};\n+\tstruct i2c_msg read_msg_2 = {\n+\t\t.addr = client->addr,\n+\t\t.flags = I2C_M_RD,\n+\t\t.buf = &val,\n+\t\t.len = 1,\n+\t};\n+\n+\tstruct i2c_msg write_msg = {\n+\t\t.addr = client->addr,\n+\t\t.flags = 0,\n+\t\t.len = 3,\n+\t\t.buf = buf,\n+\t};\n+\n+\tret = __i2c_transfer(client->adapter, &read_msg_1, 1);\n+\tif (ret != 1)\n+\t{\n+\t\tpr_err(\"error (addr %02x reg 0x002 error (ret == %i)\\n\",\n+\t\t       client->addr, ret);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t\telse\n+\t\t\treturn -EREMOTEIO;\n+\t}\n+\n+\tret = __i2c_transfer(client->adapter, &read_msg_2, 1);\n+\tif (ret != 1)\n+\t{\n+\t\tpr_err(\"error (addr %02x reg 0x002 error (ret == %i)\\n\",\n+\t\t       client->addr, ret);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t\telse\n+\t\t\treturn -EREMOTEIO;\n+\t}\n+\n+\tbuf[0] = 0x00;\n+\tbuf[1] = 0x02;\n+\tval &= 0x7F;\n+\tval |= LG3306_TUNERI2C_OFF;\n+\tbuf[2] = val;\n+\tret = __i2c_transfer(client->adapter, &write_msg, 1);\n+\tif (ret != 1) {\n+\t\tpr_err(\"error (addr %02x %02x <- %02x, err = %i)\\n\",\n+\t\t       write_msg.buf[0], write_msg.buf[1], write_msg.buf[2], ret);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t\telse\n+\t\t\treturn -EREMOTEIO;\n+\t}\n+\treturn 0;\n+}\n+\n+static int lgdt3306a_probe(struct i2c_client *client,\n+\t\tconst struct i2c_device_id *id)\n+{\n+\tstruct lgdt3306a_config *config;\n+\tstruct lgdt3306a_state *state;\n+\tstruct dvb_frontend *fe;\n+\tint ret;\n+\n+\tconfig = kmemdup(client->dev.platform_data,\n+\t\t\t sizeof(struct lgdt3306a_config), GFP_KERNEL);\n+\tif (config == NULL) {\n+\t\tret = -ENOMEM;\n+\t\tgoto fail;\n+\t}\n+\n+\tconfig->i2c_addr = client->addr;\n+\tfe = lgdt3306a_attach(config, client->adapter);\n+\tif (fe == NULL) {\n+\t\tret = -ENODEV;\n+\t\tgoto err_fe;\n+\t}\n+\n+\ti2c_set_clientdata(client, fe->demodulator_priv);\n+\tstate = fe->demodulator_priv;\n+\tstate->frontend.ops.release = NULL;\n+\n+\t/* create mux i2c adapter for tuner */\n+\tstate->i2c_adap = i2c_add_mux_adapter(client->adapter, &client->dev,\n+\t\t\tclient, 0, 0, 0, lgdt3306a_select, lgdt3306a_deselect);\n+\tif (state->i2c_adap == NULL) {\n+\t\tret = -ENODEV;\n+\t\tgoto err_kfree;\n+\t}\n+\n+\t/* create dvb_frontend */\n+\tprintk(KERN_ALERT \"DEBUG: Passed %s %d \\n\",__FUNCTION__,__LINE__);\n+\tfe->ops.i2c_gate_ctrl = NULL;\n+\tprintk(KERN_ALERT \"DEBUG: Passed %s %d \\n\",__FUNCTION__,__LINE__);\n+\t*config->i2c_adapter = state->i2c_adap;\n+\tprintk(KERN_ALERT \"DEBUG: Passed %s %d \\n\",__FUNCTION__,__LINE__);\n+\t*config->fe = fe;\n+\tprintk(KERN_ALERT \"DEBUG: Passed %s %d \\n\",__FUNCTION__,__LINE__);\n+\n+\tdev_info(&client->dev, \"LG Electronics LGDT3306A successfully identified\\n\");\n+\tprintk(KERN_ALERT \"DEBUG: Passed %s %d \\n\",__FUNCTION__,__LINE__);\n+\n+\treturn 0;\n+\n+err_kfree:\n+\tkfree(state);\n+err_fe:\n+\tkfree(config);\n+fail:\n+\tdev_warn(&client->dev, \"probe failed = %d\\n\", ret);\n+\treturn ret;\n+}\n+\n+static int lgdt3306a_remove(struct i2c_client *client)\n+{\n+\tstruct lgdt3306a_state *state = i2c_get_clientdata(client);\n+\n+\ti2c_del_mux_adapter(state->i2c_adap);\n+\n+\tstate->frontend.ops.release = NULL;\n+\tstate->frontend.demodulator_priv = NULL;\n+\n+\tkfree(state->cfg);\n+\tkfree(state);\n+\n+\treturn 0;\n+}\n+\n+static const struct i2c_device_id lgdt3306a_id_table[] = {\n+\t{\"lgdt3306a\", 0},\n+\t{}\n+};\n+MODULE_DEVICE_TABLE(i2c, lgdt3306a_id_table);\n+\n+static struct i2c_driver lgdt3306a_driver = {\n+\t.driver = {\n+\t\t.name                = \"lgdt3306a\",\n+\t\t.suppress_bind_attrs = true,\n+\t},\n+\t.probe\t\t= lgdt3306a_probe,\n+\t.remove\t\t= lgdt3306a_remove,\n+\t.id_table\t= lgdt3306a_id_table,\n+};\n+\n+module_i2c_driver(lgdt3306a_driver);\n+\n MODULE_DESCRIPTION(\"LG Electronics LGDT3306A ATSC/QAM-B Demodulator Driver\");\n MODULE_AUTHOR(\"Fred Richter <frichter@hauppauge.com>\");\n MODULE_LICENSE(\"GPL\");\ndiff -uprN linux-4.4.x-orig/drivers/media/dvb-frontends/lgdt3306a.h linux-4.4.x-new/drivers/media/dvb-frontends/lgdt3306a.h\n--- linux-4.4.x-orig/drivers/media/dvb-frontends/lgdt3306a.h\t2017-10-30 17:45:15.000000000 -0400\n+++ linux-4.4.x-new/drivers/media/dvb-frontends/lgdt3306a.h\t2019-05-13 20:39:59.778870885 -0400\n@@ -21,6 +21,9 @@\n #include <linux/i2c.h>\n #include \"dvb_frontend.h\"\n \n+#define LG3306_TUNERI2C_ON  0x00\n+#define LG3306_TUNERI2C_OFF 0x80\n+\n enum lgdt3306a_mpeg_mode {\n \tLGDT3306A_MPEG_PARALLEL = 0,\n \tLGDT3306A_MPEG_SERIAL = 1,\n@@ -55,6 +58,10 @@ struct lgdt3306a_config {\n \n \t/* demod clock freq in MHz; 24 or 25 supported */\n \tint  xtalMHz;\n+\n+\t/* returned by driver if using i2c bus multiplexing */\n+\tstruct dvb_frontend **fe;\n+\tstruct i2c_adapter **i2c_adapter;\n };\n \n #if IS_REACHABLE(CONFIG_DVB_LGDT3306A)\n"
  },
  {
    "path": "hauppauge/002-Hauppauge955D-em28xx-Tuner1.patch",
    "content": "diff -uprN linux-4.4.x-orig/Documentation/video4linux/CARDLIST.em28xx linux-4.4.x-new/Documentation/video4linux/CARDLIST.em28xx\n--- linux-4.4.x-orig/Documentation/video4linux/CARDLIST.em28xx\t2017-09-25 22:52:52.000000000 -0400\n+++ linux-4.4.x-new/Documentation/video4linux/CARDLIST.em28xx\t2019-05-04 08:16:31.909682143 -0400\n@@ -96,3 +96,5 @@\n  95 -> Leadtek VC100                            (em2861)        [0413:6f07]\n  96 -> Terratec Cinergy T2 Stick HD             (em28178)\n  97 -> Elgato EyeTV Hybrid 2008 INT             (em2884)        [0fd9:0018]\n+ 98 -> PLEX PX-BCUD                             (em28178)       [3275:0085]\n+ 99 -> Hauppauge WinTV-dualHD DVB               (em28174)       [2040:0265]\ndiff -uprN linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-cards.c linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-cards.c\n--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-cards.c\t2017-10-30 17:46:49.000000000 -0400\n+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-cards.c\t2019-05-04 08:20:03.092511255 -0400\n@@ -492,6 +492,31 @@ static struct em28xx_reg_seq terratec_t2\n };\n \n /*\n+ * 2040:0265 Hauppauge WinTV-dualHD DVB\n+ * 2040:026d Hauppauge WinTV-dualHD ATSC/QAM\n+ * reg 0x80/0x84:\n+ * GPIO_0: Yellow LED tuner 1, 0=on, 1=off\n+ * GPIO_1: Green LED tuner 1, 0=on, 1=off\n+ * GPIO_2: Yellow LED tuner 2, 0=on, 1=off\n+ * GPIO_3: Green LED tuner 2, 0=on, 1=off\n+ * GPIO_5: Reset #2, 0=active\n+ * GPIO_6: Reset #1, 0=active\n+ */\n+static struct em28xx_reg_seq hauppauge_dualhd_dvb[] = {\n+\t{EM2874_R80_GPIO_P0_CTRL,      0xff, 0xff,      0},\n+\t{0x0d,                         0xff, 0xff,    200},\n+\t{0x50,                         0x04, 0xff,    300},\n+\t{EM2874_R80_GPIO_P0_CTRL,      0xbf, 0xff,    100}, /* demod 1 reset */\n+\t{EM2874_R80_GPIO_P0_CTRL,      0xff, 0xff,    100},\n+\t{EM2874_R80_GPIO_P0_CTRL,      0xdf, 0xff,    100}, /* demod 2 reset */\n+\t{EM2874_R80_GPIO_P0_CTRL,      0xff, 0xff,    100},\n+\t{EM2874_R5F_TS_ENABLE,         0x44, 0xff,     50},\n+\t{EM2874_R5D_TS1_PKT_SIZE,      0x05, 0xff,     50},\n+\t{EM2874_R5E_TS2_PKT_SIZE,      0x05, 0xff,     50},\n+\t{-1,                             -1,   -1,     -1},\n+};\n+\n+/*\n  *  Button definitions\n  */\n static struct em28xx_button std_snapshot_button[] = {\n@@ -560,6 +585,22 @@ static struct em28xx_led pctv_80e_leds[]\n \t{-1, 0, 0, 0},\n };\n \n+static struct em28xx_led hauppauge_dualhd_leds[] = {\n+\t{\n+\t\t.role      = EM28XX_LED_DIGITAL_CAPTURING,\n+\t\t.gpio_reg  = EM2874_R80_GPIO_P0_CTRL,\n+\t\t.gpio_mask = EM_GPIO_1,\n+\t\t.inverted  = 1,\n+\t},\n+\t{\n+\t\t.role      = EM28XX_LED_DIGITAL_CAPTURING_TS2,\n+\t\t.gpio_reg  = EM2874_R80_GPIO_P0_CTRL,\n+\t\t.gpio_mask = EM_GPIO_3,\n+\t\t.inverted  = 1,\n+\t},\n+\t{-1, 0, 0, 0},\n+};\n+\n /*\n  *  Board definitions\n  */\n@@ -2288,6 +2329,36 @@ struct em28xx_board em28xx_boards[] = {\n \t\t.has_dvb       = 1,\n \t\t.ir_codes      = RC_MAP_TERRATEC_SLIM_2,\n \t},\n+\t/*\n+\t * 2040:0265 Hauppauge WinTV-dualHD (DVB version).\n+\t * Empia EM28274, 2x Silicon Labs Si2168, 2x Silicon Labs Si2157\n+\t */\n+\t[EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB] = {\n+\t\t.name          = \"Hauppauge WinTV-dualHD DVB\",\n+\t\t.def_i2c_bus   = 1,\n+\t\t.i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE |\n+\t\t\t\t EM28XX_I2C_FREQ_400_KHZ,\n+\t\t.tuner_type    = TUNER_ABSENT,\n+\t\t.tuner_gpio    = hauppauge_dualhd_dvb,\n+\t\t.has_dvb       = 1,\n+\t\t.ir_codes      = RC_MAP_HAUPPAUGE,\n+\t\t.leds          = hauppauge_dualhd_leds,\n+\t},\n+\t/*\n+\t * 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM).\n+\t * Empia EM28274, 2x LG LGDT3306A, 2x Silicon Labs Si2157\n+\t */\n+\t[EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595] = {\n+\t\t.name          = \"Hauppauge WinTV-dualHD 01595 ATSC/QAM\",\n+\t\t.def_i2c_bus   = 1,\n+\t\t.i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE |\n+\t\t\t\t EM28XX_I2C_FREQ_400_KHZ,\n+\t\t.tuner_type    = TUNER_ABSENT,\n+\t\t.tuner_gpio    = hauppauge_dualhd_dvb,\n+\t\t.has_dvb       = 1,\n+\t\t.ir_codes      = RC_MAP_HAUPPAUGE,\n+\t\t.leds          = hauppauge_dualhd_leds,\n+\t},\n };\n EXPORT_SYMBOL_GPL(em28xx_boards);\n \n@@ -2411,6 +2482,10 @@ struct usb_device_id em28xx_id_table[] =\n \t\t\t.driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },\n \t{ USB_DEVICE(0x2040, 0x651f),\n \t\t\t.driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 },\n+\t{ USB_DEVICE(0x2040, 0x0265),\n+\t\t\t.driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB },\n+\t{ USB_DEVICE(0x2040, 0x026d),\n+\t\t\t.driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 },\n \t{ USB_DEVICE(0x0438, 0xb002),\n \t\t\t.driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },\n \t{ USB_DEVICE(0x2001, 0xf112),\n@@ -2804,6 +2879,8 @@ static void em28xx_card_setup(struct em2\n \tcase EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:\n \tcase EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:\n \tcase EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C:\n+\tcase EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB:\n+\tcase EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595:\n \t{\n \t\tstruct tveeprom tv;\n \ndiff -uprN linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-dvb.c linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-dvb.c\n--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-dvb.c\t2017-10-30 17:46:44.000000000 -0400\n+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-dvb.c\t2019-05-04 08:20:03.092511255 -0400\n@@ -36,6 +36,7 @@\n \n #include \"lgdt330x.h\"\n #include \"lgdt3305.h\"\n+#include \"lgdt3306a.h\"\n #include \"zl10353.h\"\n #include \"s5h1409.h\"\n #include \"mt352.h\"\n@@ -858,6 +859,17 @@ static struct tda18271_config pinnacle_8\n \t.role    = TDA18271_MASTER,\n };\n \n+static struct lgdt3306a_config hauppauge_01595_lgdt3306a_config = {\n+\t.qam_if_khz         = 4000,\n+\t.vsb_if_khz         = 3250,\n+\t.spectral_inversion = 0,\n+\t.deny_i2c_rptr      = 0,\n+\t.mpeg_mode          = LGDT3306A_MPEG_SERIAL,\n+\t.tpclk_edge         = LGDT3306A_TPCLK_RISING_EDGE,\n+\t.tpvalid_polarity   = LGDT3306A_TP_VALID_HIGH,\n+\t.xtalMHz            = 25,\n+};\n+\n /* ------------------------------------------------------------------ */\n \n static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev)\n@@ -1717,6 +1729,132 @@ static int em28xx_dvb_init(struct em28xx\n \t\t\tif (!try_module_get(client->dev.driver->owner)) {\n \t\t\t\ti2c_unregister_device(client);\n \t\t\t\tmodule_put(dvb->i2c_client_demod->dev.driver->owner);\n+\t\t\t\ti2c_unregister_device(dvb->i2c_client_demod);\n+\t\t\t\tresult = -ENODEV;\n+\t\t\t\tgoto out_free;\n+\t\t\t}\n+\n+\t\t\tdvb->i2c_client_tuner = client;\n+\t\t}\n+\t\tbreak;\n+\tcase EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB:\n+\t\t{\n+\t\t\tstruct i2c_adapter *adapter;\n+\t\t\tstruct i2c_client *client;\n+\t\t\tstruct i2c_board_info info;\n+\t\t\tstruct si2168_config si2168_config;\n+\t\t\tstruct si2157_config si2157_config;\n+\n+\t\t\t/* attach demod */\n+\t\t\tmemset(&si2168_config, 0, sizeof(si2168_config));\n+\t\t\tsi2168_config.i2c_adapter = &adapter;\n+\t\t\tsi2168_config.fe = &dvb->fe[0];\n+\t\t\tsi2168_config.ts_mode = SI2168_TS_SERIAL;\n+\t\t\tmemset(&info, 0, sizeof(struct i2c_board_info));\n+\t\t\tstrlcpy(info.type, \"si2168\", I2C_NAME_SIZE);\n+\t\t\tinfo.addr = 0x64;\n+\t\t\tinfo.platform_data = &si2168_config;\n+\t\t\trequest_module(info.type);\n+\t\t\tclient = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);\n+\t\t\tif (client == NULL || client->dev.driver == NULL) {\n+\t\t\t\tresult = -ENODEV;\n+\t\t\t\tgoto out_free;\n+\t\t\t}\n+\n+\t\t\tif (!try_module_get(client->dev.driver->owner)) {\n+\t\t\t\ti2c_unregister_device(client);\n+\t\t\t\tresult = -ENODEV;\n+\t\t\t\tgoto out_free;\n+\t\t\t}\n+\n+\t\t\tdvb->i2c_client_demod = client;\n+\n+\t\t\t/* attach tuner */\n+\t\t\tmemset(&si2157_config, 0, sizeof(si2157_config));\n+\t\t\tsi2157_config.fe = dvb->fe[0];\n+\t\t\tsi2157_config.if_port = 1;\n+#ifdef CONFIG_MEDIA_CONTROLLER_DVB\n+\t\t\tsi2157_config.mdev = dev->media_dev;\n+#endif\n+\t\t\tmemset(&info, 0, sizeof(struct i2c_board_info));\n+\t\t\tstrlcpy(info.type, \"si2157\", I2C_NAME_SIZE);\n+\t\t\tinfo.addr = 0x60;\n+\t\t\tinfo.platform_data = &si2157_config;\n+\t\t\trequest_module(info.type);\n+\t\t\tclient = i2c_new_device(adapter, &info);\n+\t\t\tif (client == NULL || client->dev.driver == NULL) {\n+\t\t\t\tmodule_put(dvb->i2c_client_demod->dev.driver->owner);\n+\t\t\t\ti2c_unregister_device(dvb->i2c_client_demod);\n+\t\t\t\tresult = -ENODEV;\n+\t\t\t\tgoto out_free;\n+\t\t\t}\n+\n+\t\t\tif (!try_module_get(client->dev.driver->owner)) {\n+\t\t\t\ti2c_unregister_device(client);\n+\t\t\t\tmodule_put(dvb->i2c_client_demod->dev.driver->owner);\n+\t\t\t\ti2c_unregister_device(dvb->i2c_client_demod);\n+\t\t\t\tresult = -ENODEV;\n+\t\t\t\tgoto out_free;\n+\t\t\t}\n+\n+\t\t\tdvb->i2c_client_tuner = client;\n+\n+\t\t}\n+\t\tbreak;\n+\tcase EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595:\n+\t\t{\n+\t\t\tstruct i2c_adapter *adapter;\n+\t\t\tstruct i2c_client *client;\n+\t\t\tstruct i2c_board_info info = {};\n+\t\t\tstruct lgdt3306a_config lgdt3306a_config;\n+\t\t\tstruct si2157_config si2157_config = {};\n+\n+\t\t\t/* attach demod */\n+\t\t\tlgdt3306a_config = hauppauge_01595_lgdt3306a_config;\n+\t\t\tlgdt3306a_config.fe = &dvb->fe[0];\n+\t\t\tlgdt3306a_config.i2c_adapter = &adapter;\n+\t\t\tstrlcpy(info.type, \"lgdt3306a\", sizeof(info.type));\n+\t\t\tinfo.addr = 0x59;\n+\t\t\tinfo.platform_data = &lgdt3306a_config;\n+\t\t\trequest_module(info.type);\n+\t\t\tclient = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus],\n+\t\t\t\t\t&info);\n+\t\t\tif (client == NULL || client->dev.driver == NULL) {\n+\t\t\t\tresult = -ENODEV;\n+\t\t\t\tgoto out_free;\n+\t\t\t}\n+\n+\t\t\tif (!try_module_get(client->dev.driver->owner)) {\n+\t\t\t\ti2c_unregister_device(client);\n+\t\t\t\tresult = -ENODEV;\n+\t\t\t\tgoto out_free;\n+\t\t\t}\n+\n+\t\t\tdvb->i2c_client_demod = client;\n+\n+\t\t\t/* attach tuner */\n+\t\t\tsi2157_config.fe = dvb->fe[0];\n+\t\t\tsi2157_config.if_port = 1;\n+\t\t\tsi2157_config.inversion = 1;\n+#ifdef CONFIG_MEDIA_CONTROLLER_DVB\n+\t\t\tsi2157_config.mdev = dev->media_dev;\n+#endif\n+\t\t\tmemset(&info, 0, sizeof(struct i2c_board_info));\n+\t\t\tstrlcpy(info.type, \"si2157\", sizeof(info.type));\n+\t\t\tinfo.addr = 0x60;\n+\t\t\tinfo.platform_data = &si2157_config;\n+\t\t\trequest_module(info.type);\n+\n+\t\t\tclient = i2c_new_device(adapter, &info);\n+\t\t\tif (client == NULL || client->dev.driver == NULL) {\n+\t\t\t\tmodule_put(dvb->i2c_client_demod->dev.driver->owner);\n+\t\t\t\ti2c_unregister_device(dvb->i2c_client_demod);\n+\t\t\t\tresult = -ENODEV;\n+\t\t\t\tgoto out_free;\n+\t\t\t}\n+\t\t\tif (!try_module_get(client->dev.driver->owner)) {\n+\t\t\t\ti2c_unregister_device(client);\n+\t\t\t\tmodule_put(dvb->i2c_client_demod->dev.driver->owner);\n \t\t\t\ti2c_unregister_device(dvb->i2c_client_demod);\n \t\t\t\tresult = -ENODEV;\n \t\t\t\tgoto out_free;\ndiff -uprN linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx.h linux-4.4.x-new/drivers/media/usb/em28xx/em28xx.h\n--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx.h\t2017-10-30 17:46:43.000000000 -0400\n+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx.h\t2019-05-04 08:20:03.092511255 -0400\n@@ -145,6 +145,9 @@\n #define EM2861_BOARD_LEADTEK_VC100                95\n #define EM28178_BOARD_TERRATEC_T2_STICK_HD        96\n #define EM2884_BOARD_ELGATO_EYETV_HYBRID_2008     97\n+#define EM28178_BOARD_PLEX_PX_BCUD                98\n+#define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB  99\n+#define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 100\n \n /* Limits minimum and default number of buffers */\n #define EM28XX_MIN_BUF 4\n@@ -412,6 +415,7 @@ enum em28xx_adecoder {\n enum em28xx_led_role {\n \tEM28XX_LED_ANALOG_CAPTURING = 0,\n \tEM28XX_LED_DIGITAL_CAPTURING,\n+\tEM28XX_LED_DIGITAL_CAPTURING_TS2,\n \tEM28XX_LED_ILLUMINATION,\n \tEM28XX_NUM_LED_ROLES, /* must be the last */\n };\ndiff -uprN linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-reg.h linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-reg.h\n--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-reg.h\t2017-10-30 17:46:42.000000000 -0400\n+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-reg.h\t2019-05-04 08:15:10.896596159 -0400\n@@ -193,6 +193,19 @@\n /* em2874 registers */\n #define EM2874_R50_IR_CONFIG    0x50\n #define EM2874_R51_IR           0x51\n+#define EM2874_R5D_TS1_PKT_SIZE 0x5d\n+#define EM2874_R5E_TS2_PKT_SIZE 0x5e\n+\t/*\n+\t * For both TS1 and TS2, In isochronous mode:\n+\t *  0x01  188 bytes\n+\t *  0x02  376 bytes\n+\t *  0x03  564 bytes\n+\t *  0x04  752 bytes\n+\t *  0x05  940 bytes\n+\t * In bulk mode:\n+\t *  0x01..0xff  total packet count in 188-byte\n+\t */\n+\n #define EM2874_R5F_TS_ENABLE    0x5f\n \n /* em2874/174/84, em25xx, em276x/7x/8x GPIO registers */\n"
  },
  {
    "path": "hauppauge/003-Hauppauge955D-em28xx-Tuner2-v6.patch",
    "content": "diff -uprN linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-cards.c linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-cards.c\n--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-cards.c\t2019-05-06 20:53:45.596707923 -0400\n+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-cards.c\t2019-05-06 20:55:37.646304153 -0400\n@@ -2341,6 +2341,7 @@ struct em28xx_board em28xx_boards[] = {\n \t\t.tuner_type    = TUNER_ABSENT,\n \t\t.tuner_gpio    = hauppauge_dualhd_dvb,\n \t\t.has_dvb       = 1,\n+\t\t.has_dual_ts   = 1,\n \t\t.ir_codes      = RC_MAP_HAUPPAUGE,\n \t\t.leds          = hauppauge_dualhd_leds,\n \t},\n@@ -2356,6 +2357,7 @@ struct em28xx_board em28xx_boards[] = {\n \t\t.tuner_type    = TUNER_ABSENT,\n \t\t.tuner_gpio    = hauppauge_dualhd_dvb,\n \t\t.has_dvb       = 1,\n+\t\t.has_dual_ts   = 1,\n \t\t.ir_codes      = RC_MAP_HAUPPAUGE,\n \t\t.leds          = hauppauge_dualhd_leds,\n \t},\n@@ -3090,6 +3092,8 @@ static void flush_request_modules(struct\n */\n static void em28xx_release_resources(struct em28xx *dev)\n {\n+\tstruct usb_device *udev;\n+\t\n \t/*FIXME: I2C IR should be disconnected */\n \n \tmutex_lock(&dev->lock);\n@@ -3098,7 +3102,8 @@ static void em28xx_release_resources(str\n \t\tem28xx_i2c_unregister(dev, 1);\n \tem28xx_i2c_unregister(dev, 0);\n \n-\tusb_put_dev(dev->udev);\n+\tif (dev->ts == PRIMARY_TS)\n+\t\tusb_put_dev(udev);\n \n \t/* Mark device as unused */\n \tclear_bit(dev->devno, em28xx_devused);\n@@ -3297,6 +3302,35 @@ static int em28xx_init_dev(struct em28xx\n \treturn 0;\n }\n \n+int em28xx_duplicate_dev(struct em28xx *dev)\n+{\n+\tint nr;\n+\tstruct em28xx *sec_dev = kzalloc(sizeof(*sec_dev), GFP_KERNEL);\n+\n+\tif (sec_dev == NULL) {\n+\t\tdev->dev_next = NULL;\n+\t\treturn -ENOMEM;\n+\t}\n+\tmemcpy(sec_dev, dev, sizeof(sizeof(*sec_dev)));\n+\t/* Check to see next free device and mark as used */\n+\tdo {\n+\t\tnr = find_first_zero_bit(em28xx_devused, EM28XX_MAXBOARDS);\n+\t\tif (nr >= EM28XX_MAXBOARDS) {\n+\t\t\t/* No free device slots */\n+\t\t\tdev_warn(&dev->udev->dev, \": Supports only %i em28xx boards.\\n\",\n+\t\t\t\t\tEM28XX_MAXBOARDS);\n+\t\t\tkfree(sec_dev);\n+\t\t\tdev->dev_next = NULL;\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\t} while (test_and_set_bit(nr, em28xx_devused));\n+\tsec_dev->devno = nr;\n+\tsnprintf(sec_dev->name, 28, \"em28xx #%d\", nr);\n+\tsec_dev->dev_next = NULL;\n+\tdev->dev_next = sec_dev;\n+\treturn 0;\n+}\n+\n /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */\n #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))\n \n@@ -3417,6 +3451,17 @@ static int em28xx_usb_probe(struct usb_i\n \t\t\t\t\t\t}\n \t\t\t\t\t}\n \t\t\t\t\tbreak;\n+\t\t\t\tcase 0x85:\n+\t\t\t\t\tif (usb_endpoint_xfer_isoc(e)) {\n+\t\t\t\t\t\tif (size > dev->dvb_max_pkt_size_isoc_ts2) {\n+\t\t\t\t\t\t\tdev->dvb_ep_isoc_ts2 = e->bEndpointAddress;\n+\t\t\t\t\t\t\tdev->dvb_max_pkt_size_isoc_ts2 = size;\n+\t\t\t\t\t\t\tdev->dvb_alt_isoc = i;\n+\t\t\t\t\t\t}\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\tdev->dvb_ep_bulk_ts2 = e->bEndpointAddress;\n+\t\t\t\t\t}\n+\t\t\t\t\tbreak;\n \t\t\t\t}\n \t\t\t}\n \t\t\t/* NOTE:\n@@ -3431,6 +3476,8 @@ static int em28xx_usb_probe(struct usb_i\n \t\t\t *  0x83\tisoc*\t\t=> audio\n \t\t\t *  0x84\tisoc\t\t=> digital\n \t\t\t *  0x84\tbulk\t\t=> analog or digital**\n+\t\t\t *  0x85\tisoc\t\t=> digital TS2\n+\t\t\t *  0x85\tbulk\t\t=> digital TS2\n \t\t\t * (*: audio should always be isoc)\n \t\t\t * (**: analog, if ep 0x82 is isoc, otherwise digital)\n \t\t\t *\n@@ -3499,6 +3546,10 @@ static int em28xx_usb_probe(struct usb_i\n \tdev->has_video = has_video;\n \tdev->ifnum = ifnum;\n \n+\tdev->ts = PRIMARY_TS;\n+\tsnprintf(dev->name, 28, \"em28xx\");\n+\tdev->dev_next = NULL;\n+\n \tif (has_vendor_audio) {\n \t\tprintk(KERN_INFO DRIVER_NAME \": Audio interface %i found %s\\n\",\n \t\t       ifnum, \"(Vendor Class)\");\n@@ -3568,6 +3619,65 @@ static int em28xx_usb_probe(struct usb_i\n \t\t\t    dev->dvb_xfer_bulk ? \"bulk\" : \"isoc\");\n \t}\n \n+\tif (dev->board.has_dual_ts && em28xx_duplicate_dev(dev) == 0) {\n+\t\tdev->dev_next->ts = SECONDARY_TS;\n+\t\tdev->dev_next->alt   = -1;\n+\t\tdev->dev_next->is_audio_only = has_vendor_audio &&\n+\t\t\t\t\t\t!(has_video || has_dvb);\n+\t\tdev->dev_next->has_video = false;\n+\t\tdev->dev_next->ifnum = ifnum;\n+\t\tdev->dev_next->model = id->driver_info;\n+\n+\t\tmutex_init(&dev->dev_next->lock);\n+\t\tretval = em28xx_init_dev(dev->dev_next, udev, interface,\n+\t\t\t\t\tdev->dev_next->devno);\n+\t\tif (retval)\n+\t\t\tgoto err_free;\n+\n+\t\tdev->dev_next->board.ir_codes = NULL; /* No IR for 2nd tuner */\n+\t\tdev->dev_next->board.has_ir_i2c = 0; /* No IR for 2nd tuner */\n+\n+\t\tif (usb_xfer_mode < 0) {\n+\t\t\tif (dev->dev_next->board.is_webcam)\n+\t\t\t\ttry_bulk = 1;\n+\t\t\telse\n+\t\t\t\ttry_bulk = 0;\n+\t\t} else {\n+\t\t\ttry_bulk = usb_xfer_mode > 0;\n+\t\t}\n+\n+\t\t/* Select USB transfer types to use */\n+\t\tif (has_dvb) {\n+\t\t\tif (!dev->dvb_ep_isoc_ts2 ||\n+\t\t\t   (try_bulk && dev->dvb_ep_bulk_ts2))\n+\t\t\t\tdev->dev_next->dvb_xfer_bulk = 1;\n+\t\t\tprintk(KERN_INFO DRIVER_NAME \": dvb ts2 set to %s mode.\\n\",\n+\t\t\t\tdev->dev_next->dvb_xfer_bulk ? \"bulk\" : \"isoc\");\n+\t\t}\n+\n+\t\tdev->dev_next->dvb_ep_isoc = dev->dvb_ep_isoc_ts2;\n+\t\tdev->dev_next->dvb_ep_bulk = dev->dvb_ep_bulk_ts2;\n+\t\tdev->dev_next->dvb_max_pkt_size_isoc = dev->dvb_max_pkt_size_isoc_ts2;\n+\t\tdev->dev_next->dvb_alt_isoc = dev->dvb_alt_isoc;\n+\n+\t\t/* Configuare hardware to support TS2*/\n+\t\tif (dev->dvb_xfer_bulk) {\n+\t\t\t/* The ep4 and ep5 are configuared for BULK */\n+\t\t\tem28xx_write_reg(dev, 0x0b, 0x96);\n+\t\t\tmdelay(100);\n+\t\t\tem28xx_write_reg(dev, 0x0b, 0x80);\n+\t\t\tmdelay(100);\n+\t\t} else {\n+\t\t\t/* The ep4 and ep5 are configuared for ISO */\n+\t\t\tem28xx_write_reg(dev, 0x0b, 0x96);\n+\t\t\tmdelay(100);\n+\t\t\tem28xx_write_reg(dev, 0x0b, 0x82);\n+\t\t\tmdelay(100);\n+\t\t}\n+\n+\t\tkref_init(&dev->dev_next->ref);\n+\t}\n+\n \tkref_init(&dev->ref);\n \n \trequest_modules(dev);\n@@ -3605,6 +3715,13 @@ static void em28xx_usb_disconnect(struct\n \tif (!dev)\n \t\treturn;\n \n+\tif (dev->dev_next != NULL) {\n+\t\tdev->dev_next->disconnected = 1;\n+\t\tprintk(KERN_INFO DRIVER_NAME \": Disconnecting %s\\n\",\n+\t\t\tdev->dev_next->name);\n+\t\tflush_request_modules(dev->dev_next);\n+\t}\n+\n \tdev->disconnected = 1;\n \n \tem28xx_info(\"Disconnecting %s\\n\", dev->name);\n@@ -3613,7 +3730,14 @@ static void em28xx_usb_disconnect(struct\n \n \tem28xx_close_extension(dev);\n \n+\tif (dev->dev_next != NULL)\n+\t\tem28xx_release_resources(dev->dev_next);\n \tem28xx_release_resources(dev);\n+\t\n+\tif (dev->dev_next != NULL) {\n+\t\tkref_put(&dev->dev_next->ref, em28xx_free_device);\n+\t\tdev->dev_next = NULL;\n+\t}\n \tkref_put(&dev->ref, em28xx_free_device);\n }\n \ndiff -uprN linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-core.c linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-core.c\n--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-core.c\t2017-10-30 17:46:43.000000000 -0400\n+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-core.c\t2019-05-06 20:55:37.642304096 -0400\n@@ -636,10 +636,18 @@ int em28xx_capture_start(struct em28xx *\n \t    dev->chip_id == CHIP_ID_EM28174 ||\n \t    dev->chip_id == CHIP_ID_EM28178) {\n \t\t/* The Transport Stream Enable Register moved in em2874 */\n-\t\trc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,\n-\t\t\t\t\t   start ?\n-\t\t\t\t\t       EM2874_TS1_CAPTURE_ENABLE : 0x00,\n-\t\t\t\t\t   EM2874_TS1_CAPTURE_ENABLE);\n+\t\tif (dev->ts == PRIMARY_TS)\n+\t\t\trc = em28xx_write_reg_bits(dev,\n+\t\t\t\tEM2874_R5F_TS_ENABLE,\n+\t\t\t\tstart ?\n+\t\t\t\tEM2874_TS1_CAPTURE_ENABLE : 0x00,\n+\t\t\t\tEM2874_TS1_CAPTURE_ENABLE);\n+\t\telse\n+\t\t\trc = em28xx_write_reg_bits(dev,\n+\t\t\t\tEM2874_R5F_TS_ENABLE,\n+\t\t\t\tstart ?\n+\t\t\t\tEM2874_TS2_CAPTURE_ENABLE : 0x00,\n+\t\t\t\tEM2874_TS2_CAPTURE_ENABLE);\n \t} else {\n \t\t/* FIXME: which is the best order? */\n \t\t/* video registers are sampled by VREF */\n@@ -1073,7 +1081,11 @@ int em28xx_register_extension(struct em2\n \tmutex_lock(&em28xx_devlist_mutex);\n \tlist_add_tail(&ops->next, &em28xx_extension_devlist);\n \tlist_for_each_entry(dev, &em28xx_devlist, devlist) {\n-\t\tops->init(dev);\n+\t\tif (ops->init) {\n+\t\t\tops->init(dev);\n+\t\t\tif (dev->dev_next != NULL)\n+\t\t\t\tops->init(dev->dev_next);\n+\t\t}\n \t}\n \tmutex_unlock(&em28xx_devlist_mutex);\n \tprintk(KERN_INFO \"em28xx: Registered (%s) extension\\n\", ops->name);\n@@ -1087,7 +1099,11 @@ void em28xx_unregister_extension(struct\n \n \tmutex_lock(&em28xx_devlist_mutex);\n \tlist_for_each_entry(dev, &em28xx_devlist, devlist) {\n-\t\tops->fini(dev);\n+\t\tif (ops->fini) {\n+\t\t\tif (dev->dev_next != NULL)\n+\t\t\t\tops->fini(dev->dev_next);\n+\t\t\tops->fini(dev);\n+\t\t}\n \t}\n \tlist_del(&ops->next);\n \tmutex_unlock(&em28xx_devlist_mutex);\n@@ -1102,8 +1118,11 @@ void em28xx_init_extension(struct em28xx\n \tmutex_lock(&em28xx_devlist_mutex);\n \tlist_add_tail(&dev->devlist, &em28xx_devlist);\n \tlist_for_each_entry(ops, &em28xx_extension_devlist, next) {\n-\t\tif (ops->init)\n+\t\tif (ops->init) {\n \t\t\tops->init(dev);\n+\t\t\tif (dev->dev_next != NULL)\n+\t\t\t\tops->init(dev->dev_next);\n+\t\t}\n \t}\n \tmutex_unlock(&em28xx_devlist_mutex);\n }\n@@ -1114,8 +1133,11 @@ void em28xx_close_extension(struct em28x\n \n \tmutex_lock(&em28xx_devlist_mutex);\n \tlist_for_each_entry(ops, &em28xx_extension_devlist, next) {\n-\t\tif (ops->fini)\n+\t\tif (ops->fini) {\n+\t\t\tif (dev->dev_next != NULL)\n+\t\t\t\tops->fini(dev->dev_next);\n \t\t\tops->fini(dev);\n+\t\t}\n \t}\n \tlist_del(&dev->devlist);\n \tmutex_unlock(&em28xx_devlist_mutex);\n@@ -1130,6 +1152,8 @@ int em28xx_suspend_extension(struct em28\n \tlist_for_each_entry(ops, &em28xx_extension_devlist, next) {\n \t\tif (ops->suspend)\n \t\t\tops->suspend(dev);\n+\t\tif (dev->dev_next != NULL)\n+\t\t\tops->suspend(dev->dev_next);\n \t}\n \tmutex_unlock(&em28xx_devlist_mutex);\n \treturn 0;\n@@ -1144,6 +1168,8 @@ int em28xx_resume_extension(struct em28x\n \tlist_for_each_entry(ops, &em28xx_extension_devlist, next) {\n \t\tif (ops->resume)\n \t\t\tops->resume(dev);\n+\t\tif (dev->dev_next != NULL)\n+\t\t\tops->resume(dev->dev_next);\n \t}\n \tmutex_unlock(&em28xx_devlist_mutex);\n \treturn 0;\ndiff -uprN linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-dvb.c linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-dvb.c\n--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-dvb.c\t2019-05-06 20:53:45.600707980 -0400\n+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-dvb.c\t2019-05-06 20:55:37.646304153 -0400\n@@ -212,7 +212,6 @@ static int em28xx_start_streaming(struct\n \t\tdvb_alt = dev->dvb_alt_isoc;\n \t}\n \n-\tusb_set_interface(dev->udev, dev->ifnum, dvb_alt);\n \trc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);\n \tif (rc < 0)\n \t\treturn rc;\n@@ -1042,8 +1041,9 @@ static void em28xx_unregister_dvb(struct\n \n static int em28xx_dvb_init(struct em28xx *dev)\n {\n-\tint result = 0;\n+\tint result = 0, dvb_alt = 0;\n \tstruct em28xx_dvb *dvb;\n+\t/* struct usb_device *udev; */\n \n \tif (dev->is_audio_only) {\n \t\t/* Shouldn't initialize IR for this interface */\n@@ -1631,7 +1631,10 @@ static int em28xx_dvb_init(struct em28xx\n \t\t\tsi2168_config.ts_mode = SI2168_TS_PARALLEL;\n \t\t\tmemset(&info, 0, sizeof(struct i2c_board_info));\n \t\t\tstrlcpy(info.type, \"si2168\", I2C_NAME_SIZE);\n-\t\t\tinfo.addr = 0x64;\n+\t\t\tif (dev->ts == PRIMARY_TS)\n+\t\t\t\tinfo.addr = 0x64;\n+\t\t\telse\n+\t\t\t\tinfo.addr = 0x67;\n \t\t\tinfo.platform_data = &si2168_config;\n \t\t\trequest_module(info.type);\n \t\t\tclient = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);\n@@ -1654,7 +1657,10 @@ static int em28xx_dvb_init(struct em28xx\n \t\t\tsi2157_config.if_port = 1;\n \t\t\tmemset(&info, 0, sizeof(struct i2c_board_info));\n \t\t\tstrlcpy(info.type, \"si2157\", I2C_NAME_SIZE);\n-\t\t\tinfo.addr = 0x60;\n+\t\t\tif (dev->ts == PRIMARY_TS)\n+\t\t\t\tinfo.addr = 0x60;\n+\t\t\telse\n+\t\t\t\tinfo.addr = 0x63;\n \t\t\tinfo.platform_data = &si2157_config;\n \t\t\trequest_module(info.type);\n \t\t\tclient = i2c_new_device(adapter, &info);\n@@ -1814,7 +1820,10 @@ static int em28xx_dvb_init(struct em28xx\n \t\t\tlgdt3306a_config.fe = &dvb->fe[0];\n \t\t\tlgdt3306a_config.i2c_adapter = &adapter;\n \t\t\tstrlcpy(info.type, \"lgdt3306a\", sizeof(info.type));\n-\t\t\tinfo.addr = 0x59;\n+\t\t\tif (dev->ts == PRIMARY_TS)\n+\t\t\t\tinfo.addr = 0x59;\n+\t\t\telse\n+\t\t\t\tinfo.addr = 0x0e;\n \t\t\tinfo.platform_data = &lgdt3306a_config;\n \t\t\trequest_module(info.type);\n \t\t\tclient = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus],\n@@ -1841,7 +1850,10 @@ static int em28xx_dvb_init(struct em28xx\n #endif\n \t\t\tmemset(&info, 0, sizeof(struct i2c_board_info));\n \t\t\tstrlcpy(info.type, \"si2157\", sizeof(info.type));\n-\t\t\tinfo.addr = 0x60;\n+\t\t\tif (dev->ts == PRIMARY_TS)\n+\t\t\t\tinfo.addr = 0x60;\n+\t\t\telse\n+\t\t\t\tinfo.addr = 0x62;\n \t\t\tinfo.platform_data = &si2157_config;\n \t\t\trequest_module(info.type);\n \n@@ -1884,6 +1896,15 @@ static int em28xx_dvb_init(struct em28xx\n \tif (result < 0)\n \t\tgoto out_free;\n \n+\tif (dev->dvb_xfer_bulk) {\n+\t\tdvb_alt = 0;\n+\t} else { /* isoc */\n+\t\tdvb_alt = dev->dvb_alt_isoc;\n+\t}\n+\n+\t/* udev = interface_to_usbdev(dev->intf); */\n+\tusb_set_interface(dev->udev, dev->ifnum, dvb_alt);\n+\t/* usb_set_interface(udev, dev->ifnum, dvb_alt); */\n \tem28xx_info(\"DVB extension successfully initialized\\n\");\n \n \tkref_get(&dev->ref);\ndiff -uprN linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx.h linux-4.4.x-new/drivers/media/usb/em28xx/em28xx.h\n--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx.h\t2019-05-06 20:53:45.600707980 -0400\n+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx.h\t2019-05-06 20:55:37.646304153 -0400\n@@ -216,6 +216,9 @@\n /* max. number of button state polling addresses */\n #define EM28XX_NUM_BUTTON_ADDRESSES_MAX\t\t5\n \n+#define PRIMARY_TS\t0\n+#define SECONDARY_TS\t1\n+\n enum em28xx_mode {\n \tEM28XX_SUSPEND,\n \tEM28XX_ANALOG_MODE,\n@@ -462,6 +465,7 @@ struct em28xx_board {\n \tunsigned int mts_firmware:1;\n \tunsigned int max_range_640_480:1;\n \tunsigned int has_dvb:1;\n+\tunsigned int has_dual_ts:1;\n \tunsigned int is_webcam:1;\n \tunsigned int valid:1;\n \tunsigned int has_ir_i2c:1;\n@@ -612,7 +616,6 @@ struct em28xx {\n \tstruct em28xx_IR *ir;\n \n \t/* generic device properties */\n-\tchar name[30];\t\t/* name (including minor) of the device */\n \tint model;\t\t/* index in the device_data struct */\n \tint devno;\t\t/* marks the number of this device */\n \tenum em28xx_chip_id chip_id;\n@@ -623,6 +626,7 @@ struct em28xx {\n \tunsigned int is_audio_only:1;\n \tenum em28xx_int_audio_type int_audio_type;\n \tenum em28xx_usb_audio_type usb_audio_type;\n+\tunsigned char name[32];\n \n \tstruct em28xx_board board;\n \n@@ -684,6 +688,8 @@ struct em28xx {\n \tu8 ifnum;\t\t/* number of the assigned usb interface */\n \tu8 analog_ep_isoc;\t/* address of isoc endpoint for analog */\n \tu8 analog_ep_bulk;\t/* address of bulk endpoint for analog */\n+\tu8 dvb_ep_isoc_ts2;\t/* address of isoc endpoint for DVB TS2*/\n+\tu8 dvb_ep_bulk_ts2;\t/* address of bulk endpoint for DVB TS2*/\n \tu8 dvb_ep_isoc;\t\t/* address of isoc endpoint for DVB */\n \tu8 dvb_ep_bulk;\t\t/* address of bulk endpoint for DVB */\n \tint alt;\t\t/* alternate setting */\n@@ -697,6 +703,8 @@ struct em28xx {\n \tint dvb_alt_isoc;\t/* alternate setting for DVB isoc transfers */\n \tunsigned int dvb_max_pkt_size_isoc;\t/* isoc max packet size of the\n \t\t\t\t\t\t   selected DVB ep at dvb_alt */\n+\tunsigned int dvb_max_pkt_size_isoc_ts2;\t/* isoc max packet size of the\n+\t\t\t\t\t\t   selected DVB ep at dvb_alt */\n \tunsigned int dvb_xfer_bulk:1;\t\t/* use bulk instead of isoc\n \t\t\t\t\t\t   transfers for DVB          */\n \tchar urb_buf[URB_MAX_CTRL_SIZE];\t/* urb control msg buffer */\n@@ -722,6 +730,9 @@ struct em28xx {\n \t/* Snapshot button input device */\n \tchar snapshot_button_path[30];\t/* path of the input dev */\n \tstruct input_dev *sbutton_input_dev;\n+\n+\tstruct em28xx   *dev_next;\n+\tint ts;\n };\n \n #define kref_to_dev(d) container_of(d, struct em28xx, ref)\n"
  },
  {
    "path": "hauppauge.sh",
    "content": "#!/bin/sh\n\n#-------------------------------------------\n# hauppauge.sh\n#\n# by: Vincent Fortier\n# email: th0ma7_AT_gmail.com\n#\n# Simplify the status, start, stop, reset\n# of tvheadend service along with loading\n# and unloading of all necessary modules\n#-------------------------------------------\n\nSERVICE=\"pkgctl-tvheadend\"\nMODULE_PATH=/usr/local/lib/modules/$(uname -r)\nMODULES=\"em28xx_dvb em28xx lgdt3306a si2157 tveeprom v4l2_common dvb_usb rc_core dvb_core videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_common videodev media\"\nRESET=em28xx_dvb\n#\ndeclare -a SYSCTL_VAR=('vm.dirty_expire_centisecs' 'vm.swappiness')\ndeclare -a SYSCTL_VALUE=('300' '1')\n#\nNOAUTOSUSPEND=em28xx\ndeclare -a AUTOSUSPEND_VAR=('autosuspend_delay_ms' 'autosuspend')\ndeclare -a AUTOSUSPEND_VALUE=('-1000' '-1')\n\nUSBAutoSuspend() {\n   usbID=$(lsusb -i | grep $NOAUTOSUSPEND | awk '{print $1}' | cut -f1 -d:)\n   if [ ! \"$usbID\" ]; then\n      echo \"kernel USB (none) autosuspend values  N/A\"\n   else\n      echo \"kernel USB ($usbID) autosuspend values...\"\n      for index in \"${!AUTOSUSPEND_VAR[@]}\"\n      do\n         declare sys=/sys/bus/usb/devices/$usbID/power/${AUTOSUSPEND_VAR[$index]}\n         declare -i current=$(cat $sys)\n         declare -i new=${AUTOSUSPEND_VALUE[$index]}\n\n         printf '\\t(%s)%-25s' $usbID ${AUTOSUSPEND_VAR[$index]}\n\n         if [ $current -eq $new ]; then\n            printf '[%5s] -> OK\\n' \"$current\"\n         else\n            [ \"$1\" = \"check\" ] || echo ${AUTOSUSPEND_VALUE[$index]} | sudo tee $sys 1>/dev/null\n            printf '[%5s] -> [%5s]\\n' \"$current\" \"$new\"\n         fi\n     done\n   fi\n}\n\nSysctl() {\n   echo \"kernel sysctl values... \"\n   for index in \"${!SYSCTL_VAR[@]}\"\n   do\n      declare -i current=$(sysctl -n ${SYSCTL_VAR[$index]})\n      declare -i new=${SYSCTL_VALUE[$index]}\n\n      printf '\\t%-30s' ${SYSCTL_VAR[$index]}\n      if [ $current -eq $new ]; then\n         printf '[%5s] -> OK\\n' \"$current\"\n      else\n         [ \"$1\" = \"check\" ] || sysctl -w ${SYSCTL_VAR[$index]}=$new 2>/dev/null 1>&2\n         printf '[%5s] -> [%5s]\\n' \"$current\" \"$new\"\n      fi\n   done\n}\n\nModuleLOAD() {\n   echo \"Loading kernel modules... \"\n   for item in $MODULES; do echo $item; done | tac | while read module\n   do\n      module_load=$(echo \"${module}.ko\" | sed 's/_/-/g')\n      printf '\\t%-30s' $module_load\n      status=$(lsmod | grep \"^$module \")\n\n      if [ $? -eq 0 -a \"status\" ]; then\n         echo \"Loaded\"\n      else\n         insmod $MODULE_PATH/$module_load\n         [ $? -eq 0 ] && echo \"OK\" || echo \"ERROR\"\n      fi\n   done\n}\n\nModuleUNLOAD() {\n   # Unload Hauppauge updated drivers\n   echo \"Unloading kernel modules... \"\n   for module in $MODULES\n   do\n      printf '\\t%-30s' $module\n\n      status=$(lsmod | grep \"^$module \")\n      if [ $? -eq 0 -a \"status\" ]; then\n         rmmod $module\n         echo -ne \"OK\\n\"\n      else\n         echo -ne \"N/A\\n\"\n      fi\n   done\n}\n\nModuleSTATUS() {\n   # Unload Hauppauge updated drivers\n   echo \"kernel module status... \"\n   for module in $MODULES\n   do\n      printf '\\t%-30s' $module\n\n      status=$(lsmod | grep \"^$module \")\n      if [ $? -eq 0 -a \"status\" ]; then\n         echo -ne \"OK\\n\"\n      else\n         echo -ne \"N/A\\n\"\n      fi\n   done\n}\n\nModuleRESET() {\n   reset=$1\n   module=$(echo \"${reset}.ko\" | sed 's/_/-/g')\n   rmmod $reset\n   sleep 1\n   insmod $MODULE_PATH/$module\n}\n\nServiceSTATUS() {\n   status=$(synoservice --status $SERVICE | grep \"\\[$SERVICE\\] status\" | awk -F\"status=\" '{print $2}' | sed -e 's/\\[//g' -e 's/\\]//g')\n   running=$(synoservice --status $SERVICE | grep \"\\[$SERVICE\\] is\" | awk -F\"is \" '{print $2}' | sed -e 's/\\.//g' -e 's/ //g')\n   pid=$(pidof tvheadend)\n   [ ! \"$pid\" ] && pid=\"---\"\n\n   case \"$1\" in\n      \"full\" ) printf '%-38s' \"Status $SERVICE...\"\n               echo $status,$running,$pid\n               ;;\n           * ) echo $running;;\n   esac\n}\n\nServiceSTART() {\n   status=$(ServiceSTATUS)\n   #echo $status 1>&2\n   printf '%-38s' \"Starting $SERVICE...\"\n\n   module=$(lsmod | grep \"^$RESET \")\n   if [ $? -ne 0 -a ! \"module\" ]; then\n      echo \"ERROR module $RESET not found!\"\n      return\n   fi\n\n   if [ \"$status\" = \"stop\" ]; then\n      synoservice --start $SERVICE\n   echo \"OK\"\n   elif [ \"$status\" = \"start\" ]; then\n      # Check if sevice isn't already started!\n      if [ \"$(pidof tvheadend)\" ]; then\n         echo \"Started\"\n      else\n         synoservice --restart $SERVICE 2>/dev/null 1>&2\n         echo \"Restart\"\n      fi\n   else\n      echo \"N/A\"\n   fi\n}\n\nServiceSTOP() {\n   status=$(ServiceSTATUS)\n   #echo $status 1>&2\n   printf '%-38s' \"Stopping $SERVICE...\"\n   if [ \"$status\" = \"start\" ]; then\n      #synoservice --disable $SERVICE\n      synoservice --stop $SERVICE 2>/dev/null 1>&2\n      echo \"OK\"\n   else\n      echo \"N/A\"\n   fi\n\n   # Is it really off?\n   if [ \"`pidof tvheadend`\" ]; then\n      printf '%-33s' \"Killing $SERVICE...\"\n      kill -9 $(pidof tvheadend)\n      echo \"killed\"\n   fi\n}\n\nUsage() {\n      echo \"$0 - Usage: start, stop, status, reset, restart, load\"\n}\n\ncase $1 in\n   start ) ModuleLOAD\n           USBAutoSuspend\n           Sysctl\n           ServiceSTART\n           ;;\n    stop ) ServiceSTOP\n           ModuleUNLOAD\n           ;;\n  status ) ServiceSTATUS full\n           ModuleSTATUS\n           USBAutoSuspend check\n           Sysctl check\n           ;;\n restart ) ServiceSTOP\n           ModuleUNLOAD\n           sleep 1\n           ModuleLOAD\n           USBAutoSuspend\n           Sysctl\n           ServiceSTART\n           ;;\n   reset ) ServiceSTOP\n           ModuleRESET $RESET\n           ServiceSTART\n           ServiceSTATUS full\n           ModuleSTATUS\n           ;;\n    load ) ModuleLOAD\n           USBAutoSuspend\n           Sysctl\n           ;;\n       * ) Usage\n           ;;\nesac\n\nexit 0\n"
  },
  {
    "path": "kernel/synocli-kernelmodule.sh",
    "content": "#!/bin/bash\n\n#########################################################################\n# Written by: th0ma7@gmail.com\n# Part of SynoCommunity Developpers\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <https://www.gnu.org/licenses/>.\n#########################################################################\n\n#------------------------------------------------\n# Make sure an argument was passed \n#------------------------------------------------\nusage() {\n   echo\n   printf '%10s %s\\n' \"Usage :\" \"$0 [-s|--spk <package>] [<insmod,start|rmmod,stop|reload,restart|status>] module1.ko module2.ko ...\"\n   printf '%20s %s\\n' \"Optional :\" \"[-c|--config <file>:<option1>,<option2>,...]\"\n   printf '%20s %s\\n' \"\" \"[-u|--udev <file>]\"\n   echo\n   printf '%40s %s\\n' \"[-s|--spk <package>] : \" \"SynoCommunity package name containing kernel modules\"\n   printf '%40s %s\\n' \"[<insmod|rmmod|reload|status>] : \" \"Action to be performed\"\n   printf '%40s %s\\n' \"[-h|--help] : \" \"Print this help\"\n   printf '%40s %s\\n' \"[-v|--verbose] : \" \"Verbose mode\"\n   echo\n   printf '%10s %s\\n' \"\" \"Examples :\"\n   printf '%20s %s\\n' \"\" \"$0 --spk synokernel-cdrom --verbose cdrom sr_mod status\"\n   printf '%20s %s\\n' \"\" \"$0 --spk synokernel-cdrom --config synokernel-cdrom.cfg:default status\"\n   printf '%20s %s\\n' \"\" \"$0 --spk synokernel-usbserial --udev 60-synokernel-usbserial.rules usbserial ch341 cp210x status\"\n   printf '%20s %s\\n' \"\" \"$0 --spk synokernel-usbserial --config synokernel-usbserial.cfg:ch341,cp210x status\"\n   echo\n}\n\n#------------------------------------------------\n# Print detailed information for debugging\n#------------------------------------------------\nverbose() {\n   printf '%20s %s\\n' \"\" \"$0 (verbose)\"\n   printf '%60s %s\\n' \"SynoCommunity kernel driver package name (SPK)\" \"[${SPK}]\"\n   printf '%60s %s\\n' \"SynoCommunity configuration file (SPK_CFG)\" \"[${SPK_CFG}]\"\n   printf '%60s %s\\n' \"SynoCommunity configuration path (SPK_CFG_PATH)\" \"[${SPK_CFG_PATH}]\"\n   printf '%60s %s\\n' \"SynoCommunity configuration option (SPK_CFG_OPT)\" \"[${SPK_CFG_OPT}]\"\n   printf '%60s %s\\n' \"Synology NAS arch (ARCH)\" \"[${ARCH}]\"\n   printf '%60s %s\\n' \"Synology DSM version (DSM_VERSION)\" \"[${DSM_VERSION}]\"\n   printf '%60s %s\\n' \"Running kernel version (KVER)\" \"[${KVER}]\"\n   printf '%60s %s\\n' \"Module action insmod|rmmod|reload|status (ACTION)\" \"[${ACTION}]\"\n   printf '%60s %s\\n' \"Kernel modules path (MPATH)\" \"[${MPATH}]\"\n   printf '%60s %s\\n' \"Full kernel modules path (KPATH)\" \"[${KPATH}]\"\n   printf '%60s %s\\n' \"Device firmware path (FPATH)\" \"[${FPATH}]\"\n   printf '%60s %s\\n' \"udev rules.d path (UPATH)\" \"[${UPATH}]\"\n   printf '%60s %s\\n' \"udev rules.d file (URULE)\" \"[${URULE}]\"\n   printf '%60s %s\\n' \"Kernel objects list (KO_LIST)\" \"[${KO_LIST}]\"\n   printf '%60s %s\\n' \"Kernel objects found (KO_PATH)\" \"[${KO_PATH}]\"\n}\n\n\n#------------------------------------------------\n# Get all kernel modules from configuration file\n# based on passed configuration option\n#------------------------------------------------\nget_ko_list() {\n   ko_list_cfg=\"\"\n\n   if [ \"${SPK_CFG}\" ]; then\n      # Check that configuration exists (if requested)\n      if [ ! -f ${SPK_CFG_PATH}/${SPK_CFG} ]; then\n         usage\n         echo -ne \"\\nERROR: Configuration file [${SPK_CFG_PATH}/${SPK_CFG}] does not exist or inaccessible...\\n\\n\"\n         exit 1\n      fi\n\n      # Always include default first\n      ko_list_cfg=$(sed -n \"s/^default:\\(.*\\)/\\1/p\" ${SPK_CFG_PATH}/${SPK_CFG})\n      if [ ! \"${ko_list_cfg}\" ]; then\n         usage\n         echo -ne \"\\nERROR: Configuration option [default] not found in file [${SPK_CFG_PATH}/${SPK_CFG}]...\\n\\n\"\n         exit 1\n      fi\n\n      IFS=\",\"\n      for config in ${SPK_CFG_OPT}\n      do\n         ko_list_cfg_tmp=$(sed -n \"s/^${config}:\\(.*\\)/\\1/p\" ${SPK_CFG_PATH}/${SPK_CFG})\n         if [ ! \"${ko_list_cfg}\" ]; then\n            usage\n            echo -ne \"\\nERROR: Configuration option [${config}:] not found in file [${SPK_CFG_PATH}/${SPK_CFG}]...\\n\\n\"\n            exit 1\n         fi\n         ko_list_cfg+=\"${ko_list_cfg_tmp} \"\n      done\n      IFS=\" \"\n   fi\n\n   # Return merged module list from config file and\n   # parameters passed as arguments but keep its order,\n   # starting with the config file followed by args\n   echo ${ko_list_cfg} ${KO_LIST_ARG} | awk '{for (i=1;i<=NF;i++) if (!a[$i]++) printf(\"%s%s\",$i,FS)}{printf(\"\\n\")}' | xargs\n}\n\n\n#------------------------------------------------\n# Modules can be passed as:\n#   <module>\n#   <module>.ko\n#   /<path>/<module>*\n# The following find the right module as needed\n#------------------------------------------------\nko_path_match() {\n   ko_find=\"\"\n   ko_path=\"\"\n   ko_missing=\"\"\n\n   for ko in $KO_LIST\n   do\n      # first check if the name\n      # matches to a path\n      if [ ! \"$(echo $ko | grep '/')\" ]; then\n         # Ensure to add .ko if needed\n         [ ! \"$(echo $ko | grep '.ko$')\" ] && ko=$ko.ko\n         # Replace any '-' or '_' by '[-_]\n         ko=$(echo ${ko} | sed -r 's/[-_]/[-_]/g')\n         # Find full module kernel object path\n         ko_find=$(find $KPATH -name $ko)\n         [ ! \"$ko_find\" ] && ko_missing+=\"$ko \"\n      fi\n      ko_path+=\"${ko_find##*${KVER}} \"\n   done\n\n   # Return missing modules in case of error\n   # else return the list of found modules\n   [ ! \"${ko_missing}\" ] \\\n      && echo \"$ko_path\" | xargs \\\n      || echo \"missing: ${ko_missing}\" | xargs\n}\n\n# exit if no parameters passed\n[ $# -eq 0 ] && usage && exit 1\n\n# must be root to load/unload kernel modules\nif [ ! \"$(id -un)\" = \"root\" ]; then\n   verbose && usage\n   echo\n   echo \"ERROR: Must have root or sudo priviledges...\"\n   echo\n   exit 1\nfi\n\n\n###\n### Global variables\n###\nSPK=\"\"                                                                 # SynoCommunity kernel driver package name\nSPK_CFG=\"\"                                                             # SynoCommunity configuration file\nSPK_CFG_OPT=\"\"                                                         # SynoCommunity configuration option\nSPK_CFG_PATH=\"\"                                                        # SynoCommunity configuration path\nACTION=\"\"                                                              # Module action insmod|rmmod|reload|status\nVERBOSE=\"FALSE\"                                                        # Set verbose mode\nHELP=\"FALSE\"                                                           # Print help\nURULE=\"FALSE\"                                                          # Set udev rules to false by default\n\nwhile [ $# -gt 0 ] \ndo\n   case $1 in\n                                        -s|--spk ) shift 1\n                                                   SPK=$1;;\n                                     -c|--config ) shift 1\n                                                   SPK_CFG=$(echo $1 | cut -f1 -d:)\n                                                   SPK_CFG_OPT=$(echo $1 | cut -f2 -d:);;\n                                       -u|--udev ) shift 1\n                                                   URULE=$(echo $1 | cut -f1 -d:);;\n                                       -h|--help ) HELP=\"TRUE\";;\n   insmod|rmmod|reload|start|stop|restart|status ) ACTION=$1;;\n                                    -v|--verbose ) VERBOSE=\"TRUE\";;\n                                               * ) KO_LIST_ARG+=\"$1 \";;\n   esac\n   shift 1;\ndone\n\n###\n### Other global variables\n###\nARCH=$(uname -a | awk '{print $NF}' | cut -f2 -d_)                     # Synology NAS arch\nDSM_VERSION=$(sed -n 's/^productversion=\"\\(.*\\)\"/\\1/p' /etc/VERSION)   # Synology DSM version\nFPATH_SYS=\"/sys/module/firmware_class/parameters/path\"                 # System module firmware path file index\nKVER=$(uname -r | awk -F. '{print $1 \".\" $2 \".\" $3}')                  # Running kernel version\nFPATH=\"\"                                                               # Device firmware path\nKPATH=\"\"                                                               # Full kernel modules path\nMPATH=\"\"                                                               # Kernel modules path\nKO_LIST=\"\"                                                             # List of kernel objects to enable|disable (includes config+args)\nKO_LIST_ARG=$(echo ${KO_LIST_ARG} | xargs)                             # List of kernel objects passed in argument\nKO_PATH=\"\"                                                             # List of found kernel objects (*.ko) with path (includes config+args)\nSYNOLOG_PATH=/var/log/packages                                         # Default log output file\nSYNOLOG=${SYNOLOG_PATH}/synocli-kernelmodule.log                       # Default log output file\n\n# If SPK is set reassign variables\nif [ -n \"${SPK}\" ]; then\n   SPK_CFG_PATH=\"/var/packages/${SPK}/target/etc\"\n   FPATH=\"/var/packages/${SPK}/target/lib/firmware\"\n   MPATH=\"/var/packages/${SPK}/target/lib/modules\"\n   UPATH=\"/var/packages/${SPK}/target/rules.d\"\n   KPATH=\"${MPATH}/${ARCH}-${DSM_VERSION}/${KVER}\"\n   SYNOLOG=\"${SYNOLOG_PATH}/synocli-kernelmodule-${SPK}.log\"\nfi\n\n# All output to SYNOLOG, STDOUT to the screen\nexec > >(tee -a ${SYNOLOG}) 2> >(tee -a ${SYNOLOG} >/dev/null)\n\n\n# Get list of kernel objects from\n# both the configuration file and\n# arguments passed on cmd line\nKO_LIST=$(get_ko_list)\n\n# Find resulting kernel object (*.ko) full path\nKO_PATH=$(ko_path_match)\n\n# exit if modules are missing/not found\nif [ \"$(echo $KO_PATH | cut -f1 -d:)\" = \"missing\" ]; then\n   verbose && usage\n   echo\n   echo \"ERROR: Missing kernel modules: [$(echo $KO_PATH | cut -f2 -d: | xargs)]\"\n   echo\n   exit 1\nfi\n\n# If verbose is set print default arguments\n[ ${VERBOSE} = \"TRUE\" ] && verbose\n\n[ ${HELP} = \"TRUE\" ] && usage && exit 0\n\n# If module path does not exists, exit\nif [ ! -d ${MPATH} ]; then\n   usage\n   echo -ne \"\\nERROR: Module path [${MPATH}] does not exist or inaccessible...\\n\\n\"\n   exit 1\nfi\n\n# Set kernel module .ko object base path\n# If does not exists, exit\nif [ ! -d ${KPATH} ]; then\n   usage\n   echo -ne \"\\nERROR: Kernel modules base path [${KPATH}] does not exist or inaccessible...\\n\\n\"\n   exit 1\nfi\n\n# Check that udev rules file exist\nif [ ! ${URULE} \"FALSE\" -a ! -f ${UPATH}/${URULE} ]; then\n   usage\n   echo -ne \"\\nERROR: udev rules.d file [${UPATH}/${URULE}] does not exist or inaccessible...\\n\\n\"\n   exit 1\nfi\n\n# load the requested modules\nload ()\n{\n   error=0\n\n   if [ \"{URULE}\" ]; then\n      echo -ne \"\\t[enable] optional udev rules...\\n\"\n      printf '%40s %-34s' \"\" \"[$URULE]\"\n      ln -s ${UPATH}/${URULE} /lib/udev/rules.d/${URULE}\n      udevadm control --reload-rules\n      if [ $? -eq 0 ]; then\n         echo -ne \" OK\\n\"\n      else\n         error=1\n         echo -ne \" N/A\\n\"\n      fi\n   fi\n\n   # Add firmware path to running kernel\n   if [ -d \"${FPATH}\" ]; then\n      echo -ne \"\\t[loading] optional firmware path...\\n\"\n      printf '%65s' \"[${FPATH}]\"\n      echo \"${FPATH}\" > ${FPATH_SYS}\n      if [ $? -eq 0 ]; then\n         echo -ne \" OK\\n\"\n      else\n         error=1\n         echo -ne \" N/A\\n\"\n      fi\n   fi\n\n   echo -ne \"\\t[insmod] kernel modules...\\n\"\n   for ko in $KO_PATH\n   do\n      module=$(echo \"${ko}\" | sed -e 's/.*\\///' -e 's/-/_/' -e 's/\\.ko//')\n      printf '%40s %-35s' $ko \"[$module]\"\n\n      status=$(lsmod | grep \"^$module \")\n      if [ $? -eq 0 -a \"status\" ]; then\n         echo \"Already Loaded\"\n      else\n         if [ -f $KPATH/$ko ]; then\n            insmod $KPATH/$ko\n            [ $? -eq 0 ] && echo \"OK\" || echo \"ERROR\"\n         else\n            echo \"ERROR: Module $KPATH/$ko not found!\"\n            error=1\n         fi\n      fi\n   done\n\n   return $error\n}\n\n\n# unload the requested modules in a reversed order\nunload ()\n{\n   error=0\n\n   # Unload drivers in reverse order\n   echo -ne \"\\t[rmmod] kernel modules...\\n\"\n   for item in $KO_PATH; do echo $item; done | tac | while read ko\n   do\n      module=$(echo \"${ko}\" | sed -e 's/.*\\///' -e 's/-/_/g' -e 's/\\.ko//')\n      printf '%40s %-35s' $ko \"[$module]\"\n      status=$(lsmod | grep \"^$module \")\n      if [ $? -eq 0 -a \"status\" ]; then\n         rmmod $module\n         echo -ne \"N/A\\n\"\n      else\n         echo -ne \"ERROR\\n\"\n         error=1\n      fi\n   done\n\n   # Remove firmware path to running kernel\n   if [ -d \"${FPATH}\" ]; then\n      echo -ne \"\\t[unloading] optional firmware path...\\n\"\n      echo \"\" > ${FPATH_SYS}\n      error=$?\n   fi\n\n   # Remove udev rules\n   if [ \"{URULE}\" ]; then\n      echo -ne \"\\t[remove] optional udev rules...\\n\"\n      printf '%40s %-34s' \"\" \"[$URULE]\"\n      rm -f /lib/udev/rules.d/${URULE}\n      udevadm control --reload-rules\n      if [ $? -eq 0 ]; then\n         echo -ne \" N/A\\n\"\n      else\n         error=1\n         echo -ne \" ERROR\\n\"\n      fi\n   fi\n\n   return $error\n}\n\n\n# Provide a status of the loaded modules\nstatus ()\n{\n   error=0\n\n   echo -ne \"\\t[status] kernel modules...\\n\"\n   for ko in $KO_PATH\n   do\n      module=$(echo \"${ko}\" | sed -e 's/.*\\///' -e 's/-/_/g' -e 's/\\.ko//')\n      printf '%40s %-35s' $ko \"[$module]\"\n\n      status=$(lsmod | grep \"^$module \")\n      if [ $? -eq 0 -a \"status\" ]; then\n         echo -ne \"OK\\n\"\n      else\n         error=1\n         echo -ne \"N/A\\n\"\n      fi\n   done\n\n   # Validate option firmware path\n   if [ -d \"${FPATH}\" ]; then\n      echo -ne \"\\t[status] of optional firmware path...\\n\"\n\t  printf '%65s' \"[${FPATH}]\"\n\n      grep -q ${FPATH} ${FPATH_SYS}\n      if [ $? -eq 0 ]; then\n         echo -ne \" OK\\n\"\n      else\n         error=1\n         echo -ne \" N/A\\n\"\n      fi\n   fi\n\n   # Validate udev rules (not much can be done)\n   if [ \"{URULE}\" ]; then\n      echo -ne \"\\t[status] of optional udev rules...\\n\"\n      printf '%40s %-34s' \"\" \"[$URULE]\"\n      if [ -h /lib/udev/rules.d/${URULE} ]; then\n         echo -ne \" OK\\n\"\n      else\n         error=1\n         echo -ne \" N/A\\n\"\n      fi\n   fi\n\n   return $error\n}\n\n\ncase $ACTION in\n    insmod|start)\n       if status; then\n           echo ${SPK} is already running\n           exit 0\n       else\n           echo Starting ${SPK} ...\n           load\n           exit $?\n       fi\n       ;;\n    rmmod|stop)\n       if status; then\n           echo Stopping ${SPK} ...\n           unload\n           exit $?\n       else\n           echo ${SPK} is not running\n           exit 0\n       fi\n       ;;\n    reload|restart)\n       echo -ne \"---\\nReloading ${SPK} package...\\n\"\n       unload\n       load\n       exit $?\n       ;;\n    status)\n       echo -ne \"---\\nStatus of ${SPK} package...\\n\"\n       if status; then\n           echo ${SPK} is running\n           exit 0\n       else\n           echo ${SPK} is not running\n           exit 1\n       fi\n       ;;\n    *) usage\n       exit 1\n       ;;\nesac\n"
  },
  {
    "path": "tvheadend-backup.sh",
    "content": "#!/bin/bash\n\nDEST=/volume1/backup/tvheadend\nDATE=`date +%Y%m%d-%H%M`\n\ntar -C /var/packages/tvheadend/target -jcvf - \\\n    --exclude='.lock' \\\n    --exclude='tvheadend.pid' \\\n    cache var \\\n    bin/tv_grab_file bin/zap2xml.pl bin/zap2xml.sh perl5 .cpan .xmltv \\\n    share/tvheadend/data/dvb-scan/atsc/ca-QC-saint-jean-sur-richelieu \\\n    > $DEST/tvheadend-backup-$DATE.tar.bz2\n"
  }
]