Making a Socket Bluetooth CF card work under Linux 2.6

This document describes how to patch a linux kernel in order to make a Socket Bluetooth CF card (Rev. G, H) work under linux 2.6. The following hardware is being used:

Software is Debian GNU/Linux 4.0 (testing at the time of this writing), Linux kernel 2.6.17 and BlueZ bluetooth stack.

Background

There are various revisions of this card using different bluetooth chipsets [www.holtmann.org] thus requiring different driver modules:

My card is a Rev. H (its UART is a 16C950, or at least compatible with that) so the information in this section primarily refers to this kind of device. However a Rev. G card should behave (almost) the same, while the Rev. E, F are completely different. If you have information about revisions not mentioned here, please send me an e-mail.

When you plug such a card into your computer and do modprobe serial_cs (note that on most linux distributions a userspace daemon does this automatically for you), you see nothing but an ordinary serial port accessible via /dev/ttyS<n>. To be able to use the bluetooth chipset which is connected to that serial line (of course, this serial link is local to the CF card only) you need to run:

hciattach /dev/ttyS<n> socket

This command will automatically load the hci_uart kernel module if necessary and associate it with the selected tty device. Replace <n> with the number of your BT card's serial port (examine dmesg after plugging in the card). Note that hciattach detaches and runs in background as a daemon.

If this succeeds you can see your bluetooth adapter and view its bluetooth MAC address:

> hcitool dev
Devices:
        hci0    00:02:34:56:AF:FE
>

If you see a similar output, you most probably don't need the patch described here. However, when using a 2.6.x kernel it is very likely that hciattach doesn't succeed, instead you get the following error message:

BCSP initialization timed out

Trouble and Solution

I found out that hciattach needs to set the BT card's UART to a baud rate of 230 400 to be able to talk to the bluetooth chipset. However, in linux 2.6 there is a bug in the core serial driver code which is also used by serial_cs that prevents the 16C950 (and several other) UARTs from being programmed to baud rates larger than 115 200. This is the highest baud rate a "classic" PC serial port can achieve: its baud rate generator is clocked with 1 843 200 Hz (called the "baud base") and has a minimal clock divisor of 16, leading to the maximum baud rate:

1 843 200 Hz / 16 = 115 200 baud

All higher baud rates require either a higher baud base or an extended UART which supports baud rate divisors smaller than 16. The former case, a higher baud base, should be no problem for the serial driver -- it only means that if your application isn't aware of the non-standard baud base you have to select a different (lower) baud rate there than you actually want to be used. However, for our Socket Bluetooth card the latter is the case: the baud base is 1 843 200 Hz, but the 16C950 supports additional divisor values between 15 and 4, which are selected by writing appropriate values to the control register TCR. For more information refer to a 16C950 datasheet, e.g. [OXCF950 rev B Datasheet, section 6.10, or SER_OXCB950_DS.pdf, section 7.10].

By comparing the serial driver in kernel 2.6 with the one from kernel 2.4.29 (which does work!) I noticed that the code necessary to set the TCR register appropriately is missing in 2.6. Having found that, it was quite easy to put together a small patch which enables the use of the two additional divisors 8 and 4 allowing additional baud rates of 230 400 baud and 460 800 baud to be selected on 16C950-compatible UARTs. This patch could make the Socket card work under each 2.6 kernel I have tried so far. Unfortunatly the vanilla kernel from www.kernel.org, as well as Debian's linux-image-2.6.17-2-686 and presumably many more, still (as of linux 2.6.17) suffer from this bug. See linux kernel mailing list archives for some insight (the patch was first posted in early 2005 and popped up occasionally in the meantime).

Patching and compiling the kernel

This is what I do to build a patched Debian linux-image-*.deb package; if you use another distribution some details might be different. First, download and extract the kernel source tree (most of the following commands have to be run as root):

> aptitude install linux-source-2.6.17
> cd /usr/src/
> tar xjf linux-source-2.6.17.tar.bz2

