Chapter 6

Linux Bluetooth Development

Introduction

Bluetooth technology is an open standard while Linux is open source. There’s some obvious synergy there: combine low cost devices with free software and you’ve got a communications technology anybody can afford.

Linux is proving to be the obvious system of choice for students and academics trying to get into Bluetooth technology on tight budgets. But don’t think it’s just for educational use: Linux is being deployed in real commercial products from local area network (LAN) access points to laptops, and more besides. To give it a real stamp of credibility, Linux Bluetooth development has backing from a Bluetooth Special Interest Group (SIG) promoter with IBM’s BlueDrekar middleware, and, of course, a myriad of smaller companies and individuals are contributing to the development of open source, too.

This chapter takes a look at what Linux can do for your Bluetooth applications, and gives you some useful insight from inside the Linux developer’s community.

Assessing Linux Bluetooth Protocol Stacks

Until recently, the Linux kernel did not come with a Bluetooth stack among its stock drivers. But shortly after this chapter was originally completed, a new Bluetooth project was released as open source and rapidly accepted into the 2.4.6 kernel. This project is called Bluez (bluez.sourceforge.net), and at the time of this writing, its recent 1.2 release includes stable Host Controller Interface (HCI) and Logical Link Control and Adaptation Layer (L2CAP) drivers, as well as user-space Radio Frequency Communications Port (RFCOMM) and Service Discovery Protocol (SDP) applications leveraged from the OpenBT project (which we’ll discuss in short order). Although it has gained acceptance into the mainline Linux kernel, it may not yet be the driver of choice for developers. As of now, it does not support as many features as some of the other available stacks. It does not yet appear to have the developer and user following that OpenBT does, and most importantly, has not been ported back to earlier kernel versions.

Currently, there are two other major Linux Bluetooth protocol stacks: IBM’s BlueDrekar and the OpenBT project. Another future contender will be Rappore Technology’s stack, which is already ported to Windows and BlueCat embedded Linux.

IBM’s BlueDrekar can be downloaded from their project Web site at www.alphaWorks.ibm.com/tech/bluedrekar. This is not an open source stack. What you get for free are the binary modules. If you want the source, you can get it, but according to their documentation and Web site, you must be a SIG member and you must sign a limited license with IBM. You will also need a license to distribute their stack.

SourceForge hosts the OpenBT project. You can find their Web site at www.sourceforge.net/projects/OpenBT. Axis Communications (www.axis.com) originally developed this stack for their embedded Linux product and most of the main developers work there. This is a truly open source stack.

If you’re an embedded developer using BlueCat Linux on your target, you can find out more about the status of Rappore’s stack at their Web site: www.rappore.com. (This stack is not open source; we won’t cover the Rappore stack in detail in this chapter.)

Comparing BlueDrekar with OpenBT by Features

The big factor that distinguishes BlueDrekar from OpenBT is source code availability. Why would you even consider a closed source solution when an open source one is available? For an x86 application developer, BlueDrekar offers more than the OpenBT stack. For embedded developers who need to cross-compile and don’t want to license source, OpenBT may be good enough.

Table 6.1 shows a breakdown of the feature differences between the two stacks, which we’ll discuss in the following sections.

Table 6.1

Feature Comparison between OpenBT and BlueDrekar

Feature OpenBT BlueDrekar
Kernel versions 2.0.x – 2.4.x 2.2.12, 2.2.14
Hardware platforms X86, ARM, MIPS, PowerPC X86
Bluetooth protocols Host Controller Interface (HCI), Logical Link Control and Adaptation Protocol (L2CAP), Service Discovery Protocol (SDP), RFCOMM, HCI-Universal Asynchronous receiver Transmitter (HCI-UART), HCI-USB HCI, L2CAP, SDP, RFCOMM, Synchronous Connection Oriented (SCO), HCI-UART
SDP server support Server, XML database Server, dynamic database
API Standard Unix device driver Custom lib Applications Programming Interface (API)
License terms AXIS OpenBT Stack license AlphaWorks

The basic Bluetooth host protocols are supported by both stacks. Beginning at the HCI, which links a host to a module, both stacks support the UART transport layer needed for basic serial communications. OpenBT goes on to also support the higher speed Universal Serial Bus (USB). L2CAP, RFCOMM, and SDP are also provided by both protocol stacks.

Kernel Versions

Developers have used the OpenBT source on a wide range of kernel versions, including uCLinux. Because the source is available, people are free to port it to whatever kernel version they require.

The BlueDrekar binaries, on the other hand, are compiled only against certain 2.2.x kernel versions at the time of this writing, so you can’t use them with older or newer kernels.

Hardware Platforms

Developers around the world have used OpenBT on a variety of processor types. This author’s company has used it on ARM and MIPS, as well as x86 processors, and according to the mailing list archives for OpenBT, some people have used it with PowerPCs as well. Again, because you have the source, if you need to port it or even just cross-compile it for a non-x86 platform, you can do so.

With BlueDrekar, you only get the x86 binaries. You don’t have the source unless you apply for a license, so obviously you’re limited to just x86 platforms.

Bluetooth Protocols

Here’s where BlueDrekar starts to catch up. The OpenBT project does not currently support the Synchronous Connection Oriented (SCO) connections used for voice, which is a major drawback. It does include support for an HCI-USB layer, however.

BlueDrekar does have support for SCO already. For BlueDrekar, you can get the source for their HCI-UART module. This is the one part of their stack, which is open source. IBM released this source under GPL with the hope that others could use it as a basis for developing the other HCI link drivers.

SDP Support

The Service Discovery Protocol (SDP) is used by a client device to find out about the services it can use on a server device. An SDP server maintains a database of services; this can be preconfigured (static), or can be built up dynamically as services register with the database system. Once a database is in place, clients send SDP requests to query its contents, and servers reply with SDP responses giving details of services supported and information needed to connect to those services.

SDP is another area where BlueDrekar is ahead of OpenBT. The OpenBT project does provide an SDP server daemon to handle SDP requests from remote devices. However, it does not yet provide an API for local applications to dynamically register themselves in the SDP database. Another disadvantage is that applications must frame their own SDP request packets and parse the resulting SDP responses.

BlueDrekar is much nicer. It also provides a server daemon, but additionally, it has an API for dynamically registering services in the local database as well as handling a lot of the details of SDP. Applications still need to know the basic components of SDP packets, but they don’t have to hand-tool the packets themselves like they do with OpenBT.

API

The OpenBT stack provides a set of device files for applications to use. These are all TTYs (terminals) and follow the standard Linux API for TTY drivers. Stack control is done via blocking ioctl calls. Since there’s no intervening library layer, all of the control I/O is synchronous. There is no event notification aspect of the API.

The BlueDrekar stack provides a library layer and a daemon (referred to collectively as middleware). Although data transfers are handled over standard drivers, control operations are done via library calls. These often employ callback mechanisms for event notification.

License Terms

Licensing is the big issue. The OpenBT project is released under the AXIS OpenBT Stack license. You can see the text of this license at http://developer.axis.com/software/bluetooth/OpenBT_license.txt. Basically, it is the GPL with some additional freedoms. If you write applications that use the stack, they will not fall under the GPL and may remain proprietary. But if you write applications that are a derived work of the applications in the OpenBT source tree, then they will fall under the GPL—unless they have nothing to do with Bluetooth technology. Note that just because the stack is under GPL doesn’t mean applications that use the stack must be. However, if you modify or add SCO support to the stack (for example) then these changes would be under GPL.

BlueDrekar is released under IBM’s Alpha Works license. You can download the binaries for free and write applications that use them, but if you want to see the source or distribute the binaries with a product then, you’ll need extra permissions. According to their Web site, you must be a Bluetooth SIG member to get this additional permission.

Other Considerations

If you’re a PC application developer, then you may not have any control over which Bluetooth stack the user has on his or her PC. The OpenBT and BlueDrekar APIs are not at all similar, so it would be tough to write an application that works on both. It’s likely you’d have to pick one particular stack and require users to install it.

If you’re an embedded developer, then chances are you’re probably not only writing applications, but you’re also trying to decide which stack to ship with your device. You have total control over which stack your application will use, because you decide which stack the user gets. Note that at the time of this writing the OpenBT stack produced a somewhat smaller image when compiled for an x86, but probably not enough to make too much of a difference. If size is important, then cross-compile the latest release of OpenBT against your target platform and check it. To compare it with BlueDrekar you’ll have to ask IBM about getting this information. The open source nature of OpenBT can be a real bonus for embedded developers because it’s easy to check things.

