Cliff Hacks Things.

Tuesday, November 27, 2007

ASUS eeePC: some sources posted.

Looks like ASUS has posted some source archives on their FTP site. On initial inspection, the asus_acpi module sources are not the ones that compiled the module that ships on the machine, but are very close. Gonna try 'em out this evening -- as long as they work, I can chalk the previous binary release up to an error on ASUS's part and forge ahead.

Edit (2007-11-30): I've pored over ASUS's source release and extracted their changes into patches. Everything seems to work -- they even released some packages that they weren't technically obligated to, like their on-screen display code for the volume and wifi buttons.

I'm delighted to report that this situation is resolved, as far as I'm concerned. ASUS has reacted admirably and swiftly, and I'm proud to have given them my money (with the small exception of the arbitrary and potentially illegal restrictions they've placed on upgrades).

Gonna go back to my enjoying my laptop now, and hopefully not get viciously attacked on Slashdot and my own blog for a while. Happy hacking!

Labels: ,

Sunday, November 25, 2007

eeebuntu tip: unloading/reloading the wifi drivers

The eee's wifi does not survive a suspend: the hardware and drivers get into different states, and things quit working. The wifi control scripts in ASUS's Linux distribution hold the key.

There's a particular sequence of rmmod/insmod calls that will force the system to rediscover the wifi, and (for me at least) fixes all the various b0rked states it can get into. I call it the "Magic Wifi Dance," because it's a ritual that I don't completely understand.

My control script is below. You'll need the Atheros drivers and the ASUS ACPI module.


#!/bin/sh

# The sequence here *may* be important.
# (It seems to fail intermittently if you deviate.)
unload_modules() {
rmmod wlan_scan_sta
rmmod wlan_tkip
rmmod wlan_wep
rmmod wlan_ccmp
rmmod wlan_acl
rmmod ath_pci
sleep 1
rmmod ath_rate_atheros
rmmod ath_hal
rmmod wlan
rmmod ath_dfs
}

# At least this one's straightforward.
load_modules() {
modprobe ath_pci
}

wifi_on() {
# Force PCI Express Hotplug to reinit
rmmod pciehp
sleep 1
# pciehp_force may be unnecessary; Xandros did it.
modprobe pciehp pciehp_force=1
sleep 1
# Switch on the hardware
echo 1 >/proc/acpi/asus/wlan
sleep 1
load_modules
}

wifi_off() {
unload_modules
echo 0 >/proc/acpi/asus/wlan
}

case $1 in
on)
wifi_on
;;
off)
wifi_off
;;
toggle)
STAT=`cat /proc/acpi/asus/wlan`
if [ "$STAT" = "1" ];
then wifi_off;
else wifi_on;
fi
;;
esac


When executed on an "eeebuntu" machine with ASUS's binary modules, that script should be capable of shutting down and bringing up the Atheros wifi card. The on and off arguments do what their names imply; the toggle argument checks the card's status in proc and responds appropriately. (I use toggle to make the keyboard's wifi key work, which I'll demonstrate in my upcoming post on ACPI events.)

For those playing along at home, this resulting control script is a good one to call from /etc/acpi/suspend.d and /etc/acpi/resume.d, with off and on arguments, respectively. (Just make sure it's called after the interfaces are shut down on suspend, and before they're brought up on resume. On default Ubuntu, calling the suspend script "56-eee-wifi-off" and the resume script "60-eee-wifi-on" gets the order right.)

Labels: ,

Saturday, November 24, 2007

eeebuntu tip: wireless with hal and NetworkManager

I've been away from Linux for quite a while, off in Mac-land, where things generally just work. I was delighted to notice, upon my return, that the community (and RedHat in particular) noticed that manually configuring your wireless interfaces is lame, and built a solution: NetworkManager.

NetworkManager is built atop hal, a daemon that interrogates hardware and makes the information available over a dbus interface. (dbus is a simple mechanism for interprocess messaging and procedure call, similar to DCOP, but more general.)

