Affix in a Nutshell: Affix - Open Source Bluetooth Protocol Stack for Linux | ||
---|---|---|
Prev | Chapter 3. Application Programming Interface | Next |
The HCI API allows user to perform low-level control of Bluetooth devices, to set it up into different modes of operation, to discover other Bluetooth devices in the radio range, and so forth. There are almost no cases when HCI functions are unused. The most common task is to make a discovery to find what devices are around.
The main notion in the HCI API is a HCI descriptor, which in fact is a Unix file descriptor. The HCI descriptor is used in many functions to specify device used with a calling function.
The HCI API function set divided into two subsets: system (Affix specific) function set and HC (Bluetooth device specific) function set. All an Affix function names have low-case style and all a HC function names have mixed-case style and are starting from HCI_ or __HCI_. The HC functions are implemented according to the Bluetooth System Specification [BTSPEC].
The Affix system function set is presented in Table 3-7.
Table 3-7. HCI API function set
function prototype | Description |
---|---|
int hci_open(char *name); | Opens device "by name" and returns HCI descriptor. |
int hci_open_id(int hd); | Opens device "by id" and returns HCI descriptor. |
int hci_get_devs(int *devs); | Stores HCI descriptors of registered Bluetooth devices in the system in the *devs* and returns its number. |
int hci_open_event(void); | Opens a channel used to receive HCI events from any device and returns HCI descriptor. |
int hci_listen_event(int fd, __u64 mask); | Sets event mask for specified device and enable event receiving of matched type. Mask value zero disable events receiving. |
int hci_recv_event(int fd, void *event, int *hd); | Receives HCI event from the HCI devices. Event is stored in the buffer pointed by "event" argument and device id from where event came is stored in variable pointed by "hd". |
int hci_exec_cmd(int fd, void *cmd, __u64 mask, int flags, void *event); | Sends HCI commands pointed by "cmd" to the device pointed by "fd", waits for the COMMAND_STATUS or COMMAND_COMPLETE event and stores it in the buffer pointed by "event". "mask" argument defines events (mask) to receive. |
int hci_open_mgr(void); | Opens Affix internal control channel an returns descriptor. |
int hci_recv_msg(int fd, __u8 *param); | Receives Affix internal control message from the channel pointed by "fd" and stores data in the buffer pointed by "param" argument. |
int hci_add_pin(BD_ADDR *bda, int Length, __u8 *Code); | Stores a pin code in the Affix control block. It is used in the case of it will be required. It's used by non-interactive applications. This pin code is used to authenticate device *bda*. |
int hci_remove_pin(BD_ADDR *bda); | Removes pin code from the Affix control block. |
int hci_add_key(BD_ADDR *bda, __u8 key_type, __u8 *key); | Stores a link key in the Affix control block. It's used if application stores link key for later use. Link key is used to authenticate device *bda*. |
int hci_remove_key(BD_ADDR *bda); | Removes link key from Affix control block. |
int hci_set_mode(int fd, int mode); | Sets functional mode of Affix kernel core (mode: AFFIX_MODE_PIN - caller handles PIN, AFFIX_MODE_KEY - caller handles key). |
int hci_get_conn(int fd, BD_ADDR *bda); | Retrieves handle of HCI ACL/SCO connection to device *bda*. |
hci_get_attr(char *name, struct hci_dev_attr *attr); | Reads device attributes:. name, id, flags. |
hci_get_attr_id(int hd, struct hci_dev_attr *attr); | Reads device attributes:. name, id, flags. |
hci_get_attr_fd(int fd, struct hci_dev_attr *attr); | Reads device attributes:. name, id, flags. |
hci_get_flags(int fd, int *flags); | Reads device flags. |
hci_set_flags(int fd, int flags); | Sets device flags: HCI_FLAGS_UP |
int hci_set_secmode(int fd, int mode); | Sets security mode: HCI_SECURITY_OPEN, HCI_SECURITY_LINK, HCI_SECURITY_AUTH, HCI_SECURITY_AUTHOR). This modes can bitwise-or'd. |
int hci_set_pkttype(int fd, int pkt_type); | Sets packet type. |
int hci_set_role(int fd, int role); | Set connection roles. |
char *hcierror(int err); | Returns HCI error message. |
char *bda2str(BD_ADDR *bda); | Converts Bluetooth address to string. |
int str2bda(BD_ADDR *p, char *str); | Converts string to Bluetooth address. |
zero (0) if no error occurred;
-1 in a case of system error, and errno is set appropriately. (like any Linux system call does);
above zero (>0) in a case of HCI error (see Bluetooth Specification for values).
General HC function format is: int HCI_Xxxx(int hd, param, param, ...);
The rest of parameters of HC functions depend on actual function and is defined according to Bluetooth Specification. Its description can be carefully read from there.
The following table presents only partial set of HC functions implemented in the Affix. A complete set can be found in hci_cmds.h.
Table 3-8. HC function set
Function | Description |
---|---|
int HCI_Inquiry(int fd, __u8 Inquiry_Length, __u8 Max_Num_Responses, INQUIRY_ITEM *Items, __u8 *Num_Responses) | Performs INQUIRY during (1.28 * Inquiry_Length) sec. and stores found bluetooth devices info in *Items* array and number of found devices in *Num_Responses*. |
int HCI_WriteScanEnable(int fd, __u8 Scan_Enable); | Sets SCAN mode (SCAN_OFF, SCAN_INQUIRY, SCAN_PAGE, SCAN_BOTH). |
int HCI_WriteClassOfDevice(int fd, __u32 Class_of_Device); | Sets a Class of Device. |
int HCI_ChangeLocalName(int fd, char *Name); | Sets a device name. All remote devices will see this name. |
int HCI_WriteAuthenticationEnable(int fd, __u8 Authentication_Enable); | Enables authentication. |
int HCI_WriteEncryptionMode(int fd, __u8 Encryption_Mode); | Sets encryption mode for ACL connections: EM_NO, EM_PP (point-to-point), EM_ALL (pp and broadcast). |
int HCI_ReadBDAddr(int fd, BD_ADDR *bda); | Reads address from device. |
int HCI_WritePageTimeout(int fd, __u16 Page_Timeout); | Sets page timeout. |
int HCI_WriteLinkPolicy(int fd, CHANDLE Connection_Handle, __u8 Link_Policy_Settings); | Sets Link Policy. |
Data structures related to the HCI interface are defined in hci_types.h header.
struct sockaddr_affix.
struct sockaddr_affix { sa_family_t family; BD_ADDR bda; uint16_t port; BD_ADDR local; /* local Bluetooth adapter to connect to */ };
INQUIRY_ITEM
typedef struct { BD_ADDR bda; __u8 PS_Repetition_Mode; __u8 PS_Period_Mode; __u8 PS_Mode; __u32 Class_of_Device:24; __u16 Clock_Offset; }__PACK__ INQUIRY_ITEM;
struct hci_dev_attr.
/* flags mask */ #define HCI_FLAGS_RUNNING 0x00000001 #define HCI_FLAGS_UP 0x00000002 #define HCI_FLAGS_ROLE 0x000000F0 #define HCI_ROLE_ALLOW_SWITCH 0x00000000 #define HCI_ROLE_DENY_SWITCH 0x00000010 #define HCI_ROLE_REMAIN_SLAVE 0x00000000 #define HCI_ROLE_BECOME_MASTER 0x00000020 #define HCI_FLAGS_SECURITY 0x0000FF00 #define HCI_SECURITY_OPEN 0x00000100 #define HCI_SECURITY_SERVICE 0x00000200 #define HCI_SECURITY_LINK 0x00000400 /* levels */ #define HCI_SECURITY_AUTH 0x00001000 #define HCI_SECURITY_ENCRYPT 0x00002000 #define HCI_SECURITY_AUTHOR 0x00004000 struct hci_dev_attr { int hd; char name[IFNAMSIZ]; BD_ADDR bda; int flags; };
struct hci_msg_hdr.
#define MGR_STATE_CHANGE 0x01 struct hci_msg_hdr { int opcode; int length; };
struct hci_state_change.
/* hci device events */ #define HCIDEV_UP 0x0001 #define HCIDEV_DOWN 0x0002 #define HCIDEV_CHANGE 0x0004 #define HCIDEV_REGISTER 0x0005 #define HCIDEV_UNREGISTER 0x0006 #define HCIDEV_ATTACH 0x0100 #define HCIDEV_DETACH 0x0200 struct hci_state_change { struct hci_msg_hdr hdr; int hd; int event; };
Here is an example of the HCI API usage.
int main(int argc, char *argv[]) { int devs[16], num, i, err; num = hci_get_devs(devs); if (num < 0) { printf("unable to get device list\n"); return -1; } if (num == 0) { printf("No Bluetooth Adapters found\n"); return 0; } for (i = 0; i < num; i++) { fd = _hci_open_id(devs[i]); if (fd < 0) { printf("Unable to open Bluetooth device: %d\n\n", devs[i]); return -1; } err = hci_get_attr(fd, &da); if (err < 0) { printf("Unable to get attribute for: %d\n", devs[i]); return -1; } printf("%s\t%s\n", da.name, bda2str(&da.bda)); if (!(da.state & HCI_STATE_UP)) { printf("\tDevice is down"); continue; } err = HCI_WriteScanEnable(fd, 0x02); if (err) { printf("unable to set scan mode\n"); return -1; } }
Next example illustrates how to discover surrounding devices.
int main(int argc, char *argv[]) { int fd, i, argind = 1; __u32 length; int err; INQUIRY_ITEM devs[20]; char *devnames[20]; char namebuf[248]; __u8 num; if (argv[argind]) { sscanf(argv[argind], "%x", &length); } else length = 8; fd = hci_open("bt0"); if (fd < 0) { printf("Unable to open device %s: %s\n", btdev, sys_errlist[errno]); return -1; } printf("Searching %d sec ...\n", length); err = HCI_Inquiry(fd, length, 20, devs, &num); print_hci_error(err); if (num == 0) { printf("done.\nNo devices found.\n"); return 0; } printf("Searching done. Resolving names ...\n"); for (i = 0; i < num; i++) { err = HCI_RemoteNameRequest(fd, &devs[i], namebuf); if (!err) devnames[i] = strdup(namebuf); else devnames[i] = NULL; } printf("done.\n"); for (i = 0; i < num; i++) { printf("%d, bda: %s, name: %s\n", i, bda2str(&devs[i].bda), devnames[i]); if (devnames[i]) free(devnames[i]); } return 0; }