蓝牙编程经典程序!

发布时间:2018-07-01 22:37:50

L2CAP sockets

Example 4-4. l2cap-server.c

#include

#include

#include

#include

#include

int main(int argc, char **argv)

{

struct sockaddr_l2 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_SEQPACKET, BTPROTO_L2CAP);

// bind socket to port 0x1001 of the first available

// bluetooth adapter

loc_addr.l2_family = AF_BLUETOOTH;

loc_addr.l2_bdaddr = *BDADDR_ANY;

loc_addr.l2_psm = htobs(0x1001);

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.l2_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);

}

Example 4-5. l2cap-client.c

#include

#include

#include

#include

#include

int main(int argc, char **argv)

{

struct sockaddr_l2 addr = { 0 };

int s, status;

char *message = "hello!";

char dest[18] = "01:23:45:67:89:AB";

if(argc < 2)

{

fprintf(stderr, "usage: %s \n", argv[0]);

exit(2);

}

strncpy(dest, argv[1], 18);

// allocate a socket

s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);

// set the connection parameters (who to connect to)

addr.l2_family = AF_BLUETOOTH;

addr.l2_psm = htobs(0x1001);

str2ba( dest, &addr.l2_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);

}

For simple usage scenarios, the only differences are the socket type specified, the protocol family, and the addressing structure. By default, L2CAP connections provide reliable datagram-oriented connections with packets delivered in order, so the socket type is SOCK_SEQPACKET, and the protocol is BTPROTO_L2CAP. The addressing structure struct sockaddr_l2 differs slightly from the RFCOMM addressing structure.

struct sockaddr_l2 {

sa_family_t l2_family;

unsigned short l2_psm;

bdaddr_t l2_bdaddr;

};

The l2_psm field specifies the L2CAP port number to use. Since it is a multibyte unsigned integer, byte ordering is significant. The htobs function, described earlier, is used here to convert numbers to Bluetooth byte order.

4.3.1. Maximum Transmission Unit

Occasionally, an application may need to adjust the maximum transmission unit (MTU) for an L2CAP connection and set it to something other than the default of 672 bytes. In BlueZ, this is done with the getsockopt and setsockopt functions.

struct l2cap_options {

uint16_t omtu;

uint16_t imtu;

uint16_t flush_to;

uint8_t mode;

};

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;

};

The omtu and imtu fields of the struct l2cap_options are used to specify the outgoing MTU and incoming MTU, respectively. The other two fields are currently unused and reserved for future use. To adjust the connection-wide MTU, both clients must adjust their outgoing and incoming MTUs. Bluetooth allows the MTU to range from a minimum of 48 bytes to a maximum of 65,535 bytes.

4.3.2. Unreliable sockets

It is slightly misleading to say that L2CAP sockets are reliable by default. Multiple L2CAP and RFCOMM connections between two devices are actually logical connections multiplexed on a single, lower level connection [1] established between them. The only way to adjust delivery semantics is to adjust them for the lower level connection, which in turn affects all L2CAP and RFCOMM connections between the two devices.

As we delve deeper into the more complex aspects of Bluetooth programming, the interface becomes a little harder to manage. Unfortunately, BlueZ does not provide an easy way to change the packet timeout for a connection. A handle to the underlying connection is first needed to make this change, but the only way to obtain a handle to the underlying connection is to query the microcontroller on the local Bluetooth adapter. Once the connection handle has been determined, a command can be issued to the microcontroller instructing it to make the appropriate adjustments. Example 4-6 shows how to do this.

Example 4-6. set-flush-to.c

#include

#include

#include

#include

#include

#include

#include

#include

int set_flush_timeout(bdaddr_t *ba, int timeout)

{

int err = 0, dd;

struct hci_conn_info_req *cr = 0;

struct hci_request rq = { 0 };

struct {

uint16_t handle;

uint16_t flush_timeout;

} cmd_param;

struct {

uint8_t status;

uint16_t handle;

} cmd_response;

// find the connection handle to the specified bluetooth device

cr = (struct hci_conn_info_req*) malloc(

sizeof(struct hci_conn_info_req) +

sizeof(struct hci_conn_info));

bacpy( &cr->bdaddr, ba );

cr->type = ACL_LINK;

dd = hci_open_dev( hci_get_route( &cr->bdaddr ) );

if( dd < 0 ) {

err = dd;

goto cleanup;

}

err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr );

if( err ) goto cleanup;

// build a command packet to send to the bluetooth microcontroller

cmd_param.handle = cr->conn_info->handle;

cmd_param.flush_timeout = htobs(timeout);

rq.ogf = OGF_HOST_CTL;

rq.ocf = 0x28;

rq.cparam = &cmd_param;

rq.clen = sizeof(cmd_param);

rq.rparam = &cmd_response;

