Linux Device Tree: What those status = “okay” are about

There are a lot of “okay” assignments in the kernel’s device tree. For example, arch/arm/boot/dts/zynq-zed.dts starts with

/dts-v1/;
#include "zynq-7000.dtsi"

and later on there’s, among others,

&sdhci0 {
	status = "okay";
};

&uart1 {
	status = "okay";
};

&usb0 {
	status = "okay";
	dr_mode = "host";
	usb-phy = <&usb_phy0>;
};

Let’s look on the last one, relating to the usb0 label. In zynq-7000.dtsi, there’s

		usb0: usb@e0002000 {
			compatible = "xlnx,zynq-usb-2.20a", "chipidea,usb2";
			status = "disabled";
			clocks = <&clkc 28>;
			interrupt-parent = <&intc>;
			interrupts = <0 21 4>;
			reg = <0xe0002000 0x1000>;
			phy_type = "ulpi";
		};

So the “okay” assignment in zynq-zed.dts overrides the one in zynq-7000.dtsi, “disabled”.

As explained on this page, if the “status” property is present, it must be “ok” or “okay”, or the device entry node is ignored (or should be). This allows the .dtsi file to include all possible hardware entries, and keep them “disabled”, and let the .dts file including it hand-pick those required, only by changing the “status”.

And indeed, in drivers/of/base.c it says

/**
 *  of_device_is_available - check if a device is available for use
 *
 *  @device: Node to check for availability
 *
 *  Returns true if the status property is absent or set to "okay" or "ok",
 *  false otherwise
 */
bool of_device_is_available(const struct device_node *device)
{
	unsigned long flags;
	bool res;

	raw_spin_lock_irqsave(&devtree_lock, flags);
	res = __of_device_is_available(device);
	raw_spin_unlock_irqrestore(&devtree_lock, flags);
	return res;

}
EXPORT_SYMBOL(of_device_is_available);

and

/**
 *  __of_device_is_available - check if a device is available for use
 *
 *  @device: Node to check for availability, with locks already held
 *
 *  Returns true if the status property is absent or set to "okay" or "ok",
 *  false otherwise
 */
static bool __of_device_is_available(const struct device_node *device)
{
	const char *status;
	int statlen;

	if (!device)
		return false;

	status = __of_get_property(device, "status", &statlen);
	if (status == NULL)
		return true;

	if (statlen > 0) {
		if (!strcmp(status, "okay") || !strcmp(status, "ok"))
			return true;
	}

	return false;
}

This feature was added to the kernel back in 2008 in commit 834d97d452208279edf11c57eca150360d2dd1d6, but it seems it took some time before it was actually adopted.

Note that when there’s no “status” entry, it’s treated as “okay”. This retains backward compatibility with DTS files that don’t care much about this feature.

bash: Jots on exiting the script

Trapping

Call a function (byebye in this case) just before exiting a script:

trap byebye exit

Or a command:

trap 'echo "Farewell cruel world!"' exit

It’s possible to catch various signals with “trap” as well.

Quit on error

In short: Have a

set -e

at the beginning of the script. It’s a global on-off feature, so it can be cancelled temporarily with something like

