bluetooth-socket
1 Introduction
This library offers support for low level socket communication to a Bluetooth Hardware Controller. Specifically a AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI socket.
Via a non-’bind’ed hci socket querying the number of Bluetooth controllers (hci0, hci1, ...) and their device info is supported.
By ’bind’ing the socket to a specific controller low level Bluetooth HCI packets may be sent to the controller supporting the development of a full/paritial implementation of the Bluetooth Specification stack in Racket.
Currently only Linux is supported. The intent by making this library its own small collection is to allow for eventual additional O/S support.
1.1 RAW Socket Capability
Use of RAW sockets is priviledged in Linux. To avoid having to run as root or via sudo set a raw capability on the racket binary as follows: sudo setcap ’cap_net_raw,cap_net_admin+eip’ /usr/local/racket/bin/racket
2 API
(require bluetooth-socket) | package: bluetooth-socket |
2.1 Types
struct
(struct HciDeviceInfo ( dev-id name bd-addr flags type features pkt-type link-policy link-mode acl-mtu acl-pkts sco-mtu sco-pkts stats) #:extra-constructor-name make-HciDeviceInfo) dev-id : exact-positive-integer? name : string? bd-addr : bytes? flags : exact-positive-integer? type : exact-positive-integer? features : bytes? pkt-type : exact-positive-integer? link-policy : exact-positive-integer? link-mode : exact-positive-integer? acl-mtu : exact-positive-integer? acl-pkts : exact-positive-integer? sco-mtu : exact-positive-integer? sco-pkts : exact-positive-integer? stats : HciDeviceStats?
A goal is to offer a high level controller device info routine which fully parses out the this call at a later time (probably in a higher level collection).
struct
(struct HciDeviceStats ( err-rx err-tx cmd-tx evt-rx acl-tx acl-rx sco-tx sco-rx byte-rx byte-tx) #:extra-constructor-name make-HciDeviceStats) err-rx : exact-positive-integer? err-tx : exact-positive-integer cmd-tx : exact-positive-integer evt-rx : exact-positive-integer acl-tx : exact-positive-integer acl-rx : exact-positive-integer sco-tx : exact-positive-integer sco-rx : exact-positive-integer byte-rx : exact-positive-integer byte-tx : exact-positive-integer
Example of typical HCI controller device information.
(HciDeviceInfo 1 "hci1" #"\23q\332}\32\0" 5 1 #"\377\377\217\376\333\377[\207" 52472 15 32768 310 10 64 8 (HciDeviceStats 0 0 77 79 0 0 0 0 1294 3287))
2.2 Controller Information
procedure
This fd can be used to query the current bluetooth controllers as well as their detailed device information and stats. See hci-connect to open a socket connection, bound to a specific controller, wrapped as standard Racket input and output ports suitable for Racket’s IO routines.
procedure
(hci-socket-close fd) → void?
fd : exact-positive-integer?
procedure
(hci-devices fd) → (listof exact-positive-integer?)
fd : exact-positive-integer?
Assume your system has a builtin HCI controller and a USB HCI controller dongle. Typically these would show on a Linux system as hci0 and hci1. The list '(0 1) would be returned giving their device ids ’0’ and ’1’. This device ids can be subsequently used to obtain their device information. See hci-device-info.
procedure
(hci-device-info fd dev-id) → HciDeviceInfo?
fd : integer? dev-id : integer?
For example:
(define s (hci-socket)) (for ([dev-id (hci-devices s)]) (print (hci-device-info s dev-id))) (hci-socket-close s)
Will query for the number of available controllers and print out their raw detailed device information and stats.
2.3 High Level Controller Communication
Procedures used to communicate with a specific controller. Communicating to a controller is done by creating a low level socket and then ’bind’ing the socket to a specific controller. The bound socket is then wrapped within standard Racket input and output ports and maybe utilized with normal I/O routines.
It is very important that the bound socket is initialized prior being used for any I/O. Initializing a socket involves a low level O/S setsockopt call to set a socket filter. Failure to set a socket filter will hang Racket when attempting any I/O via the socket. Currently the socket filter criteria is hardcoded in the library.
procedure
(hci-connect mode [dev-id]) →
input-port? output-port? mode : symbol? dev-id : : = exact-positive-integer?
IMPORTANT: Use of RAW sockets is priviledged in Linux systems. To avoid running Racket as root or via sudo set the capability of the Racket binary for raw sockets as follows:
sudo setcap ’cap_net_raw,cap_net_admin+eip’ /usr/local/racket/bin/racket
(define conn (hci-connect 'RAW 1))
procedure
(hci-disconnect fd inp outp) → void?
fd : exact-positive-integer? inp : input-port? outp : output-port?
procedure
(hci-initialize-socket fd) → void?
fd : exact-positive-integer?
IMPORTANT: A controller ’bind’ed socket, one opened via a hci-connect call, must be initialized by this call prior to using the wrapping Racket ports in I/O routines. Failure to do so will hang Racket.
A fd from a hci-socket call is not ’bind’ed to a particular controller. Therefore it is usable only for getting controller information via hci-devices and hci-device-info and is not required to be intialized (have a sockopt filter set) via this call.
procedure
(hci-read-semaphore fd) → semaphore?
fd : exact-positive-integer?
3 Example Use
Here is an example sketch of high level routines using this library to open a connection to an hci controller and reading inbound event packets from the controller. It shows use of the hci-connect and hci-read-semaphore to prevent hard blocking Racket in standard IO routines. The Racket standard IO ports from hci-connect which wrap the RAW HCI socket can be used in Racket IO read/write APIs.
(struct Hci-Connection (fd input-port output-port)) (define (hci-connection-read-semaphore conn) (hci-read-semaphore (Hci-Connection-fd conn))) (define (hci-adapter-connect mode [dev-id 0]) (let-values (([fd inp outp] (hci-connect mode dev-id))) (let* ([c (Hci-Connection fd inp outp)]) (initialize-socket fd) (write-bytes-avail (encode-command-packet init-socket-event-mask-cmd-1) outp) c))) (define (read-next-pkt c) (sync (hci-connection-read-semaphore c)) (let ((hdr (read-bytes 8 (Hci-Connection-input-port c)))) ...)) (define (inbound-read-loop c) (let ((pkt (read-next-pkt c))) (print pkt) (inbound-read-loop c))) (define adapter-inbound-event-thread (let ((conn (hci-adapter-connect 'RAW 1))) (thread (λ () (inbound-read-loop conn)))))