Bluetooth + Linux implementation physical layer: - wireless connection technology, 2.4GHz (ISM band) - several devices share a common radio channel, forming a "piconet" - each device has a unique Bluetooth address (a sequence of 6 bytes, dubbed the "BD_ADDR"). The sequence is constant and unique per device (assigned by some registration authority). The bit sequence is subdivided into LAP (bits 0-23), UAP (24-31), and NAP (32-47). Some sequences are reserved for special purposes, e.g. for "inquiries". - one of the devices in a piconet serves as the "master", the others are the "slaves" - frequency hopping patterns to avoid interference with other senders - hopping over 79 channels, each ca. 1MHz wide - hopping sequence determined by master's address and clock - 1600 timeslots/sec - raw bitrate 914560/s (TODO: verify). Shared among all devices, and communication links, protocol headers etc. - max. effective bitrate 723200/s ("DH5", 2 devices, 5 timeslots per packet in the "fast" direction, 1 in the slow one) - "physical links" only between master and a slave, "logical links" between any devices use "physical links" as their transport - types of logical links: unicast, multicast, synchronous, asynchronous, isochronous (??) - "link manager protocol (LMP)": control protocol for the physical layers (TODO: elaborate), transported over a dedicated asynchronous, connection-oriented logical link, the ACL. Each dev in the piconet automatically connects to that transport upon joining the piconet - devices may create additional transports if desired - L2CAP: higher-level protocol providing connection-oriented or -less communication over distinct "channels" (somewhat like TCP on the internet) - SDP (service discovery protocol): used for discovering he "services" provided by a given device. - TODO: more... - Linux implementation: - kernel: core, device drivers, HCI, L2CAP, RFCOMM - interfacing to userspace: Socket API (new socket types), ioctls for non-stream-orientied things - userspace wrapper libraries (bluez-libs) - userspace: SDP, OBEX, everything in higher layers ////bluez-libs/bluetooth/bluetooth.h /* BD Address */ typedef struct { uint8_t b[6]; } __attribute__((packed)) bdaddr_t; //so a bluetooth endpoint is a 6-byte number ("Bluetooth device address") So bluetooth/bluetooth.h only defines some basic utility functions. The hard work happens in the kernel. HCI ======================== ////bluez-libs/bluetooth/hci.h/hci_lib.h/hci.c //the functions here are mostly frontends to correspondings //ioctls on socket created via socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI) //(name is misleading, should be "hci_find_dev") //find first (w.r.t. ioctl HCIGETDEVLIST) connected device dev_id for which //func(, // dev_id, args) //returns non-0. Returns that device's dev_id. Returns -1 if no such //device was found. //If func is NULL, return first connected device. //QU: What is a dev_id? Has a corresponding bdaddr_t when connected // (ioctl HCIGETDEVINFO can find the bdaddr_t of a given dev_id) int hci_for_each_dev(int flag, int (*func)(int s, int dev_id, long arg), long arg); //return dev_id of first conn. device having an address other than *bdaddr int hci_get_route(bdaddr_t *bdaddr); //return dev_id of conn. device identified by str //str may either be "hci" (dev_id = the integer number 2b // returned) or a string representation of teh device's bdaddr // (looks like "xx:xx:xx:xx:xx:xx", i.e. the six bytes comprising the // bdaddr in hex representation) int hci_devid(const char *str); //gets complete hci_dev_info of device identified by dev_id int hci_devinfo(int dev_id, /*out*/struct hci_dev_info *di); struct hci_dev_info { uint16_t dev_id; char name[8]; bdaddr_t bdaddr; uint32_t flags; uint8_t type; uint8_t features[8]; uint32_t pkt_type; uint32_t link_policy; uint32_t link_mode; uint16_t acl_mtu; uint16_t acl_pkts; uint16_t sco_mtu; uint16_t sco_pkts; struct hci_dev_stats stat; }; //gets bdaddr of device dev_id int hci_devba(int dev_id, /*out*/bdaddr_t *ba) // (Wrapper around ioctl: HCIINQUIRY) // //inputs: // //dev_id - "starting device" of the inquiry? If NULL, start with // first (HCIGETDEVLIST) found device. // //nrsp - max. number of result devices // //len - ?? ( => hci_inquiry_req.length) // //flags - ?? ( => hci_inquiry_req.flags) // //return: -1 on error, else: // number of result inquiry_info structures; *ii will be // malloc()ed and filled with these inquiry_info structures int hci_inquiry(int dev_id, int len, int nrsp, const uint8_t *lap, inquiry_info **ii, long flags); L2CAP ======================== // create L2CAP connection int sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); struct sockaddr_l2 sa; sa.l2_family = AF_BLUETOOTH; sa.l2_psm = 0; sa.l2_bdaddr = ; bind(sock, (struct sockaddr *)&sa, sizeof(sa)); sa.l2_psm = htobs(SDP_PSM); sa.l2_bdaddr = ; connect(sock, (struct sockaddr *)&sa, sizeof(sa)); SDP (Service Discovery Protocol) ================================ - used to discover "services" provided by connected devices - protocol on top of L2CAP - devices provide "services", services have "attributes" - specific services and attributes have unique identifiers (UUIDs?) registered with the Bluetooth SIG (special interest group) - SDP capabilities: - discover services and attributes offered by a given device - discover all devices offering a given service - discover all devices offering services with given attributes - ... - services may be dynamically activated/deactivated on a device - but there's no event notification mechanism (so polling may be necessary) - Linux implementation: - in userspace - const bdaddr_t *src = ...; const bdaddr_t *dst = ...; struct sockaddr_l2 sa; int sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); sa.l2_family = AF_BLUETOOTH; sa.l2_psm = 0; if (bacmp(src, BDADDR_ANY) != 0) { sa.l2_bdaddr = *src; if (0 > bind(session->sock, (struct sockaddr *)&sa, sizeof(sa))) goto fail; } sa.l2_psm = htobs(SDP_PSM); sa.l2_bdaddr = *dst; connect(session->sock, (struct sockaddr *)&sa, sizeof(sa)) == 0) ...then write/read SDP PDUs to/from the socket. - sdp.h/sdp.c (from bluez-libs) provides wrapper functions in userspace //////sdp.h/sdp.c userspace library: struct sdp_list_t { //utility type: linked list of void*s sdp_list_t *next; void *data; }; struct sdp_record_t { uint32_t handle; //Spec: A service record handle is a 32-bit number that uniquely //identifies each service record within an SDP server. It is //important to note that, in general, each handle is //unique only within each SDP server. sdp_list_t *pattern; //purpose? sdp_list_t *attrlist; //list of sdp_data_t, i.e. list of lists of SDP attributes }; struct sdp_data_t { //obviously a list of SDP attributes uint16_t attrId; SDP_BOOL|SDP_UINT8|SDP_UINT16|...|SDP_UUID_..|..|SDP_TEXT_STR8|SDP_TEXT_STR16|...|SDP_SEQ_.. dtd; //type //SDP_TEXT_STR8 -- string length <256 chars; SDP_TEXT_STR16 -- string length <65536 chars int8_t int8 | int16_t int16 | .. | uuid_t uuid | char *str | sdp_data_t *dataseq val; //value sdp_data_t *next; int unitSize; //? }; //predefined attributes (attrId, dtd): // ( * See SDP Spec, section "Service Attribute Definitions" for more details.) // SDP_ATTR_SVCNAME_PRIMARY, STR8 // SDP_ATTR_SVCDESC_PRIMARY, STR8 // SDP_ATTR_PROVNAME_PRIMARY, STR8 // SDP_ATTR_PROTO_DESC_LIST, ? // SDP_ATTR_LANG_BASE_ATTR_ID_LIST, ? // SDP_ATTR_PFILE_DESC_LIST, SDP_SEQ_? (list of (uint8_t uuid, uint16_t Vnum) => sdp_profile_desc_t. Use // int sdp_get_profile_descs(const sdp_record_t *rec, sdp_list_t **profDescSeq); // to extract. *profDescSeq will be returned as a list of sdp_profile_desc_t's. // SDP_ATTR_VERSION_NUM_LIST, SDP_SEQ_? (list of uint16_t Vnum). Use // int sdp_get_server_ver(const sdp_record_t *rec, sdp_list_t **u16); to extract. // SDP_ATTR_SERVICE_ID, SDP_UUID_?. Use int sdp_get_service_id(const sdp_record_t *rec, uuid_t *uuid) to extract // SDP_ATTR_GROUP_ID, SDP_UUID_?. Use int sdp_get_group_id(const sdp_record_t *rec, uuid_t *uuid) to extract // SDP_ATTR_RECORD_STATE, SDP_UINT32 // ... //return first attribute (sdp_data_t) in rec->attrlist whose attrId equals attrId sdp_data_t *sdp_data_get(const sdp_record_t *rec, uint16_t attrId); //if first attribute (sdp_data_t) in rec->attrlist whose attrId equals attrId // is an int, return it in *value. int sdp_get_int_attr(const sdp_record_t *rec, uint16_t attrid, int *value); //same for string attributes (length must be < valuelen) int sdp_get_string_attr(const sdp_record_t *rec, uint16_t attrid, char *value, int valuelen); //allocate new attribute of type dtd and value *value sdp_data_t *sdp_data_alloc(uint8_t dtd, const void *value) ////all functions dexcribed so far work "off-line", i.e. on sdp_record_t's present //// in the local machine's memory ////"on-line" functions sdp_session_t *sdp_connect(const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags); int sdp_close(sdp_session_t *session); static inline int sdp_get_socket(const sdp_session_t *s); /* * a service search request. * * INPUT : * * sdp_list_t *search_list * list containing elements of the search * pattern. Each entry in the list is a UUID * of the service to be searched * * uint16_t max_rec_num * An integer specifying the maximum number of * entries that the client can handle in the response. * * OUTPUT : * * int return value * 0 * The request completed successfully. This does not * mean the requested services were found * -1 * The request completed unsuccessfully * * sdp_list_t *rsp_list * This variable is set on a successful return if there are * non-zero service handles. It is a singly linked list of * service records (sdp_record_t) */ int sdp_service_search_req(sdp_session_t *session, const sdp_list_t *search_list, uint16_t max_rec_num, sdp_list_t **rsp_list); /////BUGS in bluez-libs setting dtd in sdp_data_t *sdp_data_alloc(uint8_t dtd, const void *value) (in sdp.c) has no effect. ======================== ======================== ======================== data structures (userspace): 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; #define INQUIRY_INFO_SIZE 14 struct hci_inquiry_req { uint16_t dev_id; uint16_t flags; uint8_t lap[3]; uint8_t length; uint8_t num_rsp; }; data structures (kernel): struct hci_inquiry_req { __u16 dev_id; __u16 flags; __u8 lap[3]; __u8 length; __u8 num_rsp; }; typedef struct { __u8 lap[3]; __u8 length; __u8 num_rsp; } __attribute__ ((packed)) inquiry_cp; #define INQUIRY_CP_SIZE 5 struct hci_dev { struct list_head list; spinlock_t lock; atomic_t refcnt; char name[8]; unsigned long flags; __u16 id; __u8 type; bdaddr_t bdaddr; __u8 features[8]; __u16 pkt_type; __u16 link_policy; __u16 link_mode; atomic_t cmd_cnt; unsigned int acl_cnt; unsigned int sco_cnt; unsigned int acl_mtu; unsigned int sco_mtu; unsigned int acl_pkts; unsigned int sco_pkts; unsigned long cmd_last_tx; unsigned long acl_last_tx; unsigned long sco_last_tx; struct tasklet_struct cmd_task; struct tasklet_struct rx_task; struct tasklet_struct tx_task; struct sk_buff_head rx_q; struct sk_buff_head raw_q; struct sk_buff_head cmd_q; struct sk_buff *sent_cmd; struct semaphore req_lock; wait_queue_head_t req_wait_q; __u32 req_status; __u32 req_result; struct inquiry_cache inq_cache; struct conn_hash conn_hash; struct hci_dev_stats stat; void *driver_data; void *core_data; atomic_t promisc; int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); int (*send)(struct sk_buff *skb); void (*destruct)(struct hci_dev *hdev); int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg); };