set +e
[ ... something we don't care if it fails ... ]
set -e

From bash’ man page, under the “-e” flag:

Exit immediately if a pipeline (which may consist of a single simple command), a subshell command enclosed in parentheses, or one of the commands executed as part of a command list enclosed by braces (see SHELL GRAMMAR above) exits with a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test following the if or elif reserved words, part of any command executed in a && or ││ list except the command following the final && or ││, any command in a pipeline but the last, or if the command’s return value is being inverted with !. A trap on ERR, if set, is executed before the shell exits. This option applies to the shell environment and each subshell environment separately (see COMMAND EXECUTION ENVIRONMENT above), and may cause subshells to exit before executing all the commands in the subshell.

From own testing:

  • Does quit if a function returns a non-zero value (as it says above)
  • If “set -e” appears in a file containing functions, it’s applies to the script invoking the function file (-e is global)
  • If an error occurs during a trap handler with set -e active, the trap script stops at that point (i.e. trap scripts are not exempt from set -e).

overlayroot (and permission issues) on Linux Mint 18.1

Introduction

Wanting my media center computer to maintain consistent behavior and be tolerant to sudden power outages, I looked for a way to have the root filesystem read-only. This can cause problems with programs that expect to write to all kind of files allover, and media related software has this tendency to break when things divert from exactly as expected. So overlayroot is an elegant solution, as the system behaves completely as usual, only the changes are stored in RAM, and vanish on the next boot.

Only risk: Working hard on some changes, just to discover that the system was in overlay mode, and all went poof.

My system is a Linux Mint 18.1 (”Serena”) which is a derivative of Ubuntu 16.04 (”Xenial”). It runs a 4.4.0-53-generic kernel.

Starting off

Following this excellent post:

$ sudo apt-get install overlayroot

Note that the installation generates a new initramfs in /boot with the update-initramfs command-line utility, but comparing with the old one I couldn’t find anything related to overlayroot.

Overlayroot isn’t active by default. It’s possible to edit /etc/overlayroot.conf, but since I’m planning to switch back and forth with GRUB, I might as well enable it with the Linux kernel command rather than disabling it with the same.

In order to create a GRUB2 menu entry, copy /etc/grub.d/10_linux into /etc/grub.d/15_linux_overlay, and change:

os="$1"

into

os="RAM OVERLAY $1"

but even more important, add the following line somewhere at the beginning of the file:

GRUB_CMDLINE_LINUX_DEFAULT="overlayroot=\"tmpfs:swap=0,recurse=0\" $GRUB_CMDLINE_LINUX_DEFAULT"

and run update-grub. Let’s break it down:

  • overlayroot=tmpfs:  obviously means use tmpfs for overlaying the root
  • swap=0: Disable swap. This is the default, but why rely on that
  • recurse=0: Don’t overlay any of root’s subdirectories, if mounted separately. /boot/, in my case. No reason to overlay it, as I’ve set it to mount read-only anyhow

By the way, there are empty “custom” entries in /etc/grub.d/, but since I want them exactly like the original, only with a couple of changes, copying those long scripts is more accurate.

To make this the default boot, change the GRUB_DEFAULT variable in /etc/default/grub to point to the desired index, or move 15_linux_overlay to say, 09_linux_overlay. And run update-grub, or course.

But then it didn’t work

When booting Linux with overlayroot enabled, the Avahi daemon failed to start. And indeed, trying to kick it off manually (as root):

# avahi-daemon -s
avahi-daemon: error while loading shared libraries: libavahi-common.so.3: cannot stat shared object: Permission denied

Sounds weird. Let’s look at the strace of the same command:

execve("/usr/sbin/avahi-daemon", ["avahi-daemon", "-s"], [/* 30 vars */]) = 0
brk(NULL)                               = 0x20a8000

[ ... yada yada ... ]

open("/usr/lib/x86_64-linux-gnu/libavahi-common.so.3", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@/\0\0\0\0\0\0"..., 832) = 832
fstat(3, 0x7fff3f2caaa0)                = -1 EACCES (Permission denied)
close(3)                                = 0
writev(2, [{"avahi-daemon", 12}, {": ", 2}, {"error while loading shared libra"..., 36}, {": ", 2}, {"libavahi-common.so.3", 20}, {": ", 2}, {"cannot stat shared object", 25}, {": ", 2}, {"Permission denied", 17}, {"\n", 1}], 10) = 119
exit_group(127)                         = ?
+++ exited with 127 +++

WHATWHATWHAT? It’s fine to read from the file, but fstat() is denied?

And there was a similar problem with ping:

$ ping 1.1.1.1
ping: error while loading shared libraries: libcap.so.2: cannot stat shared object: Permission denied

Hint: If I moved /bin/ping to /bin/ping2, and ran ping2 instead, it ran normally.

Let’s talk about permissions

I know four types of permissions systems:

Since I issued the command as root, POSIX permissions won’t stop me. SELinux isn’t installed at all on Linux Mint 18.1, so maybe ACL?

$ cd /usr/lib/x86_64-linux-gnu
$ ls -l libavahi-common.so.*
lrwxrwxrwx 1 root root    24 Feb  3 13:00 libavahi-common.so.3 -> libavahi-common.so.3.5.3
-rw-r--r-- 1 root root 47952 Nov 24  2015 libavahi-common.so.3.5.3

$ getfacl libavahi-common.so.3
# file: libavahi-common.so.3
# owner: root
# group: root
user::rw-
group::r--
other::r--

$ getfacl libavahi-common.so.3.5.3
# file: libavahi-common.so.3.5.3
# owner: root
# group: root
user::rw-
group::r--
other::r--

Nope, nothing fishy about these files.

And this brings me back to the major hint from above: Changing the name of the executable made a difference. What functionality is sensitive to the executable’s path? Apparmor.

This gave me a major déjà vu from SELinux: That thing that’s supposed to make your system secure, but also causes mysterious failures until you’ve had enough of it, and go

$ sudo update-rc.d apparmor remove

Reboot, and then all was fine again. I have a feeling that many others will use this same command.

What it looks like

With overlayroot up and running, I get

$ df -h
Filesystem                   Size  Used Avail Use% Mounted on
udev                         3.9G     0  3.9G   0% /dev
tmpfs                        784M  9.5M  774M   2% /run
/dev/mapper/vg_ssd2-lv_root  118G  6.5G  106G   6% /media/root-ro
tmpfs-root                   3.9G  3.0M  3.9G   1% /media/root-rw
overlayroot                  3.9G  3.0M  3.9G   1% /
tmpfs                        3.9G  772K  3.9G   1% /dev/shm
tmpfs                        5.0M  4.0K  5.0M   1% /run/lock
tmpfs                        3.9G     0  3.9G   0% /sys/fs/cgroup
/dev/sda1                    239M   99M  123M  45% /boot
cgmfs                        100K     0  100K   0% /run/cgmanager/fs
tmpfs                        784M   16K  784M   1% /run/user/1000

and

$ mount
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
udev on /dev type devtmpfs (rw,nosuid,relatime,size=3987932k,nr_inodes=996983,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,noexec,relatime,size=801940k,mode=755)
/dev/mapper/vg_ssd2-lv_root on /media/root-ro type ext4 (ro,relatime,data=ordered)
tmpfs-root on /media/root-rw type tmpfs (rw,relatime)
overlayroot on / type overlay (rw,relatime,lowerdir=/media/root-ro,upperdir=/media/root-rw/overlay,workdir=/media/root-rw/overlay-workdir/_)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
[...etc...]

So clearly, my SSD was mounted read-only, and used as “lowerdir”. The overlay directory is at /media/root-rw/overlay/, and the working directory (where files are prepared before going to the overlay, in order to ensure atomicity) is /media/root-rw/overlay-workdir/.

Preventing confusion

Trying to avoid confusion on whether the system is volatile or not, I added this script to the “Startup Applications” on my Cinnamon desktop (Menu > Preferences > Startup Applications):

#!/bin/bash

# Set a dedicated background for volatile (overlay) sessions

if grep -q overlayroot /proc/cmdline ; then
  /usr/bin/gsettings set org.cinnamon.desktop.background picture-uri \
    'file:///usr/local/share/wallpaper.jpg'

I put the script in /usr/local/bin/overlay-wallpaper, and the wallpaper as /usr/local/share/wallpaper.jpg. Since this script runs only when the system is volatile, this setting is gone when the system reboots. More about setting up Gnome attributes in this post.

FWIW, adding this script as a startup application resulted in a file named “.config/autostart/Special wallpaper for overlay session.desktop” which reads:

[Desktop Entry]
Type=Application
Exec=/usr/local/bin/overlay-wallpaper
X-GNOME-Autostart-enabled=true
NoDisplay=false
Hidden=false
Name[en_US]=Overlay wallpaper
Comment[en_US]=Special wallpaper for overlay session
X-GNOME-Autostart-Delay=0

Notes

  • overlayroot is a great way to tell what files are changed while the system is running. Just
    $ ls -RC on /media/root-rw/overlay/

    tells the whole story.

  • The size of tmpfs (and hence the overlay) is half the physical RAM by default. On my machine, that’s almost 4 GB, which is probably more than ever necessary. But if tmpfs becomes full, the computer will deadlock, as stated in the kernel’s documentation. Meaning: A simple program that decides to write a huge file can freeze the system completely. If that’s a problem, use some disk partition as the lowerdir, and wipe it clean (make2fs) on each boot before mounting.
  • If you want to save something permanently, it’s possible to remount the underlying root directory in read-write mode,
    $ sudo mount -o remount,rw /media/root-ro

    Note that this doesn’t disable the overlay — changes are not saved. To have something written permanently, change or create files under /media/root-ro/.
    This may or may not have an immediate effect on the currently seen root directory, depending on whether it has an entry in the overlay directory. So it’s useful mostly for minor fixes or to save something specific.

  • Every change in the file causes a copy into the overlay directory, including changes in permissions etc. If the file is deleted, a “whiteout” file is created in the respective position in the overlay, marking that it’s absent. Something like this (after deleting eli.tar.gz from my Desktop):
    $ cd /media/root-rw/overlay/home/eli/Desktop/
    $ ls -l
    total 0
    c--------- 1 root root 0, 0 Feb 21 13:48 eli.tar.gz
  • Judging by the vast amount of patches under fs/overlayfs/ in the kernel tree since v4.4 (to say, v4.10), the whole thing seems a bit shaky. On the other hand, who cares, if it messes up just pull the plug and reboot. And frankly, it works great.

Wallpaper / desktop background + other stuff on Linux Mint 18.1 / Cinnamon

Desktop wallpaper setting

Get the current one:

$ gsettings get org.cinnamon.desktop.background picture-uri
'file:///usr/share/backgrounds/linuxmint/default_background.jpg'

And set it to another:

$ gsettings set org.cinnamon.desktop.background picture-uri 'file:///usr/share/backgrounds/linuxmint/edesigner_linuxmint.png'

Notes:

  • The “file://” prefix is mandatory.
  • There also an org.gnome.desktop.background attribute. Apparently, org.cinnamon.desktop.background overrides the former.
  • For an effect on the screen, the “set” command must be run on the active console (i.e. it doesn’t work from an remote ssh session)
  • If the URI property is faulty, the actual desktop background reverts to the default, but the faulty value is shown as is with the “get” command. This is somewhat confusing. In particular, omitting the file:// prefix makes a faulty value.

A bit more with gsettings

List all keys and values related to the desktop background:

$ gsettings list-recursively org.cinnamon.desktop.background
org.cinnamon.desktop.background picture-uri 'file:///usr/share/backgrounds/linuxmint/edesigner_linuxmint.png'
org.cinnamon.desktop.background color-shading-type 'solid'
org.cinnamon.desktop.background primary-color '#000000'
org.cinnamon.desktop.background picture-options 'zoom'
org.cinnamon.desktop.background picture-opacity 100
org.cinnamon.desktop.background secondary-color '#000000'

Or org.cinnamon, for that matter

$ gsettings list-recursively org.cinnamon

And there’s also “reset” for resetting a value to its default, and “monitor”, which prints a line each time the value of a key is changing.

The “org.cinnamon.desktop.background” strings is called a “schema”. These can be listed with:

$ gsettings list-schemas

Or the list of relocatable schemas (don’t ask me what “relocatable” means):

$ gsettings list-relocatable-schemas

These schemas can be found in /usr/share/glib-2.0/schemas/. For example, the schema for the desktop background properties can be found as /usr/share/glib-2.0/schemas/org.cinnamon.desktop.background.gschema.xml.

However  the XML files are not in effect directly. Rather, it’s the compiled file, /usr/share/glib-2.0/schemas/gschemas, generated with glib-compile-schemas.

dconf

gsetting is dconf-based, and accesses ~/.config/dconf/user binary database for obtaining non-default values. To dump all values in this database, install the dconf command-line tool:

$ sudo apt-get install dconf-cli

and then

$ dconf dump /

Or shall I be more specific?

$ dconf dump /org/cinnamon/desktop/background/
[/]
picture-uri='file:///usr/share/backgrounds/linuxmint/default_background.jpg'

Note that running “gsettings reset” on a key removes it from the dconf database:

$ gsettings reset org.cinnamon.desktop.background picture-uri
$ dconf dump /org/cinnamon/desktop/background/
$

(no output)

Linux / DVB: Playing with command-line utilities

General

Yet another collection of jots as I try out stuff. In this post, I’m playing with a Sin Hon DVB dongle, trying but failing to receive terrestrial TV with it. The OS is Linux Mint 18.1. I had also Kodi and Tvheadend installed on the system, but they aren’t discussed here.

There’s no coherent say here.

Install

# apt-get install dvb-tools dtv-scan-tables
# apt-get install w-scan

Note so self: Turn off tvheadend service before trying command-line utilities, as the former holds the resources, quite naturally.

Immediately after plugging in the DVB stick

Getting some general info:

$ dvb-fe-tool
Device Silicon Labs Si2168 (/dev/dvb/adapter0/frontend0) capabilities:
     CAN_2G_MODULATION
     CAN_FEC_1_2
     CAN_FEC_2_3
     CAN_FEC_3_4
     CAN_FEC_5_6
     CAN_FEC_7_8
     CAN_FEC_AUTO
     CAN_GUARD_INTERVAL_AUTO
     CAN_HIERARCHY_AUTO
     CAN_INVERSION_AUTO
     CAN_MULTISTREAM
     CAN_MUTE_TS
     CAN_QAM_16
     CAN_QAM_32
     CAN_QAM_64
     CAN_QAM_128
     CAN_QAM_256
     CAN_QAM_AUTO
     CAN_QPSK
     CAN_TRANSMISSION_MODE_AUTO
DVB API Version 5.10, Current v5 delivery system: DVBT
Supported delivery systems:
    [DVBT]
     DVBT2
     DVBC/ANNEX_A

What’s the status?

$ dvb-fe-tool -g
FREQUENCY = 0
MODULATION = QAM/AUTO
BANDWIDTH_HZ = 0
INVERSION = OFF
CODE_RATE_HP = AUTO
CODE_RATE_LP = AUTO
GUARD_INTERVAL = AUTO
TRANSMISSION_MODE = AUTO
HIERARCHY = AUTO
DELIVERY_SYSTEM = DVBT

Weird fact: dvb-fe-tool shows status output when executed immediately after tvheadend has set up the DVB stick, but not after dvbv5-scan

$ dvb-fe-tool -m
ERROR    FE_READ_STATUS: Resource temporarily unavailable
ERROR    FE_READ_STATUS: Resource temporarily unavailable
ERROR    FE_READ_STATUS: Resource temporarily unavailable

Scanning for channels

$ dvbv5-scan /usr/share/dvb/dvb-t/il-All
Cannot calc frequency shift. Either bandwidth/symbol-rate is unavailable (yet).
Scanning frequency #1 514000000
       (0x00) Signal= -89.00dBm
Scanning frequency #2 538000000
Lock   (0x1f) Signal= -89.00dBm C/N= 18.75dB UCB= 0 postBER= 1.00
Service Ch 1, provider Idan +: digital television
Service Ch 2, provider Idan +: digital television
Service Ch 10, provider Idan +: digital television
Service Ch 33, provider Idan +: digital television
Service Ch 99, provider Idan +: digital television
Service Ch 23, provider Idan +: digital television
Service Idan+test_2, provider Idan+: digital television
WARNING  Channel Idan+test_2 (service ID 7) not found on PMT. Skipping it.
Service Aleph, provider Idan +: digital radio
Service Bet, provider Idan +: digital radio
Service Gimmel, provider Idan +: digital radio
Service Dalet, provider Idan +: digital radio
Service Moreshet, provider Idan +: digital radio
Service 88FM, provider Idan +: digital radio
Service Musica, provider Idan +: digital radio
Service Reka, provider Idan +: digital radio
Service Galatz, provider Idan +: digital radio
Service Galgalatz, provider Idan +: digital radio
Service Radios, provider Idan +: digital radio
Service Kol Barama, provider Idan +: digital radio
Service Lev HaMdina, provider Idan +: digital radio
Service CLASSICAL bu, provider Idan +: digital radio

A dvb_channel.conf file was created, listed at the end of this post.

Don’t get too encouraged by the channel list: It’s detected even if the signal is extremely poor. Or if something is horribly wrong. Don’t know. I never managed to get this working.

Pay more attention to the postBER=1 (or if it says something like UCB= 2686 postBER= 625x10^-3). Even more discouraging is the fact that the signal levels are the same where it locked and where it didn’t. In other words, almost all signal that arrives to the tuner is noise.

After scanning, the frequency is set, but it’s not tuned in yet:

$ dvb-fe-tool -g
FREQUENCY = 538000000
MODULATION = QAM/16
BANDWIDTH_HZ = 8000000
INVERSION = OFF
CODE_RATE_HP = 2/3
CODE_RATE_LP = AUTO
GUARD_INTERVAL = 1/4
TRANSMISSION_MODE = 8K
HIERARCHY = NONE
DELIVERY_SYSTEM = DVBT
$ dvb-fe-tool -m
ERROR    FE_READ_STATUS: Resource temporarily unavailable
ERROR    FE_READ_STATUS: Resource temporarily unavailable

The regional configuration file, /usr/share/dvb/dvb-t/il-All, is part of the dtv-scan-tables:

$ cat /usr/share/dvb/dvb-t/il-All 
# Israel, Israel Broadcasting Authority's transmitters
# Generated from list in http://www.iba.org.il/reception/
[CHANNEL]
	DELIVERY_SYSTEM = DVBT
	FREQUENCY = 514000000
	BANDWIDTH_HZ = 8000000
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = NONE
	MODULATION = QAM/16
	TRANSMISSION_MODE = 8K
	GUARD_INTERVAL = 1/4
	HIERARCHY = NONE
	INVERSION = AUTO

[CHANNEL]
	DELIVERY_SYSTEM = DVBT
	FREQUENCY = 538000000
	BANDWIDTH_HZ = 8000000
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = NONE
	MODULATION = QAM/16
	TRANSMISSION_MODE = 8K
	GUARD_INTERVAL = 1/4
	HIERARCHY = NONE
	INVERSION = AUTO

This file represents the two frequencies at which DVB-T is broadcast in Israel (same stream, different frequencies in different regions of Israel).

Watching TV

One day, when I find a proper dongle, this will work. For now, I get this:

$ dvbv5-zap -c dvb_channel.conf -r 'Ch 10'
using demux '/dev/dvb/adapter0/demux0'
reading channels from file 'dvb_channel.conf'
service has pid type 06:  2642 2641 2640
tuning to 538000000 Hz
video pid 2625
  dvb_set_pesfilter 2625
audio pid 2626
  dvb_set_pesfilter 2626
       (0x00)
Lock   (0x1f) C/N= 25.25dB UCB= 0 postBER= 1.00
Lock   (0x1f) C/N= 25.25dB UCB= 0 postBER= 1.00
DVR interface '/dev/dvb/adapter0/dvr0' can now be opened
Lock   (0x1f) C/N= 25.25dB UCB= 0 postBER= 1.00

Some explanations:

  • The ‘Ch 10′ string is the identifier found in dvb_channel.conf for Israeli Channel 10 (see full listing at the end of this post)
  • The (0x00) part says that the demodulator isn’t locked (odds are that the firmware was loaded as a result of running dvbv5-zap, so it took a short while)
  • The (0x1f) part says that all demodulator locks were obtained, and the “Lock” before says it.
  • The PostBER = 1.00 clearly indicates I have a problem, and is there reason I can’t watch anything. And by the way, BER can never exceed 0.5, because if you get more than half the bits wrong, read the NOT of all bits, and the BER will go down. So BER=1 actually means no errors. But not being a schmuck about it, it’s clear that the demodulator wants to say something’s not good.
  • UCB = 0, number of uncorrected data transport blocks so far. Zero isn’t really encouraging, as it’s most likely a result of no data blocks detected at all.

To actually see the channel, go e.g.

$ mplayer -cache 800 /dev/dvb/adapter0/dvr0

Need I say that I got nothing? Mplayer ran nicely, tried to fish some info, but never got as far as opening an image window.

By the way, the BER info capability of the si2168 chip I used was due to a feature that is due for addition in kernel 4.11 (I patched mine manually). So without that, it looks like this:

$ dvbv5-zap -c dvb_channel.conf -r 'Ch 10'
using demux '/dev/dvb/adapter0/demux0'
reading channels from file 'dvb_channel.conf'
service has pid type 06:  2642 2641 2640
tuning to 538000000 Hz
video pid 2625
  dvb_set_pesfilter 2625
audio pid 2626
  dvb_set_pesfilter 2626
       (0x00)
Lock   (0x1f) C/N= 25.50dB
Lock   (0x1f) C/N= 25.50dB
DVR interface '/dev/dvb/adapter0/dvr0' can now be opened
Lock   (0x1f) C/N= 25.50dB

Using w_scan

This is what I got in Haifa with my Sin Hon TDH601 (“HD809″). Note that I didn’t offer any hints on my location, but it managed anyhow.

$ w_scan
w_scan
w_scan version 20141122 (compiled for DVB API 5.10)
WARNING: could not guess your country. Falling back to 'DE'
guessing country 'DE', use -c <country> to override
using settings for GERMANY
DVB aerial
DVB-T Europe
scan type TERRESTRIAL, channellist 4
output format vdr-2.0
WARNING: could not guess your codepage. Falling back to 'UTF-8'
output charset 'UTF-8', use -C <charset> to override
Info: using DVB adapter auto detection.
	/dev/dvb/adapter0/frontend0 -> TERRESTRIAL "Silicon Labs Si2168": very good :-))