Axis Communications originally designed the OpenBT stack to serve as a LAN Access Profile server on their embedded Linux products. If you need a PPP server over RFCOMM, then once you get the stack running on your platform, you’re basically done. However, although it functions well in this regard, developers who want to leverage the stack for other purposes should expect to do some work.

For the rest of this chapter, we’re going to discuss using the OpenBT stack. I have to pick one, just like you will. I’m not picking OpenBT because it’s a better implementation than BlueDrekar—to be perfectly frank I don’t think it is (yet). Instead, I chose it for the following reasons:

image It’s freely available.

image I’m under no restrictions to not discuss any aspect of it.

image I have access to the source, so I understand it much better.

image I’ve used it in the past on several different platforms, for several different kernel versions.

image I’ve contributed to it in the past.

image I think it has the best chance of making it into the standard Linux kernel tree (eventually).

image If I can encourage you to use it and contribute, then I benefit from your use as you can benefit from mine.

Fair Warning

It’s only fair to be perfectly clear on something at this point: the OpenBT stack is a work in progress, and is not feature-complete as a client stack. Here are the big issues, in order of severity:

image There is no way to bind RFCOMM server channels for server applications other than PPP.

image There is no interface for dynamic SDP registration.

image Applications must assemble their own SDP requests and parse the SDP responses.

image There is no SCO support.

image There are no interfaces for supporting other protocols above L2CAP.

image The stack still has many bugs ranging from annoying behavior to full system lockups.

Also, as with any implementation, the stack still has some bugs—especially when supporting client applications. You can get a list of the current known bugs from the OpenBT Web site on SourceForge.

Nonetheless, OpenBT has one major advantage: the source is open. It goes without saying that one of the reasons I know about all these problems is because I can look in the source and see them. I can also look in the source and fix them.

That being said, let’s talk about the basics of how the OpenBT stack works. From here on, when I use the term Bluetooth driver I’ll be referring to the OpenBT stack. Specifically, I will be referring to version 0.0.2, released in March of 2001.

Understanding the Linux Bluetooth Driver

The first thing you should do is go to the OpenBT project Web site, download their latest release, and then follow the instructions for installing and using the driver. Go ahead and play with included applications until you’re satisfied that you’ve got things working on your system. If you don’t have Bluetooth hardware, that’s okay, because the stack includes several options for simulating hardware connections between two devices. You don’t even necessarily need more than one PC to try it out.

Note that the OpenBT stack comes with a lot of options about user mode, kernel mode, and real versus simulated hardware connections. In this chapter, I’m going to limit the discussion to using the kernel mode driver with real hardware. In the end, your application will have to work under these conditions anyway.

In this section, we’ll first talk about what the Bluetooth driver is, and tour some of its visible properties. Then we’ll cover the basics of using the Bluetooth driver interfaces.

Learning about the Kernel Driver

The actual kernel Bluetooth driver is the bt.o module. This is built in the linux/drivers/char/bluetooth directory of the OpenBT source tree. This loadable module implements a TTY (terminal) driver and an ldisc, the line discipline that affects how the data stream to a terminal is interpreted. I’ll explain those terms in more detail after taking a quick look at what happens when you load the Bluetooth driver into the kernel.

Investigating the Kernel Module

To load the Bluetooth driver into the kernel, execute the following command in a terminal window as root:

$ insmod bt.o

Now let’s browse through the proc directory and see what just happened. Enter this:

$ cat /proc/devices

One of the char driver entries will be listed as bt. This is our driver. On the same line, you’ll see its major number. This major number uniquely identifies the Bluetooth driver in the kernel. Later, when we look at the Bluetooth device files, we’ll see that their major number matches up with this, effectively binding them to this driver. This is what tells the Linux kernel which driver to invoke when we make system calls like open on those device files.

Now enter this:

$ ls /proc/bt_*

And you’ll see the proc files installed by the driver. Enter this to see some status information on the driver:

$ cat /proc/bt_status

Finally enter this:

$ cat /proc/tty/drivers; cat /proc/tty/ldiscs

The first command lists all the TTY drivers currently registered in the kernel. Ours is now one of them. The second lists all the ldiscs currently registered in the kernel. Note bt_ldisc—that’s ours.

What Exactly Is a TTY?

One way to think of a TTY is as a subclass of a character driver. A TTY implements the same interface as a character driver and then some. In fact, you might think of a TTY as a character driver with an attached filter. The filter sits in the kernel between the TTY and an upper layer. This filter is called an ldisc, or “line discipline.”

So What’s an ldisc?

A line discipline (ldisc) monitors and even modifies the data stream that passes between an upper layer and the TTY. It might do things like look for special control characters in the data stream. It might even reformat the data stream into protocol packets of some kind or other.

image Developing & Deploying …

What Exactly Do You Mean by “Character Driver”?

A character driver is one of the basic driver types supported by the Linux kernel (some others are block drivers and network drivers). A character driver represents a connectionless data stream over some type of device. All character drivers must support the following system calls: open, close, and write. Most character drivers also support the read, select, and ioctl system calls. Examples of character drivers you might find on your system are /dev/audio, /dev/ttyS0 (the serial TTY), and /dev/mem.

One really important feature of the relationship between a TTY and its ldisc is that you can change the ldisc at runtime. In effect, you can swap filters. In the next section, I’ll show you how this affects the Bluetooth driver.

Building Driver Stacks in the Linux Kernel

Figure 6.1 is a simplified diagram of the default TTY driver configuration after you load the bt.o module. You see how both the bt and serial TTY drivers use the N_TTY ldisc as an adapter between themselves and the standard TTY I/O code? The N_TTY ldisc is suitable for console TTY drivers. It does things like scan for control characters in the byte stream. But an application can change any TTY driver’s line discipline by using a special ioctl call. For example, we could have an application change the serial driver’s line discipline to be bt_ldisc instead of N_TTY.

image

Figure 6.1 Default TTY Driver Configuration

Guess what? That’s exactly how we make the Bluetooth driver talk to a Bluetooth card attached by a serial cable. Figure 6.2 shows a picture of this. The bt_ldisc in effect will route all data to and from the serial port through the Bluetooth driver. That’s where all the parsing and packet assembly will take place.

image

Figure 6.2 Stacked TTY Driver Configuration

In summary, line disciplines are important because they allow user-space applications to stack TTY drivers in the kernel. Note that this is exactly how PPP works over a TTY—and therefore RFCOMM devices must be TTY drivers.

Understanding the Bluetooth Driver Interface

Now that you understand what the Bluetooth driver is, how exactly do applications use it? They use it by making system calls on the Bluetooth device files.

Investigating the Bluetooth Device Files

You may have noticed during the installation that at one point you had to create some files in the /dev directory. Take a look at them now by entering:

$ ls -l /dev/ttyBT*

These device files are your application’s interface to the Bluetooth driver. Notice that all the devices have the same major number but different minor numbers (if you’re not sure how to tell, then check the man page for ls). Having the same major number means that the same kernel driver implements them all. The different minor numbers represent different instances of an interface to the kernel driver.

There are two types of Bluetooth device files: data device files and control device files. Table 6.2 shows the main differences between them.

Table 6.2

Comparison of the Control and Data Device Files

Feature /dev/ttyBTC /dev/ttyBT[0-6]
Can open before stack is initialized YES NO
Multiple processes can open at the same time YES NO
Can transfer data over an RFCOMM connection NO YES
Can execute stack control ioctls YES NO

Using the RFCOMM TTY Drivers

The data device files are named /dev/ttyBT0 through /dev/ttyBT6. These are all instances of RFCOMM TTYs. Once they’re opened and connected, they behave exactly like serial ports, as we’ll see later. Only one process at a time can open any individual RFCOMM TTY. All the standard system calls which work over standard character drivers and all of the ioctls, which work over standard TTY drivers, also work over the RFCOMM TTY driver.

The minor number for the RFCOMM TTY’s has special significance to the Bluetooth driver. Each minor number corresponds to a line number used internally by the driver to index a connection session. Each possible RFCOMM or SDP connection, which the driver can make with a remote peer, is represented internally by a session. Since there are seven RFCOMM TTYs, there are seven session “objects” maintained by the driver.

The only trick to using the RFCOMM TTY device files is in understanding the concept of an RFCOMM session. Within the driver, each RFCOMM session has a state machine. The driver indexes sessions internally by a line number. When opening an RFCOMM device file, the line number comes from the minor number of the device file. When connecting to a remote service, you specify the local line number as one of the connection parameters. Figure 6.3 illustrates the state machine for a single session.

image

Figure 6.3 The RFCOMM Session State Diagram

