Bluetooth on FreeBSD

Note: Modification of this text is now a Bluetooth chapter of The FreeBSD Handbook.

 

I purchased an Anycom bluetooth USB dongle and here is my experience with making it work under FreeBSD.

Compiling bluetooth support

The Bluetooth stack is developed only for the 5-CURRENT branch. I suggest you to track -CURRENT, but you can use 5.1-RELEASE if you want. Bluetooth support is now integrated in FreeBSD CVS tree. If you run 5.0-RELEASE, I suggest you to upgrade to 5.1-RELEASE or -CURRENT.

Some userland utilities are not in FreeBSD CVS tree. You want to download latest snapshot from Maksim Yevmenkin's site, build new libbluetooth from src/lib/libbluetooth and then build ports subdirectory manually:

make && make install && make cleandir

Plugging device

Edit your /boot/loader.conf and add the line

ng_ubt_load="YES"

Reboot. You should now have a working Bluetooth stack. Plug in your USB dongle and watch this on your console (or syslog):

ubt0: vendor 0x0a12 product 0x0001, rev 1.10/5.25, addr 2
ubt0: Interface 0 endpoints: interrupt=0x81, bulk-in=0x82, bulk-out=0x2
ubt0: Interface 1 (alt.config 5) endpoints: isoc-in=0x83, isoc-out=0x3;
      wMaxPacketSize=49; nframes=6, buffer size=294

In the archive you downloaded, there is a rc.bluetooth in src/share/examples directory. Copy it to some convenient place, like /etc/rc.bluetooth. This script is used to start and stop the Bluetooth stack. It's a good idea to stop the stack before unplugging the device. But it's not (usually) fatal. Start the stack. You will receive output similar to this:

# /etc/rc.bluetooth start ubt0
BD_ADDR: 00:02:72:00:d4:1a
Features: 0xff 0xff 0xf 00 00 00 00 00 
<3-Slot> <5-Slot> <Encryption> <Slot offset>
<Timing accuracy> <Switch> <Hold mode> <Sniff mode>
<Park mode> <RSSI> <Channel quality> <SCO link>
<HV2 packets> <HV3 packets> <u-law log> <A-law log> <CVSD>
<Paging scheme> <Power control> <Transparent SCO data> 
Max. ACL packet size: 192 bytes
Number of ACL packets: 8
Max. SCO packet size: 64 bytes
Number of SCO packets: 8

Problem: My kernel spits out these lines when starting rc.bluetooth:

Mar 26 11:18:34 tylendel kernel: link_elf: symbol _mtx_assert   undefined
Mar 26 11:18:34 tylendel nomad[576]: Failed to load ng_btsocket

Solution: Your kernel options do not match the snapshot Makefile options. Either edit the module Makefile in snapshot and remove -DWITNESSxxx and -DINVARIANTSxxx, or add options WITNESS, options WITNESS_SKIPSPIN, options INVARIANTS and options INVARIANTS_SUPPORT to your kernel configuration. Those options are used to catch locking bugs.

Problem: I plug in the USB Bluetooth device and I get this:

kernel: uhub0: device problem, disabling port 1

Solution: There is no solution. The USB stack did not attach and activate your device. This is not a Bluetooth problem. This seems to occur when the device is connected via USB hub. To find out if you have an extra hub, run:

# usbdevs -v -d
Controller /dev/usb0:
addr 1: full speed, self powered, config 1, UHCI root hub(0x0000), Intel (0x0000), rev 1.00
  uhub0
 port 1 powered
 port 2 addr 2: full speed, self powered, config 1, UT-USB41 hub(0x1446), Texas Instruments(0x0451), rev 1.00
    uhub1
  port 1 powered
  port 2 powered
  port 3 powered
  port 4 powered

Here, Maksim's notebook Toshiba Tecra 8100 in the docking station, has an extra hub (addr 2). The only solution is to plug the Bluetooth device directly into the root hub (into notebook).

Inquiry and HCI

Now it's time to discover some nearby bluetooth devices. I used my Ericsson T39 mobile phone for all examples. Discovering devices and many other interesting tasks is done with the hccontrol utility. You shall receive a list of discoverable devices in a few seconds:

