Bluetooth in Linux

Bluetooth Architecture

Bluetooth is both a hardware-based radio system and a software stack.
The heart of this specification is the protocol stack, which is used to define how Bluetooth works. The Bluetooth protocol stack is a set of layered programs. Each layer in a protocol stack talks to the layer above it and to the layer below it.
Think of Bluetooth as having two well-defined layers of functionality in the stack:
  • the upper level software stack
  • The upper layers of the Bluetooth protocol stack can be implemented on the host system.
  • the lower level hardware-based radio system

The illustration shows the layers from the Bluetooth specification shaded in gray:
There are two routes for audio:
  • a direct link between the baseband and the application layer
  • through the HCI(UART, USB,...)
The most chip/chip sets provide direct access to the baseband. Some, however, do not support audio across HCI. A common block diagram of the Bluetooth protocol stack:
  • the protocol layer
    • Logical Link Control and Adaptation Protocol (L2CAP)
    • L2CAP supports several important protocol requirements:
      • Protocol Multiplexing
      • Segmentation & Reassembly
      • Quality of Service
      • Groups
      • The L2CAP group abstraction permits implementations to efficiently map protocol groups on to piconets.
    • SDP
    • SDP provides a means for applications to query services and their characteristics.
    • RFCOMM(Radio frequency communication) protocol
    • The RFCOMM protocol provides emulation of serial ports over L2CAP. RFCOMM provides transport capabilities for upper-level services.
      Bluetooth Framework natively supports Bluetooth Classic RFCOMM protocol in client and server modes. That means that Bluetooth Framework can act as RFCOMM client (connects to other Bluetooth enabled devices) or as RFCOMM server (accepts connection from other Bluetooth enabled devices). That also means that you do not need to create and/or use any virtual COM ports to be able to communicate with Bluetooth enabled devices.
    • Bluetooth Network Encapsulation Protocol (BNEP)
    • BNEP encapsulates packets from various networking protocols, and the packets are transported directly over L2CAP. This makes Bluetooth-enabled devices have the ability to form networks and exchange information.
    • Telephony Control Protocol Specification, Binary (TCS binary)
    • This defines the call control signaling for establishment of voice and data calls between Bluetooth devices.
  • HCI Interface
  • The HCI is a standardized Bluetooth interface for sending commands, receiving events, and for sending and receiving data. It is typically realized as a serial interface, using either RS232 or USB communication devices.
    The HCI is used to bridge the Host and Controller devices. Commands and Events can either be specified, or can be vendor specific for extensibility.
    The HCI supports four types of packets and difined their formats:
    • Command Packet
    • Asynchronous Data Packet
    • Synchronous Data Packet
    • Event Packet
    It is a single standard interface for accessing the Bluetooth baseband capabilities, the hardware status, and control registers.
  • the radio system level
    • Bluetooth radio (layer)
    • the lowest defined layer of the Bluetooth specification. It defines the requirements of the Bluetooth transceiver device operating in the 2.4-GHz ISM band.
    • baseband and link control layer
    • The baseband handles channel processing and timing, and the link control handles the channel access control. There are two different kinds of physical links: synchronous connection oriented (SCO) and asynchronous connectionless (ACL). An ACL link carries data packets, whereas an SCO link supports real-time audio traffic.
    • Audio data
    • Audio is uniquely treated in Bluetooth communication. Audio data is typically routed directly to and from the baseband layer over an SCO link. Of course, if a data channel is used (e.g., in VoIP applications), audio data will be transmitted over an ACL link.
    • Link Manager Protocol (LMP)
    • LMP is responsible for link setup and link configuration between Bluetooth devices, managing and negotiating the baseband packet sizes. The LMP manages the security aspects, such as authentication and encryption, by generating, exchanging, and checking link and encryption keys.

Bluetooth in Android

Overview

Android provides a default Bluetooth stack that supports both Classic Bluetooth and Bluetooth Low Energy. Using Bluetooth, Android devices can create personal area networks to send and receive data with nearby Bluetooth devices.
A Bluetooth application communicates with the Bluetooth process through Binder. The Bluetooth process uses JNI to communicate with the Bluetooth stack.
  • Application framework
  • At the application framework level is application code, which uses the android.bluetooth APIs to interact with the Bluetooth hardware. Internally, this code calls the Bluetooth server process through the Binder IPC mechanism.
  • Bluetooth system service
  • The Bluetooth system service, located in packages/apps/Bluetooth, is packaged as an Android app and implements the Bluetooth services and profiles at the Android framework layer. This app calls into the native Bluetooth stack via JNI.
  • JNI
  • The JNI code associated with android.bluetooth is located in packages/apps/Bluetooth/jni. The JNI code calls into the Bluetooth stack when certain Bluetooth operations occur, such as when devices are discovered.
  • Bluetooth stack
  • The default Bluetooth stack is provided in AOSP and is located in system/bt. The stack implements the generic Bluetooth HAL and customizes it with extensions and configuration changes.
  • Vendor implementation
  • Vendor devices interact with the Bluetooth stack using the Hardware Interface Design Language (HIDL).
  • HIDL
  • HIDL defines the interface between the Bluetooth stack and the vendor implementation. To generate the Bluetooth HIDL files, pass the Bluetooth interface files into the HIDL generation tool. The interface files are located in hardware/interfaces/bluetooth.
The Bluetooth HAL is located in /hardware/libhardware/include/hardware/bluetooth.h. The bluetooth.h file contains the basic interface for the Bluetooth stack, and you must implement its functions.

Bluetooth Services