In Figure 6.3, you can see the three parameters that specify the state of a session are: whether or not the device file is open, whether or not the TTY is hung up, and whether or not an RFCOMM connection to a remote peer exists. The important points to take away from this are as follows:

image The driver hangs up the TTY when an existing RFCOMM connection gets disconnected.

image The only way to return a hung-up TTY to normal is to close and reopen the device file.

image Data can only be transferred in the open/normal/connected state.

One very interesting consequence is that one process can establish an RFCOMM connection on a session without opening its device file, and another process can then open the device file and transfer data across the connection.

Multiplexing over RFCOMM

All of the RFCOMM device files operate independently of one another. Each represents a different potential RFCOMM channel. That’s all you really need to know about multiplexing! You don’t have to worry about it much at the application layer. If you have an application that can handle multiple connections, it should open and listen on multiple RFCOMM device files. Figure 6.4 illustrates this.

image

Figure 6.4 Multiple Simultaneous RFCOMM Connections

When you open an RFCOMM device file, your process gets exclusive access to it. True, other processes can establish RFCOMM connections for it, but yours is the only one that can transfer data through it. None of your data transfers will affect any other RFCOMM session (other than using up some of the link’s bandwidth).

However, there are things your application can do that will affect other processes using the Bluetooth driver. Most of the ioctl calls specific to the Bluetooth driver have global affects. For example, if your application decides that it needs to shut down or reinitialize the stack, it could interrupt another application’s data transfer.

The OpenBT stack lacks a central stack manager. In other words, there is no single process responsible for running the driver in an orderly fashion. The Bluetooth driver itself does not enforce any policy. For example, it does not decide when to enable Inquiry Scans, or security procedures. All policy is left to the applications. And the OpenBT source tree does not come with a central management application to make sure applications don’t conflict with one another. If one application wants Inquiry Scan enabled and another wants it disabled, the winner is whoever issued the ioctl call last.

So how can you write applications that cooperate well with others? Short answer: you can’t. This is a problem for desktop applications. For embedded developers, odds are you control all the applications that will use Bluetooth and you can design your own cooperation strategy.

The one device file /dev/ttyBTC is a special device, dedicated to controlling the kernel driver as a whole. We’ll see later how to use this device to initialize and shut down the Bluetooth stack. Any number of processes can open /dev/ttyBTC at the same time.

Note that there are no device files for SDP, L2CAP, or any of the other Bluetooth protocols implemented by the driver. We’ll see that we can access SDP and HCI using ioctl calls on any of the devices. And there simply is no interface to L2CAP—it’s completely internal to the driver.

Can you add your own device files to implement other protocol layers above L2CAP? That’s a pretty frequent question to the bluetooth-dev mailing list. And the disappointing answer is no, not without modifying the stack itself—but remember, you do have the source.

Although the Bluetooth driver is “just another TTY driver,” there are some specific things you need to understand about its interface. You need to be familiar with some of the more important ioctl calls used to control Bluetooth-specific features, and you need to know the difference between the control device file and the other device files.

Installing a Line Discipline over an RFCOMM TTY

Because the RFCOMM device files are TTYs, you can set up line disciplines above the RFCOMM layer. This is exactly the way PPP works. In the same way that the Bluetooth driver sets up a line discipline above the serial driver, PPP sets up a line discipline above the Bluetooth driver. The whole key to using RFCOMM comes from understanding this principle. Any application that works over a TTY will work over an RFCOMM TTY, once the underlying RFCOMM connection has been established. Any process can establish that connection—it doesn’t have to be the process that will use the TTY to transfer data.

Using the Control Driver

The control device file is /dev/ttyBTC. Unlike the other Bluetooth device files, this one isn’t used to transfer data between different devices. This one is only used to control the local Bluetooth driver. Whenever you need to issue a stack control ioctl, you should do it using this device file. This includes the ioctl calls for initialization, shutdown, security, connection, hardware control, and so on.

The most important role of this device file is to initialize the driver. Until the driver is initialized, you cannot open any of the other device files. You can only open the control device file. However, once the stack is initialized, you cannot only open the other devices’ files, but you can use them to execute all of the stack control ioctls which can be used on /dev/ttyBTC. In a way, the only purpose of the control device file is to initialize the stack.

Using Open Source Development Applications

The OpenBT source tree comes with several applications. You can use these applications to:

image Provide your SDP server.

image Manually establish PPP connections between devices.

image Manually establish RFCOMM connections between devices.

image Browse the SDP database on a target device.

image Provide examples to learn how to write applications for the stack.

image Provide a starting point for your own application.

Depending on what you want to use the Bluetooth stack for, you may not need to write any code at all. For instance, once you establish a PPP connection over RFCOMM, all the power of the standard GNU network applications is at your disposal—the Bluetooth connection is just like any other network connection. All existing applications that use a socket interface are instantly ported to use Bluetooth: Web browsers, Web servers, FTP, Telnet, and so on.

Investigating the OpenBT Applications

The OpenBT source tree comes with some applications. Table 6.3 summarizes their features.

Table 6.3

Summary of Features in OpenBT Applications

image

Understanding the btd and btduser Applications

The btd application will probably be the most useful for you. The difference between btd and btduser is that btd is meant to work with the kernel mode Bluetooth driver, while btduser works with the user mode Bluetooth driver. Many people prefer btduser since it is less prone to lock up your system if things go badly. However, the OpenBT developers do not support it as well as btd.

For btd you have to install the Bluetooth kernel driver (i.e., insmod bt.o). For btduser, you don’t. Other than that, their usage is basically the same.

The btd application can take a number of different arguments on startup. An example follows. If you’re curious about other arguments besides the one I mention, then look in the sdp.c source file. At the top of the main() routine, you’ll see the argument parsing. From that, you can figure out what the other arguments to btd are. The README that comes with OpenBT talks about starting btd, but it is not always up-to-date. Remember, OpenBT is still early in its development, and often the source code is the best documentation.

Understanding the sdp_Server Application

The sdp_server application provides you with an SDP database server daemon. Once you’ve installed the Bluetooth driver, you can start this daemon and it will automatically receive and respond to SDP queries from remote devices.

If you start the daemon with no arguments, it will automatically use /etc/sdp.xml as the SDP database file and /tmp/sdp_sock as the source of SDP requests. The /tmp/sdp_sock file is a Unix socket created by the btduser application. You can specify a different XML file as the first argument to sdp_server and a different source device as the second argument. Note that if you provide one argument, you must provide the other as well. If you want to use the SDP server when the Bluetooth driver is in kernel mode, then you should specify /proc/sdp_srv as the source of SDP requests.

The following is an example of starting the sdp_daemon with command-line arguments:

$ sdp_daemon /tmp/my_sdp_database.xml /proc/sdp_srv &

image SECURITY ALERT

Warning! Never remove the Bluetooth driver while the sdp_server daemon is using /proc/sdp_srv. If you do so in the current release version of the stack (0.0.2 at the time of this writing), you will get a kernel panic when you stop the daemon. Future versions of the stack will probably not allow you to remove the driver while the sdp_server daemon is using it.

Understanding the BluetoothPN Application

This application provides a GUI that displays the SDP database on a remote device. It provides some examples of how to make SDP requests and process their results.

Establishing a PPP Connection Using the btd Application

The quickest, most useful way to establish and exploit a Bluetooth connection from Linux is to use the standard GNU network applications over PPP. And the easiest way to do that is with the btd application. Let’s look at an example.

It assumes the following setup:

image Two Linux PCs configured to use PPP; one will be the server and one the client.

image Both PCs are connected to Ericsson Bluetooth Developer kits via RS232 to /dev/ttyS0.

image The OpenBT Bluetooth driver is installed in both PCs kernels.

image There us an open terminal window with root permissions on each PC.

image The server should have the “local” and “nodetach” options specified in its /etc/ppp/options file (see man(8) pppd).

image The client should have the “local,” “nodetach,” and “noauth” options specified in its /etc/ppp/options file.

Here are the steps:

1. On the server:

    $ btd --server --physdev=/dev/ttyS0 --speed=57600 --modem=0

2. On the client:

    $ btd --client --physdev=/dev/ttyS0 --speed=57600 -modem=0

3. On the client, you will now see a menu of options. Select an HCI Inquiry for one device, with a maximum timeout of about five seconds:

    > inq 1 5

