Tuesday, August 28, 2012

Kernel hibernate bug

Bah. Got bit by a kernel bug preventing wakeup from hibernation.
3.0.0-12-generic wakes up fine.
3.2.0-30-generic doesn't wake up.
Seeing lots of flurry about various similar kernel bugs. So much activity that I'm not going to add to the confusion by trying to troubleshoot or see if it's already reported or fixed upstream. I'll just stay downgraded for now...

Since Ubuntu doesn't remove old kernels yet, it's easy. Simply remove the newest kernel, and use update-grub to boot from the last (known working) kernel. Then I reboot.

sudo apt-get remove linux-image-3.2.0-30-generic linux-headers-3.2.0-30*
sudo update-grub
sudo shutdown -r now 

External C-Media USB speakers on Ubuntu 11.10

I have a small-but-spiffy set of USB speakers, handy for travel. Here's how I got them to work.

First of all, there are a *lot* of moving pieces, and eliminating many potential sources of the problem (no sound emerges from the speakers) is critical to success.

For example, the speakers work on another system, so they aren't broken.

Other USB devices work in the port, so there's not a hardware defect.

Sound in general works on the laptop.

dmesg shows the speakers recognized appropriately when plugged in:
$ dmesg
[245788.608422] usb 1-1: ath9k_htc: USB layer initialized
[245789.124045] usb 3-1: new full speed USB device number 7 using ohci_hcd
[245789.261042] ADDRCONF(NETDEV_UP): wlan1: link is not ready
[245789.331223] input: C-Media USB Headphone Set   as /devices/pci0000:00/0000:00:13.1/usb3/3-1/3-1:1.3/input/input11
[245789.331380] generic-usb 0003:0D8C:000C.0003: input,hidraw0: USB HID v1.00 Device [C-Media USB Headphone Set  ] on usb-0000:00:13.1-1/input3

( If the hardware were not recognized, that would be a driver [kernel module] issue)

alsa shows the hardware properly recognized and configured. Alsa also creates a GUI mixer for the USB "card" when it's plugged in:
$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: SB [HDA ATI SB], device 0: ALC272X Analog [ALC272X Analog]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: Set [C-Media USB Headphone Set], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

Pulseaudio offers a good clue. It shows the USB card, but there is no sink for the USB card's output. Aha!
$ pacmd list-cards
Welcome to PulseAudio! Use "help" for usage information.
>>> 2 card(s) available.
    index: 0
 name: 
 driver: 
 owner module: 4
 properties:
               # It's the onboard audio
               # ...
 active profile: 
 sinks:
  alsa_output.pci-0000_00_14.2.analog-stereo/#0: Internal Audio Analog Stereo
 sources:
  alsa_output.pci-0000_00_14.2.analog-stereo.monitor/#0: Monitor of Internal Audio Analog Stereo
               # ...
    index: 7
 name: 
 driver: 
 owner module: 35
 properties:
  alsa.card = "1"
  alsa.card_name = "C-Media USB Headphone Set"
  alsa.long_card_name = "C-Media USB Headphone Set at usb-0000:00:13.1-1, full speed"
  alsa.driver_name = "snd_usb_audio"
  device.bus_path = "pci-0000:00:13.1-usb-0:1:1.0"
  sysfs.path = "/devices/pci0000:00/0000:00:13.1/usb3/3-1/3-1:1.0/sound/card1"
  udev.id = "usb-0d8c_C-Media_USB_Headphone_Set-00-Set"
  device.bus = "usb"
  device.vendor.id = "0d8c"
  device.vendor.name = "C-Media Electronics, Inc."
  device.product.id = "000c"
  device.product.name = "Audio Adapter"
  device.serial = "0d8c_C-Media_USB_Headphone_Set"
  device.form_factor = "headphone"
  device.string = "1"
  device.description = "Audio Adapter"
  module-udev-detect.discovered = "1"
  device.icon_name = "audio-headphones-usb"
 profiles:
  output:analog-stereo: Analog Stereo Output (priority 6000)
  output:analog-stereo+input:analog-mono: Analog Stereo Output + Analog Mono Input (priority 6001)
  input:analog-mono: Analog Mono Input (priority 1)
  off: Off (priority 0)
 active profile: 
 ports:
  analog-output-speaker: Analog Speakers (priority 10000, available: unknown)
  analog-input-microphone: Analog Microphone (priority 8700, available: unknown)
                # See? No output sink. 

