Showing posts with label ubuntu. Show all posts
Showing posts with label ubuntu. Show all posts

Sunday, May 15, 2016

How to fix a USB audio device that interferes with mouse clicks in Linux

(This post includes an overview of how to solve similar problems to the one I encountered.  If you're looking for just the solution?  Skip to "The solution" below.)

I recently got a Jabra USB headset.  It's a very nice headset with active noise cancelling, a decent microphone, and a USB audio controller that includes a few buttons to control the audio volume.  These buttons work directly with the OS by presenting as a USB keyboard and sending the same keystrokes that the volume up/down buttons on a real keyboard would.

Unfortunately, it also presents itself as a USB mouse with 12 buttons and takes precedence over any actual pointing devices attached to the system.  Why a USB audio device needs to present itself to the system as a mouse is beyond me.  (The only theory I've heard that makes any sense at all is that there's some Windows-specific driver that would intercept mouse events and do some sort of deeper OS integration.)  Whatever the reason, it's a terrible "feature" and an abuse of the USB HID specification.

But rant over. Fortunately because X (the graphics engine of desktop Linux systems) allows the user fine control over the system, this is pretty easy to fix.  I'm including the steps I used to troubleshoot and fix this so that if anyone runs into similar issues in the future they know what to do.

Narrowing the issue down to X


As soon as I started using this headset I noticed something strange: I couldn't click the mouse anywhere outside of the window that I had on top when I plugged it in.  This persisted until I unplugged the USB cable, when suddenly mouse clicks worked again.  When I plugged the USB cable back in, mouse clicks stopped working again.  Weird, but at least consistent!

Knowing that it had something to do with the USB device I started looking at a few system logs.  I ran 'dmesg', which shows the device driver logs for the system, but didn't seem anything unusual.  So it wasn't a Linux issue, which meant it had to be in X.

Diagnosing in X


The first thing I did was to see what X saw when I had the device plugged in.  I ran 'xinput', which shows a list of all of the input devices attached to the system.  Before plugging in the device I saw:

⎡ Virtual core pointer                     id=2 [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer               id=4 [slave  pointer  (2)]
⎜   ↳ ELAN Touchscreen                         id=10 [slave  pointer  (2)]
⎜   ↳ DLL0665:01 06CB:76AD Touchpad           id=12 [slave  pointer  (2)]
⎣ Virtual core keyboard                   id=3 [master keyboard (2)]
    ↳ Virtual core XTEST keyboard             id=5 [slave  keyboard (3)]
    ↳ Power Button                             id=6 [slave  keyboard (3)]
    ↳ Video Bus                               id=7 [slave  keyboard (3)]
    ↳ Power Button                             id=8 [slave  keyboard (3)]
    ↳ Sleep Button                             id=9 [slave  keyboard (3)]
    ↳ Integrated_Webcam_HD                     id=11 [slave  keyboard (3)]
    ↳ AT Translated Set 2 keyboard             id=13 [slave  keyboard (3)]
    ↳ Dell WMI hotkeys                         id=14 [slave  keyboard (3)]

and afterwards I saw:

⎡ Virtual core pointer                     id=2 [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer               id=4 [slave  pointer  (2)]
⎜   ↳ ELAN Touchscreen                         id=10 [slave  pointer  (2)]
⎜   ↳ DLL0665:01 06CB:76AD Touchpad           id=12 [slave  pointer  (2)]
⎜   ↳ GN Netcom A/S Jabra EVOLVE LINK MS       id=15 [slave  pointer  (2)]
⎣ Virtual core keyboard                   id=3 [master keyboard (2)]
    ↳ Virtual core XTEST keyboard             id=5 [slave  keyboard (3)]
    ↳ Power Button                             id=6 [slave  keyboard (3)]
    ↳ Video Bus                               id=7 [slave  keyboard (3)]
    ↳ Power Button                             id=8 [slave  keyboard (3)]
    ↳ Sleep Button                             id=9 [slave  keyboard (3)]
    ↳ Integrated_Webcam_HD                     id=11 [slave  keyboard (3)]
    ↳ AT Translated Set 2 keyboard             id=13 [slave  keyboard (3)]
    ↳ Dell WMI hotkeys                         id=14 [slave  keyboard (3)]

The highlighted line shows the change: this was the device I had plugged in.  (It appears that GN Netcom A/S is the actual manufacturer and Jabra the brand.)

This told me I was on the right track: the USB headset was showing up as an input device.  This could be why it was interfering with the mouse!  But I needed more information.  Fortunately, xinput has a more verbose mode, 'xinput list --long':

⎜   ↳ GN Netcom A/S Jabra EVOLVE LINK MS       id=15 [slave  pointer  (2)]
Reporting 16 classes:
Class originated from: 15. Type: XIButtonClass
Buttons supported: 12
Button labels: "Button 0" "Button 1" "Button 2" "Button Wheel Up" "Button Wheel Down" "Button Horiz Wheel Left" "Button Horiz Wheel Right" "Button 3" "Button 4" "Button 5" "Button 6" "Button 7"
Button state:
Class originated from: 15. Type: XIKeyClass
Keycodes supported: 248
Class originated from: 15. Type: XIValuatorClass
Detail for Valuator 0:
 Label: Abs X
 Range: 0.000000 - 1000.000000
 Resolution: 0 units/m
 Mode: absolute
 Current value: 1600.000000
...


(I've trimmed this down quite a bit; 'xinput list --long' really is long.)

From this I saw that it was reporting itself as a mouse device.  (Why does a USB headset need to pretend to the OS to be a mouse?  I still don't know.)  Something in that list of mouse buttons was messing with my actual mouse.  So I needed to disable that functionality while leaving the rest unchanged.

Fine-grained device control in X


The X device control system allows you to make a lot of tweaks to how devices work.  You can change how a keyboard or mouse work, how quickly the pointer moves when you move the mouse, how it accelerates based on the direction you are moving... all sorts of things.  But for my purposes I needed to use the functionality to change the order of mouse buttons.  This is normally for doing things like making a mouse more comfortable for left-handed users (e.g. changing the button order on the mouse from "1 2 3" to "3 2 1" so the left-hand index finger is the primary button).  But you can also map physical buttons to "button 0", which means disabled.  So I just disabled all 12 (why 12‽) mouse buttons.

I did this with the help of the X device control manual here: www.x.org/archive/X11R7.5/doc/man/man4/evdev.4.html

The device control system looks in a special location for files that contain instructions  to change how devices work.  On Ubuntu this location is /usr/share/X11/xorg.conf.d/ but may be different on other Linux distributions.

The solultion


I created /usr/share/X11/xorg.conf.d/50-jabra.conf with the following contents:

Section "InputClass"
Identifier "Jabra"
        MatchProduct "GN Netcom A/S Jabra EVOLVE LINK MS"
Option "ButtonMapping" "0 0 0 0 0 0 0 0 0 0 0 0"
EndSection

I then restarted the X system to make sure the changes would take effect.  (A reboot is the simplest way to do this if you're unsure how.)

Once that was finished I could plug and unplug my USB headset with no issues!

Followup


I submitted a bug report to the Ubuntu bug tracking system with my fix attached.  Hopefully it will be picked up and added to the distribution.  In the meantime I'll leave this post up in case anyone else has similar issues.

Friday, January 23, 2015

Building and running the Alljoyn 'About' sample application

I got some nifty color-changing networked light bulbs via a Kickstarter campaign recently.  Hopefully I'll have more to say about those later when I have a bit more time (as I took some unboxing shots and poked around in the router they shipped with it and found some curious things).  But in the meantime, I've been poking around at the Allseen Alliance's Alljoyn framework.

Alljoyn is a framework that allows all sorts of nifty communication and collaboration between devices.  Imagine getting a new window-mounted air conditioner and having it and your thermostat immediately start talking to each other and getting your house to the right temperature, or your lights dimming in response to you starting a movie on your television.  There are all sorts of cool things this sort of communication could allow.

But for now I'm just trying to get it built and working with my light bulbs.  Tonight I spent a little bit of time getting it built and running the simple 'About' sample application that ships with it.  I thought the notes I took might be useful if anyone else starts playing with it. And unfortunately some of the docs about running the 'About' samples are a bit wrong, so maybe this will help someone out.

And if you do play with Alljoyn, please let me know!  Double bonus points if you can point me at some tutorials that don't start with the assumption that you've read the whole codebase and every document ever produced.  I just want to make some light bulbs blink!

Set up the environment

I used the following:

  • Virtual machine (VirtualBox) with 2x x86_64 CPUs, 1GB RAM, 16 GB disk, bridged network
  • Ubuntu 14.04 Server
  • Default packageset plus OpenSSH server
  • Applied full updates (apt-get update ; apt-get upgrade) as of 23 Jan 2015 and reboot

I initially started with libvirt but after a few minutes of trying to convince it to bridge the network device, I decided to just go with VirtualBox rather than spend all night tinkering with that.

Install prerequisites

git:

sudo apt-get install git
git config --global user.email "email@server.tld"
git config --global user.name "User Name"

Repo:

(per http://source.android.com/source/downloading.html#installing-repo)

mkdir ~/bin
PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

Build tools:

(per https://allseenalliance.org/developers/develop/building/linux)
[NB: ia32-libs was not available in the package archives but didn't seem to be necessary]

sudo apt-get install build-essential libgtk2.0-dev libssl-dev xsltproc libxml2-dev scons libssl-dev #ia32-libs

Grab sources

Per https://wiki.allseenalliance.org/develop/downloading_the_source

mkdir -p src/alljoyn
cd src/alljoyn

# Uncomment other versions below if you want something other than 14.06
#repo init -u https://git.allseenalliance.org/gerrit/devtools/manifest
#repo init -u https://git.allseenalliance.org/gerrit/devtools/manifest -b refs/tags/v14.06 -m versioned.xml
repo init -u https://git.allseenalliance.org/gerrit/devtools/manifest -b RB14.06
repo sync

Build samples

export AJ_ROOT=`pwd`
cd core/alljoyn
scons BINDINGS=cpp WS=off BT=off ICE=off SERVICES="about,notification,controlpanel,config,onboarding,sample_apps"

Run 'about' sample

(per https://allseenalliance.org/developers/develop/run-sample-apps/about/linux)

cd $AJ_ROOT
export TARGET_CPU=x86_64

# NB: The following are in the docs but are wrong.  Corrections below.
#export LD_LIBRARY_PATH=$AJ_ROOT/core/alljoyn/build/linux/$TARGET_CPU/debug/dist/cpp/lib:$LD_LIBRARY_PATH
#$AJ_ROOT/core/alljoyn/build/linux/$TARGET_CPU/debug/dist/cpp/bin/samples/AboutService

export LD_LIBRARY_PATH=$AJ_ROOT/core/alljoyn/build/linux/$TARGET_CPU/debug/dist/cpp/lib:$AJ_ROOT/core/alljoyn/build/linux/$TARGET_CPU/debug/dist/about/lib:$LD_LIBRARY_PATH
$AJ_ROOT/core/alljoyn/build/linux/$TARGET_CPU/debug/dist/about/bin/AboutClient

Sunday, July 20, 2014

Using Netflix in XBMC with Ubuntu 14.04

I use XBMC to run my home theater system.  It's a great piece of software that can be configured to look and act in a huge variety of ways while remaining straightforward to use and set up.  One of its great features is an add-on system with a rich set of third-party multimedia tools.

Until recently, it was pretty straightforward to have a nice experience in XBMC under Linux using a combination of XBMCFlix and Pipelight.  Pipelight provides a way to run the Silverlight video streaming libraries under Linux and XBMCFlix launches the Netflix player in a full-screen Chrome window.  Installing these add-ons meant that Netflix worked well[1] and integrated right into XBMC.

Recently, however, Google has dropped support for the Netscape Plugin API (NPAPI) which Pipelight relies on to make Silverlight work.  Since XBMCFlix launches Chrome and can't be configured to use Firefox, this means that upgrading to a recent version of Chrome breaks Netflix support in XBMC on Linux.

Fortunately it's easy to work around this and trick the Chrome launcher used by XBMC to use Firefox instead.  It's a dirty hack and isn't a great solution for a general purpose desktop (especially as it involves uninstalling Chrome, which you might want to keep) but it does the trick on my entertainment system.

Here's how to do it:

  1. Uninstall Chrome or Chromium.  (On Ubuntu or other Debian-based distribution, you can do this in the Software Center or by typing "sudo apt-get remove google-chrome" (or "sudo apt-get remove chromium-browser" in a terminal.)
  2. Launch Firefox, put it in full-screen mode by pressing F11, and exit it with Alt-F4.  This will make it default to full-screen next time it's launched.
  3. Using your favorite text editor, create a script in your home directory called "google-chrome".  (It's important to make it all lower case and include the hyphen.  It needs to be named exactly "google-chrome", nothing else.)
  4. Copy and paste the following into the text file:

    #!/bin/bash

    for i in $@; do
        if [[ $i =~ ^http ]]
        then
          urls="$urls $i"
        fi
    done

    firefox $urls
  5. Save the file.
  6. Make your script executable so it can be launched by XBMC by opening a terminal and typing "chmod a+x google-chrome".
  7. Put your script where the launcher for Chrome would normally live by typing "sudo mv google-chrome /usr/bin".
Once this is done, you can install Pipelight and XBMCFlix in XBMC as you normally would.  When XBMCFlix goes to launch Chrome, it will launch Firefox instead.

Again, this is a bit of an ugly hack, but if you have a PC that does nothing but run XBMC this will get you up and working quickly.  There are ways that a script like this could be exploited, but the odds of Netflix including one in a URL to target Linux users of XBMC are pretty low, and if you have a single-purpose home theater system there's probably not much damage that could be done anyway.

Now go enjoy some movies!


1: Well, after you lie to Netflix about what operating system you're running. It's 2014 and they are still doing browser user-agent sniffing as if it were useful or effective.

Sunday, March 30, 2014

Adding hardware support to MAAS

MAAS and power

MAAS, or Metal as a Service, is a tool for treating physical servers similarly to cloud resources.  It lets you take a pile of hardware and assign workloads to it without worrying about all the support infrastructure underneath.  In effect, you can plug a bunch of systems into a network and have a Hadoop or OpenStack cluster up, running, and properly configured inside of half an hour.  It's really cool stuff.

But MAAS is targeted at enterprise-level hardware.  It expects things like IPMI to be available to control the hardware at a basic level.  (If your tool is going to be installing an operating system onto a computer, it needs to be able to control that computer at a level below the OS.)  I have a stack of older desktops sitting around gathering dust that I wanted to use as a cluster for experimenting at home.  They still function quite well, but being desktops they don't have enterprise features like IPMI.

Fortunately most desktop BIOSes have a setting to control what they do after a power outage.  Crucially, they can be set to turn the system on when electrical power is restored, regardless of whether the system was on or off when power was lost.  This means I could use an externally controlled power switch in place of IPMI to allow MAAS to power my desktops on and off.

Power on after power failure.  (Helpfully hidden under a random submenu in the BIOS rather than under the 'POWER SETTINGS' heading.)


I used the Web Power Switch 7 from Digital Loggers for this project.  (Warning: almost every page on their site has autoplaying audio and/or video.  Ugh.)  I'll be completely honest here -- price was the reason I chose this switch.  It allows control of up to 8 outlets and cost me just under $140 from Amazon.

My shiny new power switch.  (What do you mean, your datacenter doesn't have an oriental rug?)

Power templates


There are a few bits of MAAS that I had to touch to fully enable support for my DLI switch.  The first and most obvious was creating a power control template to tell MAAS how to talk to the switch.

When MAAS is installed, the templates that tell it how to talk to power management systems are put in /etc/maas/templates/power.  These are shell scripts with a bit of Python evaluation and substitution (surrounded by {{double braces}})that MAAS uses at runtime to build a command to control a specific system.  There are several examples to use there already, and these can be modified to suit your environment.  For example, the SeaMicro SM15000 hardware template looks like this:

# -*- mode: shell-script -*-
#
# Control a system via ipmipower, sending the seamicro specific hex codes
#
{{py: power_mode = 1 if power_change == 'on' else 6 }}

{{ipmitool}} -I lanplus \
    -H {{power_address}} -U {{power_user}} \
    -P {{power_pass}} raw 0x2E 1 0x00 0x7d 0xab \
    {{power_mode}} 0 {{system_id}}

I started by simply replacing one of these templates with my own code.  Because the DLI Web Power Switch can be controlled via HTTP, I just needed to build a GET request for the outlet I wanted to control and send it to the switch, like this:

#!/bin/sh
# -*- mode: shell-script -*-
#
# Control a system via the Digital Loggers, Inc. RESTful interface
#
{{py: power_mode = 'ON' if power_change == 'on' else 'OFF' }}

wget --auth-no-challenge -O /dev/null \
    http://{{power_user}}:{{power_pass}}@{{power_address}}/outlet?{{system_id}}={{power_mode}}

Once I had this template replacing the original sm15k template, I was able to control my switch by telling MAAS I had SeaMicro hardware.  Success!

Better integration


I could have stopped there with a working system.  But it bothered me a bit to lie to MAAS about my hardware.  And what if someday I or someone else wanted to mix the DLI switch with actual SeaMicro hardware?  Replacing the sm15k template with my DLI template wouldn't work then.

It turns out that there are two places in the MAAS code that need to be modified to make it aware of a new power switch.  In the MAAS source, these are src/maasserver/power_parameters.py and src/provisioningserver/enum.pypower_parameters.py is where the real definition of a new power switch is; enum.py just needs an entry in two lists to make the UI show the switch as an option.  (There's a comment in enum.py to the effect that adding the switch name in it will not be necessary at some point in the future, but at least as of March 2014 it still is.)

The code needed to add a power switch is straightforward.  Provide the variables the switch will need to operate a system (e.g. outlet number, IP address of the switch, credentials, etc.) and give them names that can be referenced in the power template later.

Success!


The code I added can be seen in Launchpad (click "expand all" to see the diffs).

Once I added the code there, I was able to rebuild MAAS and see my new switch as an option.  It all works, and now my old desktops have new life!