Linux USB multiseat audio support

Posted on 20. Jan, 2010 by Bernie Thompson in udlfb

Here’s how to add audio support on top of the previous instructions for getting USB multiseat running on Linux, with a Plugable UD-160-A type device.

Add the following line to the bottom of the /lib/udev/rules.d/50-usbseat.rules file created per the previous instructions.

KERNEL=="control*", SUBSYSTEM=="sound", SUBSYSTEMS=="usb", PROGRAM="/bin/cat /sys/%p/../../../../../devnum", SYMLINK+="usbseat/%c/sound"

Then create a new /etc/X11/Xsession.d/50usbseat file which will be run at Xsession create time, with the following contents

oldIFS=$IFS
IFS=:
set $DISPLAY
IFS=.
set $2
SEAT_ID=$1
LN=`ls -al /dev/usbseat/$SEAT_ID/sound`
IFS=C
set $LN
CARD_ID=$2
export ALSA_CARD=$2
export ALSA_PCM_CARD=$2
IFS=$oldIFS

Each of the users who might need access to the USB devices needs to be added to the ‘audio’ group. On Ubuntu 9.04, this can be done with these commands to backup and then modify the groups (replace MY_USERNAME, of course) …

sudo cp /etc/group /etc/group_backup
sudo chmod a-wx /etc/group_backup
sudo adduser MY_USERNAME audio

See Ubuntu Sound TroubleShooting for details on that step.

Now, as you connect UD-160-A terminals, a new X instance and GDM login will pop up as before, but also each of them will have /dev/usbseat/%SEAT_ID%/sound linking to their sound device, and the ALSA_CARD environment variable for all processes off of that X session, set to the matching sound card ID. For apps which support ALSA/Pulse (like most browsers, flash, etc.), audio will now come out the correct terminal — all in a completely plug-and-play fashion.

If you’re wondering what the strange IFS stuff is in the above script, it’s bash’s built-in Internal Field Separator variable, which is an easy way to split strings without having to launch a separate sed or awk process.

Note, as before, these instructions are specific to and tested on an older version of Ubuntu: 9.04, and may need to be ported to other distros until the distros themselves integrate these scripts.

Thanks to Alexander Todorov’s earlier work on multiseat sound support, which demonstrated how to match the USB audio devices in udev, and which ALSA_ environment variables to set. Alexander reported some problems reliably matching the audio devices, but with these scripts (with limited testing so far), things are working as expected.

Explaining multiseat benefits

Posted on 19. Nov, 2009 by Bernie Thompson in Windows

Following on the announcement of Windows Multipoint Server 2010, HP has announced their first product to connect with it: The HP MultiSeat thin client T100. It’s a DisplayLink-based device with VGA, PS/2 keyboard and mouse ports, and audio. It’s a focused competitor to the product that Plugable sells here, and they do a great job of explaining the benefits in video form here and here.

DisplayLink’s new thin client materials also provide a bunch of additional detail, with good bullet points on why simple USB connectivity provides real value for this scenario.

Companies like Sun, nComputing, Miniframe, and Userful have sold products for Windows and Unix in this space for some time. The unix world has had various forms of multiseat capability forever — not to speak of traditional X terminals and all the thin client network protocols like RDP, ICA, and VNC on Windows and Unix, which people think of first when they think of terminals.

But when it comes to true multiseat (locally connected devices, treated as terminals), Linux has had it first, has lots of advantages in terms of licensing cost and performance, has gotten hundreds of thousands of seats in use — and yet hasn’t quite make it easy enough for end users for it to reach its full audience.

So these new product launches, now with Microsoft on board, definitely have the ability to raise more awareness of the potential of this scenario. It’s a challenge to the Unix and Linux crowd to leverage their built in advantages — and continue working on the shortcomings of Linux as a client desktop — in order to continue to provide compelling competition in this market.

Either way, the additional exposure is great for communicating the potential of multiseat.

Setting up USB multiseat with DisplayLink on Linux (GDM up to 2.20)

Posted on 16. Nov, 2009 by Bernie Thompson in UD-160-A

Four $99 UD-160-A Terminals off a single $299 netbookSoon, we’ll be able to plug inexpensive zero-state USB docks/terminals into new Linux systems, and a new graphical login will pop up in a completely plug and play fashion. Many users can then simultaneously share any single Linux PC. This is great for education, libraries, internet cafes, etc — anywhere where you have clusters of client machines with light 3D/video app demands, and want the simplicity and savings of just one server machine with many terminals connected.