Linux Without Wires: The Basics of Bluetooth


Bluetooth technology is used for short range (1 metre to 100 metres) communication. It is the most widespread wireless technology, and is divided into classes according to the power and communication range.

Bluetooth is a packet based protocol and has a master-slave structure. It works between the 2400MHz and the 2483.5MHz frequency range, and uses the frequency-hopping spread spectrum. Bluetooth divides data into packets and transmits them on one of the 79 designated channels. Each channel has a 1MHz bandwidth. Version 4.0 of Bluetooth has 40 channels, and each channel uses a 2MHz bandwidth.


Bluetooth stack


There are various types of Bluetooth stacks in the market like BlueZ, Bluedroid, Widcomm, Bluecode+, BlueLet, BlueMagic, etc.


A Bluetooth stack is software used to implement the Bluetooth host protocol stack. Bluetooth stack implementation can be of two types:
  • The first is a general-purpose implementation that is usually for desktop computers, and it is flexible because additional Bluetooth profiles can be added through drivers.
  • The second is an embedded system implementation, which is for devices where resources are limited, like peripheral devices.

BlueZ


BlueZ is a general-purpose Bluetooth stack that is used to implement the Bluetooth host protocol stack for Linux. It is open source software and the official Linux Bluetooth stack. You can download this software from http://www.bluez.org/ in the development section.
BlueZ maps Bluetooth protocol layers to kernel modules, kernel threads, user space daemons, configuration tools, utilities and libraries.

The main components of BlueZ are:
  • bluetooth.ko
  • which contains core infrastructure of BlueZ. It exports sockets of the Bluetooth family – AF_BLUETOOTH. All BlueZ modules utilise its services.
  • Bluetooth HCI packets
  • are transported over UART or USB. The corresponding BlueZ HCI implementation is hci_uart.ko and hci_usb.ko.
  • The L2CAP layer of Bluetooth
  • which is responsible for segmentation, reassembly and protocol multiplexing, is implemented by l2cap.ko.
  • bnep.ko
  • TCP/IP applications can run over Bluetooth. This emulates an Ethernet port over the L2CAP layer. The kernel thread named kbnepd is responsible for BNEP connections.
  • rfcomm.ko
  • is responsible for running serial port applications like the terminal. This emulates serial ports over the L2CAP layer. The kernel thread named krfcommd is responsible for RFCOMM connections.
  • hidp.ko
  • implements the HID (human interface device) layer. The user mode daemon hidd allows BlueZ to handle input devices like Bluetooth mice.
  • sco.ko
  • implements the synchronous connection oriented (SCO) layer to handle audio. SCO connections do not specify a channel to connect to a remote host; only the host address is specified.

The Bluetooth protocol stack vs the OSI model


At the top of the stack reside various application environments called profiles. Since the radio, baseband and link manager are usually part of Bluetooth hardware, operating system support starts at the HCI layer:

The Bluetooth protocol stack is divided into the host and controller, and interfaced by the host controller interface (HCI) layer


Bluetooth host


It includes implementations of the core Bluetooth protocols: the Bluetooth stack (or host protocol stack) and the high-level layers of the Bluetooth architecture, such as APIs and profiles. It has various layers and protocols like L2CAP, RFCOMM, SDP, etc.

  • L2CAP(Logical Link Control and Adaptation Protocol)
    • The capability to multiplex data between different higher layer protocols; hence, various protocols like RFCOM, SDP, etc, can operate over it
    • Segmentation and reassembly
    • Quality of service
    • It supports a group abstraction, enabling implementation for mapping a group on to a piconet
  • RFCOMM (Radio Frequency Communication)
  • It is a serial port emulation protocol. It emulates RS-232 control and data signals over the Bluetooth baseband. It provides roughly the same service and reliability guarantees as TCP. Whereas TCP supports up to 65,535 open ports on a single machine, RFCOMM allows only 30. This has a significant impact on how to choose port numbers for server applications.
  • SDP (Service Discovery Protocol)
  • With the help of this protocol, devices can discover what services other nearby devices support and what parameters are required to connect with them. Each service is identified by a universally unique identifier (UUID).
  • Bluetooth profile
  • The Bluetooth profile is a specification that describes how devices must use Bluetooth protocols to implement a particular task, and this is the theoretical part of the Bluetooth architecture. For ex.,
    • HSP (headset profile)
    • This defines the procedure that supports the interoperability between a headset and a mobile device, like a cellular phone, that the headset controls through AT commands, and depends upon the serial port profile.
    • SPP (serial port profile)
    • This defines the requirements for virtual serial port connections over the RFCOMM.

HCI (Host Controller Interface)


Transfer data between the Bluetooth host and the Bluetooth controller.
It contains drivers which are abstracted to a small set of functions that send and receive commands, data packets and events.
There are four types of communication packet:
  • Command
  • These packets are generated from the host and are sent to the controller or Bluetooth adapter to control the adapter. They can be used to start a device enquiry, connect to a remote device, etc.
  • Event
  • These packets are sent from the Bluetooth adapter to the host. These packets are sent whenever an event occurs such as sending information about a local Bluetooth adapter, connecting to a remote device, etc.
  • ACL data
  • ACL data packets are encapsulated by the HCI layer and transport to the Bluetooth adapter. The bare ACL packet is transmitted over the air by the Bluetooth adapter .
  • Synchronous data
  • These packets are also encapsulated at the HCI layer and pass to the Bluetooth adapter.



Bluetooth programming in C with BlueZ


Development Setup

Before coding, you need the development files for linking to the BlueZ library. For this you have to install the libbluetooth-dev package:
sudo apt-get install libbluetooth-dev