rq.rlen = sizeof(cmd_response);

rq.event = EVT_CMD_COMPLETE;

// send the command and wait for the response

err = hci_send_req( dd, &rq, 0 );

if( err ) goto cleanup;

if( cmd_response.status ) {

err = -1;

errno = bt_error(cmd_response.status);

}

cleanup:

free(cr);

if( dd >= 0) close(dd);

return err;

}

On success, the packet timeout for the low level connection to the specified device is set to timeout * 0.625 milliseconds. A timeout of 0 is used to indicate infinity, and is how to revert back to a reliable connection. The bulk of this function is comprised of code to construct the command packets and response packets used in communicating with the Bluetooth controller. The Bluetooth Specification defines the structure of these packets and the magic number 0x28. In most cases, BlueZ provides convenience functions to construct the packets, send them, and wait for the response. Setting the packet timeout, however, seems to be so rarely used that no convenience function for it currently exists.

RFCOMM sockets

As with Python, establishing and using RFCOMM connections boils down to the same socket programming techniques we already know how to use for TCP/IP programming. The only difference is that the socket addressing structures are different, and we use different functions for byte ordering of multibyte integers. Example 4-2 and Example 4-3 show how to establish a connection using an RFCOMM socket, transfer some data, and disconnect. For simplicity, the client is hard-coded to connect to ``01:23:45:67:89:AB".

Example 4-2. rfcomm-server.c

#include

#include

#include

#include

#include

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;

}

Example 4-3. rfcomm-client.c

#include

#include

#include

#include

#include

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;

}

Most of this should look familiar to the experienced network programmer. As with Internet programming, first allocate a socket with the socket system call. Instead of AF_INET, use AF_BLUETOOTH, and instead of IPPROTO_TCP, use BTPROTO_RFCOMM. Since RFCOMM provides the same delivery semantics as TCP, SOCK_STREAM can still be used for the socket type.

4.2.1. Addressing structures

To establish an RFCOMM connection with another Bluetooth device, incoming or outgoing, create and fill out a struct sockaddr_rc addressing structure. Like the struct sockaddr_in that is used in TCP/IP, the addressing structure specifies the details of an outgoing connection or listening socket.

struct sockaddr_rc {

sa_family_t rc_family;

bdaddr_t rc_bdaddr;

uint8_t rc_channel;

};

The rc_family field specifies the addressing family of the socket, and will always be AF_BLUETOOTH. For an outgoing connection, rc_bdaddr and rc_channel specify the Bluetooth address and port number to connect to, respectively. For a listening socket, rc_bdaddr specifies the local Bluetooth adapter to use, and is typically set to BDADDR_ANY to indicate that any local Bluetooth adapter is acceptable. For listening sockets, rc_channel specifies the port number to listen on.

4.2.2. A note on byte ordering

Since Bluetooth deals with the transfer of data from one machine to another, the use of a consistent byte ordering for multi-byte data types is crucial. 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 );

Like their network order counterparts, these functions convert 16 and 32 bit unsigned integers to Bluetooth byte order and back. They are used when filling in the socket addressing structures, communicating with the Bluetooth microcontroller, and when performing low level operations on transport protocol sockets.

4.2.3. Dynamically assigned port numbers

For Linux kernel versions 2.6.7 and greater, dynamically binding to an RFCOMM or L2CAP port is simple. 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. Unfortunately, for earlier versions of the Linux kernel, the only way to bind to the first available port number is to try binding to every possible port and stopping when bind doesn't fail. The following function illustrates how to do this for RFCOMM sockets.

int dynamic_bind_rc(int sock, struct sockaddr_rc *sockaddr, uint8_t *port)

{

int err;

for( *port = 1; *port <= 31; *port++ ) {

sockaddr->rc_channel = *port;

err = bind(sock, (struct sockaddr *)sockaddr, sizeof(sockaddr));

if( ! err || errno == EINVAL ) break;

}

if( port == 31 ) {

err = -1;

errno = EINVAL;

}

return err;

}

The process for L2CAP sockets is similar, but tries odd-numbered ports 4097-32767 (0x1001 - 0x7FFF) instead of ports 1-30.

4.2.4. RFCOMM summary

Advanced TCP options that are often set with setsockopt, such as receive windows and the Nagle algorithm, don't make sense in Bluetooth, and can't be used with RFCOMM sockets. Aside from this, the byte ordering, and socket addressing structure differences, programming RFCOMM sockets is virtually identical to programming TCP sockets. To accept incoming connections with a socket, use bind to reserve operating system resource, listen to put it in listening mode, and accept to block and accept an incoming connection. Creating an outgoing connection is also simple and merely involves a call to connect. Once a connection has been established, the standard calls to read, write, send, and recv can be used for data transfer.

蓝牙编程经典程序!

相关推荐