Until this is in the distros, we have to assemble a few pieces ourselves to get everything installed for this scenario. Some major components (GDM/ConsoleKit) are changing the way this kind of thing will be done, so for now the instructions here are designed only for distributions with GDM 2.20 or earlier (Ubuntu < 9.10; Fedora < F12; Debian < 5.0; etc). The instructions have been tested most extensively on Ubuntu 9.04.

gdm –version

will show what version you have on your current system. Any tweaks to make things work your specific distro are definitely welcome in the comments.

The first step is hardware. You need a USB hub with a DisplayLink USB device and free USB ports for keyboard and mouse. You can get this in one package in the form of devices like the Plugable Universal Docking Station, or buy independent devices like the UGA-125-HUB bundle. USB Audio and other devices can be made to work also, but these instructions just cover basic display, keyboard, and mouse.

With that, you're ready to start configuring your Linux setup ...

1. DisplayLink framebuffer driver

udlfb is in the staging tree of Linux kernels 2.6.32 and later. We'll install the latest version here. First, make sure you have git installed with "sudo apt-get install git-core", and create a directory which will host all the source code you download with git (e.g. ~/git/).

sudo apt-get install module-assistant
sudo module-assistant prepare
git clone http://git.plugable.com/webdav/udlfb/
cd udlfb
make
sudo make install
sudo depmod -a

Now, when you plug in a DisplayLink device, you should see a "green screen" as the driver successfully loads and sets the graphics mode to match your monitor.

2. DisplayLink X server

This will get the X server installed, ready for use by later scripts.

sudo apt-get install pkg-config xorg-dev
cd ~git
git clone http://git.plugable.com/webdav/xf-video-udlfb/
cd xf-video-udlfb
./configure
make
sudo make install

We now need to create or modify a few scripts and configuration files. You'll need to use sudo to edit files in these system directories. You can cut/paste the text below, or download the files with "git clone http://git.plugable.com/webdav/misc-udlfb/" and copy each to the right location, and fix up ownership and permissions on the files.

3. udev script

Create a file called /lib/udev/rules.d/50-usbseat.rules owned by user root, with the following contents.

# set all DisplayLink devices to configuration 1
# see http://libdlo.freedesktop.org/wiki/DeviceQuirks for more info
ATTR{idVendor}=="17e9", ATTR{bConfigurationValue}=="2", RUN="/bin/echo 1 > /sys%p/bConfigurationValue"
 
# aliases for display, kbd, mouse attached to specific hubs
 
KERNEL=="fb*",SUBSYSTEMS=="usb",PROGRAM="/bin/cat /sys/%p/../../../devnum",SYMLINK+="usbseat/%c/display",RUN+="usbseat.sh %c"
KERNEL=="mouse*", SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="03", ATTRS{bInterfaceProtocol}=="02", PROGRAM="/bin/cat /sys/%p/../../../../../devnum",SYMLINK+="usbseat/%c/mouse",RUN+="usbseat.sh %c"
KERNEL=="event*", SUBSYSTEM=="input", ATTRS{bInterfaceClass}=="03", ATTRS{bInterfaceProtocol}=="01",PROGRAM="/bin/cat /sys/%p/../../../../../devnum",SYMLINK+="usbseat/%c/keyboard",RUN+="usbseat.sh %c"
KERNEL=="control*", SUBSYSTEM=="sound", SUBSYSTEMS=="usb", PROGRAM="/bin/cat /sys/%p/../../../../../devnum", SYMLINK+="usbseat/%c/sound"
 
# Handle when keyboard and mouse are one more hub downstream. Relying on pnp order to have already set up mouse, keyboard on upstream hub if we're daisy-chaining
KERNEL=="event*", SUBSYSTEM=="input", ATTRS{bInterfaceClass}=="03", ATTRS{bInterfaceProtocol}=="01",PROGRAM="/bin/cat /sys/%p/../../../../../../devnum",SYMLINK+="usbseat/%c/keyboard",RUN+="usbseat.sh %c"
KERNEL=="mouse*", SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="03", ATTRS{bInterfaceProtocol}=="02", PROGRAM="/bin/cat /sys/%p/../../../../../../devnum",SYMLINK+="usbseat/%c/mouse",RUN+="usbseat.sh %c"