Searching Bluetooth devices


BlueZ exposes socket APIs that are similar to network socket programming. If you are familiar with network programming in Linux, learning the BlueZ APIs will be simple.

The basic data structure used to specify a 6-byte Bluetooth device address is bdaddr_t. In BlueZ, every Bluetooth address is stored as the bdaddr_t structure

typedef struct {
 uint8_t b[6];
} __attribute__((packed)) bdaddr_t;

A simple program scan_bt_devices.c that searches nearby Bluetooth devices :

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

int main(int argc, char **argv)
{
    inquiry_info *ii = NULL;
    int max_rsp, num_rsp;
    int dev_id, sock, len, flags;
    int i;
    char addr[19] = { 0 };
    char name[248] = { 0 };

    dev_id = hci_get_route(NULL);
    sock = hci_open_dev( dev_id );
    if (dev_id < 0 || sock < 0) {
        perror("opening socket");
        exit(1);
    }

    len  = 8;
    max_rsp = 255;
    flags = IREQ_CACHE_FLUSH;
    ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
    
    num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);
    if( num_rsp < 0 ) perror("hci_inquiry");

    for (i = 0; i < num_rsp; i++) {
        ba2str(&(ii+i)->bdaddr, addr);
        memset(name, 0, sizeof(name));
        if (hci_read_remote_name(sock, &(ii+i)->bdaddr, sizeof(name), name, 0) < 0)
            strcpy(name, "[unknown]");
        printf("%s  %s\n", addr, name);
    }

    free( ii );
    close( sock );
    return 0;
}

  • The header files required
  • These headers files are under in the "/usr/include/blutooth":
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/hci.h>
    #include <bluetooth/hci_lib.h>
    
  • Getting the Bluetooth adapter ID
  • The application program must specify which adapter to use when allocating system resources. Usually, there is only one adapter, so passing NULL to hci_get_route will retrieve the ID of the first available Bluetooth adapter:
    dev_id = hci_get_route (NULL);.
    
  • Opening a file handle to the HCI
  • Open a socket handle to the host controller interface so that we can pass commands to the Bluetooth controller.
    int sock = hci_open_dev(dev_id);
    
  • Prepare device's information memory
  • ii = (inquiry_info*) malloc(max_rsp * sizeof(inquiry_info));
    
    The inquiry_info structure is defined as
    typedef struct {
        bdaddr_t    bdaddr;
        uint8_t     pscan_rep_mode;
        uint8_t     pscan_period_mode;
        uint8_t     pscan_mode;
        uint8_t     dev_class[3];
        uint16_t    clock_offset;
    } __attribute__ ((packed)) inquiry_info;
    
    • For the most part, only the first entry - the bdaddr field, which gives the address of the detected device - is of any use.
    • Occasionally, there may be a use for the dev_class field, which gives information about the type of device detected (i.e. if it's a printer, phone, desktop computer, etc.) and is described in the Bluetooth Assigned Number.
    • The rest of the fields are used for low level communication, and are not useful for most purposes.
  • scan for Bluetooth devices nearby
  • num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);
    
    To get enough time for enquiry, use
    • 8 for len, that is 10.24 seconds
    • 255 for max_rsp
    If you want to flush the cache of previously detected devices, set flags to IREQ_CACHE_FLUSH; otherwise, use 0 and hci_inquiry will also return those Bluetooth devices that are out of range, but still in cache.
  • get the names of the devices
  • hci_read_remote_name(sock, &(ii+i)->bdaddr, sizeof(name), name, 0)
    
    hci_read_remote_name() tries timeout(=0) milliseconds to use the socket handle for querying the user-friendly name of the device with the help of the Bluetooth address bdaddr, and store the friendly name into name.

Build the code,
gcc scan_bt_devices.c -lbluetooth
Make sure your Bluetooth module is on before executing the built code.
rfkill lists, enabling and disabling wireless devices(Wi, Bluetooth):
  • list [id|type ...]
  • List the current state of all available devices. It is a good idea to check with list command id or type scope is appropriate before setting block or unblock. Special all type string will match everything. Use of multiple id or type arguments is supported.
  • block id|type [...]
  • Disable the corresponding device.
  • unblock id|type [...]
  • Enable the corresponding device. If the device is hard-blocked, for example via a hardware switch, it will remain unavailable though it is now soft-unblocked.

Essential Bluetooth Programming Concepts


  • Devices initiating an outgoing connection need to choose a target device and a transport protocol, before establishing a connection and transferring data.
  • Devices establishing an incoming connection need to choose a transport protocol, and then listen before accepting a connection and transferring data.

Choosing a Target Device


Every Bluetooth chip ever manufactured is imprinted with a globally unique 48-bit address, referred to as the Bluetooth address or device address.

The clients search nearby Bluetooth devices and checking the name of each device to find the target device and its associated device address.
Bluetooth devices will almost always have a user-friendly name, this needs to be translated to the device address in real communication.
Device discovery(inquiry) is the process of searching for and detecting nearby Bluetooth devices.

The device discovery process will usually take an average of 5 seconds before the phone detects the presence of the laptop, and it sometimes can take upward of 10–15 seconds.

All Bluetooth devices have two options that determine whether or not the device responds to device inquiries and
connection attempts:
  • Inquiry Scan
  • A device is in discoverable mode when inquiry scan is on.
  • Page Scan

Choosing a Transport Protocol