Using TERRESTRIAL frontend (adapter /dev/dvb/adapter0/frontend0)
-_-_-_-_ Getting frontend capabilities-_-_-_-_
Using DVB API 5.10
frontend 'Silicon Labs Si2168' supports
DVB-T2
INVERSION_AUTO
QAM_AUTO
TRANSMISSION_MODE_AUTO
GUARD_INTERVAL_AUTO
HIERARCHY_AUTO
FEC_AUTO
FREQ (42.00MHz ... 870.00MHz)
-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
Scanning DVB-T...
Scanning 7MHz frequencies...
177500: (time: 00:01.719)
184500: (time: 00:03.747)
191500: (time: 00:05.803)
198500: (time: 00:07.811)
205500: (time: 00:09.819)
212500: (time: 00:11.827)
219500: (time: 00:13.831)
226500: (time: 00:15.835)
Scanning 8MHz frequencies...
474000: (time: 00:17.843)
482000: (time: 00:19.847)
490000: (time: 00:21.855)
498000: (time: 00:23.907)
506000: (time: 00:25.911)
514000: (time: 00:27.959)
522000: (time: 00:29.967)
530000: (time: 00:31.970)
538000: (time: 00:34.022)         signal ok:	QAM_AUTO f = 538000 kHz I999B8C999D999T999G999Y999 (0:0:0)
        QAM_AUTO f = 538000 kHz I999B8C999D999T999G999Y999 (0:0:0) : updating transport_stream_id: -> (0:0:2)
        QAM_AUTO f = 538000 kHz I999B8C999D999T999G999Y999 (0:0:2) : updating network_id -> (0:4369:2)
        new transponder: (QPSK     f =      0 kHz I0B999C0D0T2G32Y0 (65314:4369:1)) 0x4000
        QAM_AUTO f = 538000 kHz I999B8C999D999T999G999Y999 (0:4369:2) : updating original_network_id -> (65314:4369:2)
        updating transponder:
           (QAM_AUTO f = 538000 kHz I999B8C999D999T999G999Y999 (65314:4369:2)) 0x0000
        to (QAM_16   f = 538000 kHz I999B8C23D0T8G4Y0 (65314:4369:2)) 0x405A
        new transponder: (QAM_16   f =      0 kHz I999B8C23D0T8G4Y0 (8959:4369:3)) 0x405A