The udev subsystem will run this script automatically each time a USB device is attached.

4. usbseat.sh script

Create a file /lib/udev/usbseat.sh owned by user root with the following contents. Make sure to "sudo chmod a+x" to make the file executable.

#!/bin/bash
# takes the "seat number" as parameter $1
# the seat number is the kernel device id of the hub the seat's devices are sitting off of
# called once for every usb device that MIGHT be part of a seat, when they arrive or remove 
 
if [[ !(-n `/bin/pidof gdm`) ]]; then
    exit 0
fi
 
seat_running=`/usr/bin/gdmdynamic -l | /bin/sed -n -e "/:$1,/p"`
 
# $ACTION environment variable is set by udev subsystem
case "$ACTION" in
	'remove')
		if [[ -n "{$seat_running}" ]]; then
			/usr/bin/gdmdynamic -v -d $1
		fi
		;;
	*)
                # A device which might be part of a seat has been added
 
		# if we already have a running seat for this #, exit
		if [[ -n "${seat_running}" ]]; then
			exit 0
		fi
 
		if [[ -e /dev/usbseat/$1/keyboard && -e /dev/usbseat/$1/mouse && -e /dev/usbseat/$1/display ]]; then
 
			# We have a newly complete seat. Start it.
			TMPFILE=`/bin/mktemp` || exit 1
			/bin/sed "s/%ID_SEAT%/$1/g" < /lib/udev/usbseat-xf86.conf.sed > $TMPFILE
			/usr/bin/gdmdynamic -v -t 2 -s 1 -a "$1=/usr/X11R6/bin/X -br :$1 -audit 0 -nolisten tcp vt07 -config $TMPFILE"
			/usr/bin/gdmdynamic -v -r $1
		fi
		;;
esac
 
exit 0

5. X config file

Create file /lib/udev/usbseat-xf86.conf.sed with contents

 
Section "ServerFlags"
	Option	"AutoEnableDevices"	"false"
	Option	"AutoAddDevices"	"false"
	Option  "DefaultLayout"		"seat"
	Option	"DontZoom"		"true"
	Option	"DontZap"		"true"
	Option	"AllowMouseOpenFail"	"yes"
EndSection
 
Section "Module"
	Load "ddc"
EndSection
 
Section "Files"                                                                                                              
    ModulePath      "/usr/lib/xorg/modules"
    ModulePath      "/usr/local/lib/xorg/modules"
EndSection
 
Section "Device"
	Identifier "dl"
	driver	   "displaylink"
	Option "fbdev"	"/dev/usbseat/%ID_SEAT%/display"
EndSection
 
Section "InputDevice"
	Identifier "keyboard"
	Driver	"evdev"
	Option	"CoreKeyboard"
	Option	"Device"	"/dev/usbseat/%ID_SEAT%/keyboard"
	Option	"XkbModel"	"evdev"
	Option	"XkbLayout"	"us"
EndSection
 
Section "InputDevice"
	Identifier "mouse"
	Driver	"mouse"
	Option	"CorePointer"
	Option	"Protocol" "ImPS/2"
	Option	"Device"	"/dev/usbseat/%ID_SEAT%/mouse"
        Option  "Buttons" "5"
	Option	"ZAxisMapping" "4 5"
EndSection
 
Section "Monitor"
	Identifier "monitor"
EndSection
 
Section "Screen"
	Identifier "screen"
	Device "dl"
	Monitor "monitor"
EndSection
 
Section "ServerLayout"
	Identifier "seat"
	Screen	0 "screen" 0 0 
	InputDevice "keyboard" "CoreKeyboard"
	InputDevice "mouse" "CorePointer"
EndSection

6. fbcon workaround

fbcon is a standard Linux kernel module, which aggressively assumes it can open any framebuffer device and take it over for use as a text terminal. Unfortunately, that's not what we want if we're going to be using that framebuffer to run its own X server. So we need to add a file /etc/modprobe.d/fbcon.conf to disable fbcon and leave our framebuffers free for other uses.

blacklist font
blacklist tileblit
blacklist bitblit
blacklist fbcon

This file will not take effect until you run

sudo update-initramfs -u