There are 4 major transport protocols and only 2 are likely to be used.
  • RFCOMM (Radio Frequency Communications)
  • RFCOMM protocol is a reliable streams-based protocol as TCP. Whereas TCP supports up to 65,535 open ports on a single machine, RFCOMM allows only 30.
  • L2CAP l (Logical Link Control and Adaption Protoco)
  • L2CAP is also a widely used transport protocol that is used when the streaming nature of RFCOMM isn’t needed.
  • ACL ( Asynchronous Connection-oriented Logical)
  • SCO (Synchronous Connection-Oriented)



Port Numbers


  • L2CAP
  • ports are called Protocol Service Multiplexers (PSM), and can take on odd-numbered values between 1 and 32,767. L2CAP reserves ports 1–1023 for standardized usage. SDP uses port 1, and RFCOMM connections are multiplexed on L2CAP port 3.
  • RFCOMM
  • channels 1–30 are available for use. RFCOMM does not have any reserved ports


Service Discovery Protocol


Every Bluetooth device maintains an SDP server listening on a well-known port number.
When a server application starts up, it registers a description of itself and a port number with the SDP server on the local device.
What the SDP server transmits to inquiring clients is referred to as a service record, also an SDP record.
The service record consists of a list of attribute/value pairs.
Except for the port number, the two most important attributes in a service record are the Service ID and the Service Class ID List.

Bluetooth assigns every single service a unique identifier, and assume that the client already knows the unique ID of the service it’s looking for.
The Bluetooth designers chose to allow every service to have a list of service classes that the service provides. So while a single service can have only one Service ID, it can have many Service Class IDs.

IETF has a standard method for developers to independently come up with their own 128-bit Universally Unique Identifiers (UUID).
The Bluetooth® Service Discovery Protocol (SDP) specification defines a way to represent a range of UUIDs (which are nominally 128 bits) in a "shorter form": 32 bits (denoted uuid32), or, 16 bits (denoted uuid16).
The Base UUID: is used for calculating 128-bit UUIDs from “short UUIDs” (uuid16 and uuid32) :
BASE_UUID = 00000000-0000-1000-8000-00805F9B34FB

 128 bit UUID = (uuid16 or uuid32) << 96 + Bluetooth Base UUID
Bluetooth defines several reserved attribute IDs that always have a special meaning, and the rest can be used arbitrarily. Some of the more common reserved attributes are:
  • Service Class ID List
  • A list of service class UUIDs that the service provides. This is the only mandatory attribute that must always appear in a service record.
    NameUUIDSpecification Allowed Usage
    ServiceDiscoveryServerServiceClassID0x1000 Bluetooth Core Specification Service Class
    BrowseGroupDescriptorServiceClassID0x1001 Bluetooth Core Specification Service Class
    SerialPort0x1101 Serial Port Profile (SPP)
    ...
    (Max value 0xFFFF)
  • Service ID
  • A single UUID identifying the specific service.
  • Service Name
  • A text string containing the name of the service.
  • Service Description
  • A text string describing the service provided.
  • Protocol Descriptor List
  • A list of protocols and port numbers used by the service.
  • Protocol ID
  • NameUUIDProtocol Specification
    SDP0x0001 Bluetooth Core Specification
    UDP0x0002 [NO USE BY PROFILES]
    RFCOMM 0x0003 RFCOMM with TS 07.10
    TCP 0x004 [NO USE BY PROFILES]
    ...
    (Max value 0xFFFF)
  • Profile Descriptor List
  • A list of Bluetooth profile descriptor that the service complies with. Each descriptor consists of a UUID and a version number.
  • Service Record Handle
  • A single unsigned integer that uniquely identifies the record within the device.

Communicating via Sockets

Given the address of a device, the transport protocol, and the port, Bluetooth programming is essentially the socket programming. In Bluetooth programming, we’ll almost always be creating either L2CAP or RFCOMM sockets.

Useful Things to Know about Bluetooth

Both 802.11 and Bluetooth use the 2.45-GHz band for communication, but Blue-tooth divides the band into 79 channels, whereas 802.11 divides the band into 14 channels. Another major difference is that a single 802.11 network typically picks a channel and stays with it, whereas devices on a Bluetooth piconet change radio frequencies very rapidly. An actively communicating Bluetooth device changes channels every 625 μs (1600 times per second). It tries to do this in a random order so that no one channel is used much more than any other channel. Supposedly, all this hopping around makes Bluetooth more robust to interference from nearby sources of evil radio waves, and allows for many Blue-tooth networks to coexist in the same place.

Bluetooth Networks

Two or more Bluetooth devices that are communicating with each other and using the same channel-hopping configuration to form a Bluetooth piconet.
  • A piconet can have up to eight devices in total.
  • One device on every piconet is designated as the master which tells other slave devices which frequencies to be used
  • The master of a piconet essentially informs every device when to transmit and when to keep quiet. Every device will always have its turn to transmit.
  • All of this network formation and master–slave role selection is handled automatically by Blue-tooth hardware and low-level device drivers.

Security

Two Bluetooth devices can conduct an authentication procedure, where they verify their respective identities. Once authenticated, they have the option of encrypting all data packets they exchange. The first time two devices go through authentication or encryption is known as the pairing procedure:
  • First, a common PIN must be given to the devices
  • the PIN is never transmitted over the air.
  • Next, each device uses the PIN to generate a link key.
  • The link key is saved on both devices, and is what’s actually used to encrypt data transmitted between the two.
  • Once two devices have been paired, applications on each device can request authentication or encryption as needed.
  • Encryption takes effect at a low level (the physical link level)
