Repository: th0ma7/synology
Branch: master
Commit: ddf793ec2222
Files: 9
Total size: 85.1 KB
Directory structure:
gitextract_iqeu8csl/
├── README-old.md
├── README.md
├── hauppauge/
│ ├── 001-Hauppauge955D-lgdt3306a-v3.patch
│ ├── 002-Hauppauge955D-em28xx-Tuner1.patch
│ ├── 003-Hauppauge955D-em28xx-Tuner2-v6.patch
│ └── hauppauge955D-SYNOApollolake-DSM622_24922-Kernel_4.4.59-20190520.tar.bz2
├── hauppauge.sh
├── kernel/
│ └── synocli-kernelmodule.sh
└── tvheadend-backup.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: README-old.md
================================================
# synology - th0ma7
Synology personnal hack, info, tools & source code
Donnations welcomed at: `0x522d164549E68681dfaC850A2cabdb95686C1fEC`
# Hauppauge WinTV DualHD HWC 955D
The following allows building kernel modules for the Hauppauge WinTV DualHD HWC 955D media adapter allowing to use TVheadend natively within the NAS.
* https://www.linuxtv.org/wiki/index.php/Hauppauge_WinTV-HVR-955Q
It was tested on the following hardware:
* model: DS918+
* OS: DSM 6.2.2 build #24922
* kernel: 4.4.59+
* arch: x86_64
* core name: Apollo Lake
Finding your running kernel:
```
$ uname -mvr
4.4.59+ #24922 SMP PREEMPT Thu Mar 28 11:07:03 CST 2019 x86_64
```
Finding your CPU type:
* https://en.wikichip.org/wiki/intel/celeron/j3455
```
$ cat /proc/cpuinfo | grep model.name | head -1
model name : Intel(R) Celeron(R) CPU J3455 @ 1.50GHz
```
## Current status
Available 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.
Working:
- `em28xx`: both tuners detected & firmware loading OK
- `lgdt3306a`: now functional
End result:
- `tvheadend`: fully detects both tuners
Work is based on the backporting of the following upstream kernel patches:
Demodulator (lgdt3306a):
* https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=4f75189024f4186a7ff9d56f4a8cb690774412ec
Adaptor (em28xx):
* https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=11a2a949d05e9d2d9823f0c45fa476743d9e462b
* https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=1586342e428d80e53f9a926b2e238d2175b9f5b5
* https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=be7fd3c3a8c5e9acbc69f887ca961df5e68cf6f0
Along with backported patches from the media-tree that b-rad-NDi made available here:
* https://github.com/b-rad-NDi/Ubuntu-media-tree-kernel-builder/tree/master/patches/fedora-22-4.4.0
## Preparation
Using a Ubuntu 18.04 OS VM in order to build the updated modules.
Install a few essential packages:
```
$ sudo apt update
$ sudo apt install build-essential ncurses-dev bc libssl-dev libc6-i386 curl
```
The toolchain & kernel sources are located here:
https://sourceforge.net/projects/dsgpl/files/
I 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:
```
$ mkdir $HOME/synology
$ mkdir $HOME/sources
```
Download the toolchain from Home / DSM 6.2 Tool Chains / Intel x86 Linux 4.4.59 (Apollolake)
```
$ 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
```
Download the synology kernel sources from Home / Synology NAS GPL Source / 22259branch / apollolake-source:
```
$ 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
```
Extract the files:
* The toolset is decompressed into the `$HOME/synology` directory
* Kernel sources are decompresed in the `$HOME/sources` directory
```
$ tar -xvf $HOME/synology/apollolake-gcc493_glibc220_linaro_x86_64-GPL.txz -C $HOME/synology
$ tar -xvf $HOME/synology/linux-4.4.x.txz -C $HOME/sources
```
Download the `lgdt3306a` and `em28xx` patches:
```
$ wget https://raw.githubusercontent.com/th0ma7/synology/master/hauppauge/001-Hauppauge955D-lgdt3306a-v3.patch -P $HOME/sources
$ wget https://raw.githubusercontent.com/th0ma7/synology/master/hauppauge/002-Hauppauge955D-em28xx-Tuner1.patch -P $HOME/sources
$ wget https://raw.githubusercontent.com/th0ma7/synology/master/hauppauge/003-Hauppauge955D-em28xx-Tuner2-v6.patch -P $HOME/sources
```
## Patching
We now have to prepare the kernel sources for compilation. Move down to the `$HOME/sources` directory:
```
$ cd $HOME/sources/linux-4.4.x
```
Frist need to copy the apollolake synology kernel configuration
```
~/sources/linux-4.4.x$ synoconfigs/apollolake .config
```
Secondly, using a text editor you need to adjust a few variables in the Makefile such as:
```
EXTRAVERSION = +
ARCH ?= x86_64
CROSS_COMPILE ?= /usr/local/x86_64-pc-linux-gnu/bin/x86_64-pc-linux-gnu-
```
Lastly, apply the necessary patches:
```
~/sources/linux-4.4.x$ patch -p1 < ../001-Hauppauge955D-lgdt3306a-v3.patch
~/sources/linux-4.4.x$ patch -p1 < ../002-Hauppauge955D-em28xx-Tuner1.patch
~/sources/linux-4.4.x$ patch -p1 < ../003-Hauppauge955D-em28xx-Tuner2-v6.patch
```
# Compilation
Modify the original configuration:
```
~/sources/linux-4.4.x$ sudo make oldconfig
~/sources/linux-4.4.x$ sudo make menuconfig
Device Drivers --->
<M> Multimedia support --->
[*] Media Controller API
[*] V4L2 sub-device userspace API
```
Everything should now all set for building the modules:
```
~/sources/linux-4.4.x$ make modules_prepare
~/sources/linux-4.4.x$ make modules M=drivers/media -j4
```
# Installation
Using SSH login as admin on the synology NAS:
```
$ ssh admin@192.168.x.x
```
Create a new local module directory (name will match kernel version):
```
$ sudo mkdir -p /usr/local/lib/modules/$(uname -r)
$ cd /usr/local/lib/modules/$(uname -r)
```
Copy the updated media drivers modules over to the NAS (or download the one from my repository on github - Use at your own risks):
```
$ sudo scp "username@192.168.x.x:~/sources/linux-4.4.x/drivers/media/usb/em28xx/*.ko" .
$ sudo scp "username@192.168.x.x:~/sources/linux-4.4.x/drivers/media/dvb-frontends/lgdt3306a.ko" .
$ sudo scp "username@192.168.x.x:~/sources/linux-4.4.x/drivers/media/dvb-frontends/media.ko" .
```
Copy the load/unload script to the NAS as well (and make it executable):
```
$ wget https://raw.githubusercontent.com/th0ma7/synology/master/hauppauge/hauppauge-load.sh
$ wget https://raw.githubusercontent.com/th0ma7/synology/master/hauppauge/hauppauge-unload.sh
$ chmod 755 hauppauge-*.sh
```
Create 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):
```
#!/bin/sh
# Load mandatory modules
sudo insmod /lib/modules/dvb-core.ko
sudo insmod /lib/modules/rc-core.ko
sudo insmod /lib/modules/dvb-usb.ko
sudo insmod /lib/modules/videodev.ko
sudo insmod /lib/modules/v4l2-common.ko
sudo insmod /lib/modules/tveeprom.ko
# Load Hauppauge updated drivers
sudo insmod /lib/modules/si2157.ko
sudo insmod /usr/local/lib/modules/$(uname -r)/media.ko
sudo insmod /usr/local/lib/modules/$(uname -r)/lgdt3306a.ko
sudo insmod /usr/local/lib/modules/$(uname -r)/em28xx.ko
sudo insmod /usr/local/lib/modules/$(uname -r)/em28xx-dvb.ko
```
Execute manually the rc script to confirm all is ok:
```
$ sudo /usr/local/etc/rc.d/hauppauge.sh
```
Normally should see the following in kernel `dmesg` (added a few DEBUG to lgdt3306a):
```
[ 557.806644] em28xx: New device HCW 955D @ 480 Mbps (2040:026d, interface 0, class 0)
[ 557.815308] em28xx: DVB interface 0 found: isoc
[ 557.820423] em28xx: chip ID is em28174
[ 558.939915] em28174 #0: EEPROM ID = 26 00 01 00, EEPROM hash = 0x3d790eca
[ 558.947531] em28174 #0: EEPROM info:
[ 558.951857] em28174 #0: microcode start address = 0x0004, boot configuration = 0x01
[ 558.966683] em28174 #0: AC97 audio (5 sample rates)
[ 558.972234] em28174 #0: 500mA max power
[ 558.976620] em28174 #0: Table at offset 0x27, strings=0x0a72, 0x187c, 0x086a
[ 558.984753] em28174 #0: Identified as Hauppauge WinTV-dualHD 01595 ATSC/QAM (card=100)
[ 558.994647] tveeprom 8-0050: Hauppauge model 204101, rev B2I6, serial# 11584195
[ 559.002824] tveeprom 8-0050: tuner model is SiLabs Si2157 (idx 186, type 4)
[ 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)
[ 559.023133] tveeprom 8-0050: audio processor is None (idx 0)
[ 559.029491] tveeprom 8-0050: has no radio, has IR receiver, has no IR transmitter
[ 559.038167] em28174 #0: dvb set to isoc mode.
[ 559.043177] em28xx: chip ID is em28174
[ 560.162726] em28174 #1: EEPROM ID = 26 00 01 00, EEPROM hash = 0x3d790eca
[ 560.170323] em28174 #1: EEPROM info:
[ 560.174326] em28174 #1: microcode start address = 0x0004, boot configuration = 0x01
[ 560.189064] em28174 #1: AC97 audio (5 sample rates)
[ 560.194613] em28174 #1: 500mA max power
[ 560.199009] em28174 #1: Table at offset 0x27, strings=0x0a72, 0x187c, 0x086a
[ 560.207139] em28174 #1: Identified as Hauppauge WinTV-dualHD 01595 ATSC/QAM (card=100)
[ 560.216915] tveeprom 10-0050: Hauppauge model 204101, rev B2I6, serial# 11584195
[ 560.225192] tveeprom 10-0050: tuner model is SiLabs Si2157 (idx 186, type 4)
[ 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)
[ 560.245327] tveeprom 10-0050: audio processor is None (idx 0)
[ 560.251757] tveeprom 10-0050: has no radio, has IR receiver, has no IR transmitter
[ 560.260220] em28xx: dvb ts2 set to isoc mode.
[ 560.465298] em28174 #0: Binding DVB extension
[ 560.476140] i2c i2c-8: Added multiplexed i2c bus 11
[ 560.481615] DEBUG: Passed lgdt3306a_probe 2351
[ 560.486720] DEBUG: Passed lgdt3306a_probe 2353
[ 560.491791] DEBUG: Passed lgdt3306a_probe 2355
[ 560.496853] DEBUG: Passed lgdt3306a_probe 2357
[ 560.501921] lgdt3306a 8-0059: LG Electronics LGDT3306A successfully identified
[ 560.509994] DEBUG: Passed lgdt3306a_probe 2360
[ 560.517015] si2157 11-0060: Silicon Labs Si2147/2148/2157/2158 successfully attached
[ 560.525695] DVB: registering new adapter (em28174 #0)
[ 560.531352] usb 1-3: DVB: registering adapter 0 frontend 0 (LG Electronics LGDT3306A VSB/QAM Frontend)...
[ 560.544142] em28174 #0: DVB extension successfully initialized
[ 560.550672] em28174 #1: Binding DVB extension
[ 560.560027] i2c i2c-10: Added multiplexed i2c bus 12
[ 560.565578] DEBUG: Passed lgdt3306a_probe 2351
[ 560.570650] DEBUG: Passed lgdt3306a_probe 2353
[ 560.575800] DEBUG: Passed lgdt3306a_probe 2355
[ 560.580886] DEBUG: Passed lgdt3306a_probe 2357
[ 560.585962] lgdt3306a 10-000e: LG Electronics LGDT3306A successfully identified
[ 560.594141] DEBUG: Passed lgdt3306a_probe 2360
[ 560.601410] si2157 12-0062: Silicon Labs Si2147/2148/2157/2158 successfully attached
[ 560.610075] DVB: registering new adapter (em28174 #1)
[ 560.615721] usb 1-3: DVB: registering adapter 1 frontend 0 (LG Electronics LGDT3306A VSB/QAM Frontend)...
[ 560.627161] em28174 #1: DVB extension successfully initialized
[ 562.882976] si2157 12-0062: found a 'Silicon Labs Si2157-A30'
[ 562.939717] si2157 12-0062: firmware version: 3.0.5
[ 562.945214] usb 1-3: DVB: adapter 1 frontend 0 frequency 0 out of range (55000000..858000000)
```
And the following USB devices with associated modules (ID may vary depending if connected using the front or back USB ports):
```
$ lsusb -Ic
|__usb1 1d6b:0002:0404 09 2.00 480MBit/s 0mA 1IF (Linux 4.4.59+ xhci-hcd xHCI Host Controller 0000:00:15.0)
1-0:1.0 (IF) 09:00:00 1EP () hub
|__1-1 2040:026d:0100 00 2.00 480MBit/s 500mA 1IF (HCW 955D 0011584195)
1-1:1.0 (IF) ff:00:00 2EPs () em28xx
|__1-4 f400:f400:0100 00 2.00 480MBit/s 200mA 1IF (Synology DiskStation 6500794064E41636)
1-4:1.0 (IF) 08:06:50 2EPs () usb-storage host5 (synoboot)
|__usb2 1d6b:0003:0404 09 3.00 5000MBit/s 0mA 1IF (Linux 4.4.59+ xhci-hcd xHCI Host Controller 0000:00:15.0)
2-0:1.0 (IF) 09:00:00 1EP () hub
```
Now reboot the NAS using the admin web page and confirm after reboot that the dmesg output and lsusb are still ok.
In 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.
================================================
FILE: README.md
================================================
# synology - th0ma7
Synology personnal hack, info, tools & source code
Donnations welcomed at: `0x522d164549E68681dfaC850A2cabdb95686C1fEC`
# Hauppauge WinTV DualHD HWC 955D
The following allows building kernel modules for the Hauppauge WinTV DualHD HWC 955D media adapter allowing to use TVheadEnd (TVH) natively within the NAS.
* https://www.linuxtv.org/wiki/index.php/Hauppauge_WinTV-HVR-955Q
In theory this procedure is also valid to build most supported DVB adaptors available from the Media Tree within the Linux Media Subsystem.
* https://linuxtv.org/wiki/index.php/ATSC_USB_devices
* https://linuxtv.org/wiki/index.php/DVB-C_USB_Devices
Tested on the following hardware:
* model: DS918+
* OS: DSM 6.2.2 build #24922
* kernel: 4.4.59+
* arch: x86_64
* core name: Apollo Lake
Finding your running kernel:
```
$ uname -mvr
4.4.59+ #24922 SMP PREEMPT Thu Mar 28 11:07:03 CST 2019 x86_64
```
Finding your CPU type:
* https://en.wikichip.org/wiki/intel/celeron/j3455
```
$ cat /proc/cpuinfo | grep model.name | head -1
model name : Intel(R) Celeron(R) CPU J3455 @ 1.50GHz
```
## Current status
I 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!!!_
For more details on b-rad-NDi project refer to:
* https://github.com/b-rad-NDi/Embedded-MediaDrivers
Working:
- `em28xx`: both tuners detected & firmware loading OK
- `lgdt3306a`: fully functional
End result:
- `tvheadend`: fully detects both tuners
Instead 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+):
* https://github.com/th0ma7/synology/raw/master/hauppauge/hauppauge955D-SYNOApollolake-DSM622_24922-Kernel_4.4.59-20190520.tar.bz2
## Preparation
Using a Ubuntu 18.04 OS to build the updated modules install a few essential packages:
```
$ sudo apt update
$ sudo apt install build-essential ncurses-dev bc libssl-dev libc6-i386 curl libproc-processtable-perl
```
Clone b-rad-NDi git repository:
```
$ git clone https://github.com/b-rad-NDi/Embedded-MediaDrivers.git
$ cd Embedded-MediaDrivers
~/Embedded-MediaDrivers$
```
Create a `SYNO-Apollolake` download directory:
```
$ mkdir dl/SYNO-Apollolake
```
Download the toolchain
* https://sourceforge.net/projects/dsgpl/files/DSM%206.2%20Tool%20Chains/
```
$ 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/
```
Download the Synology DSM kernel sources:
* https://sourceforge.net/projects/dsgpl/files/Synology%20NAS%20GPL%20Source/22259branch/
```
$ 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/
```
Initialize the repository:
```
$ ./md_builder.sh -i -d SYNO-Apollolake
```
Build a default Synology DSM kernel build (takes a while):
```
$ export MAKEOPTS="-j`nproc`"
$ ./md_builder.sh -B media -d SYNO-Apollolake
```
Configure the media tree, get the latest media tree patches that applies over the default Synology DSM kernel and build the media drivers:
```
$ ./md_builder.sh -g -d SYNO-Apollolake
$ cd build/SYNOAPOLLOLAKE/media_build
build/SYNOAPOLLOLAKE/media_build$ ./build
```
## Installation
Using SSH login as admin on the synology NAS:
```
$ ssh admin@<my.syno.nas.ip>
```
Create a new local module directory (name will match kernel version):
```
$ sudo mkdir -p /usr/local/lib/modules/$(uname -r)
$ cd /usr/local/lib/modules/$(uname -r)
```
Download 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):
```
$ cd /usr/local/lib/modules/$(uname -r)
$ sudo scp "username@<my.ubuntu.linux.ip>:~/Embedded-MediaDrivers/build/SYNOAPOLLOLAKE/media_build/v4l/*.ko" .
```
Copy the start/stop/load/reset script to the NAS (and make it executable):
```
$ cd /usr/local/lib/modules/$(uname -r)
$ wget https://raw.githubusercontent.com/th0ma7/synology/master/hauppauge.sh
$ chmod 755 hauppauge.sh
```
Create a symbolic link to `/opt/bin/hauppauge.sh` for ease of use:
```
$ sudo ln -s -T -f /usr/local/lib/modules/$(uname -r)/hauppauge.sh /opt/bin/hauppauge.sh
```
Create a local rc file locate at `/usr/local/etc/rc.d/media.sh` that will be executed at boot time:
```
$ cat << EOF | sudo tee /usr/local/etc/rc.d/media.sh
#!/bin/sh
/usr/local/lib/modules/$(uname -r)/hauppauge.sh load
EOF
$ sudo chmod 755 /usr/local/etc/rc.d/media.sh
```
Execute manually the rc script to confirm there is no error:
```
$ sudo /usr/local/etc/rc.d/media.sh
```
Validate the status:
```
$ sudo /opt/bin/hauppauge.sh status
Status pkgctl-tvheadend... N/A
kernel module status...
em28xx_dvb OK
em28xx OK
lgdt3306a OK
si2157 OK
tveeprom OK
v4l2_common OK
dvb_usb OK
rc_core OK
dvb_core OK
videobuf2_vmalloc OK
videobuf2_memops OK
videobuf2_v4l2 OK
videobuf2_common OK
videodev OK
media OK
kernel USB (1-3) autosuspend values...
(1-3)autosuspend_delay_ms [-1000] -> OK
(1-3)autosuspend [ -1] -> OK
kernel sysctl values...
vm.dirty_expire_centisecs [ 300] -> OK
vm.swappiness [ 1] -> OK
```
Normally should see something similar in kernel `dmesg`:
```
[ 557.806644] em28xx: New device HCW 955D @ 480 Mbps (2040:026d, interface 0, class 0)
[ 557.815308] em28xx: DVB interface 0 found: isoc
[ 557.820423] em28xx: chip ID is em28174
[ 558.939915] em28174 #0: EEPROM ID = 26 00 01 00, EEPROM hash = 0x3d790eca
[ 558.947531] em28174 #0: EEPROM info:
[ 558.951857] em28174 #0: microcode start address = 0x0004, boot configuration = 0x01
[ 558.966683] em28174 #0: AC97 audio (5 sample rates)
[ 558.972234] em28174 #0: 500mA max power
[ 558.976620] em28174 #0: Table at offset 0x27, strings=0x0a72, 0x187c, 0x086a
[ 558.984753] em28174 #0: Identified as Hauppauge WinTV-dualHD 01595 ATSC/QAM (card=100)
[ 558.994647] tveeprom 8-0050: Hauppauge model 204101, rev B2I6, serial# 11584195
[ 559.002824] tveeprom 8-0050: tuner model is SiLabs Si2157 (idx 186, type 4)
[ 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)
[ 559.023133] tveeprom 8-0050: audio processor is None (idx 0)
[ 559.029491] tveeprom 8-0050: has no radio, has IR receiver, has no IR transmitter
[ 559.038167] em28174 #0: dvb set to isoc mode.
[ 559.043177] em28xx: chip ID is em28174
[ 560.162726] em28174 #1: EEPROM ID = 26 00 01 00, EEPROM hash = 0x3d790eca
[ 560.170323] em28174 #1: EEPROM info:
[ 560.174326] em28174 #1: microcode start address = 0x0004, boot configuration = 0x01
[ 560.189064] em28174 #1: AC97 audio (5 sample rates)
[ 560.194613] em28174 #1: 500mA max power
[ 560.199009] em28174 #1: Table at offset 0x27, strings=0x0a72, 0x187c, 0x086a
[ 560.207139] em28174 #1: Identified as Hauppauge WinTV-dualHD 01595 ATSC/QAM (card=100)
[ 560.216915] tveeprom 10-0050: Hauppauge model 204101, rev B2I6, serial# 11584195
[ 560.225192] tveeprom 10-0050: tuner model is SiLabs Si2157 (idx 186, type 4)
[ 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)
[ 560.245327] tveeprom 10-0050: audio processor is None (idx 0)
[ 560.251757] tveeprom 10-0050: has no radio, has IR receiver, has no IR transmitter
[ 560.260220] em28xx: dvb ts2 set to isoc mode.
[ 560.465298] em28174 #0: Binding DVB extension
[ 560.476140] i2c i2c-8: Added multiplexed i2c bus 11
[ 560.501921] lgdt3306a 8-0059: LG Electronics LGDT3306A successfully identified
[ 560.509994] DEBUG: Passed lgdt3306a_probe 2360
[ 560.517015] si2157 11-0060: Silicon Labs Si2147/2148/2157/2158 successfully attached
[ 560.525695] DVB: registering new adapter (em28174 #0)
[ 560.531352] usb 1-3: DVB: registering adapter 0 frontend 0 (LG Electronics LGDT3306A VSB/QAM Frontend)...
[ 560.544142] em28174 #0: DVB extension successfully initialized
[ 560.550672] em28174 #1: Binding DVB extension
[ 560.560027] i2c i2c-10: Added multiplexed i2c bus 12
[ 560.585962] lgdt3306a 10-000e: LG Electronics LGDT3306A successfully identified
[ 560.601410] si2157 12-0062: Silicon Labs Si2147/2148/2157/2158 successfully attached
[ 560.610075] DVB: registering new adapter (em28174 #1)
[ 560.615721] usb 1-3: DVB: registering adapter 1 frontend 0 (LG Electronics LGDT3306A VSB/QAM Frontend)...
[ 560.627161] em28174 #1: DVB extension successfully initialized
[ 562.882976] si2157 12-0062: found a 'Silicon Labs Si2157-A30'
[ 562.939717] si2157 12-0062: firmware version: 3.0.5
[ 562.945214] usb 1-3: DVB: adapter 1 frontend 0 frequency 0 out of range (55000000..858000000)
```
And the following USB devices with associated modules (ID may vary depending if connected using the front or back USB ports):
```
$ lsusb -Ic
|__usb1 1d6b:0002:0404 09 2.00 480MBit/s 0mA 1IF (Linux 4.4.59+ xhci-hcd xHCI Host Controller 0000:00:15.0)
1-0:1.0 (IF) 09:00:00 1EP () hub
|__1-1 2040:026d:0100 00 2.00 480MBit/s 500mA 1IF (HCW 955D 0011584195)
1-1:1.0 (IF) ff:00:00 2EPs () em28xx
|__1-4 f400:f400:0100 00 2.00 480MBit/s 200mA 1IF (Synology DiskStation 6500794064E41636)
1-4:1.0 (IF) 08:06:50 2EPs () usb-storage host5 (synoboot)
|__usb2 1d6b:0003:0404 09 3.00 5000MBit/s 0mA 1IF (Linux 4.4.59+ xhci-hcd xHCI Host Controller 0000:00:15.0)
2-0:1.0 (IF) 09:00:00 1EP () hub
```
Now reboot the NAS using the admin web page and confirm after reboot that the dmesg output and lsusb are still ok.
In 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.
---
# hauppauge.sh
This script is intended to provide a simple method to start|stop|restart the various perequesites into getting media modules loaded onto the Synology NAS.
Basicaly what the script does:
1. Load all the necessary modules
2. Disable USB autosuspend over the Hauppauge USB ID
3. Injects a few kernel `sysctl` for optmizations
4. Starts TVH (service name `pkgctl-tvheadend`)
## Modules
The 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.
|Order | Module | `rmmod` |
|:----:|:----------------------:|:-------------------:|
| 1 | `media.ko` | `media` |
| 2 | `videodev.ko` | `videodev` |
| 3 | `videobuf2-common.ko` | `videobuf2_common` |
| 4 | `videobuf2-v4l2.ko` | `videobuf2_v4l2` |
| 5 | `videobuf2-memops.ko` | `videobuf2_memops` |
| 6 | `videobuf2-vmalloc.ko` | `videobuf2_vmalloc` |
| 7 | `dvb-core.ko` | `dvb_core` |
| 8 | `rc-core.ko` | `rc_core` |
| 9 | `dvb-usb.ko` | `dvb_usb` |
| 10 | `v4l2-common.ko` | `v4l2_common` |
| 11 | `tveeprom.ko` | `tveeprom` |
| 12 | `si2157.ko` | `si2157` |
| 13 | `lgdt3306a.ko` | `lgdt3306a` |
| 14 | `em28xx.ko` | `em28xx` |
| 15 | `em28xx-dvb.ko` | `em28xx_dvb` |
## Options
**start:** Does a full start including:
1. loading all the modules
2. disabling USB autosuspend
3. `sysctl` adjustments
4. TVH startup
**stop:** Does a stop which basicaly is:
1. TVH shutdown
2. Unloading all the modules
**restart:** Basically performs a `stop` then `start`.
**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:
1. TVH shutdown (forces it if needed)
2. unload `em28xx_dvb`
3. load of `em28xx-dvb.ko`
4. TVH startup
**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.
**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.
================================================
FILE: hauppauge/001-Hauppauge955D-lgdt3306a-v3.patch
================================================
diff -uprN linux-4.4.x-orig/drivers/media/dvb-frontends/lgdt3306a.c linux-4.4.x-new/drivers/media/dvb-frontends/lgdt3306a.c
--- linux-4.4.x-orig/drivers/media/dvb-frontends/lgdt3306a.c 2017-10-30 17:45:14.000000000 -0400
+++ linux-4.4.x-new/drivers/media/dvb-frontends/lgdt3306a.c 2019-05-13 20:41:32.123976946 -0400
@@ -16,17 +16,31 @@
* GNU General Public License for more details.
*/
+#undef pr_fmt
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <asm/div64.h>
+#include <linux/kernel.h>
#include <linux/dvb/frontend.h>
#include "dvb_math.h"
#include "lgdt3306a.h"
+#include <linux/i2c-mux.h>
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))");
+/*
+ * Older drivers treated QAM64 and QAM256 the same; that is the HW always
+ * used "Auto" mode during detection. Setting "forced_manual"=1 allows
+ * the user to treat these modes as separate. For backwards compatibility,
+ * it's off by default. QAM_AUTO can now be specified to achive that
+ * effect even if "forced_manual"=1
+ */
+static int forced_manual;
+module_param(forced_manual, int, 0644);
+MODULE_PARM_DESC(forced_manual, "if set, QAM64 and QAM256 will only lock to modulation specified");
+
#define DBG_INFO 1
#define DBG_REG 2
#define DBG_DUMP 4 /* FGR - comment out to remove dump code */
@@ -55,6 +69,8 @@ MODULE_PARM_DESC(debug, "set debug level
__ret; \
})
+#define MHz 1000000
+
struct lgdt3306a_state {
struct i2c_adapter *i2c_adap;
const struct lgdt3306a_config *cfg;
@@ -558,7 +574,12 @@ static int lgdt3306a_set_qam(struct lgdt
/* 3. : 64QAM/256QAM detection(manual, auto) */
ret = lgdt3306a_read_reg(state, 0x0009, &val);
val &= 0xfc;
- val |= 0x02; /* STDOPDETCMODE[1:0]=1=Manual 2=Auto */
+ /* Check for forced Manual modulation modes; otherwise always "auto" */
+ if(forced_manual && (modulation != QAM_AUTO)){
+ val |= 0x01; /* STDOPDETCMODE[1:0]= 1=Manual */
+ } else {
+ val |= 0x02; /* STDOPDETCMODE[1:0]= 2=Auto */
+ }
ret = lgdt3306a_write_reg(state, 0x0009, val);
if (lg_chkerr(ret))
goto fail;
@@ -590,6 +611,28 @@ static int lgdt3306a_set_qam(struct lgdt
if (lg_chkerr(ret))
goto fail;
+ /* 5.1 V0.36 SRDCHKALWAYS : For better QAM detection */
+ ret = lgdt3306a_read_reg(state, 0x000a, &val);
+ val &= 0xfd;
+ val |= 0x02;
+ ret = lgdt3306a_write_reg(state, 0x000a, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 5.2 V0.36 Control of "no signal" detector function */
+ ret = lgdt3306a_read_reg(state, 0x2849, &val);
+ val &= 0xdf;
+ ret = lgdt3306a_write_reg(state, 0x2849, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
+ /* 5.3 Fix for Blonder Tongue HDE-2H-QAM and AQM modulators */
+ ret = lgdt3306a_read_reg(state, 0x302b, &val);
+ val &= 0x7f; /* SELFSYNCFINDEN_CQS=0; disable auto reset */
+ ret = lgdt3306a_write_reg(state, 0x302b, val);
+ if (lg_chkerr(ret))
+ goto fail;
+
/* 6. Reset */
ret = lgdt3306a_soft_reset(state);
if (lg_chkerr(ret))
@@ -612,10 +655,9 @@ static int lgdt3306a_set_modulation(stru
ret = lgdt3306a_set_vsb(state);
break;
case QAM_64:
- ret = lgdt3306a_set_qam(state, QAM_64);
- break;
case QAM_256:
- ret = lgdt3306a_set_qam(state, QAM_256);
+ case QAM_AUTO:
+ ret = lgdt3306a_set_qam(state, p->modulation);
break;
default:
return -EINVAL;
@@ -642,6 +684,7 @@ static int lgdt3306a_agc_setup(struct lg
break;
case QAM_64:
case QAM_256:
+ case QAM_AUTO:
break;
default:
return -EINVAL;
@@ -696,6 +739,7 @@ static int lgdt3306a_spectral_inversion(
break;
case QAM_64:
case QAM_256:
+ case QAM_AUTO:
/* Auto ok for QAM */
ret = lgdt3306a_set_inversion_auto(state, 1);
break;
@@ -719,6 +763,7 @@ static int lgdt3306a_set_if(struct lgdt3
break;
case QAM_64:
case QAM_256:
+ case QAM_AUTO:
if_freq_khz = state->cfg->qam_if_khz;
break;
default:
@@ -727,7 +772,7 @@ static int lgdt3306a_set_if(struct lgdt3
switch (if_freq_khz) {
default:
- pr_warn("IF=%d KHz is not supportted, 3250 assumed\n",
+ pr_warn("IF=%d KHz is not supported, 3250 assumed\n",
if_freq_khz);
/* fallthrough */
case 3250: /* 3.25Mhz */
@@ -1577,6 +1622,7 @@ static int lgdt3306a_read_status(struct
switch (state->current_modulation) {
case QAM_256:
case QAM_64:
+ case QAM_AUTO:
if (lgdt3306a_qam_lock_poll(state) == LG3306_LOCK) {
*status |= FE_HAS_VITERBI;
*status |= FE_HAS_SYNC;
@@ -1619,6 +1665,7 @@ static int lgdt3306a_read_signal_strengt
* Calculate some sort of "strength" from SNR
*/
struct lgdt3306a_state *state = fe->demodulator_priv;
+ u8 val;
u16 snr; /* snr_x10 */
int ret;
u32 ref_snr; /* snr*100 */
@@ -1631,11 +1678,15 @@ static int lgdt3306a_read_signal_strengt
ref_snr = 1600; /* 16dB */
break;
case QAM_64:
- ref_snr = 2200; /* 22dB */
- break;
case QAM_256:
- ref_snr = 2800; /* 28dB */
- break;
+ case QAM_AUTO:
+ /* need to know actual modulation to set proper SNR baseline */
+ lgdt3306a_read_reg(state, 0x00a6, &val);
+ if(val & 0x04)
+ ref_snr = 2800; /* QAM-256 28dB */
+ else
+ ref_snr = 2200; /* QAM-64 22dB */
+ break;
default:
return -EINVAL;
}
@@ -1729,27 +1780,19 @@ static int lgdt3306a_get_tune_settings(s
return 0;
}
-static int lgdt3306a_search(struct dvb_frontend *fe)
+static enum dvbfe_search lgdt3306a_search(struct dvb_frontend *fe)
{
enum fe_status status = 0;
- int i, ret;
+ int ret;
/* set frontend */
ret = lgdt3306a_set_parameters(fe);
if (ret)
goto error;
- /* wait frontend lock */
- for (i = 20; i > 0; i--) {
- dbg_info(": loop=%d\n", i);
- msleep(50);
- ret = lgdt3306a_read_status(fe, &status);
- if (ret)
- goto error;
-
- if (status & FE_HAS_LOCK)
- break;
- }
+ ret = lgdt3306a_read_status(fe, &status);
+ if (ret)
+ goto error;
/* check if we have a valid signal */
if (status & FE_HAS_LOCK)
@@ -1770,7 +1813,7 @@ static void lgdt3306a_release(struct dvb
kfree(state);
}
-static struct dvb_frontend_ops lgdt3306a_ops;
+static const struct dvb_frontend_ops lgdt3306a_ops;
struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config,
struct i2c_adapter *i2c_adap)
@@ -2072,7 +2115,7 @@ static const short regtab[] = {
0x30aa, /* MPEGLOCK */
};
-#define numDumpRegs (sizeof(regtab)/sizeof(regtab[0]))
+#define numDumpRegs (ARRAY_SIZE(regtab))
static u8 regval1[numDumpRegs] = {0, };
static u8 regval2[numDumpRegs] = {0, };
@@ -2104,14 +2147,14 @@ static void lgdt3306a_DumpRegs(struct lg
}
#endif /* DBG_DUMP */
-static struct dvb_frontend_ops lgdt3306a_ops = {
+static const struct dvb_frontend_ops lgdt3306a_ops = {
.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
.info = {
.name = "LG Electronics LGDT3306A VSB/QAM Frontend",
- .frequency_min = 54000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 62500,
- .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ .frequency_min = 54 * MHz,
+ .frequency_max = 858 * MHz,
+ .frequency_stepsize = 62500,
+ .caps = FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
},
.i2c_gate_ctrl = lgdt3306a_i2c_gate_ctrl,
.init = lgdt3306a_init,
@@ -2132,6 +2175,234 @@ static struct dvb_frontend_ops lgdt3306a
.search = lgdt3306a_search,
};
+/*
+ * I2C gate logic
+ * We must use unlocked I2C I/O because I2C adapter lock is already taken
+ * by the caller (usually tuner driver).
+ * select/unselect are unlocked versions of lgdt3306a_i2c_gate_ctrl
+ */
+static int lgdt3306a_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
+{
+ struct i2c_client *client = mux_priv;
+ int ret;
+ u8 val;
+ u8 buf[3];
+
+ struct i2c_msg read_msg_1 = {
+ .addr = client->addr,
+ .flags = 0,
+ .buf = "\x00\x02",
+ .len = 2,
+ };
+ struct i2c_msg read_msg_2 = {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .buf = &val,
+ .len = 1,
+ };
+
+ struct i2c_msg write_msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 3,
+ .buf = buf,
+ };
+
+ ret = __i2c_transfer(client->adapter, &read_msg_1, 1);
+ if (ret != 1)
+ {
+ pr_err("error (addr %02x reg 0x002 error (ret == %i)\n",
+ client->addr, ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EREMOTEIO;
+ }
+
+ ret = __i2c_transfer(client->adapter, &read_msg_2, 1);
+ if (ret != 1)
+ {
+ pr_err("error (addr %02x reg 0x002 error (ret == %i)\n",
+ client->addr, ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EREMOTEIO;
+ }
+
+ buf[0] = 0x00;
+ buf[1] = 0x02;
+ val &= 0x7F;
+ val |= LG3306_TUNERI2C_ON;
+ buf[2] = val;
+ ret = __i2c_transfer(client->adapter, &write_msg, 1);
+ if (ret != 1) {
+ pr_err("error (addr %02x %02x <- %02x, err = %i)\n",
+ write_msg.buf[0], write_msg.buf[1], write_msg.buf[2], ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int lgdt3306a_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan)
+{
+ struct i2c_client *client = mux_priv;
+ int ret;
+ u8 val;
+ u8 buf[3];
+
+ struct i2c_msg read_msg_1 = {
+ .addr = client->addr,
+ .flags = 0,
+ .buf = "\x00\x02",
+ .len = 2,
+ };
+ struct i2c_msg read_msg_2 = {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .buf = &val,
+ .len = 1,
+ };
+
+ struct i2c_msg write_msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 3,
+ .buf = buf,
+ };
+
+ ret = __i2c_transfer(client->adapter, &read_msg_1, 1);
+ if (ret != 1)
+ {
+ pr_err("error (addr %02x reg 0x002 error (ret == %i)\n",
+ client->addr, ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EREMOTEIO;
+ }
+
+ ret = __i2c_transfer(client->adapter, &read_msg_2, 1);
+ if (ret != 1)
+ {
+ pr_err("error (addr %02x reg 0x002 error (ret == %i)\n",
+ client->addr, ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EREMOTEIO;
+ }
+
+ buf[0] = 0x00;
+ buf[1] = 0x02;
+ val &= 0x7F;
+ val |= LG3306_TUNERI2C_OFF;
+ buf[2] = val;
+ ret = __i2c_transfer(client->adapter, &write_msg, 1);
+ if (ret != 1) {
+ pr_err("error (addr %02x %02x <- %02x, err = %i)\n",
+ write_msg.buf[0], write_msg.buf[1], write_msg.buf[2], ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int lgdt3306a_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lgdt3306a_config *config;
+ struct lgdt3306a_state *state;
+ struct dvb_frontend *fe;
+ int ret;
+
+ config = kmemdup(client->dev.platform_data,
+ sizeof(struct lgdt3306a_config), GFP_KERNEL);
+ if (config == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ config->i2c_addr = client->addr;
+ fe = lgdt3306a_attach(config, client->adapter);
+ if (fe == NULL) {
+ ret = -ENODEV;
+ goto err_fe;
+ }
+
+ i2c_set_clientdata(client, fe->demodulator_priv);
+ state = fe->demodulator_priv;
+ state->frontend.ops.release = NULL;
+
+ /* create mux i2c adapter for tuner */
+ state->i2c_adap = i2c_add_mux_adapter(client->adapter, &client->dev,
+ client, 0, 0, 0, lgdt3306a_select, lgdt3306a_deselect);
+ if (state->i2c_adap == NULL) {
+ ret = -ENODEV;
+ goto err_kfree;
+ }
+
+ /* create dvb_frontend */
+ printk(KERN_ALERT "DEBUG: Passed %s %d \n",__FUNCTION__,__LINE__);
+ fe->ops.i2c_gate_ctrl = NULL;
+ printk(KERN_ALERT "DEBUG: Passed %s %d \n",__FUNCTION__,__LINE__);
+ *config->i2c_adapter = state->i2c_adap;
+ printk(KERN_ALERT "DEBUG: Passed %s %d \n",__FUNCTION__,__LINE__);
+ *config->fe = fe;
+ printk(KERN_ALERT "DEBUG: Passed %s %d \n",__FUNCTION__,__LINE__);
+
+ dev_info(&client->dev, "LG Electronics LGDT3306A successfully identified\n");
+ printk(KERN_ALERT "DEBUG: Passed %s %d \n",__FUNCTION__,__LINE__);
+
+ return 0;
+
+err_kfree:
+ kfree(state);
+err_fe:
+ kfree(config);
+fail:
+ dev_warn(&client->dev, "probe failed = %d\n", ret);
+ return ret;
+}
+
+static int lgdt3306a_remove(struct i2c_client *client)
+{
+ struct lgdt3306a_state *state = i2c_get_clientdata(client);
+
+ i2c_del_mux_adapter(state->i2c_adap);
+
+ state->frontend.ops.release = NULL;
+ state->frontend.demodulator_priv = NULL;
+
+ kfree(state->cfg);
+ kfree(state);
+
+ return 0;
+}
+
+static const struct i2c_device_id lgdt3306a_id_table[] = {
+ {"lgdt3306a", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, lgdt3306a_id_table);
+
+static struct i2c_driver lgdt3306a_driver = {
+ .driver = {
+ .name = "lgdt3306a",
+ .suppress_bind_attrs = true,
+ },
+ .probe = lgdt3306a_probe,
+ .remove = lgdt3306a_remove,
+ .id_table = lgdt3306a_id_table,
+};
+
+module_i2c_driver(lgdt3306a_driver);
+
MODULE_DESCRIPTION("LG Electronics LGDT3306A ATSC/QAM-B Demodulator Driver");
MODULE_AUTHOR("Fred Richter <frichter@hauppauge.com>");
MODULE_LICENSE("GPL");
diff -uprN linux-4.4.x-orig/drivers/media/dvb-frontends/lgdt3306a.h linux-4.4.x-new/drivers/media/dvb-frontends/lgdt3306a.h
--- linux-4.4.x-orig/drivers/media/dvb-frontends/lgdt3306a.h 2017-10-30 17:45:15.000000000 -0400
+++ linux-4.4.x-new/drivers/media/dvb-frontends/lgdt3306a.h 2019-05-13 20:39:59.778870885 -0400
@@ -21,6 +21,9 @@
#include <linux/i2c.h>
#include "dvb_frontend.h"
+#define LG3306_TUNERI2C_ON 0x00
+#define LG3306_TUNERI2C_OFF 0x80
+
enum lgdt3306a_mpeg_mode {
LGDT3306A_MPEG_PARALLEL = 0,
LGDT3306A_MPEG_SERIAL = 1,
@@ -55,6 +58,10 @@ struct lgdt3306a_config {
/* demod clock freq in MHz; 24 or 25 supported */
int xtalMHz;
+
+ /* returned by driver if using i2c bus multiplexing */
+ struct dvb_frontend **fe;
+ struct i2c_adapter **i2c_adapter;
};
#if IS_REACHABLE(CONFIG_DVB_LGDT3306A)
================================================
FILE: hauppauge/002-Hauppauge955D-em28xx-Tuner1.patch
================================================
diff -uprN linux-4.4.x-orig/Documentation/video4linux/CARDLIST.em28xx linux-4.4.x-new/Documentation/video4linux/CARDLIST.em28xx
--- linux-4.4.x-orig/Documentation/video4linux/CARDLIST.em28xx 2017-09-25 22:52:52.000000000 -0400
+++ linux-4.4.x-new/Documentation/video4linux/CARDLIST.em28xx 2019-05-04 08:16:31.909682143 -0400
@@ -96,3 +96,5 @@
95 -> Leadtek VC100 (em2861) [0413:6f07]
96 -> Terratec Cinergy T2 Stick HD (em28178)
97 -> Elgato EyeTV Hybrid 2008 INT (em2884) [0fd9:0018]
+ 98 -> PLEX PX-BCUD (em28178) [3275:0085]
+ 99 -> Hauppauge WinTV-dualHD DVB (em28174) [2040:0265]
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
--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-cards.c 2017-10-30 17:46:49.000000000 -0400
+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-cards.c 2019-05-04 08:20:03.092511255 -0400
@@ -492,6 +492,31 @@ static struct em28xx_reg_seq terratec_t2
};
/*
+ * 2040:0265 Hauppauge WinTV-dualHD DVB
+ * 2040:026d Hauppauge WinTV-dualHD ATSC/QAM
+ * reg 0x80/0x84:
+ * GPIO_0: Yellow LED tuner 1, 0=on, 1=off
+ * GPIO_1: Green LED tuner 1, 0=on, 1=off
+ * GPIO_2: Yellow LED tuner 2, 0=on, 1=off
+ * GPIO_3: Green LED tuner 2, 0=on, 1=off
+ * GPIO_5: Reset #2, 0=active
+ * GPIO_6: Reset #1, 0=active
+ */
+static struct em28xx_reg_seq hauppauge_dualhd_dvb[] = {
+ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0},
+ {0x0d, 0xff, 0xff, 200},
+ {0x50, 0x04, 0xff, 300},
+ {EM2874_R80_GPIO_P0_CTRL, 0xbf, 0xff, 100}, /* demod 1 reset */
+ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xdf, 0xff, 100}, /* demod 2 reset */
+ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100},
+ {EM2874_R5F_TS_ENABLE, 0x44, 0xff, 50},
+ {EM2874_R5D_TS1_PKT_SIZE, 0x05, 0xff, 50},
+ {EM2874_R5E_TS2_PKT_SIZE, 0x05, 0xff, 50},
+ {-1, -1, -1, -1},
+};
+
+/*
* Button definitions
*/
static struct em28xx_button std_snapshot_button[] = {
@@ -560,6 +585,22 @@ static struct em28xx_led pctv_80e_leds[]
{-1, 0, 0, 0},
};
+static struct em28xx_led hauppauge_dualhd_leds[] = {
+ {
+ .role = EM28XX_LED_DIGITAL_CAPTURING,
+ .gpio_reg = EM2874_R80_GPIO_P0_CTRL,
+ .gpio_mask = EM_GPIO_1,
+ .inverted = 1,
+ },
+ {
+ .role = EM28XX_LED_DIGITAL_CAPTURING_TS2,
+ .gpio_reg = EM2874_R80_GPIO_P0_CTRL,
+ .gpio_mask = EM_GPIO_3,
+ .inverted = 1,
+ },
+ {-1, 0, 0, 0},
+};
+
/*
* Board definitions
*/
@@ -2288,6 +2329,36 @@ struct em28xx_board em28xx_boards[] = {
.has_dvb = 1,
.ir_codes = RC_MAP_TERRATEC_SLIM_2,
},
+ /*
+ * 2040:0265 Hauppauge WinTV-dualHD (DVB version).
+ * Empia EM28274, 2x Silicon Labs Si2168, 2x Silicon Labs Si2157
+ */
+ [EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB] = {
+ .name = "Hauppauge WinTV-dualHD DVB",
+ .def_i2c_bus = 1,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = hauppauge_dualhd_dvb,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .leds = hauppauge_dualhd_leds,
+ },
+ /*
+ * 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM).
+ * Empia EM28274, 2x LG LGDT3306A, 2x Silicon Labs Si2157
+ */
+ [EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595] = {
+ .name = "Hauppauge WinTV-dualHD 01595 ATSC/QAM",
+ .def_i2c_bus = 1,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = hauppauge_dualhd_dvb,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .leds = hauppauge_dualhd_leds,
+ },
};
EXPORT_SYMBOL_GPL(em28xx_boards);
@@ -2411,6 +2482,10 @@ struct usb_device_id em28xx_id_table[] =
.driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
{ USB_DEVICE(0x2040, 0x651f),
.driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 },
+ { USB_DEVICE(0x2040, 0x0265),
+ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB },
+ { USB_DEVICE(0x2040, 0x026d),
+ .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 },
{ USB_DEVICE(0x0438, 0xb002),
.driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
{ USB_DEVICE(0x2001, 0xf112),
@@ -2804,6 +2879,8 @@ static void em28xx_card_setup(struct em2
case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C:
+ case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB:
+ case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595:
{
struct tveeprom tv;
diff -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
--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-dvb.c 2017-10-30 17:46:44.000000000 -0400
+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-dvb.c 2019-05-04 08:20:03.092511255 -0400
@@ -36,6 +36,7 @@
#include "lgdt330x.h"
#include "lgdt3305.h"
+#include "lgdt3306a.h"
#include "zl10353.h"
#include "s5h1409.h"
#include "mt352.h"
@@ -858,6 +859,17 @@ static struct tda18271_config pinnacle_8
.role = TDA18271_MASTER,
};
+static struct lgdt3306a_config hauppauge_01595_lgdt3306a_config = {
+ .qam_if_khz = 4000,
+ .vsb_if_khz = 3250,
+ .spectral_inversion = 0,
+ .deny_i2c_rptr = 0,
+ .mpeg_mode = LGDT3306A_MPEG_SERIAL,
+ .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE,
+ .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH,
+ .xtalMHz = 25,
+};
+
/* ------------------------------------------------------------------ */
static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev)
@@ -1717,6 +1729,132 @@ static int em28xx_dvb_init(struct em28xx
if (!try_module_get(client->dev.driver->owner)) {
i2c_unregister_device(client);
module_put(dvb->i2c_client_demod->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ dvb->i2c_client_tuner = client;
+ }
+ break;
+ case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB:
+ {
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ struct i2c_board_info info;
+ struct si2168_config si2168_config;
+ struct si2157_config si2157_config;
+
+ /* attach demod */
+ memset(&si2168_config, 0, sizeof(si2168_config));
+ si2168_config.i2c_adapter = &adapter;
+ si2168_config.fe = &dvb->fe[0];
+ si2168_config.ts_mode = SI2168_TS_SERIAL;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+ info.addr = 0x64;
+ info.platform_data = &si2168_config;
+ request_module(info.type);
+ client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ dvb->i2c_client_demod = client;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+ si2157_config.fe = dvb->fe[0];
+ si2157_config.if_port = 1;
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &si2157_config;
+ request_module(info.type);
+ client = i2c_new_device(adapter, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ module_put(dvb->i2c_client_demod->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ module_put(dvb->i2c_client_demod->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ dvb->i2c_client_tuner = client;
+
+ }
+ break;
+ case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595:
+ {
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ struct i2c_board_info info = {};
+ struct lgdt3306a_config lgdt3306a_config;
+ struct si2157_config si2157_config = {};
+
+ /* attach demod */
+ lgdt3306a_config = hauppauge_01595_lgdt3306a_config;
+ lgdt3306a_config.fe = &dvb->fe[0];
+ lgdt3306a_config.i2c_adapter = &adapter;
+ strlcpy(info.type, "lgdt3306a", sizeof(info.type));
+ info.addr = 0x59;
+ info.platform_data = &lgdt3306a_config;
+ request_module(info.type);
+ client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus],
+ &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ dvb->i2c_client_demod = client;
+
+ /* attach tuner */
+ si2157_config.fe = dvb->fe[0];
+ si2157_config.if_port = 1;
+ si2157_config.inversion = 1;
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ si2157_config.mdev = dev->media_dev;
+#endif
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", sizeof(info.type));
+ info.addr = 0x60;
+ info.platform_data = &si2157_config;
+ request_module(info.type);
+
+ client = i2c_new_device(adapter, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ module_put(dvb->i2c_client_demod->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod);
+ result = -ENODEV;
+ goto out_free;
+ }
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ module_put(dvb->i2c_client_demod->dev.driver->owner);
i2c_unregister_device(dvb->i2c_client_demod);
result = -ENODEV;
goto out_free;
diff -uprN linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx.h linux-4.4.x-new/drivers/media/usb/em28xx/em28xx.h
--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx.h 2017-10-30 17:46:43.000000000 -0400
+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx.h 2019-05-04 08:20:03.092511255 -0400
@@ -145,6 +145,9 @@
#define EM2861_BOARD_LEADTEK_VC100 95
#define EM28178_BOARD_TERRATEC_T2_STICK_HD 96
#define EM2884_BOARD_ELGATO_EYETV_HYBRID_2008 97
+#define EM28178_BOARD_PLEX_PX_BCUD 98
+#define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB 99
+#define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 100
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -412,6 +415,7 @@ enum em28xx_adecoder {
enum em28xx_led_role {
EM28XX_LED_ANALOG_CAPTURING = 0,
EM28XX_LED_DIGITAL_CAPTURING,
+ EM28XX_LED_DIGITAL_CAPTURING_TS2,
EM28XX_LED_ILLUMINATION,
EM28XX_NUM_LED_ROLES, /* must be the last */
};
diff -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
--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-reg.h 2017-10-30 17:46:42.000000000 -0400
+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-reg.h 2019-05-04 08:15:10.896596159 -0400
@@ -193,6 +193,19 @@
/* em2874 registers */
#define EM2874_R50_IR_CONFIG 0x50
#define EM2874_R51_IR 0x51
+#define EM2874_R5D_TS1_PKT_SIZE 0x5d
+#define EM2874_R5E_TS2_PKT_SIZE 0x5e
+ /*
+ * For both TS1 and TS2, In isochronous mode:
+ * 0x01 188 bytes
+ * 0x02 376 bytes
+ * 0x03 564 bytes
+ * 0x04 752 bytes
+ * 0x05 940 bytes
+ * In bulk mode:
+ * 0x01..0xff total packet count in 188-byte
+ */
+
#define EM2874_R5F_TS_ENABLE 0x5f
/* em2874/174/84, em25xx, em276x/7x/8x GPIO registers */
================================================
FILE: hauppauge/003-Hauppauge955D-em28xx-Tuner2-v6.patch
================================================
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
--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-cards.c 2019-05-06 20:53:45.596707923 -0400
+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-cards.c 2019-05-06 20:55:37.646304153 -0400
@@ -2341,6 +2341,7 @@ struct em28xx_board em28xx_boards[] = {
.tuner_type = TUNER_ABSENT,
.tuner_gpio = hauppauge_dualhd_dvb,
.has_dvb = 1,
+ .has_dual_ts = 1,
.ir_codes = RC_MAP_HAUPPAUGE,
.leds = hauppauge_dualhd_leds,
},
@@ -2356,6 +2357,7 @@ struct em28xx_board em28xx_boards[] = {
.tuner_type = TUNER_ABSENT,
.tuner_gpio = hauppauge_dualhd_dvb,
.has_dvb = 1,
+ .has_dual_ts = 1,
.ir_codes = RC_MAP_HAUPPAUGE,
.leds = hauppauge_dualhd_leds,
},
@@ -3090,6 +3092,8 @@ static void flush_request_modules(struct
*/
static void em28xx_release_resources(struct em28xx *dev)
{
+ struct usb_device *udev;
+
/*FIXME: I2C IR should be disconnected */
mutex_lock(&dev->lock);
@@ -3098,7 +3102,8 @@ static void em28xx_release_resources(str
em28xx_i2c_unregister(dev, 1);
em28xx_i2c_unregister(dev, 0);
- usb_put_dev(dev->udev);
+ if (dev->ts == PRIMARY_TS)
+ usb_put_dev(udev);
/* Mark device as unused */
clear_bit(dev->devno, em28xx_devused);
@@ -3297,6 +3302,35 @@ static int em28xx_init_dev(struct em28xx
return 0;
}
+int em28xx_duplicate_dev(struct em28xx *dev)
+{
+ int nr;
+ struct em28xx *sec_dev = kzalloc(sizeof(*sec_dev), GFP_KERNEL);
+
+ if (sec_dev == NULL) {
+ dev->dev_next = NULL;
+ return -ENOMEM;
+ }
+ memcpy(sec_dev, dev, sizeof(sizeof(*sec_dev)));
+ /* Check to see next free device and mark as used */
+ do {
+ nr = find_first_zero_bit(em28xx_devused, EM28XX_MAXBOARDS);
+ if (nr >= EM28XX_MAXBOARDS) {
+ /* No free device slots */
+ dev_warn(&dev->udev->dev, ": Supports only %i em28xx boards.\n",
+ EM28XX_MAXBOARDS);
+ kfree(sec_dev);
+ dev->dev_next = NULL;
+ return -ENOMEM;
+ }
+ } while (test_and_set_bit(nr, em28xx_devused));
+ sec_dev->devno = nr;
+ snprintf(sec_dev->name, 28, "em28xx #%d", nr);
+ sec_dev->dev_next = NULL;
+ dev->dev_next = sec_dev;
+ return 0;
+}
+
/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
@@ -3417,6 +3451,17 @@ static int em28xx_usb_probe(struct usb_i
}
}
break;
+ case 0x85:
+ if (usb_endpoint_xfer_isoc(e)) {
+ if (size > dev->dvb_max_pkt_size_isoc_ts2) {
+ dev->dvb_ep_isoc_ts2 = e->bEndpointAddress;
+ dev->dvb_max_pkt_size_isoc_ts2 = size;
+ dev->dvb_alt_isoc = i;
+ }
+ } else {
+ dev->dvb_ep_bulk_ts2 = e->bEndpointAddress;
+ }
+ break;
}
}
/* NOTE:
@@ -3431,6 +3476,8 @@ static int em28xx_usb_probe(struct usb_i
* 0x83 isoc* => audio
* 0x84 isoc => digital
* 0x84 bulk => analog or digital**
+ * 0x85 isoc => digital TS2
+ * 0x85 bulk => digital TS2
* (*: audio should always be isoc)
* (**: analog, if ep 0x82 is isoc, otherwise digital)
*
@@ -3499,6 +3546,10 @@ static int em28xx_usb_probe(struct usb_i
dev->has_video = has_video;
dev->ifnum = ifnum;
+ dev->ts = PRIMARY_TS;
+ snprintf(dev->name, 28, "em28xx");
+ dev->dev_next = NULL;
+
if (has_vendor_audio) {
printk(KERN_INFO DRIVER_NAME ": Audio interface %i found %s\n",
ifnum, "(Vendor Class)");
@@ -3568,6 +3619,65 @@ static int em28xx_usb_probe(struct usb_i
dev->dvb_xfer_bulk ? "bulk" : "isoc");
}
+ if (dev->board.has_dual_ts && em28xx_duplicate_dev(dev) == 0) {
+ dev->dev_next->ts = SECONDARY_TS;
+ dev->dev_next->alt = -1;
+ dev->dev_next->is_audio_only = has_vendor_audio &&
+ !(has_video || has_dvb);
+ dev->dev_next->has_video = false;
+ dev->dev_next->ifnum = ifnum;
+ dev->dev_next->model = id->driver_info;
+
+ mutex_init(&dev->dev_next->lock);
+ retval = em28xx_init_dev(dev->dev_next, udev, interface,
+ dev->dev_next->devno);
+ if (retval)
+ goto err_free;
+
+ dev->dev_next->board.ir_codes = NULL; /* No IR for 2nd tuner */
+ dev->dev_next->board.has_ir_i2c = 0; /* No IR for 2nd tuner */
+
+ if (usb_xfer_mode < 0) {
+ if (dev->dev_next->board.is_webcam)
+ try_bulk = 1;
+ else
+ try_bulk = 0;
+ } else {
+ try_bulk = usb_xfer_mode > 0;
+ }
+
+ /* Select USB transfer types to use */
+ if (has_dvb) {
+ if (!dev->dvb_ep_isoc_ts2 ||
+ (try_bulk && dev->dvb_ep_bulk_ts2))
+ dev->dev_next->dvb_xfer_bulk = 1;
+ printk(KERN_INFO DRIVER_NAME ": dvb ts2 set to %s mode.\n",
+ dev->dev_next->dvb_xfer_bulk ? "bulk" : "isoc");
+ }
+
+ dev->dev_next->dvb_ep_isoc = dev->dvb_ep_isoc_ts2;
+ dev->dev_next->dvb_ep_bulk = dev->dvb_ep_bulk_ts2;
+ dev->dev_next->dvb_max_pkt_size_isoc = dev->dvb_max_pkt_size_isoc_ts2;
+ dev->dev_next->dvb_alt_isoc = dev->dvb_alt_isoc;
+
+ /* Configuare hardware to support TS2*/
+ if (dev->dvb_xfer_bulk) {
+ /* The ep4 and ep5 are configuared for BULK */
+ em28xx_write_reg(dev, 0x0b, 0x96);
+ mdelay(100);
+ em28xx_write_reg(dev, 0x0b, 0x80);
+ mdelay(100);
+ } else {
+ /* The ep4 and ep5 are configuared for ISO */
+ em28xx_write_reg(dev, 0x0b, 0x96);
+ mdelay(100);
+ em28xx_write_reg(dev, 0x0b, 0x82);
+ mdelay(100);
+ }
+
+ kref_init(&dev->dev_next->ref);
+ }
+
kref_init(&dev->ref);
request_modules(dev);
@@ -3605,6 +3715,13 @@ static void em28xx_usb_disconnect(struct
if (!dev)
return;
+ if (dev->dev_next != NULL) {
+ dev->dev_next->disconnected = 1;
+ printk(KERN_INFO DRIVER_NAME ": Disconnecting %s\n",
+ dev->dev_next->name);
+ flush_request_modules(dev->dev_next);
+ }
+
dev->disconnected = 1;
em28xx_info("Disconnecting %s\n", dev->name);
@@ -3613,7 +3730,14 @@ static void em28xx_usb_disconnect(struct
em28xx_close_extension(dev);
+ if (dev->dev_next != NULL)
+ em28xx_release_resources(dev->dev_next);
em28xx_release_resources(dev);
+
+ if (dev->dev_next != NULL) {
+ kref_put(&dev->dev_next->ref, em28xx_free_device);
+ dev->dev_next = NULL;
+ }
kref_put(&dev->ref, em28xx_free_device);
}
diff -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
--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-core.c 2017-10-30 17:46:43.000000000 -0400
+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-core.c 2019-05-06 20:55:37.642304096 -0400
@@ -636,10 +636,18 @@ int em28xx_capture_start(struct em28xx *
dev->chip_id == CHIP_ID_EM28174 ||
dev->chip_id == CHIP_ID_EM28178) {
/* The Transport Stream Enable Register moved in em2874 */
- rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
- start ?
- EM2874_TS1_CAPTURE_ENABLE : 0x00,
- EM2874_TS1_CAPTURE_ENABLE);
+ if (dev->ts == PRIMARY_TS)
+ rc = em28xx_write_reg_bits(dev,
+ EM2874_R5F_TS_ENABLE,
+ start ?
+ EM2874_TS1_CAPTURE_ENABLE : 0x00,
+ EM2874_TS1_CAPTURE_ENABLE);
+ else
+ rc = em28xx_write_reg_bits(dev,
+ EM2874_R5F_TS_ENABLE,
+ start ?
+ EM2874_TS2_CAPTURE_ENABLE : 0x00,
+ EM2874_TS2_CAPTURE_ENABLE);
} else {
/* FIXME: which is the best order? */
/* video registers are sampled by VREF */
@@ -1073,7 +1081,11 @@ int em28xx_register_extension(struct em2
mutex_lock(&em28xx_devlist_mutex);
list_add_tail(&ops->next, &em28xx_extension_devlist);
list_for_each_entry(dev, &em28xx_devlist, devlist) {
- ops->init(dev);
+ if (ops->init) {
+ ops->init(dev);
+ if (dev->dev_next != NULL)
+ ops->init(dev->dev_next);
+ }
}
mutex_unlock(&em28xx_devlist_mutex);
printk(KERN_INFO "em28xx: Registered (%s) extension\n", ops->name);
@@ -1087,7 +1099,11 @@ void em28xx_unregister_extension(struct
mutex_lock(&em28xx_devlist_mutex);
list_for_each_entry(dev, &em28xx_devlist, devlist) {
- ops->fini(dev);
+ if (ops->fini) {
+ if (dev->dev_next != NULL)
+ ops->fini(dev->dev_next);
+ ops->fini(dev);
+ }
}
list_del(&ops->next);
mutex_unlock(&em28xx_devlist_mutex);
@@ -1102,8 +1118,11 @@ void em28xx_init_extension(struct em28xx
mutex_lock(&em28xx_devlist_mutex);
list_add_tail(&dev->devlist, &em28xx_devlist);
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->init)
+ if (ops->init) {
ops->init(dev);
+ if (dev->dev_next != NULL)
+ ops->init(dev->dev_next);
+ }
}
mutex_unlock(&em28xx_devlist_mutex);
}
@@ -1114,8 +1133,11 @@ void em28xx_close_extension(struct em28x
mutex_lock(&em28xx_devlist_mutex);
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->fini)
+ if (ops->fini) {
+ if (dev->dev_next != NULL)
+ ops->fini(dev->dev_next);
ops->fini(dev);
+ }
}
list_del(&dev->devlist);
mutex_unlock(&em28xx_devlist_mutex);
@@ -1130,6 +1152,8 @@ int em28xx_suspend_extension(struct em28
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
if (ops->suspend)
ops->suspend(dev);
+ if (dev->dev_next != NULL)
+ ops->suspend(dev->dev_next);
}
mutex_unlock(&em28xx_devlist_mutex);
return 0;
@@ -1144,6 +1168,8 @@ int em28xx_resume_extension(struct em28x
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
if (ops->resume)
ops->resume(dev);
+ if (dev->dev_next != NULL)
+ ops->resume(dev->dev_next);
}
mutex_unlock(&em28xx_devlist_mutex);
return 0;
diff -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
--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-dvb.c 2019-05-06 20:53:45.600707980 -0400
+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-dvb.c 2019-05-06 20:55:37.646304153 -0400
@@ -212,7 +212,6 @@ static int em28xx_start_streaming(struct
dvb_alt = dev->dvb_alt_isoc;
}
- usb_set_interface(dev->udev, dev->ifnum, dvb_alt);
rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
if (rc < 0)
return rc;
@@ -1042,8 +1041,9 @@ static void em28xx_unregister_dvb(struct
static int em28xx_dvb_init(struct em28xx *dev)
{
- int result = 0;
+ int result = 0, dvb_alt = 0;
struct em28xx_dvb *dvb;
+ /* struct usb_device *udev; */
if (dev->is_audio_only) {
/* Shouldn't initialize IR for this interface */
@@ -1631,7 +1631,10 @@ static int em28xx_dvb_init(struct em28xx
si2168_config.ts_mode = SI2168_TS_PARALLEL;
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "si2168", I2C_NAME_SIZE);
- info.addr = 0x64;
+ if (dev->ts == PRIMARY_TS)
+ info.addr = 0x64;
+ else
+ info.addr = 0x67;
info.platform_data = &si2168_config;
request_module(info.type);
client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);
@@ -1654,7 +1657,10 @@ static int em28xx_dvb_init(struct em28xx
si2157_config.if_port = 1;
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "si2157", I2C_NAME_SIZE);
- info.addr = 0x60;
+ if (dev->ts == PRIMARY_TS)
+ info.addr = 0x60;
+ else
+ info.addr = 0x63;
info.platform_data = &si2157_config;
request_module(info.type);
client = i2c_new_device(adapter, &info);
@@ -1814,7 +1820,10 @@ static int em28xx_dvb_init(struct em28xx
lgdt3306a_config.fe = &dvb->fe[0];
lgdt3306a_config.i2c_adapter = &adapter;
strlcpy(info.type, "lgdt3306a", sizeof(info.type));
- info.addr = 0x59;
+ if (dev->ts == PRIMARY_TS)
+ info.addr = 0x59;
+ else
+ info.addr = 0x0e;
info.platform_data = &lgdt3306a_config;
request_module(info.type);
client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus],
@@ -1841,7 +1850,10 @@ static int em28xx_dvb_init(struct em28xx
#endif
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "si2157", sizeof(info.type));
- info.addr = 0x60;
+ if (dev->ts == PRIMARY_TS)
+ info.addr = 0x60;
+ else
+ info.addr = 0x62;
info.platform_data = &si2157_config;
request_module(info.type);
@@ -1884,6 +1896,15 @@ static int em28xx_dvb_init(struct em28xx
if (result < 0)
goto out_free;
+ if (dev->dvb_xfer_bulk) {
+ dvb_alt = 0;
+ } else { /* isoc */
+ dvb_alt = dev->dvb_alt_isoc;
+ }
+
+ /* udev = interface_to_usbdev(dev->intf); */
+ usb_set_interface(dev->udev, dev->ifnum, dvb_alt);
+ /* usb_set_interface(udev, dev->ifnum, dvb_alt); */
em28xx_info("DVB extension successfully initialized\n");
kref_get(&dev->ref);
diff -uprN linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx.h linux-4.4.x-new/drivers/media/usb/em28xx/em28xx.h
--- linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx.h 2019-05-06 20:53:45.600707980 -0400
+++ linux-4.4.x-new/drivers/media/usb/em28xx/em28xx.h 2019-05-06 20:55:37.646304153 -0400
@@ -216,6 +216,9 @@
/* max. number of button state polling addresses */
#define EM28XX_NUM_BUTTON_ADDRESSES_MAX 5
+#define PRIMARY_TS 0
+#define SECONDARY_TS 1
+
enum em28xx_mode {
EM28XX_SUSPEND,
EM28XX_ANALOG_MODE,
@@ -462,6 +465,7 @@ struct em28xx_board {
unsigned int mts_firmware:1;
unsigned int max_range_640_480:1;
unsigned int has_dvb:1;
+ unsigned int has_dual_ts:1;
unsigned int is_webcam:1;
unsigned int valid:1;
unsigned int has_ir_i2c:1;
@@ -612,7 +616,6 @@ struct em28xx {
struct em28xx_IR *ir;
/* generic device properties */
- char name[30]; /* name (including minor) of the device */
int model; /* index in the device_data struct */
int devno; /* marks the number of this device */
enum em28xx_chip_id chip_id;
@@ -623,6 +626,7 @@ struct em28xx {
unsigned int is_audio_only:1;
enum em28xx_int_audio_type int_audio_type;
enum em28xx_usb_audio_type usb_audio_type;
+ unsigned char name[32];
struct em28xx_board board;
@@ -684,6 +688,8 @@ struct em28xx {
u8 ifnum; /* number of the assigned usb interface */
u8 analog_ep_isoc; /* address of isoc endpoint for analog */
u8 analog_ep_bulk; /* address of bulk endpoint for analog */
+ u8 dvb_ep_isoc_ts2; /* address of isoc endpoint for DVB TS2*/
+ u8 dvb_ep_bulk_ts2; /* address of bulk endpoint for DVB TS2*/
u8 dvb_ep_isoc; /* address of isoc endpoint for DVB */
u8 dvb_ep_bulk; /* address of bulk endpoint for DVB */
int alt; /* alternate setting */
@@ -697,6 +703,8 @@ struct em28xx {
int dvb_alt_isoc; /* alternate setting for DVB isoc transfers */
unsigned int dvb_max_pkt_size_isoc; /* isoc max packet size of the
selected DVB ep at dvb_alt */
+ unsigned int dvb_max_pkt_size_isoc_ts2; /* isoc max packet size of the
+ selected DVB ep at dvb_alt */
unsigned int dvb_xfer_bulk:1; /* use bulk instead of isoc
transfers for DVB */
char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
@@ -722,6 +730,9 @@ struct em28xx {
/* Snapshot button input device */
char snapshot_button_path[30]; /* path of the input dev */
struct input_dev *sbutton_input_dev;
+
+ struct em28xx *dev_next;
+ int ts;
};
#define kref_to_dev(d) container_of(d, struct em28xx, ref)
================================================
FILE: hauppauge.sh
================================================
#!/bin/sh
#-------------------------------------------
# hauppauge.sh
#
# by: Vincent Fortier
# email: th0ma7_AT_gmail.com
#
# Simplify the status, start, stop, reset
# of tvheadend service along with loading
# and unloading of all necessary modules
#-------------------------------------------
SERVICE="pkgctl-tvheadend"
MODULE_PATH=/usr/local/lib/modules/$(uname -r)
MODULES="em28xx_dvb em28xx lgdt3306a si2157 tveeprom v4l2_common dvb_usb rc_core dvb_core videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_common videodev media"
RESET=em28xx_dvb
#
declare -a SYSCTL_VAR=('vm.dirty_expire_centisecs' 'vm.swappiness')
declare -a SYSCTL_VALUE=('300' '1')
#
NOAUTOSUSPEND=em28xx
declare -a AUTOSUSPEND_VAR=('autosuspend_delay_ms' 'autosuspend')
declare -a AUTOSUSPEND_VALUE=('-1000' '-1')
USBAutoSuspend() {
usbID=$(lsusb -i | grep $NOAUTOSUSPEND | awk '{print $1}' | cut -f1 -d:)
if [ ! "$usbID" ]; then
echo "kernel USB (none) autosuspend values N/A"
else
echo "kernel USB ($usbID) autosuspend values..."
for index in "${!AUTOSUSPEND_VAR[@]}"
do
declare sys=/sys/bus/usb/devices/$usbID/power/${AUTOSUSPEND_VAR[$index]}
declare -i current=$(cat $sys)
declare -i new=${AUTOSUSPEND_VALUE[$index]}
printf '\t(%s)%-25s' $usbID ${AUTOSUSPEND_VAR[$index]}
if [ $current -eq $new ]; then
printf '[%5s] -> OK\n' "$current"
else
[ "$1" = "check" ] || echo ${AUTOSUSPEND_VALUE[$index]} | sudo tee $sys 1>/dev/null
printf '[%5s] -> [%5s]\n' "$current" "$new"
fi
done
fi
}
Sysctl() {
echo "kernel sysctl values... "
for index in "${!SYSCTL_VAR[@]}"
do
declare -i current=$(sysctl -n ${SYSCTL_VAR[$index]})
declare -i new=${SYSCTL_VALUE[$index]}
printf '\t%-30s' ${SYSCTL_VAR[$index]}
if [ $current -eq $new ]; then
printf '[%5s] -> OK\n' "$current"
else
[ "$1" = "check" ] || sysctl -w ${SYSCTL_VAR[$index]}=$new 2>/dev/null 1>&2
printf '[%5s] -> [%5s]\n' "$current" "$new"
fi
done
}
ModuleLOAD() {
echo "Loading kernel modules... "
for item in $MODULES; do echo $item; done | tac | while read module
do
module_load=$(echo "${module}.ko" | sed 's/_/-/g')
printf '\t%-30s' $module_load
status=$(lsmod | grep "^$module ")
if [ $? -eq 0 -a "status" ]; then
echo "Loaded"
else
insmod $MODULE_PATH/$module_load
[ $? -eq 0 ] && echo "OK" || echo "ERROR"
fi
done
}
ModuleUNLOAD() {
# Unload Hauppauge updated drivers
echo "Unloading kernel modules... "
for module in $MODULES
do
printf '\t%-30s' $module
status=$(lsmod | grep "^$module ")
if [ $? -eq 0 -a "status" ]; then
rmmod $module
echo -ne "OK\n"
else
echo -ne "N/A\n"
fi
done
}
ModuleSTATUS() {
# Unload Hauppauge updated drivers
echo "kernel module status... "
for module in $MODULES
do
printf '\t%-30s' $module
status=$(lsmod | grep "^$module ")
if [ $? -eq 0 -a "status" ]; then
echo -ne "OK\n"
else
echo -ne "N/A\n"
fi
done
}
ModuleRESET() {
reset=$1
module=$(echo "${reset}.ko" | sed 's/_/-/g')
rmmod $reset
sleep 1
insmod $MODULE_PATH/$module
}
ServiceSTATUS() {
status=$(synoservice --status $SERVICE | grep "\[$SERVICE\] status" | awk -F"status=" '{print $2}' | sed -e 's/\[//g' -e 's/\]//g')
running=$(synoservice --status $SERVICE | grep "\[$SERVICE\] is" | awk -F"is " '{print $2}' | sed -e 's/\.//g' -e 's/ //g')
pid=$(pidof tvheadend)
[ ! "$pid" ] && pid="---"
case "$1" in
"full" ) printf '%-38s' "Status $SERVICE..."
echo $status,$running,$pid
;;
* ) echo $running;;
esac
}
ServiceSTART() {
status=$(ServiceSTATUS)
#echo $status 1>&2
printf '%-38s' "Starting $SERVICE..."
module=$(lsmod | grep "^$RESET ")
if [ $? -ne 0 -a ! "module" ]; then
echo "ERROR module $RESET not found!"
return
fi
if [ "$status" = "stop" ]; then
synoservice --start $SERVICE
echo "OK"
elif [ "$status" = "start" ]; then
# Check if sevice isn't already started!
if [ "$(pidof tvheadend)" ]; then
echo "Started"
else
synoservice --restart $SERVICE 2>/dev/null 1>&2
echo "Restart"
fi
else
echo "N/A"
fi
}
ServiceSTOP() {
status=$(ServiceSTATUS)
#echo $status 1>&2
printf '%-38s' "Stopping $SERVICE..."
if [ "$status" = "start" ]; then
#synoservice --disable $SERVICE
synoservice --stop $SERVICE 2>/dev/null 1>&2
echo "OK"
else
echo "N/A"
fi
# Is it really off?
if [ "`pidof tvheadend`" ]; then
printf '%-33s' "Killing $SERVICE..."
kill -9 $(pidof tvheadend)
echo "killed"
fi
}
Usage() {
echo "$0 - Usage: start, stop, status, reset, restart, load"
}
case $1 in
start ) ModuleLOAD
USBAutoSuspend
Sysctl
ServiceSTART
;;
stop ) ServiceSTOP
ModuleUNLOAD
;;
status ) ServiceSTATUS full
ModuleSTATUS
USBAutoSuspend check
Sysctl check
;;
restart ) ServiceSTOP
ModuleUNLOAD
sleep 1
ModuleLOAD
USBAutoSuspend
Sysctl
ServiceSTART
;;
reset ) ServiceSTOP
ModuleRESET $RESET
ServiceSTART
ServiceSTATUS full
ModuleSTATUS
;;
load ) ModuleLOAD
USBAutoSuspend
Sysctl
;;
* ) Usage
;;
esac
exit 0
================================================
FILE: kernel/synocli-kernelmodule.sh
================================================
#!/bin/bash
#########################################################################
# Written by: th0ma7@gmail.com
# Part of SynoCommunity Developpers
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#########################################################################
#------------------------------------------------
# Make sure an argument was passed
#------------------------------------------------
usage() {
echo
printf '%10s %s\n' "Usage :" "$0 [-s|--spk <package>] [<insmod,start|rmmod,stop|reload,restart|status>] module1.ko module2.ko ..."
printf '%20s %s\n' "Optional :" "[-c|--config <file>:<option1>,<option2>,...]"
printf '%20s %s\n' "" "[-u|--udev <file>]"
echo
printf '%40s %s\n' "[-s|--spk <package>] : " "SynoCommunity package name containing kernel modules"
printf '%40s %s\n' "[<insmod|rmmod|reload|status>] : " "Action to be performed"
printf '%40s %s\n' "[-h|--help] : " "Print this help"
printf '%40s %s\n' "[-v|--verbose] : " "Verbose mode"
echo
printf '%10s %s\n' "" "Examples :"
printf '%20s %s\n' "" "$0 --spk synokernel-cdrom --verbose cdrom sr_mod status"
printf '%20s %s\n' "" "$0 --spk synokernel-cdrom --config synokernel-cdrom.cfg:default status"
printf '%20s %s\n' "" "$0 --spk synokernel-usbserial --udev 60-synokernel-usbserial.rules usbserial ch341 cp210x status"
printf '%20s %s\n' "" "$0 --spk synokernel-usbserial --config synokernel-usbserial.cfg:ch341,cp210x status"
echo
}
#------------------------------------------------
# Print detailed information for debugging
#------------------------------------------------
verbose() {
printf '%20s %s\n' "" "$0 (verbose)"
printf '%60s %s\n' "SynoCommunity kernel driver package name (SPK)" "[${SPK}]"
printf '%60s %s\n' "SynoCommunity configuration file (SPK_CFG)" "[${SPK_CFG}]"
printf '%60s %s\n' "SynoCommunity configuration path (SPK_CFG_PATH)" "[${SPK_CFG_PATH}]"
printf '%60s %s\n' "SynoCommunity configuration option (SPK_CFG_OPT)" "[${SPK_CFG_OPT}]"
printf '%60s %s\n' "Synology NAS arch (ARCH)" "[${ARCH}]"
printf '%60s %s\n' "Synology DSM version (DSM_VERSION)" "[${DSM_VERSION}]"
printf '%60s %s\n' "Running kernel version (KVER)" "[${KVER}]"
printf '%60s %s\n' "Module action insmod|rmmod|reload|status (ACTION)" "[${ACTION}]"
printf '%60s %s\n' "Kernel modules path (MPATH)" "[${MPATH}]"
printf '%60s %s\n' "Full kernel modules path (KPATH)" "[${KPATH}]"
printf '%60s %s\n' "Device firmware path (FPATH)" "[${FPATH}]"
printf '%60s %s\n' "udev rules.d path (UPATH)" "[${UPATH}]"
printf '%60s %s\n' "udev rules.d file (URULE)" "[${URULE}]"
printf '%60s %s\n' "Kernel objects list (KO_LIST)" "[${KO_LIST}]"
printf '%60s %s\n' "Kernel objects found (KO_PATH)" "[${KO_PATH}]"
}
#------------------------------------------------
# Get all kernel modules from configuration file
# based on passed configuration option
#------------------------------------------------
get_ko_list() {
ko_list_cfg=""
if [ "${SPK_CFG}" ]; then
# Check that configuration exists (if requested)
if [ ! -f ${SPK_CFG_PATH}/${SPK_CFG} ]; then
usage
echo -ne "\nERROR: Configuration file [${SPK_CFG_PATH}/${SPK_CFG}] does not exist or inaccessible...\n\n"
exit 1
fi
# Always include default first
ko_list_cfg=$(sed -n "s/^default:\(.*\)/\1/p" ${SPK_CFG_PATH}/${SPK_CFG})
if [ ! "${ko_list_cfg}" ]; then
usage
echo -ne "\nERROR: Configuration option [default] not found in file [${SPK_CFG_PATH}/${SPK_CFG}]...\n\n"
exit 1
fi
IFS=","
for config in ${SPK_CFG_OPT}
do
ko_list_cfg_tmp=$(sed -n "s/^${config}:\(.*\)/\1/p" ${SPK_CFG_PATH}/${SPK_CFG})
if [ ! "${ko_list_cfg}" ]; then
usage
echo -ne "\nERROR: Configuration option [${config}:] not found in file [${SPK_CFG_PATH}/${SPK_CFG}]...\n\n"
exit 1
fi
ko_list_cfg+="${ko_list_cfg_tmp} "
done
IFS=" "
fi
# Return merged module list from config file and
# parameters passed as arguments but keep its order,
# starting with the config file followed by args
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
}
#------------------------------------------------
# Modules can be passed as:
# <module>
# <module>.ko
# /<path>/<module>*
# The following find the right module as needed
#------------------------------------------------
ko_path_match() {
ko_find=""
ko_path=""
ko_missing=""
for ko in $KO_LIST
do
# first check if the name
# matches to a path
if [ ! "$(echo $ko | grep '/')" ]; then
# Ensure to add .ko if needed
[ ! "$(echo $ko | grep '.ko$')" ] && ko=$ko.ko
# Replace any '-' or '_' by '[-_]
ko=$(echo ${ko} | sed -r 's/[-_]/[-_]/g')
# Find full module kernel object path
ko_find=$(find $KPATH -name $ko)
[ ! "$ko_find" ] && ko_missing+="$ko "
fi
ko_path+="${ko_find##*${KVER}} "
done
# Return missing modules in case of error
# else return the list of found modules
[ ! "${ko_missing}" ] \
&& echo "$ko_path" | xargs \
|| echo "missing: ${ko_missing}" | xargs
}
# exit if no parameters passed
[ $# -eq 0 ] && usage && exit 1
# must be root to load/unload kernel modules
if [ ! "$(id -un)" = "root" ]; then
verbose && usage
echo
echo "ERROR: Must have root or sudo priviledges..."
echo
exit 1
fi
###
### Global variables
###
SPK="" # SynoCommunity kernel driver package name
SPK_CFG="" # SynoCommunity configuration file
SPK_CFG_OPT="" # SynoCommunity configuration option
SPK_CFG_PATH="" # SynoCommunity configuration path
ACTION="" # Module action insmod|rmmod|reload|status
VERBOSE="FALSE" # Set verbose mode
HELP="FALSE" # Print help
URULE="FALSE" # Set udev rules to false by default
while [ $# -gt 0 ]
do
case $1 in
-s|--spk ) shift 1
SPK=$1;;
-c|--config ) shift 1
SPK_CFG=$(echo $1 | cut -f1 -d:)
SPK_CFG_OPT=$(echo $1 | cut -f2 -d:);;
-u|--udev ) shift 1
URULE=$(echo $1 | cut -f1 -d:);;
-h|--help ) HELP="TRUE";;
insmod|rmmod|reload|start|stop|restart|status ) ACTION=$1;;
-v|--verbose ) VERBOSE="TRUE";;
* ) KO_LIST_ARG+="$1 ";;
esac
shift 1;
done
###
### Other global variables
###
ARCH=$(uname -a | awk '{print $NF}' | cut -f2 -d_) # Synology NAS arch
DSM_VERSION=$(sed -n 's/^productversion="\(.*\)"/\1/p' /etc/VERSION) # Synology DSM version
FPATH_SYS="/sys/module/firmware_class/parameters/path" # System module firmware path file index
KVER=$(uname -r | awk -F. '{print $1 "." $2 "." $3}') # Running kernel version
FPATH="" # Device firmware path
KPATH="" # Full kernel modules path
MPATH="" # Kernel modules path
KO_LIST="" # List of kernel objects to enable|disable (includes config+args)
KO_LIST_ARG=$(echo ${KO_LIST_ARG} | xargs) # List of kernel objects passed in argument
KO_PATH="" # List of found kernel objects (*.ko) with path (includes config+args)
SYNOLOG_PATH=/var/log/packages # Default log output file
SYNOLOG=${SYNOLOG_PATH}/synocli-kernelmodule.log # Default log output file
# If SPK is set reassign variables
if [ -n "${SPK}" ]; then
SPK_CFG_PATH="/var/packages/${SPK}/target/etc"
FPATH="/var/packages/${SPK}/target/lib/firmware"
MPATH="/var/packages/${SPK}/target/lib/modules"
UPATH="/var/packages/${SPK}/target/rules.d"
KPATH="${MPATH}/${ARCH}-${DSM_VERSION}/${KVER}"
SYNOLOG="${SYNOLOG_PATH}/synocli-kernelmodule-${SPK}.log"
fi
# All output to SYNOLOG, STDOUT to the screen
exec > >(tee -a ${SYNOLOG}) 2> >(tee -a ${SYNOLOG} >/dev/null)
# Get list of kernel objects from
# both the configuration file and
# arguments passed on cmd line
KO_LIST=$(get_ko_list)
# Find resulting kernel object (*.ko) full path
KO_PATH=$(ko_path_match)
# exit if modules are missing/not found
if [ "$(echo $KO_PATH | cut -f1 -d:)" = "missing" ]; then
verbose && usage
echo
echo "ERROR: Missing kernel modules: [$(echo $KO_PATH | cut -f2 -d: | xargs)]"
echo
exit 1
fi
# If verbose is set print default arguments
[ ${VERBOSE} = "TRUE" ] && verbose
[ ${HELP} = "TRUE" ] && usage && exit 0
# If module path does not exists, exit
if [ ! -d ${MPATH} ]; then
usage
echo -ne "\nERROR: Module path [${MPATH}] does not exist or inaccessible...\n\n"
exit 1
fi
# Set kernel module .ko object base path
# If does not exists, exit
if [ ! -d ${KPATH} ]; then
usage
echo -ne "\nERROR: Kernel modules base path [${KPATH}] does not exist or inaccessible...\n\n"
exit 1
fi
# Check that udev rules file exist
if [ ! ${URULE} "FALSE" -a ! -f ${UPATH}/${URULE} ]; then
usage
echo -ne "\nERROR: udev rules.d file [${UPATH}/${URULE}] does not exist or inaccessible...\n\n"
exit 1
fi
# load the requested modules
load ()
{
error=0
if [ "{URULE}" ]; then
echo -ne "\t[enable] optional udev rules...\n"
printf '%40s %-34s' "" "[$URULE]"
ln -s ${UPATH}/${URULE} /lib/udev/rules.d/${URULE}
udevadm control --reload-rules
if [ $? -eq 0 ]; then
echo -ne " OK\n"
else
error=1
echo -ne " N/A\n"
fi
fi
# Add firmware path to running kernel
if [ -d "${FPATH}" ]; then
echo -ne "\t[loading] optional firmware path...\n"
printf '%65s' "[${FPATH}]"
echo "${FPATH}" > ${FPATH_SYS}
if [ $? -eq 0 ]; then
echo -ne " OK\n"
else
error=1
echo -ne " N/A\n"
fi
fi
echo -ne "\t[insmod] kernel modules...\n"
for ko in $KO_PATH
do
module=$(echo "${ko}" | sed -e 's/.*\///' -e 's/-/_/' -e 's/\.ko//')
printf '%40s %-35s' $ko "[$module]"
status=$(lsmod | grep "^$module ")
if [ $? -eq 0 -a "status" ]; then
echo "Already Loaded"
else
if [ -f $KPATH/$ko ]; then
insmod $KPATH/$ko
[ $? -eq 0 ] && echo "OK" || echo "ERROR"
else
echo "ERROR: Module $KPATH/$ko not found!"
error=1
fi
fi
done
return $error
}
# unload the requested modules in a reversed order
unload ()
{
error=0
# Unload drivers in reverse order
echo -ne "\t[rmmod] kernel modules...\n"
for item in $KO_PATH; do echo $item; done | tac | while read ko
do
module=$(echo "${ko}" | sed -e 's/.*\///' -e 's/-/_/g' -e 's/\.ko//')
printf '%40s %-35s' $ko "[$module]"
status=$(lsmod | grep "^$module ")
if [ $? -eq 0 -a "status" ]; then
rmmod $module
echo -ne "N/A\n"
else
echo -ne "ERROR\n"
error=1
fi
done
# Remove firmware path to running kernel
if [ -d "${FPATH}" ]; then
echo -ne "\t[unloading] optional firmware path...\n"
echo "" > ${FPATH_SYS}
error=$?
fi
# Remove udev rules
if [ "{URULE}" ]; then
echo -ne "\t[remove] optional udev rules...\n"
printf '%40s %-34s' "" "[$URULE]"
rm -f /lib/udev/rules.d/${URULE}
udevadm control --reload-rules
if [ $? -eq 0 ]; then
echo -ne " N/A\n"
else
error=1
echo -ne " ERROR\n"
fi
fi
return $error
}
# Provide a status of the loaded modules
status ()
{
error=0
echo -ne "\t[status] kernel modules...\n"
for ko in $KO_PATH
do
module=$(echo "${ko}" | sed -e 's/.*\///' -e 's/-/_/g' -e 's/\.ko//')
printf '%40s %-35s' $ko "[$module]"
status=$(lsmod | grep "^$module ")
if [ $? -eq 0 -a "status" ]; then
echo -ne "OK\n"
else
error=1
echo -ne "N/A\n"
fi
done
# Validate option firmware path
if [ -d "${FPATH}" ]; then
echo -ne "\t[status] of optional firmware path...\n"
printf '%65s' "[${FPATH}]"
grep -q ${FPATH} ${FPATH_SYS}
if [ $? -eq 0 ]; then
echo -ne " OK\n"
else
error=1
echo -ne " N/A\n"
fi
fi
# Validate udev rules (not much can be done)
if [ "{URULE}" ]; then
echo -ne "\t[status] of optional udev rules...\n"
printf '%40s %-34s' "" "[$URULE]"
if [ -h /lib/udev/rules.d/${URULE} ]; then
echo -ne " OK\n"
else
error=1
echo -ne " N/A\n"
fi
fi
return $error
}
case $ACTION in
insmod|start)
if status; then
echo ${SPK} is already running
exit 0
else
echo Starting ${SPK} ...
load
exit $?
fi
;;
rmmod|stop)
if status; then
echo Stopping ${SPK} ...
unload
exit $?
else
echo ${SPK} is not running
exit 0
fi
;;
reload|restart)
echo -ne "---\nReloading ${SPK} package...\n"
unload
load
exit $?
;;
status)
echo -ne "---\nStatus of ${SPK} package...\n"
if status; then
echo ${SPK} is running
exit 0
else
echo ${SPK} is not running
exit 1
fi
;;
*) usage
exit 1
;;
esac
================================================
FILE: tvheadend-backup.sh
================================================
#!/bin/bash
DEST=/volume1/backup/tvheadend
DATE=`date +%Y%m%d-%H%M`
tar -C /var/packages/tvheadend/target -jcvf - \
--exclude='.lock' \
--exclude='tvheadend.pid' \
cache var \
bin/tv_grab_file bin/zap2xml.pl bin/zap2xml.sh perl5 .cpan .xmltv \
share/tvheadend/data/dvb-scan/atsc/ca-QC-saint-jean-sur-richelieu \
> $DEST/tvheadend-backup-$DATE.tar.bz2
gitextract_iqeu8csl/ ├── README-old.md ├── README.md ├── hauppauge/ │ ├── 001-Hauppauge955D-lgdt3306a-v3.patch │ ├── 002-Hauppauge955D-em28xx-Tuner1.patch │ ├── 003-Hauppauge955D-em28xx-Tuner2-v6.patch │ └── hauppauge955D-SYNOApollolake-DSM622_24922-Kernel_4.4.59-20190520.tar.bz2 ├── hauppauge.sh ├── kernel/ │ └── synocli-kernelmodule.sh └── tvheadend-backup.sh
Condensed preview — 9 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (93K chars).
[
{
"path": "README-old.md",
"chars": 12333,
"preview": "# synology - th0ma7\nSynology personnal hack, info, tools & source code\n\nDonnations welcomed at: `0x522d164549E68681d"
},
{
"path": "README.md",
"chars": 13221,
"preview": "# synology - th0ma7\nSynology personnal hack, info, tools & source code\n\nDonnations welcomed at: `0x522d164549E68681d"
},
{
"path": "hauppauge/001-Hauppauge955D-lgdt3306a-v3.patch",
"chars": 13518,
"preview": "diff -uprN linux-4.4.x-orig/drivers/media/dvb-frontends/lgdt3306a.c linux-4.4.x-new/drivers/media/dvb-frontends/lgdt3306"
},
{
"path": "hauppauge/002-Hauppauge955D-em28xx-Tuner1.patch",
"chars": 11858,
"preview": "diff -uprN linux-4.4.x-orig/Documentation/video4linux/CARDLIST.em28xx linux-4.4.x-new/Documentation/video4linux/CARDLIST"
},
{
"path": "hauppauge/003-Hauppauge955D-em28xx-Tuner2-v6.patch",
"chars": 14967,
"preview": "diff -uprN linux-4.4.x-orig/drivers/media/usb/em28xx/em28xx-cards.c linux-4.4.x-new/drivers/media/usb/em28xx/em28xx-card"
},
{
"path": "hauppauge.sh",
"chars": 5737,
"preview": "#!/bin/sh\n\n#-------------------------------------------\n# hauppauge.sh\n#\n# by: Vincent Fortier\n# email: th0ma7_AT_gmail."
},
{
"path": "kernel/synocli-kernelmodule.sh",
"chars": 15088,
"preview": "#!/bin/bash\n\n#########################################################################\n# Written by: th0ma7@gmail.com\n# "
},
{
"path": "tvheadend-backup.sh",
"chars": 377,
"preview": "#!/bin/bash\n\nDEST=/volume1/backup/tvheadend\nDATE=`date +%Y%m%d-%H%M`\n\ntar -C /var/packages/tvheadend/target -jcvf - \\\n "
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the th0ma7/synology GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 9 files (85.1 KB), approximately 30.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.