546000: (time: 00:35.198)
554000: (time: 00:37.250)
562000: (time: 00:39.254)
570000: (time: 00:41.306)
578000: (time: 00:43.358)
586000: (time: 00:45.414)
594000: (time: 00:47.462)
602000: (time: 00:49.514)
610000: (time: 00:51.522)
618000: (time: 00:53.574)
626000: (time: 00:55.578)
634000: (time: 00:57.586)
642000: (time: 00:59.638)
650000: (time: 01:01.641)
658000: (time: 01:03.697)
666000: (time: 01:05.701)
674000: (time: 01:07.705)
682000: (time: 01:09.753)
690000: (time: 01:11.801)
698000: (time: 01:13.857)
706000: (time: 01:15.909)
714000: (time: 01:17.913)
722000: (time: 01:19.917)
730000: (time: 01:21.969)
738000: (time: 01:24.025)
746000: (time: 01:26.073)
754000: (time: 01:28.081)
762000: (time: 01:30.085)
770000: (time: 01:32.092)
778000: (time: 01:34.096)
786000: (time: 01:36.104)
794000: (time: 01:38.108)
802000: (time: 01:40.112)
810000: (time: 01:42.164)
818000: (time: 01:44.216)
826000: (time: 01:46.220)
834000: (time: 01:48.276)
842000: (time: 01:50.328)
850000: (time: 01:52.332)
858000: (time: 01:54.384)
Scanning DVB-T2...
Scanning 7MHz frequencies...
177500: (time: 01:56.388)
184500: (time: 01:58.396)
191500: (time: 02:00.400)
198500: (time: 02:02.403)
205500: (time: 02:04.407)
212500: (time: 02:06.455)
219500: (time: 02:08.511)
226500: (time: 02:10.519)
Scanning 8MHz frequencies...
474000: (time: 02:12.571)
482000: (time: 02:14.579)
490000: (time: 02:16.583)
498000: (time: 02:18.587)
506000: (time: 02:20.591)
514000: (time: 02:22.643)
522000: (time: 02:24.695)
530000: (time: 02:26.699)
538000: skipped (already known transponder)
546000: (time: 02:28.751)
554000: (time: 02:30.755)
562000: (time: 02:32.762)
570000: (time: 02:34.766)
578000: (time: 02:36.774)
586000: (time: 02:38.778)
594000: (time: 02:40.782)
602000: (time: 02:42.786)
610000: (time: 02:44.790)
618000: (time: 02:46.842)
626000: (time: 02:48.846)
634000: (time: 02:50.850)
642000: (time: 02:52.858)
650000: (time: 02:54.862)
658000: (time: 02:56.866)
666000: (time: 02:58.874)
674000: (time: 03:00.878)
682000: (time: 03:02.881)
690000: (time: 03:04.889)
698000: (time: 03:06.893)
706000: (time: 03:08.897)
714000: (time: 03:10.901)
722000: (time: 03:12.909)
730000: (time: 03:14.917)
738000: (time: 03:16.921)
746000: (time: 03:18.929)
754000: (time: 03:20.933)
762000: (time: 03:22.937)
770000: (time: 03:24.941)
778000: (time: 03:26.945)
786000: (time: 03:28.949)
794000: (time: 03:30.957)
802000: (time: 03:32.961)
810000: (time: 03:34.964)
818000: (time: 03:36.968)
826000: (time: 03:38.976)
834000: (time: 03:41.028)
842000: (time: 03:43.080)
850000: (time: 03:45.132)
858000: (time: 03:47.184)
tune to: QAM_16   f = 538000 kHz I999B8C23D0T8G4Y0 (65314:4369:2) (time: 03:49.188)
	service = Ch 1 (Idan +)
	service = Ch 2 (Idan +)
	service = Ch 10 (Idan +)
	service = Ch 33 (Idan +)
	service = Ch 99 (Idan +)
	service = Ch 23 (Idan +)
	service = Idan+test_2 (Idan+)
	service = Aleph (Idan +)
	service = Bet (Idan +)
	service = Gimmel (Idan +)
	service = Dalet (Idan +)
	service = Moreshet (Idan +)
	service = 88FM (Idan +)
	service = Musica (Idan +)
	service = Reka (Idan +)
	service = Galatz (Idan +)
	service = Galgalatz (Idan +)
	service = Radios (Idan +)
	service = Kol Barama (Idan +)
	service = Lev HaMdina (Idan +)
	service = CLASSICAL bu (Idan +)
retrying with center_frequency = 514000000
tune to: QAM_16   f = 514000 kHz I999B8C23D0T8G4Y0 (8959:4369:3) (time: 04:02.968)
----------no signal----------
tune to: QAM_AUTO f = 514000 kHz I999B8C999D0T999G999Y0 (8959:4369:3) (time: 04:08.995)  (no signal)
----------no signal----------
(time: 04:15.019) dumping lists (20 services)
..
Ch 1;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:2561=27:2562@17:0;1025:0:1:65314:2:0
Ch 2;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:2593=27:2594@17,2595:0;2608,2609,2610:0:2:65314:2:0
Ch 10;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:2625=27:2626@17,2627:0;2640,2641,2642:0:3:65314:2:0
Ch 33;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:2657=27:2658@17:0:0:4:65314:2:0
Ch 99;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:2689=27:2690@17:0;2704:0:5:65314:2:0
Ch 23;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:3585=27:3586@17:0:0:6:65314:2:0
Aleph;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:2817@17:0:0:21:65314:2:0
Bet;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:2833@17:0:0:22:65314:2:0
Gimmel;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:2849@17:0:0:23:65314:2:0
Dalet;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:2865@17:0:0:24:65314:2:0
Moreshet;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:2881@17:0:0:25:65314:2:0
88FM;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:2897@17:0:0:26:65314:2:0
Musica;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:2913@17:0:0:27:65314:2:0
Reka;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:2929@17:0:0:28:65314:2:0
Galatz;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:2945@17:0:0:29:65314:2:0
Galgalatz;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:2961@17:0:0:30:65314:2:0
Radios;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:3057@17:0:0:36:65314:2:0
Kol Barama;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:3137@17:0:0:41:65314:2:0
Lev HaMdina;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:3153@17:0:0:42:65314:2:0
CLASSICAL bu;Idan +:538000:B8C23D0G4M16T8Y0:T:27500:0:3201@17:0:0:45:65314:2:0
Done, scan time: 04:15.019

The dvb_channel.conf file

As promised above, this is the dvb_channel.conf that was obtained with dvbv5-scan:

[Ch 1]
	SERVICE_ID = 1
	VIDEO_PID = 2561
	AUDIO_PID = 2562
	PID_06 = 1025
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Ch 2]
	SERVICE_ID = 2
	VIDEO_PID = 2593
	AUDIO_PID = 2594 2595
	PID_06 = 2610 2609 2608
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Ch 10]
	SERVICE_ID = 3
	VIDEO_PID = 2625
	AUDIO_PID = 2626 2627
	PID_06 = 2642 2641 2640
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Ch 33]
	SERVICE_ID = 4
	VIDEO_PID = 2657
	AUDIO_PID = 2658
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Ch 99]
	SERVICE_ID = 5
	VIDEO_PID = 2689
	AUDIO_PID = 2690
	PID_06 = 2704
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Ch 23]
	SERVICE_ID = 6
	VIDEO_PID = 3585
	AUDIO_PID = 3586
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Aleph]
	SERVICE_ID = 21
	AUDIO_PID = 2817
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Bet]
	SERVICE_ID = 22
	AUDIO_PID = 2833
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Gimmel]
	SERVICE_ID = 23
	AUDIO_PID = 2849
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Dalet]
	SERVICE_ID = 24
	AUDIO_PID = 2865
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Moreshet]
	SERVICE_ID = 25
	AUDIO_PID = 2881
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[88FM]
	SERVICE_ID = 26
	AUDIO_PID = 2897
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Musica]
	SERVICE_ID = 27
	AUDIO_PID = 2913
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Reka]
	SERVICE_ID = 28
	AUDIO_PID = 2929
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Galatz]
	SERVICE_ID = 29
	AUDIO_PID = 2945
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Galgalatz]
	SERVICE_ID = 30
	AUDIO_PID = 2961
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Radios]
	SERVICE_ID = 36
	AUDIO_PID = 3057
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Kol Barama]
	SERVICE_ID = 41
	AUDIO_PID = 3137
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[Lev HaMdina]
	SERVICE_ID = 42
	AUDIO_PID = 3153
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

[CLASSICAL bu]
	SERVICE_ID = 45
	AUDIO_PID = 3201
	FREQUENCY = 538000000
	MODULATION = QAM/16
	BANDWIDTH_HZ = 8000000
	INVERSION = AUTO
	CODE_RATE_HP = 2/3
	CODE_RATE_LP = AUTO
	GUARD_INTERVAL = 1/4
	TRANSMISSION_MODE = 8K
	HIERARCHY = NONE
	DELIVERY_SYSTEM = DVBT