There is probably a way out of this using output profiles, but I don't understand those yet. Instead, what appears to be needed is a udev rule that creates an output sink for the USB card, then redirects the default stream to use the USB card.

Upon plugging in the USB speakers, that udev rule should make the speakers the default sound device. Upon unplugging the speakers, the card and sink's disappearance will automatically force Pulseaudio to reroute the stream defaults to the onboard audio card and speakers.

Figure out the Pulseaudio index number of the USB card:
$ pacmd list-cards \
   | grep 'alsa.card_name = "C-Media USB Headphone Set"' --before-context=6 \
   | grep index \
   | cut -d' ' -f6

7

Figure out the alsa hardware number:
$ aplay -l | grep C-Media | cut -d' ' -f2 | cut -d: -f1

1

Add an alsa sink for the USB Card:
$ pacmd load-module module-alsa-sink device=hw:1,0

Figure out the Pulseaudio index number of the new alsa sink:
$ pacmd list-sinks | grep 'alsa.card_name = "C-Media USB Headphone Set"' --before-context=37 | grep index | cut -d' ' -f6

2

Change the default to the new sink
$ pacmd set-default-sink 2

Since the USB speakers are MUCH LOUDER than the onboard speakers, start them at a much lower level!
amixer -c 1 sset "Speaker" 5

So the final script should look something like:
#!/bin/sh
# This script makes the C-Media external USB speakers the
# default audio output. It should be run by udev.

# Figure out the Pulseaudio index number of the USB card
card_index=$( pacmd list-cards \
              | grep 'alsa.card_name = "C-Media USB Headphone Set"' \
              --before-context=6 | grep index | cut -d' ' -f6 )
echo "The USB card index is $card_index"

# Figure out the alsa hardware number
alsa_hardware=$( aplay -l | grep C-Media | cut -d' ' -f2 | cut -d: -f1 )
echo "The alsa hardware number is $alsa_hardware"

# Test if a USB sink already exists. If not, create one
if [ $( pacmd list-sinks | grep -q C-Media ) ]; then
   # Add an alsa sink for the USB Card
   pacmd load-module module-alsa-sink device=hw:${alsa_hardware},0
   echo "New alsa sink added"
fi

# Figure out the Pulseaudio index number of the alsa sink
alsa_sink_index=$( pacmd list-sinks \
                  | grep 'alsa.card_name = "C-Media USB Headphone Set"' \
                  --before-context=50 | grep index | cut -d' ' -f6 )
echo "Alsa sink index is $alsa_sink_index"

# If the current default sink is not the USB, change it
current_sink_index=$(pacmd list-sinks | grep '*' | cut -d' ' -f5)
if [ $current_sink_index -ne $alsa_sink_index ]; then
   # Change the default to the alsa sink
   pacmd set-default-sink ${alsa_sink_index}
   echo "Default sink changed from $current_sink_index to $alsa_sink_index"
else
   echo "Default sink not changed - it is already $current_sink_index"
fi

# Set the USB speaker levels very low to start
amixer -c ${card_index} sset "Speaker" 5
echo "Speaker volume changed"

exit 0
Save the script (newspeaker.sh), and remember to make it executable with chmod +x newspeaker.sh. Test it a bit. When the speakers are plugged in and the script run manually, the audio from the USB speakers is excellent. After unplugging, sound reverts to the onboard speakers. This is not a headphone jack - the audio must be stopped/started to use the changed

Thursday, August 2, 2012

Dbus Tutorial - Introspection: Figuring Out The Rules

Introduction
Introspection
Network Manager 
Create a Service
Gobject Introspection


Last time, we discussed what to use dbus for and the basics of structuring a dbus command. We went over how to structure the grammar of a command so it makes sense (mapping the destination, path, method, and message elements), and we went over the syntax (stringing together those elements in a coherent way).

This lesson is about figuring out what methods are available and how to use them.


Introspection

dbus is introspectable. That means you can ask dbus what commands are available.

Here's an introspection example. You can see that the return is XML wrapped inside a string (you don't need to read it all):