This provides you with the kernel sources under /usr/src/linux-source-2.6.17/. Of course you can also un-tar to a different location if you like, only make sure to have around 300-600 MiB of free disk space there (depending on kernel version and the number of modules you select). As far as I know this source tree already contains the debian-specific patches (please notify me if that's not true).

To compile a kernel under Debian using make-kpkg you need (at least) the following packages (kernel-package depends on the gcc package so you will get a compiler if you don't have one yet):

> aptitude install  kernel-package libncurses5-dev

Now configure the kernel, either manually (cd linux-source-2.6.17 ; make menuconfig) or simply by using an existing configuration for that kernel version. In this example I copy the configuration from the debian binary kernel package (I already had installed that kernel anyway) for my platform, then compile and install the patched kernel:

> aptitude install linux-image-2.6.17-2-686          # get Debian's default kernel .config file
> cd /usr/src/linux-source-2.6.17
> cp -a /boot/config-2.6.17-2-686 .config
> patch -p1 </path/to/serial_8250.c_2.6.17.patch
> make-kpkg clean  # only needed if make-kpkg kernel_image has been run in this source tree before
> make-kpkg --initrd --append-to-version -8250patch kernel_image
> cd ..
> dpkg -i linux-image-2.6.17-8250patch_2.6.17-9_i386.deb

Ensure that your boot loader is configured correctly for booting the new kernel image, and reboot. After that, hciattach should do its job without further complaints.

Automating hciattach on card insert/removal

On kernels 2.6.13 or later, the debian package pcmciautils is being used to configure which drivers need to be loaded for a specific PC-card. Note that the files in /etc/pcmcia/ as well as the cardmgr daemon aren't used anymore, they apply to kernels 2.6.12 and older and the pcmcia-cs debian package. [this might be debian specific?] Instead, the "new" approach uses udev rules thus the udevd daemon can also handle pcmcia card inserts/removals, so there is no more need for an additional user space daemon.

To have hciattach called automatically when inserting the bluetooth card I had to add the following line to /etc/udev/bluez-pcmcia-support.rules:

# Socket CF+ Personal Network Card Rev 2.5
SUBSYSTEM=="tty", BUS=="pcmcia", SYSFS{prod_id1}=="Socket", SYSFS{prod_id2}=="CF+ Personal Network Card Rev 2.5", RUN+="bluetooth_serial"

This file already contained a number of similar lines for other serial bluetooth devices, I just copied one and put in the correct values for prod_id<n> for my card. Those values can be read from /sys/bus/pcmcia/devices/*/prod_id* after plugging in the card.

This setup tells udevd to run /lib/udev/bluetooth_serial when a card with the given identification is being inserted or removed. The bluetooth_serial script then starts or stops hciattach, respectively. It also makes sure that the correct /dev/ttyS<n> is being used by hciattach (the BT card might well get a different device number on each insertion). In Debian 4.0 (Etch), /etc/udev/bluez-pcmcia-support.rules and /lib/udev/bluetooth_serial are both contained in the package bluez-pcmcia-support.

Hint: to debug udev configuration changes, udevcontrol log_priority=debug might be rather helpful: it makes udevd be more verbose (see syslog). Don't forget to restart udevd or send it a SIGHUP after altering its configuration or rule files (in Debian this can be done with invoke-rc.d udev restart).

Suspend problems?

At least on my system, the serial driver crashes when I do a suspend-to-disk or suspend-to-ram while the serial device is in use by an application (no matter if it's hciattach or, e.g. minicom). The same happens with the stock debian kernel so the baud rate patch doesn't cause this behaviour. However I didn't dig into this issue any further, instead I just unload ("eject") the pcmcia driver before suspending and reload it after resuming:

pccardctl eject 1

echo shutdown > /sys/power/disk
echo disk > /sys/power/state

pccardctl insert 1

On an X21 socket 1 is the CF slot, and socket 0 is the PC card slot. If you don't know which slot your BT card is plugged in, you could also omit the socket number in the pccardctl call to unload/reload the drivers of each pcmcia card - this should rarely be a problem.

References