$ hccontrol -n ubt0hci inquiry
Inquiry result, num_responses=1
Inquiry result #0
        BD_ADDR: 00:80:37:29:19:a4
        Page Scan Rep. Mode: 0x1
        Page Scan Period Mode: 00
        Page Scan Mode: 00
        Class: 52:02:04
        Clock offset: 0x78ef
Inquiry complete. Status: No error [00]

BD_ADDR is the unique address of bluetooth device, similar to MAC addresses of network cards. You will need this address for further communication with a device. Let's try to read the device's name:

$ hccontrol -n ubt0hci remote_name_request 00:80:37:29:19:a4 0 0 0
BD_ADDR: 00:80:37:29:19:a4
Name: Pav's T39

Now try to perform a discovery on your mobile phone (or any other device). You should find your computer as your.host.name (ubt0). You can try a simple connection with your phone:

# hccontrol -n ubt0hci create_connection 00:80:37:29:19:a4
BD_ADDR: 00:80:37:29:19:a4
Connection handle: 41
Encryption mode: Disabled [0]

The connection handle is important here, as you will need it for any further handling of the connection. Don't panic if you forget it, you can list active raw connections anytime:

$ hccontrol -n ubt0hci read_connection_list               
Remote BD_ADDR    Handle Type Mode Role Encrypt Pending Queue State
00:80:37:29:19:a4     41  ACL    0 MAST    NONE       0     0 OPEN

Reading quality of connection:

$ hccontrol -n ubt0hci get_link_quality 41
Connection handle: 41
Link quality: 255

It's on the maximal possible quality. Well, both devices are some two feet apart from each other, so it's no big surprise. :) Try hccontrol help for list of all commands. Now disconnect:

# hccontrol -n ubt0hci disconnect 41
Connection handle: 41
Reason: Connection terminated by local host [0x16]

L2CAP

L2CAP is a higher level of connection in Bluetooth standards. The most interesting command is l2ping, which can be used to ping other devices:

# l2ping -a 00:80:37:29:19:a4
0 bytes from 0:80:37:29:19:a4 seq_no=0 time=48.633 ms result=0 
0 bytes from 0:80:37:29:19:a4 seq_no=1 time=37.551 ms result=0 
0 bytes from 0:80:37:29:19:a4 seq_no=2 time=28.324 ms result=0 
0 bytes from 0:80:37:29:19:a4 seq_no=3 time=46.150 ms result=0

The l2control utility is used to configure L2CAP connections:

$ l2control -a 00:02:72:00:d4:1a read_connection_list
L2CAP connections:
Remote BD_ADDR    Handle Flags Pending State
00:80:37:29:19:a4     41             0 OPEN

Another diagnostic tool is btsockstat. It does a similar job as hccontrol read_connection_list does, but on a higher level - it prints Bluetooth sockets, logical connections on top of HCI raw connections. Here is a dial-up connection (over a RFCOMM socket):

$ btsockstat 
Active L2CAP sockets
PCB      Recv-Q Send-Q Local address/PSM        Foreign address    CID   State
c2ae2900      0      0 00:02:72:00:d4:1a/3      00:80:37:29:19:a4  68    OPEN
c238e200      0      0 */3                      *                  0     LISTEN
Active RFCOMM sockets
PCB      Recv-Q Send-Q Local address      Foreign address    Chan DLCI State
c266c400      0      0 00:02:72:00:d4:1a  00:80:37:29:19:a4  1    2    OPEN

Pairing of devices

By default, Bluetooth communication is not authorized and any device can talk to any other device. Some devices, like mobile phones, requires authentication for some functionality, like Internet connections. This is done with PIN numbers - you enter the same (up to 16 digits long) number on both devices. This operation is called pairing. The daemon that answers pairing requests is hcsecd. Copy over src/usr.sbin/bluetooth/hcsecd/hcsecd.conf to /usr/local/etc and edit it. Section for my mobile phone, with the PIN arbitrary set to 1234:

device {
        bdaddr  00:80:37:29:19:a4;
        name    "Pav's T39";
        key     nokey;
        pin     "1234";
}

You can choose any PIN you like. Note that some devices, like headsets, have a fixed PIN built in. If you're originating pairing from PC, you need to turn on authentication:

# hccontrol -n ubt0hci write_authentication_enable 1
# hccontrol -n ubt0hci read_authentication_enable
Authentication Enable: Enabled [1]

If you're originating pairing on remote device, this is not necessary. Now, start hcsecd -d. The -d switch is to force the daemon to stay in the terminal and not fork to the background, so you can see what's happening. Set the phone to receive pairing and initiate the HCI connection to the remote device. The phone should say that pairing was successful, and let you enter the PIN. Enter the same PIN as you have in your hcsecd.conf. Now your PC and phone is paired. This will appear in hcsecd output:

hcsecd[16484]: Got Link_Key_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4
hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', link key doesn't exist
hcsecd[16484]: Sending Link_Key_Negative_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4
hcsecd[16484]: Got PIN_Code_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4
hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', PIN code exists
hcsecd[16484]: Sending PIN_Code_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4

A little cleanup: set authentication back to "not required" and kill hcsecd:

# hccontrol -n ubt0hci write_authentication_enable 0
# killall hcsecd

You can pair in opposite direction too (originating pairing on the phone). If your phone (like my Ericsson T39) does not support role switching, consult the section Phone can't connect to me below.

Service browsing (SDP)

If you want to know which services a Bluetooth device offers, and on which RFCOMM channels, build libbluetooth and sdp-1.0rc3 from ports directory. Then, run sdptool and observe (the output snipped a bit, as this tool is quite talky):

# sdptool browse 00:80:37:29:19:a4
Browsing 00:80:37:29:19:A4 ...
Service Name: Dial-up Networking
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 1

Service Name: Fax
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 2

Service Name: Voice gateway
Service Class ID List:
  "Headset Audio Gateway" (0x1112)
  "Generic Audio" (0x1203)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 3

... and so on. You will need the channel number later for using a given service. Some devices does not support browsing, they return an empty list, but you can try searching for a specific service. Example is my Palm m125 and the OPUSH service:

# sdptool search --bdaddr 00:07:e0:00:0b:ca OPUSH

Dial-up Networking (DUN)

We will use the ordinary ppp command to connect, with rfcomm_pppd, a wrapper that convert RFCOMM Bluetooth connections to something ppp can handle. You will need to configure ppp. Open the rfcomm_pppd man page, there are some good examples of ppp configurations there. Copy the rfcomm-dialup section to /etc/ppp/ppp.conf. I raised the timeout from the example's 30 seconds to 10 minutes (600 seconds), and added

set nat enable

to enable sharing of my Bluetooth Internet connection on my home network. (Note: to use NAT you have to turn on the net.inet.ip.forwarding sysctl.) If you have your phone paired with your PC, you should be ready to connect:

# rfcomm_pppd -a 00:80:37:29:19:a4 -c -C 1 -d -l rfcomm-dialup

where -a 00:80:37:29:19:a4 is the BD_ADDR of your phone, -C 1 is RFCOMM Channel of the DUN service on the phone, -l rfcomm-dialup selects rfcomm-dialup section from ppp.conf and -d again prevents ppp to detach from terminal and become a daemon. To disconnect, simply kill rfcomm_pppd. Note that this will disconnect you from Internet, but Bluetooth connection to your phone is still up and running.

OBEX Push

OBEX is a widely used protocol for simple file transfers between devices. It's main use is in infrared communication, where it's used for generic file sending between notebooks, or Palm handhelds, and for sending contact cards or calendar entries between mobile phones and other devices.

The OBEX client is implemented in the obexapp utility. It needs the openobex library from snapshot and the devel/glib12 library from the FreeBSD ports collection. First, find which channel on remote device is IrMC Synchronization. On my Ericsson T39 it's channel 11. I was able to use service OBEX Object Push (channel 10) too. Implementation of OBEX widely differs from phone to phone. Some commonly used remote filenames are in the obexapp manual page. Note that obexapp does not require root privileges to operate. Now, start obexapp in client mode. We're going to download device info from the phone and send one new card to the phone directory:

$ obexapp -a 00:80:37:29:19:a4 -C 10
obex> get
get: remote file> telecom/devinfo.txt
get: local file> devinfo-t39.txt
Success, response: OK, Success (0x20)
obex> put
put: local file> new.vcf
put: remote file> new.vcf
Success, response: OK, Success (0x20)
obex> di
Success, response: OK, Success (0x20)