4. If the inquiry succeeds, the program will report the Bluetooth Device Address (BD ADDR) of the server’s Bluetooth card on the terminal. For example, it might return 11:22:33:44:55:66 (it’s unlikely, but this is just an example). Next, create an RFCOMM connection to server channel 2 of that device, using line O. When the server btd application detects the connection, it will spawn PPP and pass in /dev/ttyBTO on the command line as the TTY. The line 0 argument maps to /dev/ttyBT0 on the local device. When the client btd application spawns PPP, it will also pass /dev/ttyBT0 to the local PPP as the TTY. Here’s the command:

    > rf_conn 11:22:33:44:55:66 2 0

5. If the command succeeds, then after a few seconds you will see the connected message on the client’s terminal window. On the server, you should see PPP start up and wait for an incoming PPP connection. At this point, we’re ready to start PPP on the client. Here’s the command:

    > ppp

6. If the PPP connection succeeds, you should see a message like this on both the client and server side:

image

7. At this point, you can test the connection. First, on either the client or server, open a terminal window and use ifconfig to determine the IP address of the remote PPP connection. It should report the ppp connection similar to this:

image

8. Now, open another terminal window on the client and ping the remote IP.

    > ping 192.168.1.17

Those ping responses are coming back across the Bluetooth link! Pretty exciting, eh? Well, the first time anyway. You can also go ahead and try some other network commands like Telnet and FTP. Have some fun.

image Debugging …

Watching Driver Debug Messages

If you want to watch exchanges between the stack and the card (a good idea for debugging problems) then you can turn on some of the debug messages before you compile the stack. Edit the btdebug.h file in the OpenBT source tree. My favorite macro to turn on is BT_DATAFLOW_DEBUG. Change its #define from 0 to 1 and then recompile and insert the OpenBT module. Then, when you’re running your application, open another terminal and execute this command to see the running transactions between the host and the card (on most systems you must be root to do this):

$ tail - f /var/log/messages

If you see a lot of messages to the effect of “HCI timeout” in this debug, then chances are your card is not responding to HCI commands from the host. You should make sure your serial port is set up right and you are using the right type of cable (null modem for Ericsson Bluetooth Developer Kits; other hardware may vary). A good way to double-check your serial port settings is to do this:

$ cat /proc/tty/driver/serial

The btd application provides the quickest way to get started, but it assumes that:

image You know the remote server channel number without doing an SDP discovery.

image You want to use PPP over RFCOMM, and not some other application.

If you have other requirements, then you’ll need to produce your own application. If you’re willing to accept a GPL-like license on your application, then you can use btd.c as a starting point to make a derived work.

Writing Your Own Minimal Application

Admittedly, btd.c has grown to become rather large and complicated. You’re probably wondering, “What’s the bare minimum I need to establish a connection?” The following source will give you a starting point. This program does essentially the same thing as btd, and makes the same assumptions. But it boils down btd.c into the absolute minimum amount of code needed to establish an RFCOMM connection.

image

image

I’ll explain most of the things this application is doing in the next section, “Connecting to a Bluetooth Device,” but first I’ll show you how to use the application.

I defined the SYSCALL macro so that I could show a real example of checking system call returns while conserving space in the text. It does a primitive form of exception handling (if you can call exiting the application exception handling) that shows the user what the error is.

The tty_init routine is based on the fd_setup routine in btd.c. It sets up the serial port TTY to work in raw mode, sets the baud rate, hardware flow control, and so on.

Note that this program has the server device’s BD ADDR hard-coded into the declaration of the bt_connection struct! Yours will differ, so change this before trying it. A real-world application wouldn’t do this, of course.

To build the program, put the following Makefile in the same directory:

image

Change the bt_mod_inc_dir variable to match the location where you installed the OpenBT source tree. Assuming you saved this file as simple.c, to make the server, type:

$ make simple

And to make the client, type:

$ make EXTRA_CFLAGS=-DCLIENT simple

First run the program on the server, and then run it on the client. Next, open new terminal windows on the server and client. On one, type:

$ cat /dev/ttyBT0

And on the other one, type:

$ echo hello > /dev/ttyBT0

You should see “hello” appear on the opposite side. Any program that works over a character device or a TTY should work over this connection. Go ahead and try some others. Try catting a binary file, too, just to see why we need to make TTYs raw before we can safely transmit binary data.

Connecting to a Bluetooth Device

At this point, you’re probably impatient to start writing some code. I know I would be. In fact, if you’re like me, this is probably the first section you jumped to. In this section, I’ll give you some examples to start with and talk through some of the issues. I’ll show you how to get the stack up and talking to the hardware, how to discover other Bluetooth devices, and how to find and connect to applications on those devices.

For all of these examples, I used the following setup:

image The OpenBT Bluetooth driver version 0.0.2

image Ericsson Bluetooth development h/w, ROK 101, firmware revision P9A

image RS-232 connection between the host and the Ericsson card

image Red Hat 6.2

image Linux 2.2.18 kernel

In the rest of this section, we’ll see how to initialize the stack, look for remote devices, do SDP queries and initiate and shut down connections. I’ll also show an example of adding a new service to the XML database.

Initializing the Bluetooth Stack

Figure 6.1 illustrated what your system is like after you load the Bluetooth module and connect the serial cable between the host and the card. At this point, the Bluetooth driver and the serial port driver are both registered as TTY drivers in the kernel, but both are idle. Both are using the default N_TTY line discipline and standard termios settings. The Bluetooth line discipline is registered in the kernel, but nothing is using it. No data is moving between the host and the card.

The Bluetooth driver must use the serial driver to talk to the card. In order to do this, we need to “hook up” the Bluetooth driver on top of the serial driver so that when it sends data, it sends it through the serial driver to the serial port; and when the serial driver receives data from the serial port, it pushes it up to the Bluetooth driver.

We also must change the default settings of the serial driver. For one thing, the default settings are not compatible with binary data. That’s because TTYs are commonly used for things which require some control character processing, like consoles. That won’t work for us because this processing might change, replace, or insert certain values in the data passing through the TTY. We just want the TTY to pass the data exactly as we tell it to.

Also, the default baud rate for serial ports is typically 9600. But the Ericsson Bluetooth Developer’s kit will expect us to talk to it at 57600—at least until we can tell it to switch to a different baud rate. This default baud rate is vendor-specific. Unfortunately, it is not part of the HCI UART spec.

Of course, if you’re using USB instead of serial, then you don’t have to worry about any of this. The USB Bluetooth driver provides a TTY interface, but the baud rate is meaningless.

Preparing the Serial Driver

The following example shows how to open the serial port and make it a raw TTY. When it’s raw, that means it won’t mess with our data as it moves between the Bluetooth driver and the serial port. If you don’t make it raw, it will try to filter the data stream looking for special characters. If you think this is confusing, just try using cat on /dev/ttyS0. It works great … for text files. Try it with a binary and you’ll probably hose your terminal settings. But we can fix this by using a raw TTY. The following code shows how to do this:

image

Whether or not you need hardware flow control depends on the Bluetooth hardware you’re using. Some products are okay with it, while some specifically tell you not to use it. The Ericsson hardware seems to work okay either way. Note that many embedded devices have custom UART hardware. Sometimes these don’t support the hardware lines necessary for hardware flow control. If you have trouble getting the Bluetooth driver to talk to the card, then find out whether or not this setting is correct for your hardware.

Observant readers will wonder if we need to fix the termios setting for the Bluetooth driver itself. After all, it’s a TTY driver. Won’t we have the same problem with binary data? Yes—once we start trying to read or write from it. But that’s fine at this point. It won’t affect any of the ioctl calls we’ll be doing. Later, when we want to transfer binary data, we’ll address this. If we just set the driver up for another application like PPP, then that application should be responsible for dealing with this (PPP does).

Stacking the Drivers

Now that the serial driver is ready, we can connect it to the Bluetooth driver. Remember that the Bluetooth stack registered its own line discipline with the kernel when we loaded the module. The way we stack the drivers is by telling the serial port to switch from using the N_TTY line discipline to the Bluetooth line discipline. That way, when the serial driver receives data, it will push it up into the Bluetooth stack, and when the Bluetooth stack wants to send data, it has a handle to the serial driver.

image

The N_BT constant uniquely identifies the Bluetooth line discipline among all other line disciplines registered in the kernel. This identifier is what tells the serial TTY to use the Bluetooth stack as its upper layer interface. It’s defined in btcommon.h—part of the OpenBT source tree.

The TIOCSETD ioctl replaces the serial port’s current line discipline with the one specified. It also causes the Bluetooth line discipline’s open() routine to be called, passing in the serial port’s TTY. This gives the Bluetooth stack a handle to the serial TTY driver so it can use it as the lower layer. At this point, Figure 6.2 shows our driver configuration in the kernel.