This was obtained on February 8th, 2017. It’s probably going to change over time.

Linux and DVB: Failing with a Sin Hon TDH 601 / HD809 USB dongle

General

These are my jots as I attempted to use an DVB USB dongle to receive digital TV in Haifa, Israel. Spoiler: I never managed to get my dongle working properly. Maybe because it was faulty to begin with, and maybe because I missed something. It seems more like the former.

It says “HD809″ and has Vendor/Product IDs 3344/24a0 (see below). 0x3344 = Leaguer Microelectronics (Shenzhen, China), 24a0 is unknown.

Here’s what it looks like:

Sin Hon TDH601 DVB-T2 dongle

Probably a Sin Hon TDH601. According to the manufacturer, it should run with Android 4.1 with OTG on the USB plug, which according to this page means any kernel after 3.0.31. And my phone should detect the dongle, which it doesn’t. As a matter of fact, looking into Android’s kernel v4.4 (which is pretty much a plain Linux kernel) there is nothing matching the “24a0″ string under drivers/. So no.

A look inside:

Sin Hon TDH601 DVB-T2 dongle under the hood

and

Sin Hon TDH601 DVB-T2 dongle under the hood(click to enlarge)

Can you see the little short-circuit on LME2510C two pins to the upper left? And the two of Si2168′s, at the right? I don’t know if they should be there, but I know I messed up completely trying to separate them.

Back to Linux and drivers: Found the device mentioned, leading to this patch, which outlines the devices: LME2510C USB interface (a.k.a. LM04 or QQBOX, not QBOX, with drivers for Windows), Si2168-B40 demodulator, Si2157-A30 tuner. The Windows driver for non-C LME2510 appears to be the same one.

Apparently, LME2510C is a USB front-end for a tuner and a demodulator, each controller by I2C. Calling the driver by the front end surely causes some confusion. It may be useful to know, that the same pair of tuner and demodulator is used in the PCTV tripleStick (292e), so except for the LME2510C USB front end, all that applies to the 292e probably goes for the Sin Hon dongle.

Antti Palosaari’s Linux repo obtained with

$ git clone git://git.linuxtv.org/anttip/media_tree.git/

where the lme2510c_tdh601branch contains the commit from June 2016, adding the drivers/media/usb/dvb-usb-v2/lme2510.c file (not to be confused with rc-lme2510.c, which contains key maps for a remote control it seems). It requires dvb-usb-lme2510c-0.fw as a firmware file.

Not to be confused with drivers/media/usb/dvb-usb-v2/lmedm04.c, which relates to the same USB interface, but with different devices behind it.

Found 0x3344/0x1122 supported in the 3.12 kernel of Zedboard (LME’s remote control?)

Incorporation in Linux Mint 18.1

Grabbed the Linux Kernel git:

$ git clone git://kernel.ubuntu.com/ubuntu/ubuntu-xenial.git

and checked out the Ubuntu-4.4.0-53.74 tag

Copied configuration file:

$ cp /boot/config-4.4.0-53-generic .config

And on the machine itself:

$ sudo apt-get install libssl-dev
$ make modules_prepare

libssl-dev is required for one of the kernel scripts (sign-file). And running this on my own computer failed with

  CC      kernel/bounds.s
gcc: unrecognized option '-no-pie'
cc1: error: unrecognized command line option "-fstack-protector-strong"
make[1]: *** [kernel/bounds.s] Error 1
make: *** [prepare0] Error 2

(so much for a vintage distribution and compiler)

Tried to compile the directory in question:

$ make M=drivers/media/usb/dvb-usb-v2

That went through nice, however there was a complaint about missing Module.symvers, and hence the module didn’t get into the kernel with insmod.

So after patching the kernel with commit f92f72c1138e33e3a2976724e8b4bc6775b53ee8 from Antti Palosaari’s repo (creating a text patch and applying it with git am on Ubuntu’s kernel) I compiled the kernel and modules completely on the target machine

$ make -j 6 bzImage modules

And that took some time, of course. It seems like this is the only way to obtain Module.symvers.

Eventually, I went down the entire way from the lme2510c_tdh601branch in the media-tree git repo to some common point with the kernel I used (4.4.0-53-generic) and cherry-picked whatever was in the middle. This required some manual adjustments. In particular, this enabled the UCB and BER statistics collection.

Finally, the module was put in place:

# cp drivers/media/usb/dvb-usb-v2/lme2510c.ko /lib/modules/4.4.0-53-generic/kernel/drivers/media/usb/dvb-usb-v2/
# depmod -a

and plugging in the dongle was enough:

[12200.242253] usb 1-2: new high-speed USB device number 6 using xhci_hcd
[12200.371223] usb 1-2: config 1 interface 0 altsetting 1 bulk endpoint 0x81 has invalid maxpacket 64
[12200.371235] usb 1-2: config 1 interface 0 altsetting 1 bulk endpoint 0x1 has invalid maxpacket 64
[12200.371241] usb 1-2: config 1 interface 0 altsetting 1 bulk endpoint 0x2 has invalid maxpacket 64
[12200.371248] usb 1-2: config 1 interface 0 altsetting 1 bulk endpoint 0x8A has invalid maxpacket 64
[12200.371681] usb 1-2: New USB device found, idVendor=3344, idProduct=24a0
[12200.371691] usb 1-2: New USB device strings: Mfr=0, Product=0, SerialNumber=3
[12200.371697] usb 1-2: SerialNumber: 䥈児
[12201.486687] usb 1-2: dvb_usb_v2: found a 'Sin Hon TDH601' in cold state
[12201.489636] usb 1-2: dvb_usb_v2: downloading firmware from file 'dvb-usb-lme2510c-0.fw'
[12201.516925] usbcore: registered new interface driver lme2510c
[12201.549778] usb 1-2: USB disconnect, device number 6
[12201.549992] usb 1-2: dvb_usb_v2: 'Sin Hon TDH601' successfully deinitialized and disconnected
[12201.826659] usb 1-2: new high-speed USB device number 7 using xhci_hcd
[12201.955630] usb 1-2: New USB device found, idVendor=3344, idProduct=24a0
[12201.955642] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[12201.955648] usb 1-2: Product: 䝇䝇
[12201.955656] usb 1-2: Manufacturer: Leaguerme
[12201.955661] usb 1-2: SerialNumber: LMEUSBSN0001
[12201.957020] usb 1-2: dvb_usb_v2: found a 'Sin Hon TDH601' in warm state
[12201.957302] usb 1-2: dvb_usb_v2: will pass the complete MPEG2 transport stream to the software demuxer
[12201.957324] DVB: registering new adapter (Sin Hon TDH601)
[12201.972436] i2c i2c-9: Added multiplexed i2c bus 10
[12201.972444] si2168 9-0064: Silicon Labs Si2168 successfully attached
[12201.979987] si2157 10-0060: Silicon Labs Si2147/2148/2157/2158 successfully attached
[12201.980015] usb 1-2: DVB: registering adapter 0 frontend 0 (Silicon Labs Si2168)...
[12201.980442] usb 1-2: dvb_usb_v2: 'Sin Hon TDH601' successfully initialized and connected

with lsmod’s relevant part going

Module                  Size  Used by
si2157                 16384  1
si2168                 20480  1
i2c_mux                16384  1 si2168
lme2510c               16384  0
dvb_usb_v2             36864  1 lme2510c
dvb_core              122880  1 dvb_usb_v2
rc_core                28672  1 dvb_usb_v2

The relevant device files reside in /dev/dvb/adapter0. I made them R/W accessible to all manually to begin with.

And then, when attempting to scan, I got the following in dmesg:

[13200.818391] si2168 9-0064: found a 'Silicon Labs Si2168-B40'
[13200.818461] si2168 9-0064: Direct firmware load for dvb-demod-si2168-b40-01.fw failed with error -2
[13200.818493] si2168 9-0064: Direct firmware load for dvb-demod-si2168-02.fw failed with error -2
[13200.818499] si2168 9-0064: firmware file 'dvb-demod-si2168-02.fw' not found
[13200.823201] si2157 10-0060: found a 'Silicon Labs Si2157-A30'
[13200.873313] si2157 10-0060: firmware version: 3.0.5

So I grabbed the two files from the dvb-firmware Github repo.

The firmware for the driver itself (dvb-usb-lme2510c-0.fw) was downloaded from Palosaari’s site.

Firmware for the si2168 (dvb-demod-si2168-b40-01.fw) was downloaded from here (version 4.0.25). Doesn’t seem like there’s a major difference between the revisions (in the sense that none worked for me. I tried several).

dvb-demod-si2168-02.fw is the fallback firmware for all si2168 demodulators. There’s no point using it when a specific firmware file is available.

The si2157 tuner doesn’t require any firmware file.

All firmware files were copied to /lib/firmware on the target machine.

Missing firmware symptoms

When both dvb-demod-si2168-02.fw and dvb-demod-si2168-b40-01.fw are missing or have not been loaded properly:

[ 1089.192756] si2168 9-0064: Direct firmware load for dvb-demod-si2168-b40-01.fw failed with error -2
[ 1089.193730] si2168 9-0064: please install firmware file 'dvb-demod-si2168-b40-01.fw'
[ 1089.193753] si2168 9-0064: downloading firmware from file 'dvb-demod-si2168-02.fw'
[ 1089.509845] si2168 9-0064: firmware version: B 4.0.4

And this results with something like this:

$ dvbv5-scan /usr/share/dvb/dvb-t/il-All
Cannot calc frequency shift. Either bandwidth/symbol-rate is unavailable (yet).
Scanning frequency #1 514000000
       (0x00) Signal= 0.00dBm
Scanning frequency #2 538000000
       (0x00) Signal= 0.00dBm

But with dvb-demod-si2168-02.fw loaded (and dvb-demod-si2168-b40-01.fw not present):

$ dvbv5-scan /usr/share/dvb/dvb-t/il-All
Cannot calc frequency shift. Either bandwidth/symbol-rate is unavailable (yet).
Scanning frequency #1 514000000
       (0x00) Signal= -85.00dBm
Scanning frequency #2 538000000
Lock   (0x1f) Signal= -85.00dBm C/N= 20.25dB UCB= 0 postBER= 1.00
Service Ch 1, provider Idan +: digital television
Service Ch 2, provider Idan +: digital television
[... etc ...]

The important lesson is that channels are detected without dvb-demod-si2168-b40-01.fw, only no data can be obtained (the BER is horribly high). Explained why above.

Final status

I destroyed the hardware completely when trying to fix what seemed to be a short circuit between two pins of the LME2510C chip. Unfortunately, I messed things up (fine-pitch soldering is a skill, too bad I don’t possess it), and the device isn’t detected on the USB bus properly. Or more precisely, one gets

[12135.429658] usb 1-4: new full-speed USB device number 13 using xhci_hcd
[12135.541697] usb 1-4: device descriptor read/64, error -71
[12135.757589] usb 1-4: device descriptor read/64, error -71
[12135.973634] usb 1-4: new full-speed USB device number 14 using xhci_hcd
[12136.085569] usb 1-4: device descriptor read/64, error -71
[12136.301560] usb 1-4: device descriptor read/64, error -71
[12136.517668] usb 1-4: new full-speed USB device number 15 using xhci_hcd
[12136.517909] usb 1-4: Device not responding to setup address.
[12136.721700] usb 1-4: Device not responding to setup address.
[12136.925512] usb 1-4: device not accepting address 15, error -71
[12137.037492] usb 1-4: new full-speed USB device number 16 using xhci_hcd
[12137.037709] usb 1-4: Device not responding to setup address.
[12137.241728] usb 1-4: Device not responding to setup address.
[12137.445494] usb 1-4: device not accepting address 16, error -71
[12137.445653] usb usb1-port4: unable to enumerate USB device

Raw session

A proper plug-in syslog (device is OK, and drivers and firmware in place):

[ 1175.580411] usb 1-4: new high-speed USB device number 7 using xhci_hcd
[ 1175.708790] usb 1-4: config 1 interface 0 altsetting 1 bulk endpoint 0x81 has invalid maxpacket 64
[ 1175.708803] usb 1-4: config 1 interface 0 altsetting 1 bulk endpoint 0x1 has invalid maxpacket 64
[ 1175.708810] usb 1-4: config 1 interface 0 altsetting 1 bulk endpoint 0x2 has invalid maxpacket 64
[ 1175.708817] usb 1-4: config 1 interface 0 altsetting 1 bulk endpoint 0x8A has invalid maxpacket 64
[ 1175.709033] usb 1-4: New USB device found, idVendor=3344, idProduct=24a0
[ 1175.709041] usb 1-4: New USB device strings: Mfr=0, Product=0, SerialNumber=3
[ 1175.709048] usb 1-4: SerialNumber: 䥈児
[ 1175.711681] usb 1-4: dvb_usb_v2: found a 'Sin Hon TDH601' in cold state
[ 1175.711789] usb 1-4: dvb_usb_v2: downloading firmware from file 'dvb-usb-lme2510c-0.fw'
[ 1175.775782] usb 1-4: USB disconnect, device number 7
[ 1175.775955] usb 1-4: dvb_usb_v2: 'Sin Hon TDH601' successfully deinitialized and disconnected
[ 1176.052434] usb 1-4: new high-speed USB device number 8 using xhci_hcd
[ 1176.181496] usb 1-4: New USB device found, idVendor=3344, idProduct=24a0
[ 1176.181510] usb 1-4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 1176.181519] usb 1-4: Product: 䝇䝇
[ 1176.181527] usb 1-4: Manufacturer: Leaguerme
[ 1176.181534] usb 1-4: SerialNumber: LMEUSBSN0001
[ 1176.183341] usb 1-4: dvb_usb_v2: found a 'Sin Hon TDH601' in warm state
[ 1176.183804] usb 1-4: dvb_usb_v2: will pass the complete MPEG2 transport stream to the software demuxer
[ 1176.183833] DVB: registering new adapter (Sin Hon TDH601)
[ 1176.192171] i2c i2c-9: Added multiplexed i2c bus 10
[ 1176.192182] si2168 9-0064: Silicon Labs Si2168-B40 successfully identified
[ 1176.192188] si2168 9-0064: firmware version: B 4.0.2
[ 1176.197203] si2157 10-0060: Silicon Labs Si2147/2148/2157/2158 successfully attached
[ 1176.197228] usb 1-4: DVB: registering adapter 0 frontend 0 (Silicon Labs Si2168)...
[ 1176.197483] usb 1-4: dvb_usb_v2: 'Sin Hon TDH601' successfully initialized and connected

And when running e.g. dvbv5-zap:

[ 1320.568204] si2168 9-0064: downloading firmware from file 'dvb-demod-si2168-b40-01.fw'
[ 1321.527006] si2168 9-0064: firmware version: B 4.0.25
[ 1321.538051] si2157 10-0060: found a 'Silicon Labs Si2157-A30'
[ 1321.588369] si2157 10-0060: firmware version: 3.0.5

And lsusb:

# lsusb -v

Bus 001 Device 003: ID 3344:24a0
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  idVendor           0x3344
  idProduct          0x24a0
  bcdDevice            0.00
  iManufacturer           0
  iProduct                0
  iSerial                 3 ???
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           76
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xc0
      Self Powered
    MaxPower              500mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       1
      bNumEndpoints           7
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x86  EP 6 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x87  EP 7 IN
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x03fc  1x 1020 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x0a  EP 10 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x8a  EP 10 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
Device Qualifier (for other device speed):
  bLength                10
  bDescriptorType         6
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  bNumConfigurations      1
Device Status:     0x0000
  (Bus Powered)

The lsusb was made on another computer, but that shouldn’t matter.

Linux Mint 18.1 + i915: Updating the Intel Graphics stack

Running Linux Mint 18.1 (kernel 4.4.0-53) on a Gigabyte Brix BACE-3160 (with an i915 graphics controller) , I had all kind of minor graphics artifacts (in particular a sluggish mouse pointer and Kodi felt heavy). So obviously, I went for updating the graphics stack.

Intel supplies an tool for doing this automagically on a selected list of distributions. In particular, the Graphics Update Tool 2.0.2 is intended for Ubuntu 16.04. Which is fine, since Mint 18.1 is derived from this distribution exactly.

So I downloaded it, and installed it:

$ sudo dpkg -i intel-graphics-update-tool_2.0.2_amd64.deb

That didn’t work all that nice, because there were missing dependencies. Easily fixed with

$ sudo apt-get -f install

And then launch the tool itself:

$ sudo intel-graphics-update-tool

And this is where the tool refused, because the distribution doesn’t match. So I tricked it by replacing the file it checks (a thanks goes to strace). First, save the real one:

$ sudo mv /etc/lsb-release /etc/lsb-release-mint

and then edit /etc/lsb-release to mimic Ubuntu. That is, saying this:

DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04 LTS"

And then run the tool again. Now everything went through fine. And for the record, it really helped with those graphics issues.

By the way, among the things it did was to add an apt-get repository. Namely, adding the file /etc/apt/sources.list.d/intellinuxgraphics.list with the following:

deb https://download.01.org/gfx/ubuntu/16.04/main xenial main #Intel Graphics drivers

For the record, this is /etc/lsb-release for Linux Mint 18.1:

DISTRIB_ID=LinuxMint
DISTRIB_RELEASE=18.1
DISTRIB_CODENAME=serena
DISTRIB_DESCRIPTION="Linux Mint 18.1 Serena"

In case someone needs to fake Linux Mint…

Note that the effective initrd.img file (probably in /boot) must be updated (I’m not 100% about it, but it seems like the update tool did it): Odds are that the kernel modules related to graphics are loaded from the initrd.img, and not /lib/modules, as part of displaying the graphical splash screen (Plymouth) until Gnome is up and running. Unless you’ve disabled it, like myself.

PCIe: Xilinx’ pipe_clock module and its timing constraints

Introduction

In several versions of Xilinx’ wrapper for the integrated PCIe block, it’s the user application logic’s duty to instantiate the module which generates the “pipe clock”. It typically looks something like this:

pcie_myblock_pipe_clock #
      (
          .PCIE_ASYNC_EN                  ( "FALSE" ),                 // PCIe async enable
          .PCIE_TXBUF_EN                  ( "FALSE" ),                 // PCIe TX buffer enable for Gen1/Gen2 only
          .PCIE_LANE                      ( LINK_CAP_MAX_LINK_WIDTH ), // PCIe number of lanes
          // synthesis translate_off
          .PCIE_LINK_SPEED                ( 2 ),
          // synthesis translate_on
          .PCIE_REFCLK_FREQ               ( PCIE_REFCLK_FREQ ),        // PCIe reference clock frequency
          .PCIE_USERCLK1_FREQ             ( PCIE_USERCLK1_FREQ ),      // PCIe user clock 1 frequency
          .PCIE_USERCLK2_FREQ             ( PCIE_USERCLK2_FREQ ),      // PCIe user clock 2 frequency
          .PCIE_DEBUG_MODE                ( 0 )
      )
      pipe_clock_i
      (

          //---------- Input -------------------------------------
          .CLK_CLK                        ( sys_clk ),
          .CLK_TXOUTCLK                   ( pipe_txoutclk_in ),     // Reference clock from lane 0
          .CLK_RXOUTCLK_IN                ( pipe_rxoutclk_in ),
          .CLK_RST_N                      ( pipe_mmcm_rst_n ),      // Allow system reset for error_recovery
          .CLK_PCLK_SEL                   ( pipe_pclk_sel_in ),
          .CLK_PCLK_SEL_SLAVE             ( pipe_pclk_sel_slave),
          .CLK_GEN3                       ( pipe_gen3_in ),

          //---------- Output ------------------------------------
          .CLK_PCLK                       ( pipe_pclk_out),
          .CLK_PCLK_SLAVE                 ( pipe_pclk_out_slave),
          .CLK_RXUSRCLK                   ( pipe_rxusrclk_out),
          .CLK_RXOUTCLK_OUT               ( pipe_rxoutclk_out),
          .CLK_DCLK                       ( pipe_dclk_out),
          .CLK_OOBCLK                     ( pipe_oobclk_out),
          .CLK_USERCLK1                   ( pipe_userclk1_out),
          .CLK_USERCLK2                   ( pipe_userclk2_out),
          .CLK_MMCM_LOCK                  ( pipe_mmcm_lock_out)

      );

Consequently, some timing constraints that are related to the PCIe block’s internal functionality aren’t added automatically by the wrapper’s own constraints, but must be given explicitly by the user of the block, typically by following an example design.

This post discusses the implications of this situation. Obviously, none of this applies to PCIe block wrappers which handle this instantiation internally.

What is the pipe clock?

For our narrow purposes, the PIPE interface is the parallel data part of the SERDES attached to the Gigabit Transceivers (MGTs), which drive the physical PCIe lanes. For example, data to a Gen1 lane, running at 2.5 GT/s, requires 2.0 Gbit/s of payload data (as it’s expanded by a 10/8 ratio with 10b/8b encoding). If the SERDES is fed with 16 bits in parallel, a 125 MHz clock yields the correct data rate (125 MHz * 16 = 2 GHz).

By the same coin, a Gen2 interface requires a 250 MHz clock to support a payload data rate of 4.0 Gbit/s per lane (expanded into 5 GT/s with 10b/8b encoding).

The clock mux

If a PCIe block is configured for Gen2, it’s required to support both rates: 5 GT/s, and also be able to fall back to 2.5 GT/s if the link partner doesn’t support Gen2 or if the link doesn’t work properly at the higher rate.