Now, when you reboot and run "lsmod" you should not see fbcon in the loaded modules. And in /sys/class/graphics you should see fb0, instead of fbcon.

7. xrandr workaround

The DisplayLink X server currently has limited RANDR support, but later versions of GDM assume better. So for the time being, a workaround is required to get GDM applications (including gdmlogin) to display properly within the actual screen area -- otherwise they tend to think the screen has a strange rotation, and display themselves completely off it.

Add these lines into /etc/gdm/Init/Default, right after the definition of gdmwhich()

XRANDR=`gdmwhich xrandr`
if [ "x$XRANDR" != "x" ]; then
  $XRANDR -o 0
fi

8. /etc/rc.local script

Add the following lines to your /etc/rc.local script to check for attached usb terminals that were attached (at boot), and udev found them before GDM was running.

oldIFS=$IFS
IFS=/
for seat in /dev/usbseat/*; do
	set $seat
	/lib/udev/usbseat.sh $4
done
IFS=$oldIFS

9. /etc/init.d/gdm patch

In recent versions of X, the system largely assumes that you'll only run one X server, and it will own all devices. So to support multiseat with multiple X servers easily, we need to have two configurations: normal (using only your primary graphics), and multiseat (where your primary graphics isn't used). We do this by detecting whether you have a USB terminal attached and configured, and if so coming up with a different gdm configuration.

Add these lines to your /etc/init.d/gdm script, just after the section to "Allow cdd to override the config" (around line 35).

# Allow usbseat to override the config
if [ -f /etc/gdm/gdm-usbseat.conf ]; then
	for usbseat in /dev/usbseat/*; do
		seatid=${usbseat##*/}
		if [ -e "/dev/usbseat/$seatid/keyboard" -a -e "/dev/usbseat/$seatid/mouse" -a -e "/dev/usbseat/$seatid/display" ]; then
			CONFIG_FILE="--config=/etc/gdm/gdm-usbseat.conf"
		fi
	done
fi

10. Create /etc/gdm/gdm-usbseat.conf

This is the alternative gdm.conf that will be used when a USB terminal is present at boot:

[daemon]
DynamicXServers=true
FlexibleXServers=0
Greeter=/usr/lib/gdm/gdmgreeter
 
[security]
 
[xdmcp]
 
[gui]
 
[greeter]
 
[chooser]
 
[debug]
 
[servers]
0=inactive

Now, when you boot with USB terminal(s) attached, graphical logins will come up on all of those, while your primary display will remain a text console.

usb multiseat with Edubuntu

See my video from Linux Plumbers 2009 for more thoughts and background on USB multiseat.

Unsolved Problems

Beyond reworking for the new GDM/ConsoleKit versions, and getting all this just "built in" to the distributions, there are at least several major problems yet:

input duplication

[Update Feb 19, 2010 - fixed by removing "-sharevts -novtswitch" from the X start line and substituting a specific vt "vt07". Listing above now has problem resolved]

Input to any of the terminals will be duplicated to your primary console. This has a bunch of very nasty effects, including often duplicating your login to both your session and the console, and possibly causing strange effects like ctrl-c within any terminal will often cause that whole X session to close. This seems to be http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=525736, which at the bottom reports a workaround involving faketty, which unfortunately didn't work for me. You can also see a short discussion of this bug at https://fedorahosted.org/pipermail/multiseat/2009-April/date.html There ought to be a simple solution here -- any comments on that welcome.

mouse scroll wheel doesn't work

[Update Feb 19, 2010 - fixed by changing Protocol from "auto" to "ImPS/2" in the xorg.conf]

This is a problem with some other ways of doing multiseat: https://fedorahosted.org/pipermail/multiseat/2009-February/000004.html Again, probably a small tweak might resolve this.

unplug terminal doesn't tear down (or reconnect)
Currently, when you physically disconnect a terminal, everything remains running. By default, each user can only be logged in once, which means that you must do gdmdynamic -l to find the zombie instance and sudo gdmdynamic -d $seat to kill it. But the behavior users would really like to see is disconnecting the terminal leaves all apps running, but logging in from that terminal (or any other) reconnects and just transparently starts where the user left off.

Comments on other distros (e.g. yum vs. apt-get), better solutions, and other problems are very much appreciated.

Page 3 of 512345