Starting Communication between the PC and the Card

Once the drivers are stacked, the host can start talking to the hardware. There are some specific things the Bluetooth stack needs to find out from the card before it does anything else. It also needs to do some internal initialization as well.

image

If you’re going to initialize the stack, you have to use the /dev/ttyBTC device (the control device). The Bluetooth data devices (for example, /dev/ttyBT0) won’t work. In fact, you can’t even open these other devices until the stack is initialized. This, and the fact that multiple processes can open /dev/ttyBTC at the same time, makes it unique. Note that closing /dev/ttyBTC is safe. The stack will remain initialized. To shut it down, we’ll use the BTSHUTDOWN ioctl. You’ll learn more on that in the section “Disconnecting” later in the chapter.

The BTINITSTACK ioctl tells the Bluetooth driver to initialize itself and begin talking to the Bluetooth hardware. It will query the hardware for things like buffer sizes and numbers, read the local BD_ADDR, and so forth. As an application writer, you don’t really need to worry about the details. There is one thing you should know, however: this ioctl call can return before initialization is complete. For this reason, it’s sometimes a good idea to pause your application before continuing.

image Debugging …

Detecting UART Overruns

A common problem people have (especially on embedded devices) is UART overruns. A UART overrun is what happens when data is coming in on the serial port too fast for the serial driver to read it. Embedded devices with slow CPUs, bad IRQ latency, and/or cheap UART hardware sometimes see this problem.

$ cat /proc/tty/driver/serial

The preceding command will show you if your UART is getting receive overruns. If an “oe” field appears in the report, then this gives a count of the number of UART overruns detected by the serial driver. If you are having problems with data corruption, then definitely check for this.

Switching to a Higher Baud Rate

If we want the Bluetooth driver and the hardware to use a higher baud rate we can tell it to do so now. At 57600 baud, the bottleneck will be the serial connection between the host and the card. This doesn’t mean we’ll lose data. We just won’t be taking full advantage of what the radio can do. If we jack it up to 115200 baud, then we’re more in line with the maximum radio data rate of 723.2 Kbps, which is already pretty slow compared to currently extant wired media. Keep in mind that this only affects the baud rate between the host and the Bluetooth hardware. In other words, we’re not changing the radio characteristics of the card in any way.

image NOTE

Keep in mind that if you change the baud rate from the power on default, if you ever shut down the stack, you’ll need to physically reset the hardware before starting it up again. Both the stack and the hardware have to start up at the same baud rate or they won’t talk to each other.

image

The HCISETBAUDRATE ioctl will try to send a vendor-specific command to tell the hardware to change the baud rate. Keep in mind that the command to switch baud rates is vendor-specific. Some vendors might not provide this feature. This is an example of why it’s important for your application to check the return results of system calls. In this case, if the ioctl call fails, then presumably the card won’t change its baud rate. This could be because it has a fixed baud rate, or because it uses a different vendor-specific command, either way we’d better just leave the serial port baud rate alone or the Bluetooth driver will lose communication with the card.

image Developing & Deploying …

Avoiding Race Conditions When Changing Baud Rates

Incidentally, there’s something of a race condition here between when the card switches baud rates and when the serial port switches baud rates. What happens if the card sends us data at the higher baud rate before we manage to change the serial port settings? If this happens, it is usually not fatal, but it’s essential to change the serial port immediately after changing the card’s baud rate. You should also stop any data streams before changing baud rates.

Finding Neighboring Devices

Now that the Bluetooth driver is talking to the hardware we can engage in some Bluetooth traffic. Of course, we’ll need somebody to talk to. In order to find other Bluetooth devices in range, we’ll do an HCI Inquiry. Also, we probably want to let other devices find us, too, so we’ll see how to tell the hardware to respond to other device’s Inquiries.

Letting Other Bluetooth Devices Discover Us

By default, the Ericsson Bluetooth Development Kit hardware doesn’t respond to other device’s inquiries. This is okay, because we don’t really want other people trying to connect with us until we’re ready. The following example shows how to enable both scan and inquiry responses:

image

The HCIWRITESCANENABLE ioctl takes a bit mask parameter. Only the first two bits have meaning. Bit 0 corresponds to Page Scan, and bit 1 corresponds to Inquiry Scan. You set the bit to enable the corresponding scan type. To find out more about Page Scan and Inquiry Scan, consult the Bluetooth Core Specification. For now, just realize that other devices won’t see you if you don’t turn on scan enable.

Sending an HCI Inquiry

To find other neighboring devices use the HCIINQUIRY ioctl. This ioctl takes a parameter of type inquiry_results, which serves both as an in-param and an out-param. The btcommon.h header defines this structure.

image

The nbr_of_units field specifies the maximum number of responses, which the hardware should listen for before ending the Inquiry procedure. The valid range for this value is 0 through 255. But 0 means an unlimited number of responses! Not a good idea since you’ve only allocated a finite amount of space in which to receive responses.

The inq_time field specifies the time, in units of 1.28 seconds, which the hardware should allow for the Inquiry to finish. The hardware will terminate the Inquiry procedure if either it receives the maximum number of responses, or the said amount of time expires—whichever comes first. The valid range for this value is 0x01–0x30, or 1.28–61.44 seconds.

The bd_addr field marks the start of a block of memory set aside for the Inquiry responses. By default, there isn’t any space for responses. One way to make space is to allocate enough memory for the inquiry_results structure, plus some extra for the responses. It turns out that the driver will only store the BD ADDR from each response, so you’ll need to set aside 6 bytes per response. One way to do this is to wrap it with your own structure that has a static buffer, like this:

image

The inquiry response actually carries extra information, like the class of device responding. This information is not passed up the stack at the moment, but it’s worth being aware that it’s there, as in the future the driver may change to store more information. If that happens, of course, more memory would have to be allocated for responses.

The ioctl call will block until either the Inquiry completes, or an error occurs. Possible errors include timeouts waiting for the hardware to send the expected HCI commands. If the call is successful, then the inq argument contains information from any inquiry responses received. Note that the ioctl returns success even if no remote devices responded.

Upon successful return, the nbr_of_units field now indicates the actual number of responses received (this is less than or equal to the number you specified) and the bd_addr field contains the received BD ADDRs of remote devices.

Using Service Discovery

Once you’ve discovered another device, you’re ready to find out what services it offers. Likewise, you may want other devices to discover the services your application provides. By now, you probably know that this is where the Service Discovery Protocol comes into play.

Let me reiterate some of the caveats regarding SDP on OpenBT:

image You cannot dynamically register a new service in the SDP database.

image Your application must know how to assemble SDP requests and parse SDP responses.

image Services cannot register themselves with the RFCOMM layer.

With these limitations, you may well wonder what’s the point of even discussing SDP. Well, there are some benefits:

image You can statically add services to the SDP database, and for embedded developers this may work well enough.

image Your client applications will know how to discover and connect to services running on a stack, which correctly supports RFCOMM registration.

image OpenBT

In the rest of this section, we’ll talk about how to connect to a remote SDP server, how to send requests, and how to process responses. This will cover the client side of things and should be useful even with the current state of the OpenBT stack. After that, we’ll look at an example regarding how to add a service to the SDP database.

Connecting to a Remote SDP Server

Before you can do a query, you need to establish an SDP connection with the remote device. Anytime we need to establish a connection, we’ll use the BTCONNECT ioctl call. This call takes a parameter of type bt_connection. The btcommon.h header defines this structure.

image

image

The bd field is the BD ADDR of the remote device you want to connect to. For instance, you can use one of the BD ADDRs discovered in your inquiry.

L2CAP uses a Protocol Service Multiplexor field (PSM) to uniquely identify an instance of a higher layer protocol using an L2CAP connection. For some protocols, this value is well-known (i.e., in the Core Specification), and for others you have to discover it. The Bluetooth Core Specification defines the PSM for SDP to be 1.

The id field is a combination of the PSM for the protocol instance you want to connect to: the line number and the SDP ID. The high 16 bits of the id field indicate the PSM. The next 8 bits of the id field specify the line or session number. Remember the session state machine in Figure 6.2? This value identifies one of those sessions. It also maps to the minor number of a Bluetooth TTY (/dev/ttyBT0, and so on). When we specify a line here, we’re telling the Bluetooth driver to use the session associated with one of the Bluetooth TTYs.

The lowest 8 bits represent the SDP connection ID. For the BTCONNECT call, these are not important. Later, when we look at the BT_SDP_REQUEST ioctl, we will see how these bits are used.