In the most common setting (or always?), the pipe clock is muxed between two source clocks by this piece of code (in the pipe_clock module):

    //---------- PCLK Mux ----------------------------------
    BUFGCTRL pclk_i1
    (
        //---------- Input ---------------------------------
        .CE0                        (1'd1),
        .CE1                        (1'd1),
        .I0                         (clk_125mhz),
        .I1                         (clk_250mhz),
        .IGNORE0                    (1'd0),
        .IGNORE1                    (1'd0),
        .S0                         (~pclk_sel),
        .S1                         ( pclk_sel),
        //---------- Output --------------------------------
        .O                          (pclk_1)
    );
    end

So pclk_sel, which is a registered version of the CLK_PCLK_SEL input port is used to switch between a 125 MHz clock (pclk_sel == 0) and a 250 MHz clock (clk_sel == 1), both clocks generated from the same MMCM_ADV block in the pipe_clock module.

The BUFGMUX’ output, pclk_1 is assigned as the pipe clock output (CLK_PCLK). It’s also used in other ways, depending on the instantiation parameters of pipe_clock.

Constraints for Gen1 PCIe blocks

If a PCIe block is configured for Gen1 only, there’s no question about the pipe clock’s frequency: It’s 125 MHz. As a matter of fact, if the PCIE_LINK_SPEED instantiation parameter is set to 1, one gets (by virtue of Verilog’s generate commands)

    BUFG pclk_i1
    (
        //---------- Input ---------------------------------
        .I                          (clk_125mhz),
        //---------- Output --------------------------------
        .O                          (clk_125mhz_buf)
    );
    assign pclk_1 = clk_125mhz_buf;

But never mind this — it’s never used: Even when the block is configured as Gen1 only, PCIE_LINK_SPEED is set to 3 in the example design’s instantiation, and we all copy from it.

Instead, the clock mux is used and fed with pclk_sel=0. The constraints reflect this with the following lines appearing in the example design’s XDC file for Gen1 PCIe blocks (only!):

set_case_analysis 1 [get_pins {pcie_myblock_support_i/pipe_clock_i/pclk_i1_bufgctrl.pclk_i1/S0}]
set_case_analysis 0 [get_pins {pcie_myblock_support_i/pipe_clock_i/pclk_i1_bufgctrl.pclk_i1/S1}]
set_property DONT_TOUCH true [get_cells -of [get_nets -of [get_pins {pcie_myblock_support_i/pipe_clock_i/pclk_i1_bufgctrl.pclk_i1/S0}]]]

The first two commands tell the timing analysis tools to assume that the clock mux’ inputs are S0=1 and S1=0, and hence that the mux forwards the 125 MHz clock (connected to I0).

The DONT_TOUCH constraint works around a bug in early Vivado revisions, as explained in AR #62296: The S0 input is assigned ~pclk_sel, which requires a logic inverter. This inverter was optimized into the BUFCTRL primitive by the synthesizer, flipping the meaning of the first set_case_analysis constraints. Which caused the timing tools to analyze the design as if both S0 and S1 were set to zero, hence no clock output, and no constraining of the relevant paths.

The problem with this set of constraints is their cryptic nature: It’s not clear at all why they are there, just by reading the XDC file. If the user of the PCIe block decides, for example, to change from a 8x Gen1 configuration to 4x Gen2, everything will appear to work nicely, since all clocks except the pipe clock remain the same. It takes some initiative and effort to figure out that these constraints are incorrect for a Gen2 block.

To make things even worse, almost all relevant paths will meet the 250 MHz (4 ns) requirement even when constrained for 125 MHz on a sparsely filled FPGA, simply because there’s little logic along these paths. So odds are that everything will work fine during the initial tests (before the useful logic is added to the design), and later on the PCIe interface may become shaky throughout the design process, as some paths accidentally exceed the 4 ns limit.

Dropping the set_case_analysis constraints

As these constraints are relaxing by their nature, what happens if they are dropped? Once could expect that the tools would work a bit harder to ensure that all relevant paths meet timing with either 125 MHz or 250 MHz, or simply put, that the constraining would occur as if pclk_1 was always driven with a 250 MHz clock.

But this isn’t how timing calculations are made. The tools can’t just pick the faster clock from a clock mux and follow through, since the logic driven by the clock might interact with other clock domains. If so, a slower clock might require stricter timing due to different relations between the source and target clock’s frequencies.

So what actually happens is that the timing tools mark all logic driven by the pipe clock as having multiple clocks: The timing of each path going to and from any such logic element is calculated for each of the two clocks. Even the timing for paths going between logic elements that are both driven by the pipe clock are calculated four times, covering the four combinations of the 125 MHz and 250 MHz clocks, as source and destination clocks.

From a practical point of view, this is rather harmless, since both clocks come from the same MMCM_ADV, and are hence aligned. Making these excessive timing calculations always ends up with the equivalent for the 250 MHz clock only (some clock skew uncertainty possibly added for going between the two clocks). Since timing is met easily on these paths, this extra work adds very little to the implementation efforts (and how long it takes to finish).

On the other hand, this adds some dirt to the timing report. First, the multiple clocks are reported (excerpt from the Timing Report):

7. checking multiple_clock
--------------------------
 There are 2598 register/latch pins with multiple clocks. (HIGH)

Later on, the paths between logic driven by the pipe clock are counted as inter clock paths: Once from 125 MHz to 250 MHz, and vice versa. This adds up to a large number of bogus inter clock paths:

------------------------------------------------------------------------------------------------
| Inter Clock Table
| -----------------
------------------------------------------------------------------------------------------------

From Clock    To Clock          WNS(ns)      TNS(ns)  TNS Failing Endpoints  TNS Total Endpoints      WHS(ns)      THS(ns)  THS Failing Endpoints  THS Total Endpoints
----------    --------          -------      -------  ---------------------  -------------------      -------      -------  ---------------------  -------------------
clk_250mhz    clk_125mhz          0.114        0.000                      0                 5781        0.053        0.000                      0                 5781
clk_125mhz    clk_250mhz          0.114        0.000                      0                 5764        0.053        0.000                      0                 5764

Since a single endpoint might produce many paths (e.g. a block RAM), there’s no need for a correlation between the number of endpoints and the number of paths. However the similarity between the figures of the two directions seems to indicate that the vast majority of these paths are bogus.

So dropping the set_case_analysis constraints boils down to some noise in the timing report. I can think of two ways to eliminate it:

  • Issue set_case_analysis constraints setting S0=0, S1=1, so the tools assume a 250 MHz clock. This covers the Gen2 case as well as Gen1.
  • Use the constraints of the example design for a Gen2 block (shown below).

Even though both ways (in particular the second) seem OK to me, I prefer taking the dirt in the timing report and not add constraints without understanding the full implications. Being more restrictive never hurts (as long as the design meets timing).

Constraints for Gen2 PCIe blocks

If a PCIe block is configured for Gen2, it has to be able to work a Gen1 as well. So the set_case_analysis constraints are out of the question.

Instead, this is what one gets in the example design:

create_generated_clock -name clk_125mhz_x0y0 [get_pins pcie_myblock_support_i/pipe_clock_i/mmcm_i/CLKOUT0]
create_generated_clock -name clk_250mhz_x0y0 [get_pins pcie_myblock_support_i/pipe_clock_i/mmcm_i/CLKOUT1]
create_generated_clock -name clk_125mhz_mux_x0y0 \
                        -source [get_pins pcie_myblock_support_i/pipe_clock_i/pclk_i1_bufgctrl.pclk_i1/I0] \
                        -divide_by 1 \
                        [get_pins pcie_myblock_support_i/pipe_clock_i/pclk_i1_bufgctrl.pclk_i1/O]
#
create_generated_clock -name clk_250mhz_mux_x0y0 \
                        -source [get_pins pcie_myblock_support_i/pipe_clock_i/pclk_i1_bufgctrl.pclk_i1/I1] \
                        -divide_by 1 -add -master_clock [get_clocks -of [get_pins pcie_myblock_support_i/pipe_clock_i/pclk_i1_bufgctrl.pclk_i1/I1]] \
                        [get_pins pcie_myblock_support_i/pipe_clock_i/pclk_i1_bufgctrl.pclk_i1/O]
#
set_clock_groups -name pcieclkmux -physically_exclusive -group clk_125mhz_mux_x0y0 -group clk_250mhz_mux_x0y0

This may seem tangled, but says something quite simple: The 125 MHz and 250 MHz clocks are physically exclusive (see AR #58961 for an elaboration on this). In other words, these constraints declare that no path exists between logic driven by one clock and logic driven by the other. If such path is found, it’s bogus.

So this drops all the bogus paths mentioned above. Each path between logic driven by the pipe clock is now calculated twice (for 125 MHz and 250 MHz, but not across the clocks). This seems to yield the same practical results as without these constraints, but without complaints about multiple clocks, and of course no inter-clock paths.

Both clocks are still related to the pipe clock however. For example, checking a register driven by the pipe clock yields (Tcl session):

get_clocks -of_objects [get_pins -hier -filter {name=~*/pipe_clock_i/pclk_sel_reg1_reg[0]/C}]
clk_250mhz_mux_x0y0 clk_125mhz_mux_x0y0

Not surprisingly, this register is attached to two clocks. The multiple clock complaint disappeared thanks to the set_clock_groups constraint (even the lower “asynchronous” flag is enough for this purpose).

So can these constraints be used for a Gen1-only block, as a safer alternative for the set_case_analysis constraints? It seems so. Is it a good bargain for getting rid of those extra notes in the timing report? It’s a matter of personal choice. Or knowing for sure.

Bonus: Meaning of some instantiation parameters of pipe_clock

This is the meaning according to dissection of Kintex-7′s pipe_clock Verilog file. It’s probably the same for other targets.

PCIE_REFCLK_FREQ: The frequency of the reference clock

  • 1 => 125 MHz
  • 2 => 250 MHz
  • Otherwise: 100 MHz

CLKFBOUT_MULT_F is set to that the MCMM_ADV’s internal VCO always runs at 1 GHz. Hence the constant CLKOUT0_DIVIDE_F = 8 makes clk_125mhz run at 125 MHz (dividing by 8), and CLKOUT1_DIVIDE = 4 makes clk_250mhz run at 250 MHz (dividing by 8)

PCIE_USERCLK1_FREQ: The frequency of the module’s CLK_USERCLK1 output, which is among others the clock with the user interface (a.k.a. user_clk_out or axi_clk)

  • 1 => 31.25 MHz
  • 2 => 62.5 MHz
  • 3 => 125 MHz
  • 4 => 250 MHz
  • 5 => 500 MHz
  • Otherwise: 62.5 MHz

PCIE_USERCLK2_FREQ: The frequency of the module’s CLK_USERCLK2 output. Not used in most applications. Same frequency mapping as PCIE_USERCLK1_FREQ.

Turning off autosave on WordPress (since it hangs)

After upgrading to WordPress 4.7.2, and thought it would be smashing fun, I found my “Publish” button disabled due to a draft being saved forever. There have been many complaints about this all over the web. I didn’t manage to find a solution to this problem, just a workaround: Disable autosaving altogether.

This is a reasonable measure in my case, since I press the “Update” button frequently enough anyhow. The autosaving is more of an annoyance to me. I tried to think about a single time I used an autosaved revision. My answer was never.

Some have attempted to increase the autosave interval (a.k.a. AUTOSAVE_INTERVAL) to 24 hours or so, but reported that it didn’t help.

And I should mention that I didn’t upgrade the themes or anything else but WordPress itself. Maybe more upgrading (and trouble)?

I went for the solution presented on this page, and it actually works.

Essentially, the idea is to add the following piece of code at the end of the functions.php file of the active theme, just before the closing “?>” of the file.

function disable_autosave() {
    wp_deregister_script('autosave');
}
add_action('wp_print_scripts','disable_autosave');

(in my case, the file was wp-content/themes/cognoblue/functions.php, as I’m using the Cognoblue theme).

That’s it. I cleared my browser’s cache after this change, just to be safe. No more autosave, and no more problems with the Update/Publish button.

Note that this doesn’t disable revisions (which I actually like). And don’t ask me why and how this works. I have no idea.

Making a mirror of a WordPress blog on my own computer

This is a note to self on how to create a mirror copy of this blog on my own computer. My own peculiar methods are all over.

  • Create a virtual host on my apache httpd server (port 99)
  • Uncompress the site’s entire public_html directory into the virtual host site’s root directory (that’slazy, I got a lot of unnecessary files this way)
  • Create a git repo, and add the blog/ directory so I have a look on what happens (not really necessary, but I make git repos everywhere)
  • Create a new database, and fill it with an SQL backup of the site:
    $ mysql -D blogmirror < ~/Desktop/blogdb-Mon_17.01.23-05.23.sql
  • Change the database settings in wp_config.php. For a local database, which requires no password, it became (DB_HOST remained “localhost”):
    define('DB_NAME', 'blogmirror');
    
    /** MySQL database username */
    define('DB_USER', 'eli');
    
    /** MySQL database password */
    define('DB_PASSWORD', '');
  • Change WordPress’ view of the blog’s root (following this post), or it diverts from the allocated port 99. Add the following two lines to wp_config.php:
    define('WP_HOME','http://10.10.10.10:99/blog');
    define('WP_SITEURL','http://10.10.10.10:99/blog');

    Note that there’s no trailing slash. Adding one makes double slashes on the URLs generated by WordPress.

That’s it. At this point the blog worked. Log in with the same user and password as the real blog.

Automatic upgrade of the mirror

Create a new user, sitemirror, but don’t create the user’s home directory. Instead, edit /etc/passwd to set the home directory at the site’s root directory (not the public_html, but where public_html can be found).

Also, change the ownership of that directory to that user, or the modification through ftp won’t work:

# chown -R sitemirror sitemirror/

And start the ftp service (it doesn’t matter that the WordPress has an SFTP option. It goes for regular port 21 anyhow):

# service vsftpd start

Also, make the path accessible to the sitemirror user (in my case, it meant making the home directory accessible to all. Temporarily!)

Now, on the automatic upgrade page, go for localhost and the username/password of the ad-hoc sitemirror user.

And you get something like this:

Screenshot after upgrading

 

And now revert the damage made above: Shut down vsftpd (and verify port 21 is closed), fix the ownership of the sitemirror directory, and restore the ownership of the home directory.

Then remove the sitemirror user (but not its home directory, obviously).

This is for upgrading the local mirror blog. On the real blog, it was just clicking on the button for upgrading. No questions asked about FTP or anything like that.

But the irony is that after upgrading (to 4.7.2), I attempted to publish this post, but “Publish” button was inactive, and the editor stuck on “Saving Draft…”. To double the irony, I upgraded just because one has to, as a security measure. All was fine.

Oddly enough, I didn’t have a similar problem with the (upgraded) mirrored blog.

I wrote a separate post on how I worked around this issue.

How I love upgrading. Actually, I love downgrading more.