Most devices are without input capabilities, the Simple Pairing procedure is introduced since Bluetooth 2.1:
  • the PIN is automatically generated
  • the user is only prompted to accept or reject a pairing

Profiles

Bluetooth Profiles define standardized ways to perform high-level application tasks such as transferring files, playing music, using nearby printers, and so on. You can find the specification and all the details for that particular profile on the Bluetooth Web site

Host Controller Interface

The Host Controller Interface (HCI) defines how a computer (the Host) interacts and communicates with a local Bluetooth adapter (the Controller). All communications that occur between the two are encapsulated within HCI packets:
  • Command packet
  • This type of packet is sent from the host computer to the Bluetooth adapter, and is used to control the adapter.
  • Event packet
  • This type of packet is generated by the Bluetooth adapter and sent to the host computer whenever an event of interest occurs.
  • ACL(Asynchronous Connection-Less) data packet
  • Synchronous data packet

Limitations – Things Bluetooth Can’t Do

Few capabilities are not in Bluetooth:
  • Announce the presence of a device
  • Detect when a remote device is inquiring for nearby devices
  • Determine the Bluetooth address of an inquiring device
  • Distance to a remote Bluetooth device
  • Broadcast messages

Device Discovery Explained

Bluetooth splits the 2.4-GHz band into 79 channels, 32 in these are used for detecting nearby devices and establishing connections. An inquiring device transmits inquiry messages on these channels, and a discoverable Bluetooth device periodically listens for and replies to the inquiry messages.
  • Inquiring for Nearby Devices
  • The inquiring device divides the 32 channels into two sets, called train A and train B. The inquiring device transmits an inquiry message on each channel in train A. Once train A has been exhausted, the inquiring device repeats the process for train B. A single transmit/listen cycle for each channel takes 625 μs. Since there are 16 channels in each train, and each train is repeated 256 times, the inquiring device spends 625 μs × 16 × 256 = 2.56 s in each train before switching. By default, a single device inquiry switches trains four times for a total device inquiry lasting 2.56 × 4 = 10.24 s.
  • Listening for Incoming Connections/Inquiries
  • A discoverable Bluetooth device periodically enters the inquiry scan substate. Two parameters, the inquiry scan interval and the inquiry scan window, determine how frequently a device enters the inquiry scan substate and how much time the device spends there, respectively.

C Programming With GNU/Linux

Choosing a Communication Target

simplescan.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>


int main ( int argc , char * * argv )
{
    inquiry_info *devices = NULL;
    int max_rsp, num_rsp;
    int adapter_id , sock , len , flags;
    int i;
    char addr[19] = { 0 };
    char name[248] = { 0 };

    adapter_id = hci_get_route(NULL);
    sock = hci_open_dev( adapter_id );
    if (adapter_id < 0 || sock < 0) {
        perror("opening socket");
        exit(1);
    }

    len  = 8;
    max_rsp = 255;
    flags = IREQ_CACHE_FLUSH;
    devices = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
    
    num_rsp = hci_inquiry(adapter_id, len, max_rsp, NULL, &devices, flags);
    if( num_rsp < 0 ) perror("hci_inquiry");

    for (i = 0; i < num_rsp; i++) {
        ba2str(&(devices+i)->bdaddr, addr);
        memset(name, 0, sizeof(name));
        if (hci_read_remote_name(sock, &(devices+i)->bdaddr, sizeof(name), name, 0) < 0)
            strcpy(name, "[unknown]");
        printf("%s  %s\n", addr, name);
    }

    free( devices );
    close( sock );
    return 0;
}
Build the code:
gcc −o simplescan simplescan.c -lbluetooth
  • Representing Bluetooth Addresses
  • typedef struct {
      uint8_t b[6]; 
    } __attribute__((packed)) bdaddr_t ;
    
    The basic data structure used to specify a Bluetooth device address is bdaddr_t. Two convenience functions, str2ba() and ba2str() , can be used to convert between strings and bdaddr_t structures.
  • Choosing and Opening a Local Bluetooth Adapter
  • Local Bluetooth adapters are assigned identifying numbers starting with 0. Usually, there is only one adapter. Passing NULL to hci_get_route() will retrieve the number of the first available Bluetooth adapter. After the index of the adapter is decided, use hci_open_dev() to allocate resources to use this adapter. The result returned by hci_open_dev() is a handle to the resources.
  • Device Discovery
  • Bluetooth device discovery operation is initiated by the function hci_inquiry, which issues the inquiry signal and returns a list of devices that respond along with some of their basic information.
    int hci_inquiry(int dev_id, int len, int nrsp, const uint8_t *lap, inquiry_info **devs, long flags)
    
    The inquiry lasts for at most (1.28 * len) seconds, and at most max_rsp responding devices will be returned in the output parameter devs. The suggestion is to use max_rsp=255 for a standard 10.24-s inquiry. The devs parameter is an array of inquiry_info structures:
    typedef struct {
        bdaddr_t    bdaddr;
        uint8_t     pscan_rep_mode;
        uint8_t     pscan_period_mode;
        uint8_t     pscan_mode;
        uint8_t     dev_class[3];
        uint16_t    clock_offset;
    } __attribute__ ((packed)) inquiry_info;
    
    Only bdaddr and dev_class are useful for the application programming, remained fields are for low level programming. The final parameter, flags , indicates whether or not to use previously discovered device information(0) or to start afresh(IREQ CACHE FLUSH).
  • Name Lookup
  • int hci_read_remote_name(int hci_sock, const bdaddr_t *ba, int len, 
                             char *name, int timeout)
    
    This function attempts, for at most timeout milliseconds, to use the socket hci_sock in order to query the device with Bluetooth address addr for its user-friendly name. On success, it returns 0 and the first len bytes of the device’s user-friendly name stored in the supplied character array: name .
  • Error Handling