To make things easier on yourself, you should include the sdp.h header so you can use the CREATE_SDP_ID macro. This macro automatically fills in the PSM. The following example shows its usage:

image

The BTCONNECT ioctl blocks until the connection completes or an error occurs. It returns an SDP connection ID on success. This is a little out of the ordinary for a system call, which should normally return 0 on success!

Sending an SDP Request

After a successful BTCONNECT call, we can start sending SDP requests to a remote device. We’ll send SDP requests (and receive responses) by using the BT_SDP_REQUEST ioctl. This call takes a parameter of type bt_sdp_request. The header btcommon.h defines this structure.

image

image Developing & Deploying …

Picking an SDP Line Number

When you specify a line number for an SDP connection, you must specify the line number of a session that is in the closed/disconnected state. Unfortunately, there is no way for your application to know a priori which sessions are in this state. Until the OpenBT developers introduce a fix for this problem, your application will have to use a trial-and-error algorithm. If a BTCONNECT ioctl fails, this means the session state is not suitable for SDP, and your application can try another one. This problem is not specific to the Bluetooth stack—it applies to any device file.

The conID field has the same format as the id field of the bt_connect structure. Again, we’ll use the CREATE_SDP_ID macro, but this time, when we pass in the SDP index, it will be the value returned by the BTCONNECT ioctl.

The sdpCommand field is the actual SDP command. For example, the ServiceSearchRequest command is 0x02. See the SDP chapter of the Bluetooth Core Specification for other commands.

The pduPayload field is a buffer where we have to put the raw SDP protocol, which comprises our request. The driver will build the SDP packet header for us, but we have to provide the payload of the request in this buffer. Unfortunately, nobody has provided a nice library to build these requests for us. Yet. You can consult the Core Specification or other references to learn more about constructing your own payloads. But one thing you need to note: the SDP specification defines multibyte fields to be “big endian.” So, when you define these fields in your payload, you need to put the high bytes first.

The pduLength field indicates the number of bytes in our payload buffer. Note that we’re limited to 256 bytes.

The requestResponse field is a buffer where we’ll find the response to our request when the ioctl call returns (assuming we received a response).

The responseLength field tells us how many bytes we received in our response when the ioctl call returns. If this is zero, then it’s safe to assume we didn’t get the response.

Let’s look at an example of a service search request for our custom echo service:

image

Remember my warning about multibyte fields and endianness? Look at the Service Class UUID field in our example. We put the high byte before the low byte in our buffer. Likewise for the MaxServiceRecordCount field. Sometimes developers are tempted to define structs, which correspond to protocol packets so that they can fill out the struct and then copy it to the buffer (or cast the buffer to a struct of that type). Beware of doing this! If your application is running on a little-endian processor, then this will not work correctly for SDP. You will get the bytes backwards. The ugly but reliable technique in the previous example will work correctly regardless of the endianness of your host processor. Another alternative is to define or use existing macros that do safe byte-swapping conversions.

Processing an SDP Response

The BT_SDP_REQUEST ioctl call will block while the Bluetooth driver sends the request and waits for the response. If the ioctl succeeded, then the response will appear in the bt_sdp_request struct, which you passed in.

The responseLength field tells you how many bytes are in the requestResponse buffer. If this field is zero, then the Bluetooth driver did not receive any response before timing out.

The first byte of a well-formed response indicates the SDP status of the response. Zero means success; non-zero indicates an SDP error. Consult the SDP spec if you want your application to decode the error type. Remember: the ioctl call can succeed even when the SDP request fails.

image

If the number of ServiceRecords is zero, then the remote device does not support the service we were looking for. Otherwise, using the service handle, we can send more SDP requests to fetch back attributes of the matching ServiceRecords. The ultimate goal is to establish a connection, so we should send an AttributeRequest for the ProtocolDescriptorList next and parse the RFCOMM server channel out of the response. The purpose of this chapter is not to teach you how to parse SDP, so I’ll leave that as an exercise for the reader.

When your application is finished making requests, it should close the SDP connection by using the BTDISCONNECT ioctl call. That way, the remote server can free up any resources it has committed to servicing your connection. However, the current release of OpenBT appears to have a bug in it such that BTDISCONNECT does not work for SDP connections.

Adding a Service to the Local Database

The SDP service database is an XML file. Remember that we can use the sdp_server daemon to handle SDP queries from remote devices to our local database. To add a service, we edit an XML file and pass it as an argument when we start the sdp_server daemon.

Example: Adding an Echo Service

Here’s an example of adding an echo service. It uses RFCOMM over L2CAP as its protocol stack. We place it within the <bluetoothSDP></bluetoothSDP> tags of the XML file:

image

image

In the <ServiceClasses> tag, add this:

EchoServerServiceClassID = “0x1302”

I pulled the EchoServerServiceClassID out of thin air (there is no echo server in the Bluetooth specification), so for all I know it conflicts with an existing class ID! Just another reason why OpenBT needs an SDP interface before armies of irresponsible hackers like myself start filling the world with pirate IDs. I did make sure that the ServiceRecordHandle didn’t conflict with any of the other ones in the file, however.

The “Bluetooth assigned numbers” part of the Bluetooth specification lists the numbers that have been allocated. You can use Universally Unique IDs (UUIDs) to safely allocate your own numbers.

Querying the Local Database

Currently there is no interface to query the local SDP database from within your application. If you want to do this, then you can look at how the sdp_server code invokes the XML parser and processes queries from remote devices.

Connecting to a Bluetooth Service

Usually the purpose of making SDP requests is to discover if a remote device supports a particular service, and if so, what the pertinent connection parameters are. Once this discovery phase is over, your application needs to connect to the actual service. Connecting involves two steps: opening a data device and connecting its associated line.

Using a Data Device

So far, all of the examples have used /dev/ttyBTC as the device. Once we’re ready to actually begin transferring data across a session, we’ll need to open one of the data TTYs. Recall from our session state machine that a session must be in the opened/normal/connected state to transfer data. If you look back at Figure 6.2, you’ll see that it really doesn’t matter whether we establish the RFCOMM connection first or open the TTY first.

Opening a data device is trivial, but here’s the code in case you have any doubts about how to do it:

int bt_fd = open(“/dev/ttyBT0”, O_RDWR);

On success, the device is all yours. If the open fails and errno is EBUSY, then some other process already has it. In this case, you can just keep trying the other devices (e.g., /dev/ttyBT1) until you find one that’s available. Unfortunately, there isn’t really a cleaner way to tell if a device is already being used.

If the open fails and errno is EPERM, then the stack is not initialized. In this case, you can open the control device and use the INITSTACK ioctl call (see earlier) to initialize it and then try again.

Creating a Connection

The SDP transactions give you the parameters you need to know to establish a connection to a remote service. And, in fact, you’ve already seen the command to establish a connection: the BTCONNECT ioctl. We used it to establish an SDP connection. But this time, you’ll be connecting to a different protocol to access the service—which protocol depends on the particular service and what it’s ProtocolDescriptorList indicated.

Here’s an example of establishing an RFCOMM connection.

image

The CREATE_RFCOMM_ID macro is similar to the CREATE_SDP_ID macro. You can find it in the rfcomm.h header.

The line parameter should match the minor number of the TTY you intend to use for data transfers.

The server_channel parameter should match the value obtained from the ProtocolDescriptorList you get during the SDP session. See the SDP chapter of the Bluetooth Core Specification for an explanation.

Accepting a Connection

Remember the caveat about not being able to register services with RFCOMM? Well, that makes accepting a connection random luck. It could be done better, and maybe in the future it will be, so I’ll start by explaining how I believe connection acceptance should work. At the moment, the protocol stack has many compromises, and you’ll have to use it as is, so I’ll go on to explain how connection acceptance works now.

Understanding the Way It Should Work

When you register a service with SDP, and you provide a parameter in the RFCOMM Protocol Descriptor, that parameter is supposed to identify the server channel your application will be listening on. The remote client gets this value and uses it to request a connection to your service. When the RFCOMM driver sees a connection come in on that channel number, it should make sure that the correct server application gets it.

Understanding the Way It Does Work

The problem with the OpenBT stack is that there is no way for the RFCOMM driver to map a server channel to a session on the side receiving the connection request (everything works fine on the side initiating the connection—it just associates the connection with the session indexed by the line number).

Instead, when the RFCOMM driver gets a connection request it looks for the first available TTY, starting with minor number 0, and associates the connection with that session.

This is why btd works. It doesn’t really matter which server channel the client requests as long as it is a legal value (even numbers 2 through 60). The first connection on the server side will go to the session for ttyBT0—which is what btd, by default, passes to PPP when it spawns it.