If you install Ubuntu and subsequently bring up your network using a panel applet, you're using NetworkManager -- and boy, is it nice. Unfortunately, most instructions for getting the eee's wireless working on Ubuntu revolve around the "old-world" configuration mechanism in /etc/network/interfaces. While Ubuntu has graphical configurators that generate this file, using it prevents you from applying the full power of NetworkManager. (Fun fact: NetworkManager will silently ignore any interfaces that are configured in /etc/network/interfaces.)

NetworkManager and hal are really poorly documented. (No offense to the authors intended; the tech is impressive, and I understand that sometimes precious little time is left over to write a design document.) I don't understand the system well enough to write a general tutorial, so here's a specific one: how to get NetworkManager and hal playing nice with the binary Atheros drivers on "eeebuntu" -- Ubuntu 7.10 using the ASUS-compatible 2.6.21.4 kernel I described in my last post.

Getting hal to create ath0 automatically



The Atheros wireless drivers create two network interfaces: wlan0 by default, which seems to be good for very little, and ath0 on demand, which is what you actually use to speak to the intarweb. You have to explicitly request creation of the ath0 interface like so:
wlanconfig ath0 create wlandev wlan0 wlanmode sta


hal, by default, sees the wlan0 interface as an 802.e interface, and does not see ath0 at all. What we want is for hal to ignore wlan0 and treat ath0 as an 802.11 interface. hal supports policy files (written in XML, which surprised me in a Linux tool) that rewrite its view of devices -- and that's what we'll use to fix this.

But first -- we need ath0 created automatically when wlan0 appears. A simple script suffices. I've put mine in a file called /etc/hal/callouts/eee-wifi for reasons that will become apparent in the next step. The script need only contain:

#!/bin/sh

case $HALD_ACTION in
add)
sleep 1 # May be unnecessary, experiment
/sbin/wlanconfig ath0 create wlandev wlan0 wlanmode sta
sleep 1
;;
remove)
/sbin/wlanconfig ath0 destroy
;;
esac


Notice that the script takes its input, not from a command-line parameter, but from an environment variable named HALD_ACTION. This will make sense in a minute. For now, you can test the script thusly:

# Should create ath0 if wlan0 exists and drivers are loaded
env HALD_ACTION=add /etc/hal/callouts/eee-wifi
# Should remove ath0
env HALD_ACTION=remove /etc/hal/callouts/eee-wifi


Now, we convince hal to invoke our script when wlan0 appears or disappears. Create a file named /etc/hal/fdi/policy/10-networking-ath0.fdi containing the following:

<?xml version="1.0" encoding="ISO-8859-1"?>
<deviceinfo version="0.2">
<device>
<match key="net.interface" string="wlan0">
<append key="info.callouts.add" type="strlist">/etc/hal/callouts/eee-wifi</append>
</match>
</device>
</deviceinfo>


This adds our script as a "callout" for wlan0 -- hal's notion of an event handler. hal will invoke our script when wlan0 comes or goes, and set HALD_ACTION appropriately.

Restart hal (or killall -HUP hald) and try loading or unloading the Atheros drivers. ath0 should be created and destroyed automatically. (Make sure the ath0 interface is down when you do this.)

Letting NetworkManager configure ath0



For NetworkManager to configure an interface, a few conditions must be met:

  1. hal must see the interface and recognize it correctly (e.g. seeing ath0 as a wired Ethernet connection is not good);

  2. The interface must not be configured in /etc/network/interfaces. If you currently have a stanza configuring ath0 or wlan0, remove it.



First things first. Modify the 10-networking-ath0.fdi policy we created in the last step by adding the code marked below:

<?xml version="1.0" encoding="ISO-8859-1"?>
<deviceinfo version="0.2">
<device>
<match key="net.interface" string="wlan0">
<append key="info.callouts.add" type="strlist">/etc/hal/callouts/eee-wifi</append>
<merge key="info.capabilities" type="strlist">net</merge>
<append key="info.capabilities" type="strlist">net.80211</append>
<merge key="info.category" type="string">net.80211</merge>
<merge key="net.interface" type="string">ath0</merge>
<merge key="net.80211.mac_address" type="copy_property">net.80203.mac_address</merge>
<remove key="net.80203.mac_address"></remove>
<merge key="linux.sysfs_path" type="string">/sys/class/net/ath0</merge>

</match>
</device>
</deviceinfo>

Note: special thanks to "Triarm" on the eeeuser forums for pointing out this approach.

Line by line, this does the following:

  1. Replaces wlan0's info.capabilities property with the single item "net", indicating that it's a network interface.

  2. Appends "net.80211", indicating that it's a wireless interface.

  3. Changes the device's category, again to indicate that it's a wireless interface.

  4. Switches the entry to refer to ath0, instead of wlan0.

  5. Copies the 802.3 (wired Ethernet) MAC address to the 802.11 MAC address (I'm surprised that these are separate).

  6. Removes the old 802.3 MAC address.

  7. Rewrites the entry's sysfs path to point to ath0's.



To sum up, at this point we have:

  1. Instructed hal to invoke our callout when wlan0 is added or removed, thereby automagically creating/destroying ath0.

  2. Rewritten the device descriptor for wlan0 to describe ath0 (which is unfortunately not picked up automatically by hal).

  3. Removed wlan0/ath0 configuration from /etc/network/interfaces. (You remembered that, right?)



Bring NetworkManager down, restart or HUP hald, and bring NetworkManager back up. (The control script for NetworkManager on Ubuntu 7.10 is at /etc/dbus-1/event.d/25NetworkManager.)

Assuming I haven't forgotten anything, NetworkManager should now offer to configure your wireless, and the interface should show up in configurators like nm-applet.

If it doesn't work, try rebooting. (I know it's lame to say that, but the Atheros drivers are incredibly sensitive and can get into weird states that make your interface stop working, usually after you remove/readd them or suspend/resume. I have a magic dance that repairs the driver, but it's lengthy so I'll post it in a followup.)

Labels: ,

Friday, November 23, 2007

eeebuntu tip: using a custom kernel with ASUS's modules

I like the eee hardware but dislike the software. Personal preference; when I can't use a Mac I use Ubuntu. Readers will surely like some other distro (I know I used to be big on Gentoo), but this tip should not be distro-specific.

I wanted to put Ubuntu 7.10 on my eee without sacrificing any features. I've largely pulled it off, but it's a long journey down a narrow, undocumented road (sigh, Linux). The journey starts with the proverbial single step, which in this case is replacing the kernel with a largely identical one built from source. (If you're going to do anything cool to the kernel, you've got to be able to build it.)

I started out by installing Ubuntu 7.10 off a live stick. This isn't strictly necessary -- the kernel we're building will probably work in Xandros -- but since Ubuntu is my eventual goal, I aimed high. The live stick boots without a hitch (into Compiz, no less!) and the install was uneventful. Note before installing: there are files on the eee's filesystem you will need to complete this tutorial! It is worth your time to make a backup in an accessible form, such as dd'ing the filesystem to somewhere you can loop-mount it.

(Caveat lector: I actually installed onto an SD card, leaving the internal flash disk unmodified, but doing this requires a bunch of extra steps that are not documented here. It's sufficiently involved that I'll have to describe it in a followup.)

The install was largely uneventful and is not the topic of this post.

