diff --git a/docs/manual/markdown/docs/appendix/.gitignore b/docs/manual/markdown/docs/appendix/.gitignore
new file mode 100644
index 000000000..8a52b234e
--- /dev/null
+++ b/docs/manual/markdown/docs/appendix/.gitignore
@@ -0,0 +1 @@
+apis.md
diff --git a/docs/manual/markdown/docs/appendix/apis.md b/docs/manual/markdown/docs/appendix/apis.md
deleted file mode 100644
index 9763c2967..000000000
--- a/docs/manual/markdown/docs/appendix/apis.md
+++ /dev/null
@@ -1,897 +0,0 @@
-
-
-## Run Loop API
-
-
- /**
- * @brief Set timer based on current time in milliseconds.
- */
- void run_loop_set_timer(timer_source_t *a, uint32_t timeout_in_ms);
-
- /**
- * @brief Set callback that will be executed when timer expires.
- */
- void run_loop_set_timer_handler(timer_source_t *ts, void (*process)(timer_source_t *_ts));
-
- /**
- * @brief Add/Remove timer source.
- */
- void run_loop_add_timer(timer_source_t *timer);
- int run_loop_remove_timer(timer_source_t *timer);
-
- /**
- * @brief Init must be called before any other run_loop call. Use RUN_LOOP_EMBEDDED for embedded devices.
- */
- void run_loop_init(RUN_LOOP_TYPE type);
-
- /**
- * @brief Set data source callback.
- */
- void run_loop_set_data_source_handler(data_source_t *ds, int (*process)(data_source_t *_ds));
-
- /**
- * @brief Add/Remove data source.
- */
- void run_loop_add_data_source(data_source_t *dataSource);
- int run_loop_remove_data_source(data_source_t *dataSource);
-
- /**
- * @brief Execute configured run loop. This function does not return.
- */
- void run_loop_execute(void);
-
- // hack to fix HCI timer handling
- #ifdef HAVE_TICK
- /**
- * @brief Sets how many milliseconds has one tick.
- */
- uint32_t embedded_ticks_for_ms(uint32_t time_in_ms);
- /**
- * @brief Queries the current time in ticks.
- */
- uint32_t embedded_get_ticks(void);
- /**
- * @brief Queries the current time in ms
- */
- uint32_t embedded_get_time_ms(void);
- /**
- * @brief Allows to update BTstack system ticks based on another already existing clock.
- */
- void embedded_set_ticks(uint32_t ticks);
- #endif
- #ifdef EMBEDDED
- /**
- * @brief Sets an internal flag that is checked in the critical section just before entering sleep mode. Has to be called by the interrupt handler of a data source to signal the run loop that a new data is available.
- */
- void embedded_trigger(void);
- /**
- * @brief Execute run_loop once. It can be used to integrate BTstack's timer and data source processing into a foreign run loop (it is not recommended).
- */
- void embedded_execute_once(void);
- #endif
-
-
-## HCI API
-
-
- le_connection_parameter_range_t gap_le_get_connection_parameter_range(void);
- void gap_le_set_connection_parameter_range(le_connection_parameter_range_t range);
-
- /* LE Client Start */
-
- le_command_status_t le_central_start_scan(void);
- le_command_status_t le_central_stop_scan(void);
- le_command_status_t le_central_connect(bd_addr_t addr, bd_addr_type_t addr_type);
- le_command_status_t le_central_connect_cancel(void);
- le_command_status_t gap_disconnect(hci_con_handle_t handle);
- void le_central_set_scan_parameters(uint8_t scan_type, uint16_t scan_interval, uint16_t scan_window);
-
- /* LE Client End */
-
- void hci_connectable_control(uint8_t enable);
- void hci_close(void);
-
- /**
- * @note New functions replacing: hci_can_send_packet_now[_using_packet_buffer]
- */
- int hci_can_send_command_packet_now(void);
-
- /**
- * @brief Gets local address.
- */
- void hci_local_bd_addr(bd_addr_t address_buffer);
-
- /**
- * @brief Set up HCI. Needs to be called before any other function.
- */
- void hci_init(hci_transport_t *transport, void *config, bt_control_t *control, remote_device_db_t const* remote_device_db);
-
- /**
- * @brief Set class of device that will be set during Bluetooth init.
- */
- void hci_set_class_of_device(uint32_t class_of_device);
-
- /**
- * @brief Set Public BD ADDR - passed on to Bluetooth chipset if supported in bt_control_h
- */
- void hci_set_bd_addr(bd_addr_t addr);
-
- /**
- * @brief Registers a packet handler. Used if L2CAP is not used (rarely).
- */
- void hci_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
-
- /**
- * @brief Requests the change of BTstack power mode.
- */
- int hci_power_control(HCI_POWER_MODE mode);
-
- /**
- * @brief Allows to control if device is discoverable. OFF by default.
- */
- void hci_discoverable_control(uint8_t enable);
-
- /**
- * @brief Creates and sends HCI command packets based on a template and a list of parameters. Will return error if outgoing data buffer is occupied.
- */
- int hci_send_cmd(const hci_cmd_t *cmd, ...);
-
- /**
- * @brief Deletes link key for remote device with baseband address.
- */
- void hci_drop_link_key_for_bd_addr(bd_addr_t addr);
-
- /* Configure Secure Simple Pairing */
-
- /**
- * @brief Enable will enable SSP during init.
- */
- void hci_ssp_set_enable(int enable);
-
- /**
- * @brief If set, BTstack will respond to io capability request using authentication requirement.
- */
- void hci_ssp_set_io_capability(int ssp_io_capability);
- void hci_ssp_set_authentication_requirement(int authentication_requirement);
-
- /**
- * @brief If set, BTstack will confirm a numeric comparison and enter '000000' if requested.
- */
- void hci_ssp_set_auto_accept(int auto_accept);
-
- /**
- * @brief Get addr type and address used in advertisement packets.
- */
- void hci_le_advertisement_address(uint8_t * addr_type, bd_addr_t addr);
-
- /**
- * @brief Set callback for Bluetooth Hardware Error
- */
- void hci_set_hardware_error_callback(void (*fn)(void));
-
-
-
-## L2CAP API
-
-
- /**
- * @brief Set up L2CAP and register L2CAP with HCI layer.
- */
- void l2cap_init(void);
-
- /**
- * @brief Registers a packet handler that handles HCI and general BTstack events.
- */
- void l2cap_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size));
-
- /**
- * @brief Creates L2CAP channel to the PSM of a remote device with baseband address. A new baseband connection will be initiated if necessary.
- */
- void l2cap_create_channel_internal(void * connection, btstack_packet_handler_t packet_handler, bd_addr_t address, uint16_t psm, uint16_t mtu);
-
- /**
- * @brief Disconnects L2CAP channel with given identifier.
- */
- void l2cap_disconnect_internal(uint16_t local_cid, uint8_t reason);
-
- /**
- * @brief Queries the maximal transfer unit (MTU) for L2CAP channel with given identifier.
- */
- uint16_t l2cap_get_remote_mtu_for_local_cid(uint16_t local_cid);
-
- /**
- * @brief Sends L2CAP data packet to the channel with given identifier.
- */
- int l2cap_send_internal(uint16_t local_cid, uint8_t *data, uint16_t len);
-
- /**
- * @brief Registers L2CAP service with given PSM and MTU, and assigns a packet handler. On embedded systems, use NULL for connection parameter.
- */
- void l2cap_register_service_internal(void *connection, btstack_packet_handler_t packet_handler, uint16_t psm, uint16_t mtu, gap_security_level_t security_level);
-
- /**
- * @brief Unregisters L2CAP service with given PSM. On embedded systems, use NULL for connection parameter.
- */
- void l2cap_unregister_service_internal(void *connection, uint16_t psm);
-
- /**
- * @brief Accepts/Deny incoming L2CAP connection.
- */
- void l2cap_accept_connection_internal(uint16_t local_cid);
- void l2cap_decline_connection_internal(uint16_t local_cid, uint8_t reason);
-
- /**
- * @brief Request LE connection parameter update
- */
- int l2cap_le_request_connection_parameter_update(uint16_t handle, uint16_t interval_min, uint16_t interval_max, uint16_t slave_latency, uint16_t timeout_multiplier);
-
- /**
- * @brief Non-blocking UART write
- */
- int l2cap_can_send_packet_now(uint16_t local_cid);
- int l2cap_reserve_packet_buffer(void);
- void l2cap_release_packet_buffer(void);
-
- /**
- * @brief Get outgoing buffer and prepare data.
- */
- uint8_t *l2cap_get_outgoing_buffer(void);
-
- int l2cap_send_prepared(uint16_t local_cid, uint16_t len);
-
- int l2cap_send_prepared_connectionless(uint16_t handle, uint16_t cid, uint16_t len);
-
- /**
- * @brief Bluetooth 4.0 - allows to register handler for Attribute Protocol and Security Manager Protocol.
- */
- void l2cap_register_fixed_channel(btstack_packet_handler_t packet_handler, uint16_t channel_id);
-
- uint16_t l2cap_max_mtu(void);
- uint16_t l2cap_max_le_mtu(void);
-
- int l2cap_send_connectionless(uint16_t handle, uint16_t cid, uint8_t *data, uint16_t len);
-
-
-## RFCOMM API
-
-
- /**
- * @brief Set up RFCOMM.
- */
- void rfcomm_init(void);
-
- /**
- * @brief Set security level required for incoming connections, need to be called before registering services.
- */
- void rfcomm_set_required_security_level(gap_security_level_t security_level);
-
- /**
- * @brief Register packet handler.
- */
- void rfcomm_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size));
-
- /**
- * @brief Creates RFCOMM connection (channel) to a given server channel on a remote device with baseband address. A new baseband connection will be initiated if necessary. This channel will automatically provide enough credits to the remote side
- */
- void rfcomm_create_channel_internal(void * connection, bd_addr_t addr, uint8_t channel);
-
- /**
- * @brief Creates RFCOMM connection (channel) to a given server channel on a remote device with baseband address. new baseband connection will be initiated if necessary. This channel will use explicit credit management. During channel establishment, an initial amount of credits is provided.
- */
- void rfcomm_create_channel_with_initial_credits_internal(void * connection, bd_addr_t addr, uint8_t server_channel, uint8_t initial_credits);
-
- /**
- * @brief Disconnects RFCOMM channel with given identifier.
- */
- void rfcomm_disconnect_internal(uint16_t rfcomm_cid);
-
- /**
- * @brief Registers RFCOMM service for a server channel and a maximum frame size, and assigns a packet handler. On embedded systems, use NULL for connection parameter. This channel provides automatically enough credits to the remote side.
- */
- void rfcomm_register_service_internal(void * connection, uint8_t channel, uint16_t max_frame_size);
-
- /**
- * @brief Registers RFCOMM service for a server channel and a maximum frame size, and assigns a packet handler. On embedded systems, use NULL for connection parameter. This channel will use explicit credit management. During channel establishment, an initial amount of credits is provided.
- */
- void rfcomm_register_service_with_initial_credits_internal(void * connection, uint8_t channel, uint16_t max_frame_size, uint8_t initial_credits);
-
- /**
- * @brief Unregister RFCOMM service.
- */
- void rfcomm_unregister_service_internal(uint8_t service_channel);
-
- /**
- * @brief Accepts/Deny incoming RFCOMM connection.
- */
- void rfcomm_accept_connection_internal(uint16_t rfcomm_cid);
- void rfcomm_decline_connection_internal(uint16_t rfcomm_cid);
-
- /**
- * @brief Grant more incoming credits to the remote side for the given RFCOMM channel identifier.
- */
- void rfcomm_grant_credits(uint16_t rfcomm_cid, uint8_t credits);
-
- /**
- * @brief Checks if RFCOMM can send packet. Returns yes if packet can be sent.
- */
- int rfcomm_can_send_packet_now(uint16_t rfcomm_cid);
-
- /**
- * @brief Sends RFCOMM data packet to the RFCOMM channel with given identifier.
- */
- int rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len);
-
- /**
- * @brief Sends Local Line Status, see LINE_STATUS_..
- */
- int rfcomm_send_local_line_status(uint16_t rfcomm_cid, uint8_t line_status);
-
- /**
- * @brief Send local modem status. see MODEM_STAUS_..
- */
- int rfcomm_send_modem_status(uint16_t rfcomm_cid, uint8_t modem_status);
-
- /**
- * @brief Configure remote port
- */
- int rfcomm_send_port_configuration(uint16_t rfcomm_cid, rpn_baud_t baud_rate, rpn_data_bits_t data_bits, rpn_stop_bits_t stop_bits, rpn_parity_t parity, rpn_flow_control_t flow_control);
-
- /**
- * @brief Query remote port
- */
- int rfcomm_query_port_configuration(uint16_t rfcomm_cid);
-
- /**
- * @brief Allow to create RFCOMM packet in outgoing buffer.
- */
- int rfcomm_reserve_packet_buffer(void);
- void rfcomm_release_packet_buffer(void);
- uint8_t * rfcomm_get_outgoing_buffer(void);
- uint16_t rfcomm_get_max_frame_size(uint16_t rfcomm_cid);
- int rfcomm_send_prepared(uint16_t rfcomm_cid, uint16_t len);
-
-
-## SDP API
-
-
- /**
- * @brief Set up SDP.
- */
- void sdp_init(void);
-
- void sdp_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size));
-
- #ifdef EMBEDDED
- /**
- * @brief Register service record internally - this version doesn't copy the record therefore it must be forever accessible. Preconditions:
- - AttributeIDs are in ascending order;
- - ServiceRecordHandle is first attribute and valid.
- * @return ServiceRecordHandle or 0 if registration failed.
- */
- uint32_t sdp_register_service_internal(void *connection, service_record_item_t * record_item);
- #endif
-
- #ifndef EMBEDDED
- /**
- * @brief Register service record internally - this version creates a copy of the record precondition: AttributeIDs are in ascending order => ServiceRecordHandle is first attribute if present.
- * @return ServiceRecordHandle or 0 if registration failed
- */
- uint32_t sdp_register_service_internal(void *connection, uint8_t * service_record);
- #endif
-
- /**
- * @brief Unregister service record internally.
- */
- void sdp_unregister_service_internal(void *connection, uint32_t service_record_handle);
-
-
-## SDP Client API
-
-
-
- /**
- * @brief Queries the SDP service of the remote device given a service search pattern and a list of attribute IDs. The remote data is handled by the SDP parser. The SDP parser delivers attribute values and done event via a registered callback.
- */
- void sdp_client_query(bd_addr_t remote, uint8_t * des_serviceSearchPattern, uint8_t * des_attributeIDList);
-
- #ifdef HAVE_SDP_EXTRA_QUERIES
- void sdp_client_service_attribute_search(bd_addr_t remote, uint32_t search_serviceRecordHandle, uint8_t * des_attributeIDList);
- void sdp_client_service_search(bd_addr_t remote, uint8_t * des_serviceSearchPattern);
- #endif
-
-
-## SDP RFCOMM Query API
-
-
- /**
- * @brief SDP Query RFCOMM event to deliver channel number and service name byte by byte.
- */
- typedef struct sdp_query_rfcomm_service_event {
- uint8_t type;
- uint8_t channel_nr;
- uint8_t * service_name;
- } sdp_query_rfcomm_service_event_t;
-
- /**
- * @brief Registers a callback to receive RFCOMM service and query complete event.
- */
- void sdp_query_rfcomm_register_callback(void(*sdp_app_callback)(sdp_query_event_t * event, void * context), void * context);
-
- void sdp_query_rfcomm_deregister_callback(void);
-
- /**
- * @brief Searches SDP records on a remote device for RFCOMM services with a given UUID.
- */
- void sdp_query_rfcomm_channel_and_name_for_uuid(bd_addr_t remote, uint16_t uuid);
-
- /**
- * @brief Searches SDP records on a remote device for RFCOMM services with a given service search pattern.
- */
- void sdp_query_rfcomm_channel_and_name_for_search_pattern(bd_addr_t remote, uint8_t * des_serviceSearchPattern);
-
-
-## GATT Client API
-
-
- typedef struct gatt_complete_event{
- uint8_t type;
- uint16_t handle;
- uint16_t attribute_handle;
- uint8_t status;
- } gatt_complete_event_t;
-
- typedef struct le_service{
- uint16_t start_group_handle;
- uint16_t end_group_handle;
- uint16_t uuid16;
- uint8_t uuid128[16];
- } le_service_t;
-
- typedef struct le_service_event{
- uint8_t type;
- uint16_t handle;
- le_service_t service;
- } le_service_event_t;
-
- typedef struct le_characteristic{
- uint16_t start_handle;
- uint16_t value_handle;
- uint16_t end_handle;
- uint16_t properties;
- uint16_t uuid16;
- uint8_t uuid128[16];
- } le_characteristic_t;
-
- typedef struct le_characteristic_event{
- uint8_t type;
- uint16_t handle;
- le_characteristic_t characteristic;
- } le_characteristic_event_t;
-
- typedef struct le_characteristic_value_event{
- uint8_t type;
- uint16_t handle;
- uint16_t value_handle;
- uint16_t value_offset;
- uint16_t blob_length;
- uint8_t * blob;
- } le_characteristic_value_event_t;
-
- typedef struct le_characteristic_descriptor{
- uint16_t handle;
- uint16_t uuid16;
- uint8_t uuid128[16];
- } le_characteristic_descriptor_t;
-
- typedef struct le_characteristic_descriptor_event{
- uint8_t type;
- uint16_t handle;
- le_characteristic_descriptor_t characteristic_descriptor;
- uint16_t value_length;
- uint16_t value_offset;
- uint8_t * value;
- } le_characteristic_descriptor_event_t;
-
- /**
- * @brief Set up GATT client.
- */
- void gatt_client_init(void);
-
- /**
- * @brief Register callback (packet handler) for GATT client. Returns GATT client ID.
- */
- uint16_t gatt_client_register_packet_handler (gatt_client_callback_t callback);
-
- /**
- * @brief Unregister callback (packet handler) for GATT client.
- */
- void gatt_client_unregister_packet_handler(uint16_t gatt_client_id);
-
- /**
- * @brief MTU is available after the first query has completed. If status is equal to BLE_PERIPHERAL_OK, it returns the real value, otherwise the default value of 23.
- */
- le_command_status_t gatt_client_get_mtu(uint16_t handle, uint16_t * mtu);
-
- /**
- * @brief Returns if the GATT client is ready to receive a query. It is used with daemon.
- */
- int gatt_client_is_ready(uint16_t handle);
-
- /**
- * @brief Discovers all primary services. For each found service, an le_service_event_t with type set to GATT_SERVICE_QUERY_RESULT will be generated and passed to the registered callback. The gatt_complete_event_t, with type set to GATT_QUERY_COMPLETE, marks the end of discovery.
- */
- le_command_status_t gatt_client_discover_primary_services(uint16_t gatt_client_id, uint16_t con_handle);
-
- /**
- * @brief Discovers a specific primary service given its UUID. This service may exist multiple times. For each found service, an le_service_event_t with type set to GATT_SERVICE_QUERY_RESULT will be generated and passed to the registered callback. The gatt_complete_event_t, with type set to GATT_QUERY_COMPLETE, marks the end of discovery.
- */
- le_command_status_t gatt_client_discover_primary_services_by_uuid16(uint16_t gatt_client_id, uint16_t con_handle, uint16_t uuid16);
- le_command_status_t gatt_client_discover_primary_services_by_uuid128(uint16_t gatt_client_id, uint16_t con_handle, const uint8_t * uuid);
-
- /**
- * @brief Finds included services within the specified service. For each found included service, an le_service_event_t with type set to GATT_INCLUDED_SERVICE_QUERY_RESULT will be generated and passed to the registered callback. The gatt_complete_event_t with type set to GATT_QUERY_COMPLETE, marks the end of discovery. Information about included service type (primary/secondary) can be retrieved either by sending an ATT find information request for the returned start group handle (returning the handle and the UUID for primary or secondary service) or by comparing the service to the list of all primary services.
- */
- le_command_status_t gatt_client_find_included_services_for_service(uint16_t gatt_client_id, uint16_t con_handle, le_service_t *service);
-
- /**
- * @brief Discovers all characteristics within the specified service. For each found characteristic, an le_characteristics_event_t with type set to GATT_CHARACTERISTIC_QUERY_RESULT will be generated and passed to the registered callback. The gatt_complete_event_t with type set to GATT_QUERY_COMPLETE, marks the end of discovery.
- */
- le_command_status_t gatt_client_discover_characteristics_for_service(uint16_t gatt_client_id, uint16_t con_handle, le_service_t *service);
-
- /**
- * @brief The following four functions are used to discover all characteristics within the specified service or handle range, and return those that match the given UUID. For each found characteristic, an le_characteristic_event_t with type set to GATT_CHARACTERISTIC_QUERY_RESULT will be generated and passed to the registered callback. The gatt_complete_event_t with type set to GATT_QUERY_COMPLETE, marks the end of discovery.
- */
- le_command_status_t gatt_client_discover_characteristics_for_handle_range_by_uuid16(uint16_t gatt_client_id, uint16_t con_handle, uint16_t start_handle, uint16_t end_handle, uint16_t uuid16);
- le_command_status_t gatt_client_discover_characteristics_for_handle_range_by_uuid128(uint16_t gatt_client_id, uint16_t con_handle, uint16_t start_handle, uint16_t end_handle, uint8_t * uuid);
- le_command_status_t gatt_client_discover_characteristics_for_service_by_uuid16 (uint16_t gatt_client_id, uint16_t con_handle, le_service_t *service, uint16_t uuid16);
- le_command_status_t gatt_client_discover_characteristics_for_service_by_uuid128(uint16_t gatt_client_id, uint16_t con_handle, le_service_t *service, uint8_t * uuid128);
-
- /**
- * @brief Discovers attribute handle and UUID of a characteristic descriptor within the specified characteristic. For each found descriptor, an le_characteristic_descriptor_event_t with type set to GATT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT will be generated and passed to the registered callback. The gatt_complete_event_t with type set to GATT_QUERY_COMPLETE, marks the end of discovery.
- */
- le_command_status_t gatt_client_discover_characteristic_descriptors(uint16_t gatt_client_id, uint16_t con_handle, le_characteristic_t *characteristic);
-
- /**
- * @brief Reads the characteristic value using the characteristic's value handle. If the characteristic value is found, an le_characteristic_value_event_t with type set to GATT_CHARACTERISTIC_VALUE_QUERY_RESULT will be generated and passed to the registered callback. The gatt_complete_event_t with type set to GATT_QUERY_COMPLETE, marks the end of read.
- */
- le_command_status_t gatt_client_read_value_of_characteristic(uint16_t gatt_client_id, uint16_t con_handle, le_characteristic_t *characteristic);
- le_command_status_t gatt_client_read_value_of_characteristic_using_value_handle(uint16_t gatt_client_id, uint16_t con_handle, uint16_t characteristic_value_handle);
-
- /**
- * @brief Reads the long characteristic value using the characteristic's value handle. The value will be returned in several blobs. For each blob, an le_characteristic_value_event_t with type set to GATT_CHARACTERISTIC_VALUE_QUERY_RESULT and updated value offset will be generated and passed to the registered callback. The gatt_complete_event_t with type set to GATT_QUERY_COMPLETE, mark the end of read.
- */
- le_command_status_t gatt_client_read_long_value_of_characteristic(uint16_t gatt_client_id, uint16_t con_handle, le_characteristic_t *characteristic);
- le_command_status_t gatt_client_read_long_value_of_characteristic_using_value_handle(uint16_t gatt_client_id, uint16_t con_handle, uint16_t characteristic_value_handle);
-
- /**
- * @brief Writes the characteristic value using the characteristic's value handle without an acknowledgment that the write was successfully performed.
- */
- le_command_status_t gatt_client_write_value_of_characteristic_without_response(uint16_t gatt_client_id, uint16_t con_handle, uint16_t characteristic_value_handle, uint16_t length, uint8_t * data);
-
- /**
- * @brief Writes the authenticated characteristic value using the characteristic's value handle without an acknowledgment that the write was successfully performed.
- */
- le_command_status_t gatt_client_signed_write_without_response(uint16_t gatt_client_id, uint16_t con_handle, uint16_t handle, uint16_t message_len, uint8_t * message);
-
- /**
- * @brief Writes the characteristic value using the characteristic's value handle. The gatt_complete_event_t with type set to GATT_QUERY_COMPLETE, marks the end of write. The write is successfully performed, if the event's status field is set to 0.
- */
- le_command_status_t gatt_client_write_value_of_characteristic(uint16_t gatt_client_id, uint16_t con_handle, uint16_t characteristic_value_handle, uint16_t length, uint8_t * data);
- le_command_status_t gatt_client_write_long_value_of_characteristic(uint16_t gatt_client_id, uint16_t con_handle, uint16_t characteristic_value_handle, uint16_t length, uint8_t * data);
-
- /**
- * @brief Writes of the long characteristic value using the characteristic's value handle. It uses server response to validate that the write was correctly received. The gatt_complete_event_t with type set to GATT_QUERY_COMPLETE marks the end of write. The write is successfully performed, if the event's status field is set to 0.
- */
- le_command_status_t gatt_client_reliable_write_long_value_of_characteristic(uint16_t gatt_client_id, uint16_t con_handle, uint16_t characteristic_value_handle, uint16_t length, uint8_t * data);
-
- /**
- * @brief Reads the characteristic descriptor using its handle. If the characteristic descriptor is found, an le_characteristic_descriptor_event_t with type set to GATT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT will be generated and passed to the registered callback. The gatt_complete_event_t with type set to GATT_QUERY_COMPLETE, marks the end of read.
- */
- le_command_status_t gatt_client_read_characteristic_descriptor(uint16_t gatt_client_id, uint16_t con_handle, le_characteristic_descriptor_t * descriptor);
-
- /**
- * @brief Reads the long characteristic descriptor using its handle. It will be returned in several blobs. For each blob, an le_characteristic_descriptor_event_t with type set to GATT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT will be generated and passed to the registered callback. The gatt_complete_event_t with type set to GATT_QUERY_COMPLETE, marks the end of read.
- */
- le_command_status_t gatt_client_read_long_characteristic_descriptor(uint16_t gatt_client_id, uint16_t con_handle, le_characteristic_descriptor_t * descriptor);
-
- /**
- * @brief Writes the characteristic descriptor using its handle. The gatt_complete_event_t with type set to GATT_QUERY_COMPLETE, marks the end of write. The write is successfully performed, if the event's status field is set to 0.
- */
- le_command_status_t gatt_client_write_characteristic_descriptor(uint16_t gatt_client_id, uint16_t con_handle, le_characteristic_descriptor_t * descriptor, uint16_t length, uint8_t * data);
- le_command_status_t gatt_client_write_long_characteristic_descriptor(uint16_t gatt_client_id, uint16_t con_handle, le_characteristic_descriptor_t * descriptor, uint16_t length, uint8_t * data);
-
- /**
- * @brief Writes the client characteristic configuration of the specified characteristic. It is used to subscribe for notifications or indications of the characteristic value. For notifications or indications specify: GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION resp. GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION as configuration value.
- */
- le_command_status_t gatt_client_write_client_characteristic_configuration(uint16_t gatt_client_id, uint16_t con_handle, le_characteristic_t * characteristic, uint16_t configuration);
-
-
-## PAN API
-
-
- /**
- * @brief Creates SDP record for PANU BNEP service in provided empty buffer.
- * @note Make sure the buffer is big enough.
- *
- * @param service is an empty buffer to store service record
- * @param security_desc
- * @param name if NULL, the default service name will be assigned
- * @param description if NULL, the default service description will be assigned
- */
- void pan_create_panu_service(uint8_t *service, const char *name, const char *description, security_description_t security_desc);
-
- /**
- * @brief Creates SDP record for GN BNEP service in provided empty buffer.
- * @note Make sure the buffer is big enough.
- *
- * @param service is an empty buffer to store service record
- * @param security_desc
- * @param name if NULL, the default service name will be assigned
- * @param description if NULL, the default service description will be assigned
- * @param IPv4Subnet is optional subnet definition, e.g. "10.0.0.0/8"
- * @param IPv6Subnet is optional subnet definition given in the standard IETF format with the absolute attribute IDs
- */
- void pan_create_gn_service(uint8_t *service, const char *name, const char *description, security_description_t security_desc,
- const char *IPv4Subnet, const char *IPv6Subnet);
-
- /**
- * @brief Creates SDP record for NAP BNEP service in provided empty buffer.
- * @note Make sure the buffer is big enough.
- *
- * @param service is an empty buffer to store service record
- * @param name if NULL, the default service name will be assigned
- * @param security_desc
- * @param description if NULL, the default service description will be assigned
- * @param net_access_type type of available network access
- * @param max_net_access_rate based on net_access_type measured in byte/s
- * @param IPv4Subnet is optional subnet definition, e.g. "10.0.0.0/8"
- * @param IPv6Subnet is optional subnet definition given in the standard IETF format with the absolute attribute IDs
- */
- void pan_create_nap_service(uint8_t *service, const char *name, const char *description, security_description_t security_desc,
- net_access_type_t net_access_type, uint32_t max_net_access_rate, const char *IPv4Subnet, const char *IPv6Subnet);
-
-
-## BNEP API
-
-
- /**
- * @brief Set up BNEP.
- */
- void bnep_init(void);
-
- /**
- * @brief Check if a data packet can be send out.
- */
- int bnep_can_send_packet_now(uint16_t bnep_cid);
-
- /**
- * @brief Send a data packet.
- */
- int bnep_send(uint16_t bnep_cid, uint8_t *packet, uint16_t len);
-
- /**
- * @brief Set the network protocol filter.
- */
- int bnep_set_net_type_filter(uint16_t bnep_cid, bnep_net_filter_t *filter, uint16_t len);
-
- /**
- * @brief Set the multicast address filter.
- */
- int bnep_set_multicast_filter(uint16_t bnep_cid, bnep_multi_filter_t *filter, uint16_t len);
-
- /**
- * @brief Set security level required for incoming connections, need to be called before registering services.
- */
- void bnep_set_required_security_level(gap_security_level_t security_level);
-
- /**
- * @brief Register packet handler.
- */
- void bnep_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size));
-
- /**
- * @brief Creates BNEP connection (channel) to a given server on a remote device with baseband address. A new baseband connection will be initiated if necessary.
- */
- int bnep_connect(void * connection, bd_addr_t addr, uint16_t l2cap_psm, uint16_t uuid_dest);
-
- /**
- * @brief Disconnects BNEP channel with given identifier.
- */
- void bnep_disconnect(bd_addr_t addr);
-
- /**
- * @brief Registers BNEP service, set a maximum frame size and assigns a packet handler. On embedded systems, use NULL for connection parameter.
- */
- void bnep_register_service(void * connection, uint16_t service_uuid, uint16_t max_frame_size);
-
- /**
- * @brief Unregister BNEP service.
- */
- void bnep_unregister_service(uint16_t service_uuid);
-
-
-## GAP API
-
-
- /**
- * @brief Enable/disable bonding. Default is enabled.
- * @param enabled
- */
- void gap_set_bondable_mode(int enabled);
-
- /**
- * @brief Start dedicated bonding with device. Disconnect after bonding.
- * @param device
- * @param request MITM protection
- * @return error, if max num acl connections active
- * @result GAP_DEDICATED_BONDING_COMPLETE
- */
- int gap_dedicated_bonding(bd_addr_t device, int mitm_protection_required);
-
- gap_security_level_t gap_security_level_for_link_key_type(link_key_type_t link_key_type);
- gap_security_level_t gap_security_level(hci_con_handle_t con_handle);
-
- void gap_request_security_level(hci_con_handle_t con_handle, gap_security_level_t level);
- int gap_mitm_protection_required_for_security_level(gap_security_level_t level);
-
- /**
- * @brief Sets local name.
- * @note has to be done before stack starts up
- * @param name is not copied, make sure memory is accessible during stack startup
- */
- void gap_set_local_name(const char * local_name);
-
-
-## SM API
-
-
- /**
- * @brief Security Manager event
- */
- typedef struct sm_event {
- uint8_t type; ///< See SM_...
- uint8_t addr_type;
- bd_addr_t address;
- uint32_t passkey; ///< only used for SM_PASSKEY_DISPLAY_NUMBER
- uint16_t le_device_db_index; ///< only used for SM_IDENTITY_RESOLVING_..
- uint8_t authorization_result; ///< only use for SM_AUTHORIZATION_RESULT
- } sm_event_t;
-
- /**
- * @brief Initializes the Security Manager, connects to L2CAP
- */
- void sm_init(void);
-
- /**
- * @brief Set secret ER key for key generation as described in Core V4.0, Vol 3, Part G, 5.2.2
- * @param er
- */
- void sm_set_er(sm_key_t er);
-
- /**
- * @brief Set secret IR key for key generation as described in Core V4.0, Vol 3, Part G, 5.2.2
- */
- void sm_set_ir(sm_key_t ir);
-
- /**
- *
- * @brief Registers OOB Data Callback. The callback should set the oob_data and return 1 if OOB data is availble
- * @param get_oob_data_callback
- */
- void sm_register_oob_data_callback( int (*get_oob_data_callback)(uint8_t addres_type, bd_addr_t addr, uint8_t * oob_data));
-
- /**
- *
- * @brief Registers packet handler. Called by att_server.c
- */
- void sm_register_packet_handler(btstack_packet_handler_t handler);
-
- /**
- * @brief Limit the STK generation methods. Bonding is stopped if the resulting one isn't in the list
- * @param OR combination of SM_STK_GENERATION_METHOD_
- */
- void sm_set_accepted_stk_generation_methods(uint8_t accepted_stk_generation_methods);
-
- /**
- * @brief Set the accepted encryption key size range. Bonding is stopped if the result isn't within the range
- * @param min_size (default 7)
- * @param max_size (default 16)
- */
- void sm_set_encryption_key_size_range(uint8_t min_size, uint8_t max_size);
-
- /**
- * @brief Sets the requested authentication requirements, bonding yes/no, MITM yes/no
- * @param OR combination of SM_AUTHREQ_ flags
- */
- void sm_set_authentication_requirements(uint8_t auth_req);
-
- /**
- * @brief Sets the available IO Capabilities
- * @param IO_CAPABILITY_
- */
- void sm_set_io_capabilities(io_capability_t io_capability);
-
- /**
- * @brief Let Peripheral request an encrypted connection right after connecting
- * @note Not used normally. Bonding is triggered by access to protected attributes in ATT Server
- */
- void sm_set_request_security(int enable);
-
- /**
- * @brief Trigger Security Request
- * @note Not used normally. Bonding is triggered by access to protected attributes in ATT Server
- */
- void sm_send_security_request(uint16_t handle);
-
- /**
- * @brief Decline bonding triggered by event before
- * @param addr_type and address
- */
- void sm_bonding_decline(uint8_t addr_type, bd_addr_t address);
-
- /**
- * @brief Confirm Just Works bonding
- * @param addr_type and address
- */
- void sm_just_works_confirm(uint8_t addr_type, bd_addr_t address);
-
- /**
- * @brief Reports passkey input by user
- * @param addr_type and address
- * @param passkey in [0..999999]
- */
- void sm_passkey_input(uint8_t addr_type, bd_addr_t address, uint32_t passkey);
-
- /**
- *
- * @brief Get encryption key size.
- * @param addr_type and address
- * @return 0 if not encrypted, 7-16 otherwise
- */
- int sm_encryption_key_size(uint8_t addr_type, bd_addr_t address);
-
- /**
- * @brief Get authentication property.
- * @param addr_type and address
- * @return 1 if bonded with OOB/Passkey (AND MITM protection)
- */
- int sm_authenticated(uint8_t addr_type, bd_addr_t address);
-
- /**
- * @brief Queries authorization state.
- * @param addr_type and address
- * @return authorization_state for the current session
- */
- authorization_state_t sm_authorization_state(uint8_t addr_type, bd_addr_t address);
-
- /**
- * @brief Used by att_server.c to request user authorization.
- * @param addr_type and address
- */
- void sm_request_authorization(uint8_t addr_type, bd_addr_t address);
-
- /**
- * @brief Report user authorization decline.
- * @param addr_type and address
- */
- void sm_authorization_decline(uint8_t addr_type, bd_addr_t address);
-
- /**
- * @brief Report user authorization grant.
- * @param addr_type and address
- */
- void sm_authorization_grant(uint8_t addr_type, bd_addr_t address);
-
- /**
- * @brief Support for signed writes, used by att_server.
- * @note Message and result are in little endian to allows passing in ATT PDU without flipping them first.
- */
- int sm_cmac_ready(void);
- void sm_cmac_start(sm_key_t k, uint16_t message_len, uint8_t * message, uint32_t sign_counter, void (*done_handler)(uint8_t hash[8]));
-
- /**
- * @brief Identify device in LE Device DB.
- * @param handle
- * @return index from le_device_db or -1 if not found/identified
- */
- int sm_le_device_index(uint16_t handle );
diff --git a/docs/manual/markdown/docs/examples/.gitignore b/docs/manual/markdown/docs/examples/.gitignore
new file mode 100644
index 000000000..efcc288f8
--- /dev/null
+++ b/docs/manual/markdown/docs/examples/.gitignore
@@ -0,0 +1 @@
+generated.md
diff --git a/docs/manual/markdown/docs/examples/generated.md b/docs/manual/markdown/docs/examples/generated.md
deleted file mode 100644
index 11668b454..000000000
--- a/docs/manual/markdown/docs/examples/generated.md
+++ /dev/null
@@ -1,1368 +0,0 @@
-
-In this section, we will describe a number of examples from the
-*example/embedded* folder. To allow code-reuse with different platforms
-as well as with new ports, the low-level initialization of BTstack and
-the hardware configuration has been extracted to the various
-*platforms/PLATFORM/main.c* files. The examples only contain the
-platform-independent Bluetooth logic. But let’s have a look at the
-common init code.
-
-Listing [below](#lst:btstackInit) shows a minimal platform setup for an
-embedded system with a Bluetooth chipset connected via UART.
-
-
-
- int main(){
- // ... hardware init: watchdoch, IOs, timers, etc...
-
- // setup BTstack memory pools
- btstack_memory_init();
-
- // select embedded run loop
- run_loop_init(RUN_LOOP_EMBEDDED);
-
- // use logger: format HCI_DUMP_PACKETLOGGER, HCI_DUMP_BLUEZ or HCI_DUMP_STDOUT
- hci_dump_open(NULL, HCI_DUMP_STDOUT);
-
- // init HCI
- hci_transport_t * transport = hci_transport_h4_dma_instance();
- remote_device_db_t * remote_db = (remote_device_db_t *) &remote_device_db_memory;
- hci_init(transport, NULL, NULL, remote_db);
-
- // setup example
- btstack_main(argc, argv);
-
- // go
- run_loop_execute();
- }
-
-First, BTstack’s memory pools are setup up. Then, the standard run loop
-implementation for embedded systems is selected.
-
-The call to *hci_dump_open* configures BTstack to output all Bluetooth
-packets and it’s own debug and error message via printf. The Python
-script *tools/create_packet_log.py* can be used to convert the console
-output into a Bluetooth PacketLogger format that can be opened by the OS
-X PacketLogger tool as well as by Wireshark for further inspection. When
-asking for help, please always include a log created with HCI dump.
-
-The *hci_init* function sets up HCI to use the HCI H4 Transport
-implementation. It doesn’t provide a special transport configuration nor
-a special implementation for a particular Bluetooth chipset. It makes
-use of the *remote_device_db_memory* implementation that allows for
-re-connects without a new pairing but doesn’t persist the bonding
-information.
-
-Finally, it calls *btstack_main()* of the actual example before
-executing the run loop.
-
-
-- Hello World example:
-
- - [led_counter](#section:ledcounter): Hello World: blinking LED without Bluetooth.
-
-- GAP example:
-
- - [gap_inquiry](#section:gapinquiry): GAP Inquiry Example.
-
-- SDP Queries examples:
-
- - [sdp_general_query](#section:sdpgeneralquery): Dump remote SDP Records.
-
- - [sdp_bnep_query](#section:sdpbnepquery): Dump remote BNEP PAN protocol UUID and L2CAP PSM.
-
-- SPP Server examples:
-
- - [spp_counter](#section:sppcounter): SPP Server - Heartbeat Counter over RFCOMM.
-
- - [spp_flowcontrol](#section:sppflowcontrol): SPP Server - Flow Control.
-
-- BNEP/PAN example:
-
- - [panu_demo](#section:panudemo): PANU Demo.
-
-- Low Energy examples:
-
- - [gatt_browser](#section:gattbrowser): GATT Client - Discovering primary services and their characteristics.
-
- - [le_counter](#section:lecounter): LE Peripheral - Heartbeat Counter over GATT.
-
-- Dual Mode example:
-
- - [spp_and_le_counter](#section:sppandlecounter): Dual mode example.
-
-
-
-
-## led_counter: Hello World: blinking LED without Bluetooth
-
-
-
-
- The example demonstrates how to provide a periodic timer to toggle an LED and send debug messages to the console as a minimal BTstack test.
-
-### Periodic Timer Setup
-
-
- As timers in BTstack are single shot, the periodic counter is implemented by re-registering the timer source in the heartbeat handler callback function. [code snippet below](#ledcounter:LEDToggler) shows heartbeat handler adapted to periodically toggle an LED and print number of toggles.
-
-
-
-
-
- static void heartbeat_handler(timer_source_t *ts){
- // increment counter
- char lineBuffer[30];
- sprintf(lineBuffer, "BTstack counter %04u\n\r", ++counter);
- puts(lineBuffer);
-
- // toggle LED
- hal_led_toggle();
-
- // re-register timer
- run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS);
- run_loop_add_timer(&heartbeat);
- }
-
-
-
-
-### Main Application Setup
-
-
- [code snippet below](#ledcounter:MainConfiguration) shows main application code. It configures the heartbeat tier and adds it to the run loop.
-
-
-
-
-
- int btstack_main(int argc, const char * argv[]);
- int btstack_main(int argc, const char * argv[]){
-
- // set one-shot timer
- heartbeat.process = &heartbeat_handler;
- run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS);
- run_loop_add_timer(&heartbeat);
-
- printf("Running...\n\r");
- return 0;
- }
-
-
-
-
-## gap_inquiry: GAP Inquiry Example
-
-
-
-
- The Generic Access Profile (GAP) defines how Bluetooth devices discover and establish a connection with each other. In this example, the application discovers surrounding Bluetooth devices and collects their Class of Device (CoD), page scan mode, clock offset, and RSSI. After that, the remote name of each device is requested. In the following section we outline the Bluetooth logic part, i.e., how the packet handler handles the asynchronous events and data packets.
-
-### Bluetooth Logic
-
-
- The Bluetooth logic is implemented as a state machine within the packet handler. In this example, the following states are passed sequentially: INIT, and ACTIVE.
-
- In INIT, an inquiry scan is started, and the application transits to ACTIVE state.
-
- In ACTIVE, the following events are processed:
-
-- Inquiry result event: the list of discovered devices is processed and the Class of Device (CoD), page scan mode, clock offset, and RSSI are stored in a table.
-- Inquiry complete event: the remote name is requested for devices without a fetched name. The state of a remote name can be one of the following: REMOTE_NAME_REQUEST, REMOTE_NAME_INQUIRED, or REMOTE_NAME_FETCHED.
-- Remote name cached event: prints cached remote names provided by BTstack, if persistent storage is provided.
-- Remote name request complete event: the remote name is stored in the table and the state is updated to REMOTE_NAME_FETCHED. The query of remote names is continued.
-
-
-
- For more details on discovering remote devices, please see [here](#section:DiscoverRemoteDevices).
-
-### Main Application Setup
-
-
- [code snippet below](#gapinquiry:MainConfiguration) shows main application code. It registers the HCI packet handler and starts the Bluetooth stack.
-
-
-
-
-
- int btstack_main(int argc, const char * argv[]);
- int btstack_main(int argc, const char * argv[]) {
- hci_register_packet_handler(packet_handler);
-
- // turn on!
- hci_power_control(HCI_POWER_ON);
-
- return 0;
- }
-
-
-
-
-## sdp_general_query: Dump remote SDP Records
-
-
-
-
- The example shows how the SDP Client is used to get a list of service records on a remote device.
-
-### SDP Client Setup
-
-
- SDP is based on L2CAP. To receive SDP query events you must register a callback, i.e. query handler, with the SPD parser, as shown in [code snippet below](#sdpgeneralquery:SDPClientInit). Via this handler, the SDP client will receive the following events:
-
-- SDP_QUERY_ATTRIBUTE_VALUE containing the results of the query in chunks,
-- SDP_QUERY_COMPLETE indicating the end of the query and the status
-
-
-
-
-
-
-
- static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
- static void handle_sdp_client_query_result(sdp_query_event_t * event);
-
- static void sdp_client_init(void){
- // init L2CAP
- l2cap_init();
- l2cap_register_packet_handler(packet_handler);
-
- sdp_parser_init();
- sdp_parser_register_callback(handle_sdp_client_query_result);
- }
-
-
-
-
-### SDP Client Query
-
-
- To trigger an SDP query to get the a list of service records on a remote device, you need to call sdp_general_query_for_uuid() with the remote address and the UUID of the public browse group, as shown in [code snippet below](#sdpgeneralquery:SDPQueryUUID). In this example we used fixed address of the remote device shown in [code snippet below](#sdpgeneralquery:Remote). Please update it with the address of a device in your vicinity, e.g., one reported by the GAP Inquiry example in the previous section.
-
-
-
-
-
- static bd_addr_t remote = {0x04,0x0C,0xCE,0xE4,0x85,0xD3};
-
-
-
-
-
-
-
-
- static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
- // printf("packet_handler type %u, packet[0] %x\n", packet_type, packet[0]);
-
- if (packet_type != HCI_EVENT_PACKET) return;
- uint8_t event = packet[0];
-
- switch (event) {
- case BTSTACK_EVENT_STATE:
- if (packet[2] == HCI_STATE_WORKING){
- sdp_general_query_for_uuid(remote, SDP_PublicBrowseGroup);
- }
- break;
- default:
- break;
- }
- }
-
-
-
-
-### Handling SDP Client Query Results
-
-
- The SDP Client returns the results of the query in chunks. Each result packet contains the record ID, the Attribute ID, and a chunk of the Attribute value. In this example, we append new chunks for the same Attribute ID in a large buffer, see [code snippet below](#sdpgeneralquery:HandleSDPQUeryResult).
-
- To save memory, it's also possible to process these chunks directly by a custom stream parser, similar to the way XML files are parsed by a SAX parser. Have a look at *src/sdp_query_rfcomm.c* which retrieves the RFCOMM channel number and the service name.
-
-
-
-
-
- static void handle_sdp_client_query_result(sdp_query_event_t * event){
- sdp_query_attribute_value_event_t * ve;
- sdp_query_complete_event_t * ce;
-
- switch (event->type){
- case SDP_QUERY_ATTRIBUTE_VALUE:
- ve = (sdp_query_attribute_value_event_t*) event;
-
- // handle new record
- if (ve->record_id != record_id){
- record_id = ve->record_id;
- printf("\n---\nRecord nr. %u\n", record_id);
- }
-
- assertBuffer(ve->attribute_length);
-
- attribute_value[ve->data_offset] = ve->data;
- if ((uint16_t)(ve->data_offset+1) == ve->attribute_length){
- printf("Attribute 0x%04x: ", ve->attribute_id);
- de_dump_data_element(attribute_value);
- }
- break;
- case SDP_QUERY_COMPLETE:
- ce = (sdp_query_complete_event_t*) event;
- printf("General query done with status %d.\n\n", ce->status);
- exit(0);
- break;
- }
- }
-
-
-
-
-## sdp_bnep_query: Dump remote BNEP PAN protocol UUID and L2CAP PSM
-
-
-
-
- The example shows how the SDP Client is used to get all BNEP service records from a remote device. It extracts the remote BNEP PAN protocol UUID and the L2CAP PSM, which are needed to connect to a remote BNEP service.
-
-### SDP Client Setup
-
-
- As with the previous example, you must register a callback, i.e. query handler, with the SPD parser, as shown in [code snippet below](#sdpbnepquery:SDPClientInit). Via this handler, the SDP client will receive events:
-
-- SDP_QUERY_ATTRIBUTE_VALUE containing the results of the query in chunks,
-- SDP_QUERY_COMPLETE reporting the status and the end of the query.
-
-
-
-
-
-
-
- static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
- static void handle_sdp_client_query_result(sdp_query_event_t * event);
-
- static void sdp_client_init(void){
- // init L2CAP
- l2cap_init();
- l2cap_register_packet_handler(packet_handler);
-
- sdp_parser_init();
- sdp_parser_register_callback(handle_sdp_client_query_result);
- }
-
-
-
-
-### SDP Client Query
-
-
-
-
-
-
- static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
- if (packet_type != HCI_EVENT_PACKET) return;
- uint8_t event = packet[0];
-
- switch (event) {
- case BTSTACK_EVENT_STATE:
- // BTstack activated, get started
- if (packet[2] == HCI_STATE_WORKING){
- printf("Start SDP BNEP query.\n");
- sdp_general_query_for_uuid(remote, SDP_BNEPProtocol);
- }
- break;
- default:
- break;
- }
- }
-
-
-
-
-### Handling SDP Client Query Result
-
-
- The SDP Client returns the result of the query in chunks. Each result packet contains the record ID, the Attribute ID, and a chunk of the Attribute value, see [code snippet below](#sdpbnepquery:HandleSDPQUeryResult). Here, we show how to parse the Service Class ID List and Protocol Descriptor List, as they contain the BNEP Protocol UUID and L2CAP PSM respectively.
-
-
-
-
-
- static void handle_sdp_client_query_result(sdp_query_event_t * event){
- ...
-
- switch(ve->attribute_id){
- // 0x0001 "Service Class ID List"
- case SDP_ServiceClassIDList:
- if (de_get_element_type(attribute_value) != DE_DES) break;
- for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)){
- uint8_t * element = des_iterator_get_element(&des_list_it);
- if (de_get_element_type(element) != DE_UUID) continue;
- uint32_t uuid = de_get_uuid32(element);
- switch (uuid){
- case PANU_UUID:
- case NAP_UUID:
- case GN_UUID:
- printf(" ** Attribute 0x%04x: BNEP PAN protocol UUID: %04x\n", ve->attribute_id, uuid);
- break;
- default:
- break;
- }
- }
- break;
- ...
- case SDP_ProtocolDescriptorList:{
- printf(" ** Attribute 0x%04x: ", ve->attribute_id);
-
- uint16_t l2cap_psm = 0;
- uint16_t bnep_version = 0;
- for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)){
- if (des_iterator_get_type(&des_list_it) != DE_DES) continue;
- uint8_t * des_element = des_iterator_get_element(&des_list_it);
- des_iterator_init(&prot_it, des_element);
- uint8_t * element = des_iterator_get_element(&prot_it);
-
- if (de_get_element_type(element) != DE_UUID) continue;
- uint32_t uuid = de_get_uuid32(element);
- switch (uuid){
- case SDP_L2CAPProtocol:
- if (!des_iterator_has_more(&prot_it)) continue;
- des_iterator_next(&prot_it);
- de_element_get_uint16(des_iterator_get_element(&prot_it), &l2cap_psm);
- break;
- case SDP_BNEPProtocol:
- if (!des_iterator_has_more(&prot_it)) continue;
- des_iterator_next(&prot_it);
- de_element_get_uint16(des_iterator_get_element(&prot_it), &bnep_version);
- break;
- default:
- break;
- }
- }
- printf("l2cap_psm 0x%04x, bnep_version 0x%04x\n", l2cap_psm, bnep_version);
- }
- break;
- ...
- }
-
-
-
-
- The Service Class ID List is a Data Element Sequence (DES) of UUIDs. The BNEP PAN protocol UUID is within this list.
-
- The Protocol Descriptor List is DES which contains one DES for each protocol. For PAN serivces, it contains a DES with the L2CAP Protocol UUID and a PSM, and another DES with the BNEP UUID and the the BNEP version.
-
-## spp_counter: SPP Server - Heartbeat Counter over RFCOMM
-
-
-
-
- The Serial port profile (SPP) is widely used as it provides a serial port over Bluetooth. The SPP counter example demonstrates how to setup an SPP service, and provide a periodic timer over RFCOMM.
-
-### SPP Service Setup
-
-
- To provide an SPP service, the L2CAP, RFCOMM, and SDP protocol layers are required. After setting up an RFCOMM service with channel nubmer RFCOMM_SERVER_CHANNEL, an SDP record is created and registered with the SDP server. Example code for SPP service setup is provided in [code snippet below](#sppcounter:SPPSetup). The SDP record created by function sdp_create_spp_service consists of a basic SPP definition that uses the provided RFCOMM channel ID and service name. For more details, please have a look at it in \path{src/sdp_util.c}. The SDP record is created on the fly in RAM and is deterministic. To preserve valuable RAM, the result could be stored as constant data inside the ROM.
-
-
-
-
-
- void spp_service_setup(void){
- l2cap_init();
- l2cap_register_packet_handler(packet_handler);
-
- rfcomm_init();
- rfcomm_register_packet_handler(packet_handler);
- rfcomm_register_service_internal(NULL, RFCOMM_SERVER_CHANNEL, 0xffff); // reserved channel, mtu limited by l2cap
-
- // init SDP, create record for SPP and register with SDP
- sdp_init();
- memset(spp_service_buffer, 0, sizeof(spp_service_buffer));
- ...
- service_record_item_t * service_record_item = (service_record_item_t *) spp_service_buffer;
- sdp_create_spp_service( (uint8_t*) &service_record_item->service_record, RFCOMM_SERVER_CHANNEL, "SPP Counter");
- printf("SDP service buffer size: %u\n", (uint16_t) (sizeof(service_record_item_t) + de_get_len((uint8_t*) &service_record_item->service_record)));
- sdp_register_service_internal(NULL, service_record_item);
- ...
- }
-
-
-
-
-### Periodic Timer Setup
-
-
- The heartbeat handler increases the real counter every second, and sends a text string with the counter value, as shown in [code snippet below](#sppcounter:PeriodicCounter).
-
-
-
-
-
- static timer_source_t heartbeat;
-
- static void heartbeat_handler(struct timer *ts){
- static int counter = 0;
-
- if (rfcomm_channel_id){
- char lineBuffer[30];
- sprintf(lineBuffer, "BTstack counter %04u\n", ++counter);
- printf("%s", lineBuffer);
- if (rfcomm_can_send_packet_now(rfcomm_channel_id)) {
- int err = rfcomm_send_internal(rfcomm_channel_id, (uint8_t*) lineBuffer, strlen(lineBuffer));
- if (err) {
- log_error("rfcomm_send_internal -> error 0X%02x", err);
- }
- }
- }
- run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS);
- run_loop_add_timer(ts);
- }
-
- static void one_shot_timer_setup(void){
- // set one-shot timer
- heartbeat.process = &heartbeat_handler;
- run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS);
- run_loop_add_timer(&heartbeat);
- }
-
-
-
-
-### Bluetooth Logic
-
-
- The Bluetooth logic is implemented within the packet handler, see [code snippet below](#sppcounter:SppServerPacketHandler). In this example, the following events are passed sequentially:
-
-- BTSTACK_EVENT_STATE,
-- HCI_EVENT_PIN_CODE_REQUEST (Standard pairing) or \\ HCI_EVENT_USER_CONFIRMATION_REQUEST \\ (Secure Simple Pairing),
-- RFCOMM_EVENT_INCOMING_CONNECTION,
-- RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE, and
-- RFCOMM_EVENT_CHANNEL_CLOSED
-
-
-
- Upon receiving HCI_EVENT_PIN_CODE_REQUEST event, we need to handle authentication. Here, we use a fixed PIN code "0000".
-
- When HCI_EVENT_USER_CONFIRMATION_REQUEST is received, the user will be asked to accept the pairing request. If the IO capability is set to SSP_IO_CAPABILITY_DISPLAY_YES_NO, the request will be automatically accepted.
-
- The RFCOMM_EVENT_INCOMING_CONNECTION event indicates an incoming connection. Here, the connection is accepted. More logic is need, if you want to handle connections from multiple clients. The incoming RFCOMM connection event contains the RFCOMM channel number used during the SPP setup phase and the newly assigned RFCOMM channel ID that is used by all BTstack commands and events.
-
- If RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE event returns status greater then 0, then the channel establishment has failed (rare case, e.g., client crashes). On successful connection, the RFCOMM channel ID and MTU for this channel are made available to the heartbeat counter. After opening the RFCOMM channel, the communication between client and the application takes place. In this example, the timer handler increases the real counter every second.
-
-
-
-
-
- static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
- ...
- case HCI_EVENT_PIN_CODE_REQUEST:
- // pre-ssp: inform about pin code request
- printf("Pin code request - using '0000'\n");
- bt_flip_addr(event_addr, &packet[2]);
- hci_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, "0000");
- break;
-
- case HCI_EVENT_USER_CONFIRMATION_REQUEST:
- // ssp: inform about user confirmation request
- printf("SSP User Confirmation Request with numeric value '%06u'\n", READ_BT_32(packet, 8));
- printf("SSP User Confirmation Auto accept\n");
- break;
-
- case RFCOMM_EVENT_INCOMING_CONNECTION:
- // data: event (8), len(8), address(48), channel (8), rfcomm_cid (16)
- bt_flip_addr(event_addr, &packet[2]);
- rfcomm_channel_nr = packet[8];
- rfcomm_channel_id = READ_BT_16(packet, 9);
- printf("RFCOMM channel %u requested for %s\n", rfcomm_channel_nr, bd_addr_to_str(event_addr));
- rfcomm_accept_connection_internal(rfcomm_channel_id);
- break;
-
- case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE:
- // data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16)
- if (packet[2]) {
- printf("RFCOMM channel open failed, status %u\n", packet[2]);
- } else {
- rfcomm_channel_id = READ_BT_16(packet, 12);
- mtu = READ_BT_16(packet, 14);
- printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_channel_id, mtu);
- }
- break;
- ...
- }
-
-
-
-
-## spp_flowcontrol: SPP Server - Flow Control
-
-
-
-
- This example adds explicit flow control for incoming RFCOMM data to the SPP heartbeat counter example. We will highlight the changes compared to the SPP counter example.
-
-### SPP Service Setup
-
-
- [code snippet below](#sppflowcontrol:explicitFlowControl) shows how to provide one initial credit during RFCOMM service initialization. Please note that providing a single credit effectively reduces the credit-based (sliding window) flow control to a stop-and-wait flow control that limits the data throughput substantially.
-
-
-
-
-
- static void spp_service_setup(void){
- // init L2CAP
- l2cap_init();
- l2cap_register_packet_handler(packet_handler);
-
- // init RFCOMM
- rfcomm_init();
- rfcomm_register_packet_handler(packet_handler);
- // reserved channel, mtu limited by l2cap, 1 credit
- rfcomm_register_service_with_initial_credits_internal(NULL, rfcomm_channel_nr, 0xffff, 1);
-
- // init SDP, create record for SPP and register with SDP
- sdp_init();
- memset(spp_service_buffer, 0, sizeof(spp_service_buffer));
- service_record_item_t * service_record_item = (service_record_item_t *) spp_service_buffer;
- sdp_create_spp_service( (uint8_t*) &service_record_item->service_record, 1, "SPP Counter");
- printf("SDP service buffer size: %u\n\r", (uint16_t) (sizeof(service_record_item_t) + de_get_len((uint8_t*) &service_record_item->service_record)));
- sdp_register_service_internal(NULL, service_record_item);
- }
-
-
-
-
-### Periodic Timer Setup
-
-
- Explicit credit management is recommended when received RFCOMM data cannot be processed immediately. In this example, delayed processing of received data is simulated with the help of a periodic timer as follows. When the packet handler receives a data packet, it does not provide a new credit, it sets a flag instead, see [code snippet below](#sppflowcontrol:phManual). If the flag is set, a new credit will be granted by the heartbeat handler, introducing a delay of up to 1 second. The heartbeat handler code is shown in [code snippet below](#sppflowcontrol:hbhManual).
-
-
-
-
-
- static void heartbeat_handler(struct timer *ts){
- if (rfcomm_send_credit){
- rfcomm_grant_credits(rfcomm_channel_id, 1);
- rfcomm_send_credit = 0;
- }
- run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS);
- run_loop_add_timer(ts);
- }
-
-
-
-
-
-
-
-
- // Bluetooth logic
- static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
- ...
- case RFCOMM_DATA_PACKET:
- for (i=0;i
-
-
-
- This example implements both a PANU client and a server. In server mode, it sets up a BNEP server and registers a PANU SDP record and waits for incoming connections. In client mode, it connects to a remote device, does an SDP Query to identify the PANU service and initiates a BNEP connection.
-
-### Main application configuration
-
-
- In the application configuration, L2CAP and BNEP are initialized and a BNEP service, for server mode, is registered, before the Bluetooth stack gets started, as shown in [code snippet below](#panudemo:PanuSetup).
-
-
-
-
-
- static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
- static void handle_sdp_client_query_result(sdp_query_event_t *event);
-
- static void panu_setup(void){
- // Initialize L2CAP
- l2cap_init();
- l2cap_register_packet_handler(packet_handler);
-
- // Initialise BNEP
- bnep_init();
- bnep_register_packet_handler(packet_handler);
- // Minimum L2CAP MTU for bnep is 1691 bytes
- bnep_register_service(NULL, SDP_PANU, 1691);
-
- // Initialise SDP
- sdp_parser_init();
- sdp_parser_register_callback(handle_sdp_client_query_result);
- }
-
-
-
-
-### TUN / TAP interface routines
-
-
- This example requires a TUN/TAP interface to connect the Bluetooth network interface with the native system. It has been tested on Linux and OS X, but should work on any system that provides TUN/TAP with minor modifications.
-
- On Linux, TUN/TAP is available by default. On OS X, tuntaposx from http://tuntaposx.sourceforge.net needs to be installed.
-
- The *tap_alloc* function sets up a virtual network interface with the given Bluetooth Address. It is rather low-level as it sets up and configures a network interface.
-
- [code snippet below](#panudemo:processTapData) shows how a packet is received from the TAP network interface and forwarded over the BNEP connection.
-
- After successfully reading a network packet, the call to the *bnep_can_send_packet_now* function checks, if BTstack can forward a network packet now. If that's not possible, the received data stays in the network buffer and the data source elements is removed from the run loop. The *process_tap_dev_data* function will not be called until the data source is registered again. This provides a basic flow control.
-
-
-
-
-
- int process_tap_dev_data(struct data_source *ds)
- {
- ssize_t len;
- len = read(ds->fd, network_buffer, sizeof(network_buffer));
- if (len <= 0){
- fprintf(stderr, "TAP: Error while reading: %s\n", strerror(errno));
- return 0;
- }
-
- network_buffer_len = len;
- if (bnep_can_send_packet_now(bnep_cid)) {
- bnep_send(bnep_cid, network_buffer, network_buffer_len);
- network_buffer_len = 0;
- } else {
- // park the current network packet
- run_loop_remove_data_source(&tap_dev_ds);
- }
- return 0;
- }
-
-
-
-
-### SDP parser callback
-
-
- The SDP parsers retrieves the BNEP PAN UUID as explained in Section \ref{example:sdpbnepquery}.
-
-### Packet Handler
-
-
- The packet handler responds to various HCI Events.
-
-
-
-
-
- static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size)
- {
- ...
- switch (packet_type) {
- case HCI_EVENT_PACKET:
- event = packet[0];
- switch (event) {
- case BTSTACK_EVENT_STATE:
- if (packet[2] == HCI_STATE_WORKING) {
- printf("Start SDP BNEP query.\n");
- sdp_general_query_for_uuid(remote, SDP_BNEPProtocol);
- }
- break;
-
- ...
-
- case BNEP_EVENT_INCOMING_CONNECTION:
- // data: event(8), len(8), bnep source uuid (16), bnep destination uuid (16), remote_address (48)
- uuid_source = READ_BT_16(packet, 2);
- uuid_dest = READ_BT_16(packet, 4);
- mtu = READ_BT_16(packet, 6);
- bnep_cid = channel;
- memcpy(&event_addr, &packet[8], sizeof(bd_addr_t));
- printf("BNEP connection from %s source UUID 0x%04x dest UUID: 0x%04x, max frame size: %u\n", bd_addr_to_str(event_addr), uuid_source, uuid_dest, mtu);
- hci_local_bd_addr(local_addr);
- tap_fd = tap_alloc(tap_dev_name, local_addr);
- if (tap_fd < 0) {
- printf("Creating BNEP tap device failed: %s\n", strerror(errno));
- } else {
- printf("BNEP device \"%s\" allocated.\n", tap_dev_name);
- tap_dev_ds.fd = tap_fd;
- tap_dev_ds.process = process_tap_dev_data;
- run_loop_add_data_source(&tap_dev_ds);
- }
- break;
-
- ...
-
- case BNEP_EVENT_CHANNEL_TIMEOUT:
- printf("BNEP channel timeout! Channel will be closed\n");
- break;
-
- case BNEP_EVENT_CHANNEL_CLOSED:
- printf("BNEP channel closed\n");
- run_loop_remove_data_source(&tap_dev_ds);
- if (tap_fd > 0) {
- close(tap_fd);
- tap_fd = -1;
- }
- break;
-
- case BNEP_EVENT_READY_TO_SEND:
- // Check for parked network packets and send it out now
- if (network_buffer_len > 0) {
- bnep_send(bnep_cid, network_buffer, network_buffer_len);
- network_buffer_len = 0;
- // Re-add the tap device data source
- run_loop_add_data_source(&tap_dev_ds);
- }
-
- break;
-
- default:
- break;
- }
- break;
-
- case BNEP_DATA_PACKET:
- // Write out the ethernet frame to the tap device
- if (tap_fd > 0) {
- rc = write(tap_fd, packet, size);
- if (rc < 0) {
- fprintf(stderr, "TAP: Could not write to TAP device: %s\n", strerror(errno));
- } else
- if (rc != size) {
- fprintf(stderr, "TAP: Package written only partially %d of %d bytes\n", rc, size);
- }
- }
- break;
-
- default:
- break;
- }
- }
-
-
-
-
- When BTSTACK_EVENT_STATE with state HCI_STATE_WORKING is received and the example is started in client mode, the remote SDP BNEP query is started.
-
- In server mode, BNEP_EVENT_INCOMING_CONNECTION is received after a client has connected. The TAP network interface is then configured. A data source is set up and registered with the run loop to receive Ethernet packets from the TAP interface.
-
- The event contains both the source and destination UUIDs, as well as the MTU for this connection and the BNEP Channel ID, which is used for sending Ethernet packets over BNEP.
-
- In client mode, BNEP_EVENT_OPEN_CHANNEL_COMPLETE is received after a client has connected or when the connection fails. The status field returns the error code. It is otherwise identical to BNEP_EVENT_INCOMING_CONNECTION before.
-
- If there is a timeout during the connection setup, BNEP_EVENT_CHANNEL_TIMEOUT will be received and the BNEP connection will be closed
-
- BNEP_EVENT_CHANNEL_CLOSED is received when the connection gets closed.
-
- BNEP_EVENT_READY_TO_SEND indicates that a new packet can be send. This triggers the retry of a parked network packet. If this succeeds, the data source element is added to the run loop again.
-
- Ethernet packets from the remote device are received in the packet handler with type BNEP_DATA_PACKET. It is forwarded to the TAP interface.
-
-## gatt_browser: GATT Client - Discovering primary services and their characteristics
-
-
-
-
- This example shows how to use the GATT Client API to discover primary services and their characteristics of the first found device that is advertising its services.
-
- The logic is divided between the HCI and GATT client packet handlers. The HCI packet handler is responsible for finding a remote device, connecting to it, and for starting the first GATT client query. Then, the GATT client packet handler receives all primary services and requests the characteristics of the last one to keep the example short.
-
-
-
-### GATT client setup
-
-
- In the setup phase, a GATT client must register the HCI and GATT client packet handlers, as shown in [code snippet below](#gattbrowser:GATTClientSetup). Additionally, the security manager can be setup, if signed writes, or encrypted, or authenticated connection are required, to access the characteristics, as explained in [here](#section:smp).
-
-
-
-
-
- static uint16_t gc_id;
-
- // Handles connect, disconnect, and advertising report events,
- // starts the GATT client, and sends the first query.
- static void handle_hci_event(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
-
- // Handles GATT client query results, sends queries and the
- // GAP disconnect command when the querying is done.
- void handle_gatt_client_event(le_event_t * event);
-
- static void gatt_client_setup(void){
- // Initialize L2CAP and register HCI event handler
- l2cap_init();
- l2cap_register_packet_handler(&handle_hci_event);
-
- // Initialize GATT client
- gatt_client_init();
- // Register handler for GATT client events
- gc_id = gatt_client_register_packet_handler(&handle_gatt_client_event);
-
- // Optinoally, Setup security manager
- sm_init();
- sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
- }
-
-
-
-
-### HCI packet handler
-
-
- The HCI packet handler has to start the scanning, to find the first advertising device, to stop scanning, to connect to and later to disconnect from it, to start the GATT client upon the connection is completed, and to send the first query - in this case the gatt_client_discover_primary_services() is called, see [code snippet below](#gattbrowser:GATTBrowserHCIPacketHandler).
-
-
-
-
-
- static void handle_hci_event(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
- if (packet_type != HCI_EVENT_PACKET) return;
- advertising_report_t report;
-
- uint8_t event = packet[0];
- switch (event) {
- case BTSTACK_EVENT_STATE:
- // BTstack activated, get started
- if (packet[2] != HCI_STATE_WORKING) break;
- if (cmdline_addr_found){
- printf("Trying to connect to %s\n", bd_addr_to_str(cmdline_addr));
- le_central_connect(cmdline_addr, 0);
- break;
- }
- printf("BTstack activated, start scanning!\n");
- le_central_set_scan_parameters(0,0x0030, 0x0030);
- le_central_start_scan();
- break;
- case GAP_LE_ADVERTISING_REPORT:
- fill_advertising_report_from_packet(&report, packet);
- // stop scanning, and connect to the device
- le_central_stop_scan();
- le_central_connect(report.address,report.address_type);
- break;
- case HCI_EVENT_LE_META:
- // wait for connection complete
- if (packet[2] != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break;
- gc_handle = READ_BT_16(packet, 4);
- // query primary services
- gatt_client_discover_primary_services(gc_id, gc_handle);
- break;
- case HCI_EVENT_DISCONNECTION_COMPLETE:
- printf("\nGATT browser - DISCONNECTED\n");
- exit(0);
- break;
- default:
- break;
- }
- }
-
-
-
-
-### GATT Client event handler
-
-
- Query results and further queries are handled by the GATT client packet handler, as shown in [code snippet below](#gattbrowser:GATTBrowserQueryHandler). Here, upon receiving the primary services, the gatt_client_discover_characteristics_for_service() query for the last received service is sent. After receiving the characteristics for the service, gap_disconnect is called to terminate the connection. Upon disconnect, the HCI packet handler receives the disconnect complete event.
-
-
-
-
-
- static int search_services = 1;
-
- void handle_gatt_client_event(le_event_t * event){
- le_service_t service;
- le_characteristic_t characteristic;
- switch(event->type){
- case GATT_SERVICE_QUERY_RESULT:
- service = ((le_service_event_t *) event)->service;
- dump_service(&service);
- services[service_count++] = service;
- break;
- case GATT_CHARACTERISTIC_QUERY_RESULT:
- characteristic = ((le_characteristic_event_t *) event)->characteristic;
- dump_characteristic(&characteristic);
- break;
- case GATT_QUERY_COMPLETE:
- if (search_services){
- // GATT_QUERY_COMPLETE of search services
- service_index = 0;
- printf("\nGATT browser - CHARACTERISTIC for SERVICE ");
- printUUID128(service.uuid128); printf("\n");
- search_services = 0;
- gatt_client_discover_characteristics_for_service(gc_id, gc_handle, &services[service_index]);
- } else {
- // GATT_QUERY_COMPLETE of search characteristics
- if (service_index < service_count) {
- service = services[service_index++];
- printf("\nGATT browser - CHARACTERISTIC for SERVICE ");
- printUUID128(service.uuid128);
- printf(", [0x%04x-0x%04x]\n", service.start_group_handle, service.end_group_handle);
-
- gatt_client_discover_characteristics_for_service(gc_id, gc_handle, &service);
- break;
- }
- service_index = 0;
- gap_disconnect(gc_handle);
- }
- break;
- default:
- break;
- }
- }
-
-
-
-
-## le_counter: LE Peripheral - Heartbeat Counter over GATT
-
-
-
-
- All newer operating systems provide GATT Client functionality. The LE Counter examples demonstrates how to specify a minimal GATT Database with a custom GATT Service and a custom Characteristic that sends periodic notifications.
-
-### Main Application Setup
-
-
- [code snippet below](#lecounter:MainConfiguration) shows main application code. It initializes L2CAP, the Security Manager and configures the ATT Server with the pre-compiled ATT Database generated from $le_counter.gatt$. Finally, it configures the heartbeat handler and boots the Bluetooth stack.
-
-
-
-
-
- static int le_notification_enabled;
- static timer_source_t heartbeat;
-
- static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
- static uint16_t att_read_callback(uint16_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size);
- static int att_write_callback(uint16_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size);
- static void heartbeat_handler(struct timer *ts);
-
- static void le_counter_setup(void){
- l2cap_init();
- l2cap_register_packet_handler(packet_handler);
-
- // setup le device db
- le_device_db_init();
-
- // setup SM: Display only
- sm_init();
-
- // setup ATT server
- att_server_init(profile_data, att_read_callback, att_write_callback);
- att_dump_attributes();
- // set one-shot timer
- heartbeat.process = &heartbeat_handler;
- run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS);
- run_loop_add_timer(&heartbeat);
- }
-
-
-
-
-### Managing LE Advertisements
-
-
- To be discoverable, LE Advertisements are enabled using direct HCI Commands. As there's no guarantee that a HCI command can always be sent, the gap_run function is used to send each command as requested by the $todos$ variable. First, the advertisement data is set with hci_le_set_advertising_data. Then the advertisement parameters including the advertisement interval is set with hci_le_set_advertising_parameters. Finally, advertisements are enabled with hci_le_set_advertise_enable. See [code snippet below](#lecounter:gapRun).
-
- In this example, the Advertisement contains the Flags attribute and the device name. The flag 0x06 indicates: LE General Discoverable Mode and BR/EDR not supported.
-
-
-
-
-
- const uint8_t adv_data[] = {
- // Flags general discoverable
- 0x02, 0x01, 0x06,
- // Name
- 0x0b, 0x09, 'L', 'E', ' ', 'C', 'o', 'u', 'n', 't', 'e', 'r',
- };
-
- uint8_t adv_data_len = sizeof(adv_data);
- enum {
- SET_ADVERTISEMENT_PARAMS = 1 << 0,
- SET_ADVERTISEMENT_DATA = 1 << 1,
- ENABLE_ADVERTISEMENTS = 1 << 2,
- };
- static uint16_t todos = 0;
-
- static void gap_run(void){
-
- if (!hci_can_send_command_packet_now()) return;
-
- if (todos & SET_ADVERTISEMENT_DATA){
- printf("GAP_RUN: set advertisement data\n");
- todos &= ~SET_ADVERTISEMENT_DATA;
- hci_send_cmd(&hci_le_set_advertising_data, adv_data_len, adv_data);
- return;
- }
-
- if (todos & SET_ADVERTISEMENT_PARAMS){
- todos &= ~SET_ADVERTISEMENT_PARAMS;
- uint8_t adv_type = 0; // default
- bd_addr_t null_addr;
- memset(null_addr, 0, 6);
- uint16_t adv_int_min = 0x0030;
- uint16_t adv_int_max = 0x0030;
- hci_send_cmd(&hci_le_set_advertising_parameters, adv_int_min, adv_int_max, adv_type, 0, 0, &null_addr, 0x07, 0x00);
- return;
- }
-
- if (todos & ENABLE_ADVERTISEMENTS){
- printf("GAP_RUN: enable advertisements\n");
- todos &= ~ENABLE_ADVERTISEMENTS;
- hci_send_cmd(&hci_le_set_advertise_enable, 1);
- return;
- }
- }
-
-
-
-
-### Packet Handler
-
-
- The packet handler is only used to trigger advertisements after BTstack is ready and after disconnects, see [code snippet below](#lecounter:packetHandler). Advertisements are not automatically re-enabled after a connection was closed, even though they have been active before.
-
-
-
-
-
- static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
- switch (packet_type) {
- case HCI_EVENT_PACKET:
- switch (packet[0]) {
-
- case BTSTACK_EVENT_STATE:
- // BTstack activated, get started
- if (packet[2] == HCI_STATE_WORKING) {
- todos = SET_ADVERTISEMENT_PARAMS | SET_ADVERTISEMENT_DATA | ENABLE_ADVERTISEMENTS;
- gap_run();
- }
- break;
-
- case HCI_EVENT_DISCONNECTION_COMPLETE:
- todos = ENABLE_ADVERTISEMENTS;
- le_notification_enabled = 0;
- gap_run();
- break;
-
- default:
- break;
- }
- break;
-
- default:
- break;
- }
- gap_run();
- }
-
-
-
-
-### Heartbeat Handler
-
-
- The heartbeat handler updates the value of the single Characteristic provided in this example, and sends a notification for this characteristic if enabled, see [code snippet below](#lecounter:heartbeat).
-
-
-
-
-
- static int counter = 0;
- static char counter_string[30];
- static int counter_string_len;
-
- static void heartbeat_handler(struct timer *ts){
- counter++;
- counter_string_len = sprintf(counter_string, "BTstack counter %04u\n", counter);
- puts(counter_string);
-
- if (le_notification_enabled) {
- att_server_notify(ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE, (uint8_t*) counter_string, counter_string_len);
- }
- run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS);
- run_loop_add_timer(ts);
- }
-
-
-
-
-### ATT Read
-
-
- The ATT Server handles all reads to constant data. For dynamic data like the custom characteristic, the registered att_read_callback is called. To handle long characteristics and long reads, the att_read_callback is first called with buffer == NULL, to request the total value length. Then it will be called again requesting a chunk of the value. See [code snippet below](#lecounter:attRead).
-
-
-
-
-
-
- // ATT Client Read Callback for Dynamic Data
- // - if buffer == NULL, don't copy data, just return size of value
- // - if buffer != NULL, copy data and return number bytes copied
- // @param offset defines start of attribute value
- static uint16_t att_read_callback(uint16_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
- if (att_handle == ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE){
- if (buffer){
- memcpy(buffer, &counter_string[offset], counter_string_len - offset);
- }
- return counter_string_len - offset;
- }
- return 0;
- }
-
-
-
-
-### ATT Write
-
-
- The only valid ATT write in this example is to the Client Characteristic Configuration, which configures notification and indication. If the ATT handle matches the client configuration handle, the new configuration value is stored and used in the heartbeat handler to decide if a new value should be sent. See [code snippet below](#lecounter:attWrite).
-
-
-
-
-
- static int att_write_callback(uint16_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){
- if (att_handle != ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_CLIENT_CONFIGURATION_HANDLE) return 0;
- le_notification_enabled = READ_BT_16(buffer, 0) == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
- return 0;
- }
-
-
-
-
-## spp_and_le_counter: Dual mode example
-
-
-
-
- The SPP and LE Counter example combines the Bluetooth Classic SPP Counter and the Bluetooth LE Counter into a single application.
-
- In this Section, we only point out the differences to the individual examples and how how the stack is configured.
-
-### Advertisements
-
-
- The Flags attribute in the Advertisement Data indicates if a device is in dual-mode or not. Flag 0x02 indicates LE General Discoverable, Dual-Mode device. See [code snippet below](#sppandlecounter:advertisements).
-
-
-
-
-
- const uint8_t adv_data[] = {
- // Flags: General Discoverable
- 0x02, 0x01, 0x02,
- // Name
- 0x0b, 0x09, 'L', 'E', ' ', 'C', 'o', 'u', 'n', 't', 'e', 'r',
- };
-
-
-
-
-### Packet Handler
-
-
- The packet handler of the combined example is just the combination of the individual packet handlers.
-
-### Heartbeat Handler
-
-
- Similar to the packet handler, the heartbeat handler is the combination of the individual ones. After updating the counter, it sends an RFCOMM packet if an RFCOMM connection is active, and an LE notification if the remote side has requested notifications.
-
-
-
-
-
- static void heartbeat_handler(struct timer *ts){
-
- counter++;
- counter_string_len = sprintf(counter_string, "BTstack counter %04u\n", counter);
- // printf("%s", counter_string);
-
- if (rfcomm_channel_id){
- if (rfcomm_can_send_packet_now(rfcomm_channel_id)){
- int err = rfcomm_send_internal(rfcomm_channel_id, (uint8_t*) counter_string, counter_string_len);
- if (err) {
- log_error("rfcomm_send_internal -> error 0X%02x", err);
- }
- }
- }
-
- if (le_notification_enabled) {
- att_server_notify(ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE, (uint8_t*) counter_string, counter_string_len);
- }
- run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS);
- run_loop_add_timer(ts);
- }
-
-
-
-
-### Main Application Setup
-
-
- As with the packet and the heartbeat handlers, the combined app setup contains the code from the individual example setups.
-
-
-
-
-
- int btstack_main(void);
- int btstack_main(void)
- {
- hci_discoverable_control(1);
-
- l2cap_init();
- l2cap_register_packet_handler(packet_handler);
-
- rfcomm_init();
- rfcomm_register_packet_handler(packet_handler);
- rfcomm_register_service_internal(NULL, RFCOMM_SERVER_CHANNEL, 0xffff);
-
- // init SDP, create record for SPP and register with SDP
- sdp_init();
- memset(spp_service_buffer, 0, sizeof(spp_service_buffer));
- ...
- service_record_item_t * service_record_item = (service_record_item_t *) spp_service_buffer;
- sdp_create_spp_service( (uint8_t*) &service_record_item->service_record, RFCOMM_SERVER_CHANNEL, "SPP Counter");
- printf("SDP service buffer size: %u\n", (uint16_t) (sizeof(service_record_item_t) + de_get_len((uint8_t*) &service_record_item->service_record)));
- sdp_register_service_internal(NULL, service_record_item);
- ...
-
- hci_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
-
- // setup le device db
- le_device_db_init();
-
- // setup SM: Display only
- sm_init();
-
- // setup ATT server
- att_server_init(profile_data, att_read_callback, att_write_callback);
- att_dump_attributes();
- // set one-shot timer
- heartbeat.process = &heartbeat_handler;
- run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS);
- run_loop_add_timer(&heartbeat);
-
- // turn on!
- hci_power_control(HCI_POWER_ON);
-
- return 0;
- }
-
-
diff --git a/docs/manual/markdown/upload_site_sftp.sh b/docs/manual/markdown/upload_site_sftp.sh
index 1ebdc1cf6..5f5a65bd3 100755
--- a/docs/manual/markdown/upload_site_sftp.sh
+++ b/docs/manual/markdown/upload_site_sftp.sh
@@ -16,5 +16,6 @@ echo Uploading generated docs to ${host}/${path}/btstack with user ${user}
sftp ${user}@${host} << EOF
mkdir ${path}/btstack
put -r btstack ${path}
+ put btstack.pdf ${path}
quit
EOF