$ dbus-send --system --print-reply --dest=org.freedesktop.NetworkManager /org/freedesktop/NetworkManager org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.4 -> dest=:1.441 reply_serial=2
   string "
<node>
  <interface name="org.freedesktop.DBus.Introspectable">
    <method name="Introspect">
      <arg direction="out" name="data" type="s">
    </arg></method>
  <interface name="org.freedesktop.DBus.Properties">
    <method name="Get">
      <arg direction="in" name="interface" type="s">
      <arg direction="in" name="propname" type="s">
      <arg direction="out" name="value" type="v">
    </arg></arg></arg></method>
    <method name="Set">
      <arg direction="in" name="interface" type="s">
      <arg direction="in" name="propname" type="s">
      <arg direction="in" name="value" type="v">
    </arg></arg></arg></method>
    <method name="GetAll">
      <arg direction="in" name="interface" type="s">
      <arg direction="out" name="props" type="a{sv}">
    </arg></arg></method>
  </interface>
  <interface name="org.freedesktop.NetworkManager">
    <method name="state">
      <arg direction="out" name="state" type="u">
    </arg></method>
    <method name="SetLogging">
      <arg direction="in" name="level" type="s">
      <arg direction="in" name="domains" type="s">
    </arg></arg></method>
    <method name="GetPermissions">
      <arg direction="out" name="permissions" type="a{ss}">
    </arg></method>
    <method name="Enable">
      <arg direction="in" name="enable" type="b">
    </arg></method>
    <method name="Sleep">
      <arg direction="in" name="sleep" type="b">
    </arg></method>
    <method name="DeactivateConnection">
      <arg direction="in" name="active_connection" type="o">
    </arg></method>
    <method name="AddAndActivateConnection">
      <arg direction="in" name="connection" type="a{sa{sv}}">
      <arg direction="in" name="device" type="o">
      <arg direction="in" name="specific_object" type="o">
      <arg direction="out" name="path" type="o">
      <arg direction="out" name="active_connection" type="o">
    </arg></arg></arg></arg></arg></method>
    <method name="ActivateConnection">
      <arg direction="in" name="connection" type="o">
      <arg direction="in" name="device" type="o">
      <arg direction="in" name="specific_object" type="o">
      <arg direction="out" name="active_connection" type="o">
    </arg></arg></arg></arg></method>
    <method name="GetDeviceByIpIface">
      <arg direction="in" name="iface" type="s">
      <arg direction="out" name="device" type="o">
    </arg></arg><<method>
    <method name="GetDevices">
      <arg direction="out" name="devices" type="ao">
    </arg></method>
     <signal name="DeviceRemoved">
       <arg type="o">
     </arg> </signal>
     <signal name="DeviceAdded">
       <arg type="o">
     </arg> </signal>
     <signal name="PropertiesChanged">
       <arg type="a{sv}">
     </arg> </signal>
     <signal name="StateChanged">
       <arg type="u">
     </arg> </signal>
     <signal name="CheckPermissions">
     </signal>
     <property access="read" name="State" type="u">
     <property access="read" name="Version" type="s">
     <property access="read" name="ActiveConnections" type="ao">
     <property access="read" name="WimaxHardwareEnabled" type="b">
     <property access="readwrite" name="WimaxEnabled" type="b">
     <property access="read" name="WwanHardwareEnabled" type="b">
     <property access="readwrite" name="WwanEnabled" type="b">
     <property access="read" name="WirelessHardwareEnabled" type="b">
     <property access="readwrite" name="WirelessEnabled" type="b">
     <property access="read" name="NetworkingEnabled" type="b">
   </property> </property> </property> </property> </property> </property> </property> </property> </property> </property>
   <node name="AccessPoint">
   <node name="ActiveConnection">
   <node name="AgentManager">
   <node name="DHCP4Config">
   <node name="Devices">
   <node name="IP4Config">
   <node name="Settings">
 </node>
" </node> </node> </node> </node> </node> </node>

 
Let's see if we can translate this into something more human readable.

For example,

<node>
  <interface name="org.freedesktop.DBus.Introspectable">
    <method name="Introspect">
      <arg direction="out" name="data" type="s">
    </arg&gt;&lt;/method> 
  ... 
</node>

is the method we used for introspection (remember?).