Ubuntu 7.10 does not, out of the box, support the following hardware on the eee:

  • The Atheros wireless card. (The madwifi drivers ASUS ships are not the 9.3.3 drivers, and a freshly built set from SVN trunk doesn't work either.)

  • The ASUS ACPI features for controlling backlight and powering PCIe devices on and off. The mechanism is conceptually similar to the kernel's asus_acpi module, but as I noted in a previous post, ASUS ships a modified driver.



(Those of you who have been in the Linux/FreeBSD laptop community for some time may notice how incredibly short that list is, and the fact that it does not include multihead 3D accelerated graphics, audio, and USB 2. How far we've come.)

As a result of the botched ACPI support, suspend/resume doesn't work quite right. Suspend-to-RAM will work sometimes, kinda, after a few VT switches to thunk the display adapter. Not good enough! I want instant suspend/resume and working wireless! Waaaah, the Mac has spoiled me! :-)

The pragmatic solution, if not the most FSF-friendly one, is to build a kernel with the exact version string from ASUS's so their binary modules will work. (If you care, note your kernel will be tainted! Danger Will Robinson!)

The recipe below skips some steps and is not intended for Linux or Unix newbies. Sorry; I've been immersed in Unix long enough that I'm not the right guy to write the newbie tutorial. Feel free to steal my instructions to make one!


  1. Prereq: a Linux machine with a working set of devtools and the 2.6 version of depmod. This might be the eee, I suppose, but I used a much faster machine. (Specifically, an Ubuntu 7.10 virtual machine under VMware Fusion.)

  2. Download the sources for the matching Linux kernel version -- in this case, 2.6.21.4. (For the kernel-impaired, it comes from kernel.org's 2.6 site.)

  3. Untar.

  4. Nab the ASUS kernel configuration, helpfully shipped right on your eee. It lives in /boot/config-2.6.21.4-eeepc. Copy it into your untarred kernel sources as a file named .config .

  5. If you tried to build the kernel now, you'd notice some errors about missing unionfs. Unionfs isn't in the 2.6.21.4 mainline kernel, but ASUS uses it. If you want it, download the version for 2.6.21.7 and apply it in the kernel tree with
    patch -p1 </path/to/patch
    If you don't want it, simply delete or comment out all lines in the config containing the word UNIONFS.

  6. ASUS's config in place, build a replacement kernel by issuing
    make oldconfig && make

  7. Create a directory to hold your new kernel and modules, and issue
    make INSTALL_PATH=/your/directory install
    make INSTALL_MOD_PATH=/your/directory modules_install
    You do not need to (and should not!) run these commands as root.

  8. In your directory are a bunch of files: a kernel and associated files at the root, and a lib/modules/2.6.21.4-eeepc directory with your modules in it.

  9. Copy the files out of the directory into /boot on your eeepc. Note at this point that, if you are still running Xandros, you have just overwritten your kernel. Hope you had a backup. (If you're running Ubuntu 7.10 you have installed an additional kernel alongside the main one. This is safe.)

  10. Copy the contents of the /lib... directory, not into /boot with the others, but into /lib/modules/2.6.21.4-eeepc.

  11. Try booting your eee. You will need to get to the grub menu and change the "kernel=" parameter. (How to do this is beyond the scope of this article.) If it does not work, I'm afraid I am not the man to help you; I make Linux work through sheer dumb luck and a lot of Unix engineering experience, but a Linux guru I'm not.

  12. If it seems to work, simply copy the atheros and acpi. directories from /lib/modules/2.6.21.4-eeepc on your eee backup filesystem into the same locations on the new one, and run (as root) depmod -a.



At this point, if it worked, you have a booting Ubuntu (or ${YOUR_DISTRO}) system running on a reasonable facsimile of ASUS's 2.6.21.4 kernel. If you issue modprobe asus_acpi or modprobe ath_pci, the modules should load (and put some control files in /proc/acpi/asus, or create wlan0, respectively).

You may find that suspend/resume to RAM works with the asus_acpi module loaded. Edit your /etc/modules as you see fit.

Getting from "loading the Atheros modules" to "reading Slashdot" is unfortunately involved -- but the process is no different than the normal madwifi drivers. (Hint: until I post my scripts, search on Google for "wlanconfig ath0 create".)

Happy hacking!

Labels: ,