RFCOMM Sockets

rfcomm-server.c:

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>

int main(int argc, char **argv)
{
    struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
    char buf[1024] = { 0 };
    int s, client, bytes_read;
    socklen_t opt = sizeof(rem_addr);

    // allocate socket
    s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);

    // bind socket to port 1 of the first available 
    // local bluetooth adapter
    loc_addr.rc_family = AF_BLUETOOTH;
    loc_addr.rc_bdaddr = *BDADDR_ANY;
    loc_addr.rc_channel = (uint8_t) 1;
    bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));

    // put socket into listening mode
    listen(s, 1);

    // accept one connection
    client = accept(s, (struct sockaddr *)&rem_addr, &opt);

    ba2str( &rem_addr.rc_bdaddr, buf );
    fprintf(stderr, "accepted connection from %s\n", buf);
    memset(buf, 0, sizeof(buf));

    // read data from the client
    bytes_read = read(client, buf, sizeof(buf));
    if( bytes_read > 0 ) {
        printf("received [%s]\n", buf);
    }

    // close connection
    close(client);
    close(s);
    return 0;
}
rfcomm-client.c

#include <stdio.h>
#include <unistd.h>>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>

int main(int argc, char **argv)
{
    struct sockaddr_rc addr = { 0 };
    int s, status;
    char dest[18] = "01:23:45:67:89:AB";

    // allocate a socket
    s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);

    // set the connection parameters (who to connect to)
    addr.rc_family = AF_BLUETOOTH;
    addr.rc_channel = (uint8_t) 1;
    str2ba( dest, &addr.rc_bdaddr );

    // connect to server
    status = connect(s, (struct sockaddr *)&addr, sizeof(addr));

    // send a message
    if( status == 0 ) {
        status = write(s, "hello!", 6);
    }

    if( status < 0 ) perror("uh oh");

    close(s);
    return 0;
}

Addressing Structures

Like the struct sockaddr_in used in TCP/IP programming, the sockaddr_rc addressing structure is required.
struct sockaddr_rc {
 sa_family_t rc_family;
 bdaddr_t rc_bdaddr;
 uint8_t  rc_channel;
};
rc_bdaddr and rc_channel specify the Bluetooth address and port number. The rc_channel field of the socket addressing structure used to bind the socket is simply set to 0, and the kernel binds the socket to the first available port.

A note on byte ordering

Unlike network byte ordering, which uses a big-endian format, Bluetooth byte ordering is little-endian, where the least significant bytes are transmitted first. BlueZ provides four convenience functions to convert between host and Bluetooth byte orderings:
  • unsigned short int htobs( unsigned short int num );
  • unsigned short int btohs( unsigned short int num );
  • unsigned int htobl( unsigned int num );
  • unsigned int btohl( unsigned int num );

L2CAP Sockets

Using L2CAP sockets is quite similar to using RFCOMM sockets, with the major differences being in the
  • addressing structure
  • struct sockaddr_l2 {
        sa_family_t     l2_family;
        unsigned short  l2_psm;
        bdaddr_t        l2_bdaddr;
    };
    
    • l2_family
    • always be AF_BLUETOOTH
    • l2_psm
    • specifies the L2CAP port number to use
    • l2_bdaddr
    • the address of either a remote serveror or a local adapter.
    s = socket( AF_BLUETOOTH , SOCK_SEQPACKET , BTPROTO_L2CAP ) ;
    
    SOCK_SEQPACKET is used to indicate a socket with reliable datagram-oriented semantics where packets are delivered in the order sent.
  • the availability of a few more options to control
  • struct l2cap_options {
        uint16_t    omtu;
        uint16_t    imtu;
        uint16_t    flush_to;
        uint8_t     mode;
    };
    
    The omtu and imtu fields of the struct l2cap_options are used to specify the outgoing MTU and incoming MTU, respectively.
    • Maximum Transmission Unit
    • The default is 672 bytes. To change it:
      int set_l2cap_mtu( int sock, uint16_t mtu ) {
       struct l2cap_options opts;
          int optlen = sizeof(opts), err;
          err = getsockopt( s,  SOL_L2CAP,  L2CAP_OPTIONS, &opts, &optlen );
          if( ! err ) {
              opts.omtu = opts.imtu = mtu;
              err = setsockopt( s,  SOL_L2CAP,  L2CAP_OPTIONS, &opts, optlen );
          }
          return err;
      };
      
  • more ports

Service Discovery Protocol