See the org.freedesktop.DBus.Introspectable.Introspect?  The arg direction "out" means the the response, and type "d" means data string).


How to use introspection to create a dbus query or command

Here's a slightly more complex example of an introspection response:

  <interface name="org.freedesktop.DBus.Properties">
    ...
    <method name="GetAll">
      <arg direction="in" name="interface" type="s">
      <arg direction="out" name="props" type="a{sv}">
    </arg> </arg> </method>
    ...
  <interface name="org.freedesktop.NetworkManager"> 

This generic method, org.freedesktop.DBus.Properties.GetAll, returns a complete dump of all of some other interface's properties.

Let's go back to grammar (destination, path, method, message) and say that again:

I want to see a dump of Network manager's top-level properties.
"Hey Network Manager, please give me a printout of all of Network Manager's top-level properties."

Destination: "Hey, Network Manager": org.freedesktop.NetworkManager

Path:  "Network Manager's": org/freedesktop/NetworkManager

Method: "Give me a printout of properties": org.freedesktop.DBus.Properties.GetAll

Message: "top-level": org.freedesktop.NetworkManager

You probably noticed that this example has some duplication. When working with dbus, get used to it.


Now let's put it into the right syntax:

dbus-send   [ --system| --session] --print-reply --dest=DEST PATH METHOD [MESSAGE]


$ dbus-send --system --print-reply \
            --dest=org.freedesktop.NetworkManager \
            org/freedesktop/NetworkManager \
            org.freedesktop.DBus.Properties.GetAll \
            string:"org.freedesktop.NetworkManager"


Let's do one more slightly different example:

<interface name="org.freedesktop.NetworkManager">
    ...
    <method name="ActivateConnection">
      <arg direction="in" name="connection" type="o">
      <arg direction="in" name="device" type="o">
      <arg direction="in" name="specific_object" type="o">
      <arg direction="out" name="active_connection" type="o">
    </arg> </arg> </arg> </arg> </method>

 </node>
" </node> </node> </node> </node> </node> </node>

This method, ActivateConnection, makes a known connection into the active network connection. For example, when switching from one access point to another. There are three extra pieces of information needed, and network manager returns one piece of information in the response.

Let's go back to grammar (destination, path, method, message) and say that again:

"Hey Network Manager, make Access Point Foo (using wireless device 0 and network password settings 67) the active connection."

Destination: "Hey, Network Manager": org.freedesktop.NetworkManager

Path:  "Network Manager": org/freedesktop/NetworkManager

Method: "Make...the active connection": org.freedesktop.NetworkManager.ActivateConnection

Message 1: "Access Point Foo": org/freedesktop/NetworkManager/AccessPoint/220

Message 2: "wireless device 0": org/freedesktop/NetworkManager/Device/0

Message 3: "network password info 67": org/freedesktop/NetworkManager/Settings/67

Now let's put it into the right syntax:

dbus-send   [ --system| --session] --print-reply --dest=DEST PATH METHOD [MESSAGE]


$ dbus-send --system --print-reply \
            --dest=org.freedesktop.NetworkManager \
            org/freedesktop/NetworkManager \
            org.freedesktop.NetworkManager/ActivateConnection \
            objpath:"org/freedesktop/NetworkManager/AccessPoint/220" \
            objpath:"org/freedesktop/NetworkManager/Device/0" \
            objpath:"org/freedesktop/NetworkManager/Settings/67"

Obviously, this example won't work for you unless you do the introspection to find valid Access Points, Devices, and Settings that work together. dbus will tell you a lot of it...if you ask...but it's not a user-friendly graphical user interface. You need to ask the right questions and use your own logic.

Making introspection easier is where d-feet comes in.


d-feet makes introspection easy

d-feet is a python application (part of the d-feet package in Debian and Ubuntu) that does introspection for you while you write your program.


In this screenshot, you can see lots of the same introspection information that we retrieved before. Easier to read and understand, isn't it? See how the Interfaces and Methods are listed for each Object Path?

Without d-feet, use the following dbus-send command to find out what's available on the bus:

$ dbus-send --session --print-reply --dest="org.freedesktop.DBus" /org/freedesktop/DBus org.freedesktop.DBus.ListActivatableNames


Now you know how to find the information you need to use dbus properly.