Affix - Technical Documentation: Affix - Open Source Bluetooth Protocol Stack for Linux | ||
---|---|---|
Prev | Chapter 3. Application Programming Interface | Next |
The Socket interface provides a standard, well-documented approach
to access kernel network resources. The Affix brings to the system new
protocol family PF_AFFIX and new socket address family
"struct sockaddr_affix
":
<affix/bluetooth.h> must be included to the source file.
Standard "socket" system call is used to create a socket of PF_AFFIX family:
wheretype
parameter depends on
the protocol
parameter, which accepts following
values:BTPROTO_HCIACL. Creates HCI ACL socket. Valid type: SOCK_SEQPACKET.
BTPROTO_HCISCO. Creates HCI SCO socket. Valid type: SOCK_SEQPACKET.
BTPROTO_L2CAP. Creates L2CAP socket. Valid type: SOCK_STREAM, SOCK_SEQPACKET.
BTPROTO_L2CAP_RET. Create L2CAP socket using the retransmission mode (L2CAP 1.2). If the remote host does not support L2CAP 1.2 it is connected in basic mode automatically. Valid type: SOCK_STREAM.
BTPROTO_L2CAP_FC. Create L2CAP socket using flow control (L2CAP 1.2). If the remote device does not support L2CAP 1.2. It automatically connected in basic mode. Valid type: SOCK_STREAM.
BTPROTO_RFCOMM. Creates RFCOMM socket. Valid type: SOCK_STREAM.
The Socket interface has only one data structure as any protocol family in Unix. It is a cousin of sockaddr. The structure has a name "sockaddr_affix" and has the following prototype:
struct sockaddr_affix { sa_family_t family; int devnum; BD_ADDR bda; uint16_t port; };All fields except port has the same meaning for all protocols inside PF_AFFIX family. The "port" field has different meaning in for different protocols:
BTPROTO_L2CAP, BTPROTO_L2CAP_RET and BTPROTO_L2CAP_FC: "port" defines PSM value.
BTPROTO_RFCOMM: "port" defines server channel number.
BTPROTO_HCIACL and BTPROTO_HCISCO: "port" field is unused.
The PF_AFFIX socket family allows to use standard socket functions like connect(), bind(), listen(), accept(), send(), recv() and has additional Bluetooth and Affix specific extension. More details about socket call can be found in the UNIX manual pages.
The PF_AFFIX extension practically is implemented as ioctl() and set(get)sockopt() system calls, but for more convenient it is wrapped into the well-named functions.
Table 3-4. PF_AFFIX extra API
Function | Applicability | Purpose |
---|---|---|
int l2cap_setmtu(int fd, int mtu); | BTPROTO_L2CAP | Sets a maximum packet size that can be received on this socket. This function is used before calling connect() or bind(). |
int l2cap_getmtu(int fd); | BTPROTO_L2CAP | Gets a maximum packet size that can be sent through this socket. |
int hci_getmtu(int fd); | BTPROTO_HCIACL, BTPROTO_HCISCO | Gets a maximum packet size that can be sent through this socket. |
int l2cap_ping(int fd, char *data, int size); | BTPROTO_L2CAP | Sends a *ping* packet and wait for response. |
The RFCOMM socket has additional feature: it can be bound to TTY line. The Affix uses "/dev/btyXX" names for that, where XX is a registered line. Special functions are used to do it:
Table 3-5. RFCOMM TTY API
Function | Purpose |
---|---|
int rfcomm_open_tty(int fd, int line); | Attaches an RFCOMM connection to a TTY line *line*. If the *line* is -1 then it allocates a unused tty line and returns its number. |
int rfcomm_close_tty(int line); | Detaches an RFCOMM connection from a TTY line *line* and close it. |
int rfcomm_set_type(int fd, int type); | Changes the RFCOMM socket type to: RFCOMM_SOCKET (default) or RFCOMM_BTY. This function has to be called right after *socket()*. |
int rfcomm_bind_tty(int fd, struct sockaddr_affix *sa, int line) | Binds an RFCOMM connection to a TTY line *line*. Connection is not established yet, but the line is reserved. When into the TTY is written the line is connected automatically. |
Practical examples can be found in the Affix source code. Here are examples showing how to use PF_AFFIX sockets on a client and a server sides.
The following example illustrates usage of the Affix socket interface on the client side.
#include <stdio.h> /* Affix includes */ #include <affix/bluetooth.h> int main(int argc, char *argv[]) { struct sockaddr_affix sa; int fd, err; fd = socket(PF_AFFIX, SOCK_SEQPACKET, BTPROTO_L2CAP); if (fd < 0) { perror("socket() failed"); return 1; } sa.family = PF_AFFIX; str2bda(&sa.bda, "00:11:22:33:44:55"); /* connect to that device */ /* or sa.bda = other_bda; */ sa.port = 1; /* connect to that port (PSM) */ sa.local = BDADDR_ANY; /* to connect through certain device use: sa.local = <local device bda> */ err = connect(fd, (struct sockaddr*)&sa, sizeof(sa)); if (err) { perror("connect() failed"); return 2; } /* here is socket is ready for communication any of the standard connection oriented transmission/receiving system calls can be used. - send(), sendmsg(), write() - recv(), recvmsg(), read() */ close(fd); return 0; }The next example illustrates usage of the socket interface on the server side.
#include <stdio.h> /* Affix includes */ #include <affix/bluetooth.h> int main(int argc, char *argv[]) { struct sockaddr_affix sa, csa; int fd, cfd, err; socklen_t csa_len; /* create server socket */ fd = socket(PF_AFFIX, SOCK_SEQPACKET, BTPROTO_L2CAP); if (fd < 0) { perror("socket() failed"); return 1; } sa.family = PF_AFFIX; sa.bda = BDADDR_ANY sa.port = 1; /* accept connection to that port (PSM) */ /* to connect through certain device use: sa.local = <local device bda> */ /* bind socket to address specified by "sa" parameter */ err = bind(fd, (struct sockaddr*)&sa, sizeof(sa)); if (err) { perror("bind() failed"); return 2; } /* start listen for connection - kernel will accept connection requests */ err = listen(fd, 5) if (err) { perror("listen() failed"); return 3; } /* accept new connection and get its connection descriptor "cfd" */ csalen = sizeof(csa); cfd = accept(fd, (struct sockaddr*)&csa, &csa_len); if (cfd < 0) { perror("accept() failed"); return 4; } /* here is socket "cfd" is ready for communication any of the standard connection oriented transmission/receiving system calls can be used. - send(), sendmsg(), write() - recv(), recvmsg(), read() */ close(fd); /* close server socket */ close(cfd); /* close client socket */ return 0; }
To set socket options use setsockopt() function. The definition for the function is:
int setsockopt(int fd, int level, int optname, void *optval, int *optlen);
Affix supports socket level SOL_AFFIX and the following table lists socket options.
Table 3-6. Supported socket options
Option | Purpose |
---|---|
BTSO_MTU | Set the maximum transfer unit. |
BTSO_SECURITY | Set the socket level security. All services operate above sockets, or at least connection establishment process is done above it. The Service Level security mode allows service to set its security level. It's done through a special security API. Possible modes are HCI_SECURITY_OPEN, HCI_SECURITY_AUTH, HCI_SECURITY_ENCRYPT, HCI_SECURITY_AUTHOR. Modes can bitwise-or'd |
BTSO_EVENT_MASK | Defines what events you want to handle. See possible values in hci_types.h |
BTSO_PKT_MASK | Define what kind of packets socket should receive. Types are HCI_COMMAND, HCI_ACL, HCI_SCO, HCI_EVENT, HCI_PKT_OUTGOING and HCI_MGR. See definitions in hci_types.h. |
BTSO_PROMISC | Allow the socket to receive all the packets. |
BTSO_TYPE | Not used |
Affix implements socket interface also for Python programing language. PyAffix package contains module for Python. It supports HCI, L2CAP, RFCOMM and non-blocking sockets. To use Affix with Python install Affix kernel module and make sure you have Python version 2.2.3 or later installed.
To installing Affix module in your Python do the following:
Download latest PyAffix package from Affix web site (http://affix.sourceforge.net/pyaffix.shtml)
Extract the downloaded tar ball and change to that directory
Give the following command:
python setup.py install
Also in Python you can use standard socket functions like connect(), bind(), listen(), accept(), send() and recv(). To create Affix socket you need to call socket() function like:
import affixsocket /* Just import Affix module and you are ready to use Affix */ affix_sock = socket(PF_AFFIX,SOCK_STREAM,BTPROTO_L2CAP)
Above example creates a new Affix socket object. Protocols and types are the same as listed in the beginning of this chapter. Connecting to remote host using newly created affix_sock object is done with connect method. The socket address information is represented in the following tuple in Python: (devnum, BTaddress, port). Where devnum is a positive integer representing the device, BTaddress is a string representing the Bluetooth address and port is the channel/psm/etc to connect to.
aff_sock.connect((0,"01:02:03:A0:B0:C0",1))
To set socket options use setsockopt(level, optname, value) method. See supported levels and options in chapter Section 3.2.4.