In other words, the only way to make sure the correct server accepts the connection is to carefully control the order in which connections are made. For a shipping product with more than one server application, this would be totally unacceptable. On the other hand, the client side works fine. So, if a product is shipping with only client applications, then this problem won’t be an issue.

Transferring Data

Since the Bluetooth driver is just another TTY driver, transferring data is as simple as reading and writing from a file or any other device. You can find any number of books discussing I/O in C for Unix clones, so I’ll just provide an example showing an echo application.

Don’t forget that Bluetooth devices are TTY devices and by default they are not raw. Remember how we had to set up the serial device so that it wouldn’t interfere with a binary data stream? The same thing applies to the Bluetooth data devices. If your application is going to use read and write calls on a Bluetooth device to transfer binary data, then follow the earlier examples used on the serial device to make it raw.

image

This loop will read and echo data from our RFCOMM channel as long as it remains open. The call to read will block until data becomes available, the channel closes, or an error occurs. If, and only if, some data becomes available, then read will copy as much as it has or will fit into the buffer and return the number of bytes it put in the buffer. If the channel closes, read returns 0. If an error occurs, read returns a negative error number.

The write will queue up the data for transmission. Its semantics are similar to read. Note that this is not a perfectly reliable echo routine since it just assumes that all the bytes went out okay, but it shows the basics of I/O.

Disconnecting

Disconnecting always takes two steps: a Bluetooth disconnect and a system call to close. At most, only one side of the connection needs to execute a disconnect, and in cases where two devices go out of range, the Bluetooth stack cleans up the connection automatically. But your application will always need to do a close after a disconnection occurs. Refer to Figure 6.3 to see the state machine.

If your client application succeeds in making a connection, then it’s important to disconnect before exiting. If you don’t, then the Bluetooth driver won’t let anyone else use the line associated with the connection until someone reinitializes the stack with a BTSHUTDOWN or BTINIT ioctl call. Note that the Bluetooth driver will not automatically disconnect a line if the application closes the file descriptor or exits. You have to explicitly tell it to disconnect.

You close a connection with the BTDISCONNECT ioctl call. This call takes a parameter of type bt_connect. If you like, you can use the same one you passed in to the BTCONNECT ioctl.

ioctl(bt_fd, BTDISCONNECT, &con);

Even after doing a BTDISCONNECT, no other process can use the line associated with your device file until your application either explicitly calls close or exits. So, if you disconnect the line but don’t close the file descriptor, other applications will get EBUSY if they try to open that device file.

An application can always tell when the session disconnects from below. An RFCOMM link can disconnect if it or any layer below it disconnects, or if the remote peer goes out of range. In all these cases, the Bluetooth driver will do a hang-up on the upper TTY. This means that any time your application does a select, read, or write on the file descriptor, these system calls will return a negative value. If it is blocked on one of these calls, it will return immediately.

When this happens, your device file descriptor is pretty much out of commission. You won’t be able to do anything else with it until you close and reopen it. In this case, there’s no need to do a BTDISCONNECT ioctl call. It will just return an error since the connection doesn’t exist any more.

To summarize, when an application wants to end a session, it should call BTDISCONNECT followed by close. If an application detects a disconnection during a session, it should only call close.

Controlling a Bluetooth Device

The following list covers everything a Bluetooth application can do:

image Transferring data

image Establishing connections

image Controlling Bluetooth features

Not all applications will do all three things. For example, PPP transfers data over an RFCOMM TTY, but it knows nothing about establishing the connection it uses. In the previous section, we covered the first two items on this list. In this section, we’ll talk about controlling features of the Bluetooth device itself. We’ll see the differences between applications that use the stack and applications that control the stack, we’ll learn what things an application can control, and we’ll cover the basic scenarios that a controlling application must be able to deal with.

Distinguishing between Control and Data Applications

PPP uses the Bluetooth stack without knowing it. It requires a TTY interface. It relies on another application to set up the connection for it. For example, we saw how to use the btd application to set up the connection and then spawn PPP. Of the three items on our list, an application can do any combination of one or more of those things by itself, and cooperate with other applications to provide any capabilities it doesn’t do.

We already saw that the OpenBT project does not come with a stack manager. The btd application provides some features of a stack manager, but you’ll probably need to either extend it or write your own application that gives you a broader set of features.

In this section, let’s talk about designing our own hypothetical stack manager. On a desktop PC, this application might provide an interface for the user to monitor and control the Bluetooth device. In an embedded device, this application may provide hooks for other applications like power management, or a control panel driver to affect the Bluetooth driver.

Using ioctls to Control the Device

The first thing we should consider is what exactly an application can monitor and control. As with any other device driver, an application uses ioctl calls to perform control of the Bluetooth driver. Some ioctl calls are strictly informational and provide a way to monitor certain parameters of the Bluetooth driver.

Table 6.4 provides a summary of the ioctl calls currently supported by the OpenBT Bluetooth driver. Although you should always program to an interface and not an implementation, this advice assumes that the interfaces are stable and well documented! Currently, the only documentation on these ioctls is the source code. You can find the implementation for all of these calls in the linux/drivers/char/bluetooth/bluetooth.c file in the OpenBT source tree. Some of these are ioctls we’ve already seen in previous sections. I include them here just to give you a complete reference.

Table 6.4

Summary of OpenBT ioctls

image

image

Covering Basic Scenarios

Now that we know what our stack manager can do, what should it do? What features should it provide? Let’s consider the bare minimum. You can always add more to fit your needs. One basic assumption of our design is that the stack manager is responsible for the parameters that affect the entire driver or the hardware. In other words, a bare-bones stack manager won’t concern itself with establishing RFCOMM connections or transferring data.

As a bare minimum, the stack manager should initialize and shut down the stack at the proper times. It should detect link loss and cleanup if necessary. It would also be helpful if it kept tabs on remote devices coming in and out of the vicinity.

Example: Startup

In previous sections, we’ve seen examples of how to initialize the stack and to set it up over a lower TTY like the serial driver so that it can talk to hardware. These steps will always be necessary at some point. For an embedded solution, the Bluetooth hardware might be on board, interfacing with the CPU via a UART or some other bus. In these cases, you might have to provide your own TTY driver over a custom hardware interface. Remember, the Bluetooth driver relies on the ability to use a line discipline in order to communicate with the hardware driver. Only TTY driver’s use line disciplines, so the hardware driver must be a TTY.

But when should your stack manager start up the driver? It depends on the application. You can start it automatically when the application runs, or you can wait for a command from a User Interface (UI), or a signal from another process, and so on.

Probably the simplest thing to do on an embedded device is to start the stack on system bootup. You can do this by having the init process automatically start your stack manager from /etc/rc.local or whatever startup script you use for your configuration.

Example: Link Loss

There really isn’t any way for a central stack manager to detect a link loss. When a link with another device goes down, the Host Controller sends the host a sequence of disconnection event notices for each handle on the link. The Bluetooth driver processes these events by disconnecting all sessions on that link. Any processes using the TTYs for these sessions can detect a hang-up. But a central stack manager won’t necessarily get any kind of notification if it’s not using one of those TTYs.

Is this important? It could be if the stack manager kept local cached data about link status or peers. In that case, it would be nice to get notification so that it could clean the caches. But as it is, any active processes using the links for data will be notified. If a stack manager worked in the mode of establishing connections and then spawning applications to use them (this is how btd works with PPP), then it can determine when the process terminates on a hang-up using normal Linux process handling.

The following example illustrates this model.

image

The do_hci_inquiry() function and its friends would do what their names imply (the previous section illustrated code for implementing these kinds of functions). Once a connection is ready, the stack manager spawns a child process to use the connected TTY, then it waits for the child to exit. When the child exits, the stack manager makes sure the session is disconnected and then repeats the process.

If the link goes down at any point prior to the connection being made, one of the functions will fail and we’ll go back to try again. If the link goes down after the connection is made, the child process will exit when it detects the hung-up condition of the TTY (actually, this depends on the behavior of the child application, but most legacy applications that use TTYs will exit by default when they can’t use the TTY anymore). The do_disconnect is benign if the connection was already severed, but it makes sure the connection is cleaned up in case the child exited for a reason other than a TTY hang-up.

Note that a stack manager could handle a whole set of child applications like this, where each application is kept in a structure associating it with the relevant info needed to do SDP queries for the services it likes.

Example: User-Initiated and Automated Shutdown

If your stack management application has a user interface, then it can give the user the option of starting up or shutting down the driver. Alternatively, it might provide a means for other processes (like a power management service) to initiate a shutdown or startup via an IPC (InterProcess Communication) mechanism.