The hard-coded port numbers is not a good long-term solution. Searching a specific device for a service also involves two steps:
  • searching a device for a service
  • 
    #include <bluetooth/bluetooth.h>
    #include &ltbluetooth/sdp.h>
    #include &ltbluetooth/sdp_lib.h>
    
    int main(int argc, char **argv)
    {
        uint8_t svc_uuid_int[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
            0, 0, 0xab, 0xcd };
        uuid_t svc_uuid;
        int err;
        bdaddr_t target;
        sdp_list_t *response_list = NULL, *search_list, *attrid_list;
        sdp_session_t *session = 0;
    
        str2ba( "01:23:45:67:89:AB", &target );
    
        // connect to the SDP server running on the remote machine
        session = sdp_connect( BDADDR_ANY, &target, SDP_RETRY_IF_BUSY );
    
        // specify the UUID of the server's service we're searching for
        sdp_uuid128_create( &svc_uuid, &svc_uuid_int );
        search_list = sdp_list_append( NULL, &svc_uuid );
    
        // specify service's attributes that we want the search for matching 
        uint32_t range = 0x0000ffff;
        attrid_list = sdp_list_append( NULL, &range );
    
        // get a list of service records that have UUID 0xabcd
        err = sdp_service_search_attr_req( session, search_list, \
                SDP_ATTR_REQ_RANGE, attrid_list, &response_list);
        
    
    • uuid_t
    • All Universally Unique Identifiers (UUIDs) are represented and manipulated as uuid t data types. We’ll often use three functions to create a 128-bit UUID:
      • uuid t *sdp_uuid128_create( uuid_t * uuid , const void * data ) ;
      • uuid t *sdp_uuid32_create( uuid_t * uuid , uint32_t data ) ;
      • uuid t *sdp_uuid16_create( uuid_t * uuid , uint16_t data ) ;
    • sdp_list_t
    • This is just a linked list of not predefined data structure for different usages.
      typedef struct sdp_list sdp_list t;
      struct sdp_list {
        sdp list_t * next ;
        void * data ;
      } ;
      
      This type of list is maintained by :
      • sdp_list_t *sdp_list_append(sdp_list_t *list, void *d);
      • sdp_list_t *sdp_list_append(sdp_list_t *p, void *d)
        {
                sdp_list_t *q, *n = malloc(sizeof(sdp_list_t));
        
                if (!n)
                        return NULL;
        
                n->data = d;
                n->next = 0;
        
                if (!p)
                        return n;
        
                for (q = p; q->next; q = q->next);
                q->next = n;
        
                return p;
        }
        The 1st element is created when p is NULL in the sdp_list_t.
        
      • sdp_list_t *sdp_list_free(sdp_list_t *list, sdp_list_func_t f);
      /*
       * Profile descriptor is the Bluetooth profile metadata. If a
       * service conforms to a well-known profile, then its profile
       * identifier (UUID) is an attribute of the service. In addition,
       * if the profile has a version number it is specified here.
       */
      typedef struct {
       uuid_t uuid;
       uint16_t version;
      } sdp_profile_desc_t;
      
      /*
       * Set the profile descriptor list attribute of a record.
       *
       * Each element in the list is an object of type
       * sdp_profile_desc_t which is a definition of the
       * Bluetooth profile that this service conforms to.
       */
      int sdp_set_profile_descs(sdp_record_t *rec, const sdp_list_t *desc);
      
      /*
       * Extract the Bluetooth profile descriptor(uuid, version) sequence from a record.
       * Each element in the list is of type sdp_profile_desc_t
       * which contains the UUID of the profile and its version number
       * (encoded as major and minor in the high-order 8bits
       * and low-order 8bits respectively of the uint16_t)
       */
      int sdp_get_profile_descs(const sdp_record_t *rec, sdp_list_t **profDesc);
      
      
      If a service advertises compliance with a Bluetooth profile, then it should advertise the UUID of that profile and the version number of the profile that it complies with.
      typedef struct {
       uint32_t handle;
      
       /* Search pattern: a sequence of all UUIDs seen in this record */
       sdp_list_t *pattern;
       sdp_list_t *attrlist;
      
       /* Main service class for Extended Inquiry Response */
       uuid_t svclass;
      } sdp_record_t;
      
      
    • sdp_session_t *sdp_connect(const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags);
    • create an L2CAP connection to a Bluetooth device. Values of the flags parameter to sdp_connect:
      #define SDP_RETRY_IF_BUSY 0x01
      #define SDP_WAIT_ON_CLOSE 0x02
      #define SDP_NON_BLOCKING 0x04
      
    • int sdp_service_search_attr_req(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list, sdp_list_t **rsp_list);
    • This is a service search request combined with the service attribute request. The magic number 0x0000ffff to request a list of all the attributes describing the service
  • parsing and interpreting an SDP search result
  • 
        sdp_list_t *r = response_list;
    
        // go through each of the service records
        for (; r; r = r->next ) {
            sdp_record_t *rec = (sdp_record_t*) r->data;
            sdp_list_t *proto_list;
            
            // get a list of the protocol sequences
            if( sdp_get_access_protos( rec, &proto_list ) == 0 ) {
            sdp_list_t *p = proto_list;
    
            // go through each protocol sequence
            for( ; p ; p = p->next ) {
                sdp_list_t *pds = (sdp_list_t*)p->data;
    
                // go through each protocol of the protocol sequence
                for( ; pds ; pds = pds->next ) {
    
                    // check the protocol attributes
                    sdp_data_t *d = (sdp_data_t*)pds->data;
                    int proto = 0;
                    for( ; d; d = d->next ) {
                        switch( d->dtd ) { 
                            case SDP_UUID16:
                            case SDP_UUID32:
                            case SDP_UUID128:
                                proto = sdp_uuid_to_proto( &d->val.uuid );
                                break;
                            case SDP_UINT8:
                                if( proto == RFCOMM_UUID ) {
                                    printf("rfcomm channel: %d\n",d->val.int8);
                                }
                                break;
                        }
                    }
                }
                sdp_list_free( (sdp_list_t*)p->data, 0 );
            }
            sdp_list_free( proto_list, 0 );
    
            }
    
            printf("found service record 0x%x\n", rec->handle);
            sdp_record_free( rec );
        }
    
        sdp_close(session);
    }
    
    • sdp_record_t
    • typedef struct {
       uint32_t handle;
       sdp_list_t *pattern;
       sdp_list_t *attrlist;
      } sdp_record_t;
      
      A data structure represents a single service record being advertised by another device. There are a number of functions used to manipulate the sdp_record_t. For ex., sdp_get_access_protos() is used to Get the access protocols from the service record.
      int sdp_get_access_protos(const sdp_record_t *rec, sdp_list_t **protos);
      
    • sdp_data_t
    • Store each element of information in a service record.
      typedef struct sdp_data_struct sdp_data_t;
      
      struct sdp_data_struct {
        uint8_t dtd;
        uint16_t attrId;
        union {
         int8_t    int8;
         int16_t   int16;
         int32_t   int32;
         int64_t   int64;
         uint128_t int128;
         uint8_t   uint8;
         uint16_t  uint16;
         uint32_t  uint32;
         uint64_t  uint64;
         uint128_t uint128;
         uuid_t    uuid;
         char     *str;
         sdp_data_t *dataseq;
        } val;
        sdp_data_t *next;
        int unitSize;
      };
      
      An SDP service record consists of a list of entries, where each entry consists of an attribute/value pair. The value can be of many different types (8-bit integer, 16-bit integer, text string, UUID, etc.). There are several functions to handle this type:
      • int sdp_attr_add(sdp_record_t *rec, uint16_t attr, sdp_data_t *data);
      • Add one SDP data with assigned attr to the rec list.
      • void sdp_attr_remove(sdp_record_t *rec, uint16_t attr);
      • void sdp_attr_replace(sdp_record_t *rec, uint16_t attr, sdp_data_t *data);
  • Advertising a Service
  • Every Bluetooth device typically runs an SDP server that answers queries from other Bluetooth devices. In BlueZ, the implementation of the SDP server is called sdpd, and is usually started by the system boot scripts. sdpd handles all incoming SDP search requests. Applications that need to advertise a Bluetooth service must use inter-process communication (IPC) methods (done with the named pipe /var/run/sdp) to tell sdpd what to advertise. Registering a service with sdpd involves building the service record and registering it with the local SDP server. Each service record contains information about a single service. There is a maximum of one SDP server per Bluetooth device which can act on behalf of many Bluetooth application services.
    A service record contains attributes which can be universal attributes or private attributes for a specific service. As the "Device ID" profile as an example, it is to provide a profile specification for the identification of devices based on brand and device information. Device ID information for the device will be called the Device ID Service Record, and is identified by a unique UUID. There can be more than one Device ID Service Record in a device providing multiple services, only the one Device ID Service Record for which the PrimaryRecord attribute is set to TRUE will be used to uniquely identify the device. The Device ID Service Record attributes are listed below:
    • building the service record
    • 
      #include <bluetooth/bluetooth.h>
      #include <bluetooth/sdp.h>
      #include <bluetooth/sdp_lib.h>
      
      int main ( )
      {
      sdp session t * session = register service ( ) ;
      
      sleep ( 5 ) ;
      sdp_close( session ) ;
      return 0 ;
      }
      
      sdp_session_t *register_service()
      {
          uint32_t service_uuid_int[] = { 0, 0, 0, 0xABCD };
          uint8_t rfcomm_channel = 11;
          const char *service_name = "Roto-Rooter Data Router";
          const char *service_dsc = "An experimental plumbing router";
          const char *service_prov = "Roto-Rooter";
      
          uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid;
          sdp_list_t *l2cap_list = 0, 
                     *rfcomm_list = 0,
                     *root_list = 0,
                     *proto_list = 0, 
                     *access_proto_list = 0;
          sdp_data_t *channel = 0, *psm = 0;
      
          sdp_record_t *record = sdp_record_alloc();
      
          // set the general service ID
          sdp_uuid128_create( &svc_uuid, &service_uuid_int );
          sdp_set_service_id( record, svc_uuid );
      
          // make the service record publicly browsable
          sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
          root_list = sdp_list_append(0, &root_uuid);
          sdp_set_browse_groups( record, root_list );
      
          // set l2cap information
          sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
          l2cap_list = sdp_list_append( 0, &l2cap_uuid );
          proto_list = sdp_list_append( 0, l2cap_list );
      
          // set rfcomm information
          sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
          channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel);
          rfcomm_list = sdp_list_append( 0, &rfcomm_uuid );
          sdp_list_append( rfcomm_list, channel );
          sdp_list_append( proto_list, rfcomm_list );
      
          // attach protocol information to service record
          access_proto_list = sdp_list_append( 0, proto_list );
          sdp_set_access_protos( record, access_proto_list );
      
          // set the name, provider, and description
          sdp_set_info_attr(record, service_name, service_prov, service_dsc);
      
    • registering services with the local SDP server
    • Once the service record is complete, the application connects to the local SDP server and registers a new service
      int err = 0;
          sdp_session_t *session = 0;
      
          // connect to the local SDP server, register the service record, and 
          // disconnect
          session = sdp_connect( BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY );
          err = sdp_record_register(session, record, 0);
      
          // cleanup
          sdp_data_free( channel );
          sdp_list_free( l2cap_list, 0 );
          sdp_list_free( rfcomm_list, 0 );
          sdp_list_free( root_list, 0 );
          sdp_list_free( access_proto_list, 0 );
      
          return session;
      }
      
      The special argument BDADDR_LOCAL causes sdp_connect() to connect to the local SDP server (via the named pipe /var/run/sdp) . After sdp_record_register() is called , the service will be advertised for as long as the session with the SDP server is kept open. As soon as the SDP server detects that the socket connection is closed, it will stop advertising the service. sdp_close() terminates a session with the SDP server.


留言

熱門文章