Now let's send items in the opposite direction, from phone to PC. You'll need to compile sdp-1.0rc3 from the snapshot. SDP (Service Discovery Protocol) is used to announce our services to other devices. We already described it when we was searching for the channel number of the DUN profile earlier in this article. Here we will start a local SDP daemon and configure it:

# sdpd
# sdptool add --channel=10 OPUSH

If OPUSH does not work, you should try FTRN instead. You can check configuration of SDP daemon by browsing this magic address:

# sdptool browse ff:ff:ff:00:00:00
Browsing FF:FF:FF:00:00:00 ...
Service Name: SDP Server
Service Description: Bluetooth service discovery server
Service Provider: BlueZ
[...]
Service Name: OBEX Object Push
Service RecHandle: 0x80541d0
Service Class ID List:
  "OBEX Object Push" (0x1105)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 10
  "OBEX" (0x0008)
Profile Descriptor List:
  "OBEX Object Push" (0x1105)
    Version: 0x0100

Note the RFCOMM channel and Service RecHandle. RecHandle is needed to remove service from a sdpd configuration. Now start the OBEX daemon using the channel number registered with sdpd:

# obexapp -s -C 10

And now you should be able to send items from the phone to your PC. Received items will appear in /var/spool/obex, so make sure this directory exists, or override it with -r switch.

I had successfully sent and received these items from/to my Ericsson T39: contacts, tasks, appointments, background wallpaper, ring tone (both directions) and SMS messages as print job (from phone to PC).

Serial Port Profile (SP)

Bluetooth can be used to emulate serial port connections. To connect to a remote device, first locate the RFCOMM channel with the Serial Port profile. Then, start the Serial Port Profile Daemon rfcomm_sppd with a free pseudo tty:

# rfcomm_sppd -a 00:07:E0:00:0B:CA -c 1 -t /dev/ttyp6
rfcomm_sppd[94692]: Starting on /dev/ttyp6...

Now connect this pseudo tty to your actual terminal:

# cu -l ttyp6

Help, my phone can't connect to me!

Some devices, like my Ericsson T39 phone, does not support role switching. By default, when the PC is accepting a connection, it tries to switch roles to become master. These devices will not be able to connect. Role switching is performed when connection is being established, so we can't ask remote device if it does support role switching. But we can disable role switching on our side with this option:

# hccontrol -n ubt0hci write_node_role_switch 0

This option is new in 20030407 snapshot.

Palm handhelds

I'm the owner of a Palm m125 handheld and Bluetooth SDIO card. Pairing of a Palm with FreeBSD works fine. Sending files from FreeBSD to Palm is done using OBEX Push. You have to search for channel using sdptool, Palm does not support SDP browsing. Channel for sending files is 3 on my Palm, but it changes from time to time. I can send diary items and prc/pdb files to the Palm's memory using obexapp and connecting to service None. After file is transfered, I have to manually kill the baseband connection to Palm using hccontrol disconnect to get rid of Disconnecting... window on Palm. The FreeBSD stack intentionally leave baseband connection opened where other stacks and devices close it. There will be an auto-disconnect functionality in the next snapshot.

You can send files from Palm to FreeBSD using sdpd and obexapp same way as when we was sending business cards from the cell phone. Apply this patch to ports/sdp-1.0rc3/tools/sdptool.c, otherwise you'll not be able to send databases. Builtin PIM applications allows you to send business cards, todo items etc. Builtin Launcher in Palm allows you to send only programs over Bluetooth, if you want to send any database, you will have to buy some launcher replacement or file manager, like ZLauncher 3.

Thanks & Links

I would like to thank a lot to Maksim Yevmenkin <Maksim.Yevmenkin@cw.com>, who wrote Bluetooth stack for FreeBSD, and who provided me with useful hints and excellent support.

Surprisingly quickly someone made japanese translation of this page. Thanks.

How to connect two computers over Bluetooth
FreeBSD on IBM ThinkPad T40p notebook

Last revision: Sun 24 Aug 2003 19:19:48 GMT

 

Back to Pav's BSD corner