This example shows how a stack manager might install a signal handler to shut down or start up the stack based on requests from other processes.

image

This example assumes that if a user or another process wants to shut down the stack or bring it back up, then they will send the stack manager a SIGUSR1. Other forms of IPC might be more pertinent in different cases. The BTSHUTDOWN and BTINITSTACK ioctls take care of all the gritty details, shutting down connections, hanging up TTYs, flushing buffers, and so on.

Example: Idle Operation

Stack management applications can keep tabs on what other remote devices are in the area by doing periodic inquiries and keeping the results cached locally. You could provide an API for other applications to access this cache so that they don’t have to do their own inquiries. You could even keep a cache of remote SDP databases for devices in range.

This example shows how a stack manager might maintain a remote BD ADDR cache. You could extend this example to keep other information about remote devices in the local cache. It polls a local socket for requests from local processes to retrieve the cache. You extend this by providing a functional API to handle IPC with the stack manager daemon.

image

This is just a simple example. It uses the HCIINQUIRY command (see previous sections) with one of our wrapper structs for the inquiry results. It also has a buffer for keeping the results of HCI inquiries. Every so often it executes an HCI inquiry request to see what remote units are in the vicinity and puts their BD ADDRs in the cache.

The do_listen_for_cache_requests_with_timeout() could implement any form of IPC you like to field requests from other processes for the latest inquiry results. Every once in a while the process stops listening for requests and refreshes the cache.

The usefulness of something like this depends on how many processes are potentially doing their own HCI inquiries. But you could extend the idea to cover more expensive operations like searching remote SDP databases. Also, since we won’t automatically receive notice when another device modifies its SDP database, the process could periodically update its cache of another device’s SDP database.

Summary

The publicly available Bluetooth stacks for Linux are limited in number. As of this writing, the only two released implementations are IBM’s BlueDrekar and the OpenBT project. BlueDrekar has some nice features, looks pretty complete, and is freely available for download in binary form for x86 platforms running 2.2.x kernels. OpenBT is an open source project with support for most stack protocols and features and may work well enough for embedded devices. It has been ported to a variety of processors and can be cross-compiled, but it is still early in its development and not a fully-featured implementation. The focus of the discussion and examples is on OpenBT in this chapter because it is open source and may someday be a part of the standard Linux distribution as a stable implementation.

The OpenBT stack provides a loadable kernel module, which implements a TTY driver. It currently supports six data TTYs for RFCOMM connections and one control TTY for managing the driver. The driver internally manages RFCOMM connections with a session state machine. Applications use ioctl calls to establish the RFCOMM connection. Once an RFCOMM connection exists on a session, any application can use the TTY for that session, just like any other TTY device.

The OpenBT source tree comes with some applications that you can use as examples or starting points for derived works. The entire source is released under a modified form of the GPL, so if you create derived works that are used to implement Bluetooth operations, then these derived works will fall under the same license. The btd application provides a quick way to get network connections working over a Bluetooth link via PPP over RFCOMM. The sdp_server daemon will handle SDP requests from other devices.

Connecting to a Bluetooth device takes several steps. If your application functions as a stack manager, then it must first stack the Bluetooth driver over an underlying hardware TTY driver like serial or USB. Next, it must use a sequence of ioctl calls to initialize the stack, discover remote devices, and browse remote SDP databases to find services and connection parameters. Once an application has identified a remote service to connect to, it uses an ioctl call to establish an RFCOMM connection session. At that point, it or any other application may use the corresponding data TTY for data transfers. When the RFCOMM session disconnects, the driver performs a hang-up on the data TTY, thus signaling the end of the session.

Applications can do three things with the Bluetooth driver: transfer data, manage individual connections, and manage the overall driver. Not all applications need to do all three. Legacy applications (like PPP) that just use a TTY require another application to set up the connection and perform stack management for them. Developers may want to provide a stack management process for their system, which handles scenarios like link loss, system shutdown requests, and caching remote device data.

Solutions Fast Track

Assessing Linux Bluetooth Protocol Stacks

image The standard kernel source tree only recently accepted the Bluez Bluetooth stack, but it may not yet possess all the features some application developers require. It requires Linux 2.4.4 or greater.

image IBM’s BlueDrekar is a nice-looking implementation distributed in binary form for x86 platforms running 2.2.x. Source is not freely available to the general public.

image The OpenBT project is a not-as-nice open source project that works for most things an embedded developer would want. Source is available and has been used on x86, ARM9, ARM7, MIPS, and PowerPCs.

Understanding the Linux Bluetooth Driver

image The OpenBT stack implements TTY drivers for RFCOMM, SDP, and stack control.

image The Bluetooth driver must be stacked over a lower-layer hardware driver that implements a TTY.

image Any legacy application that uses a TTY can use RFCOMM once another application sets up the underlying RFCOMM connection.

image SDP, connection setup, and stack control are accomplished with ioctl calls.

image No interface exists for SCO, or L2CAP, although ioctls are available to support most HCI commands.

Using Open Source Development Applications

image The OpenBT source tree comes with some applications: btd/btduser, sdp_server, and BluetoothPN.

image The difference between btd and btduser is that btd is meant to work with the kernel mode Bluetooth driver while btduser works with the user mode Bluetooth driver. Many people prefer btduser since it is less prone to lock up your system if things go badly. However, the OpenBT developers do not support it as well as btd.

image The sdp_server application provides you with an SDP database server daemon. Once you’ve installed the Bluetooth driver, you can start this daemon and it will automatically receive and respond to SDP queries from remote devices.

image This application provides a GUI that displays the SDP database on a remote device. It provides some examples of how to make SDP requests and process their results.

image The quickest, most useful way to establish and exploit a Bluetooth connection from Linux is to use the standard GNU network applications over PPP. And the easiest way to do that is with the btd application.

Connecting to a Bluetooth Device

image An application manager must set up the driver stack over the hardware TTY and initialize the Bluetooth driver. This can be any application; the OpenBT source tree does not provide a general stack manager.

image Client applications must obtain the Bluetooth Device address of the remote device and—for RFCOMM connections—the channel number of the remote service in order to establish a connection.

image Once a connection is established, any application can use the TTY associated with the connection for data transfer.

image The driver indicates a disconnection event with a hang-up of the associated TTY.

Controlling a Bluetooth Device

image Use ioctl calls to control the device and get information about device status.

image Use /proc/bt_status to get information about device status.

image A stack manager must be able to deal with link loss and system shutdown requests. It should provide an interface for users as well as other processes like power management to signal shutdown requests.

Frequently Asked Questions

The following Frequently Asked Questions, answered by the authors of this book, are designed to both measure your understanding of the concepts presented in this chapter and to assist you with real-life implementation of these concepts. To have your questions about this chapter answered by the author, browse to www.syngress.com/solutions and click on the “Ask the Author” form.

Q: Is the OpenBT stack really ready for prime time on an embedded Linux device?

A: It’s the closest thing to it that has freely available source. You can ask IBM about licensing and distribution costs for BlueDrekar, but it’s hard to beat the price/performance ratio of OpenBT. If you’re faced with the prospect of leveraging OpenBT or developing your own Bluetooth stack … well, you know your project schedule better than I do!

Q: How can I get the latest source for OpenBT?

A: Go to the OpenBT Web site (www.sourceforge.net/projects/OpenBT) and look for the instructions on accessing the CVS repository. This will give you the very latest, bleeding-edge code. Occasionally new tarballs appear for download on this site as well. You might also want to subscribe to the mailing list to keep in touch with progress on this front.

Q: Can a Java application use the Linux Bluetooth stack?

A: Any language that provides some kind of access to the standard I/O system calls (read, write, and ioctl) can use the OpenBT.

Q: When I try to “insmod bt.o” I get an error about missing kernel symbols. What is this and how do I fix it?

A: This happens because the kernel which bt.o was compiled against does not match the kernel you are trying to load it into. When you build bt.o, make sure you provide the INCLUDE_DIR=<path> argument to make, indicating the path to your target kernel’s include files. Also, if your kernel has symbol versioning configured, then make sure linux/include/modversions.h is being included in the build process.

Q: I just want to use L2CAP and HCI, not RFCOMM. Is there an interface I can use to access these layers?

A: Not with OpenBT. However, if you aren’t limited to using a Linux kernel version earlier than 2.4.4 then Bluez is probably what you want. The Bluez Bluetooth stack has been distributed with kernel source since kernel version 2.4.6; the latest is available from bluez.sourceforge.net.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.1.136