Merge branch 'feature/btdm_bt_spp_vfs' into 'master'

component/bt: Add SPP profile with VFS

See merge request idf/esp-idf!1883
This commit is contained in:
Jiang Jiang Jian 2018-04-28 15:10:07 +08:00
commit ee9ab48347
22 changed files with 1219 additions and 106 deletions

View File

@ -46,14 +46,11 @@ esp_err_t esp_spp_init(esp_spp_mode_t mode)
btc_spp_args_t arg;
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
if (mode != ESP_SPP_MODE_CB) {
LOG_ERROR("%s Watch this space!", __func__);
return ESP_FAIL;
}
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_SPP;
msg.act = BTC_SPP_ACT_INIT;
arg.init.mode = mode;
return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
}
@ -166,4 +163,13 @@ esp_err_t esp_spp_write(uint32_t handle, int len, uint8_t *p_data)
return (btc_transfer_context(&msg, &arg, sizeof(btc_spp_args_t), btc_spp_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
}
esp_err_t esp_spp_vfs_register()
{
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
btc_spp_vfs_register();
return ESP_OK;
}
#endif ///defined BTC_SPP_INCLUDED && BTC_SPP_INCLUDED == TRUE

View File

@ -62,9 +62,9 @@ typedef enum {
ESP_SPP_CLOSE_EVT = 27, /*!< When SPP connection closed, the event comes */
ESP_SPP_START_EVT = 28, /*!< When SPP server started, the event comes */
ESP_SPP_CL_INIT_EVT = 29, /*!< When SPP client initiated a connection, the event comes */
ESP_SPP_DATA_IND_EVT = 30, /*!< When SPP connection received data, the event comes */
ESP_SPP_CONG_EVT = 31, /*!< When SPP connection congestion status changed, the event comes */
ESP_SPP_WRITE_EVT = 33, /*!< When SPP write operation completes, the event comes */
ESP_SPP_DATA_IND_EVT = 30, /*!< When SPP connection received data, the event comes, olny for ESP_SPP_MODE_CB */
ESP_SPP_CONG_EVT = 31, /*!< When SPP connection congestion status changed, the event comes, olny for ESP_SPP_MODE_CB */
ESP_SPP_WRITE_EVT = 33, /*!< When SPP write operation completes, the event comes, olny for ESP_SPP_MODE_CB */
ESP_SPP_SRV_OPEN_EVT = 34, /*!< When SPP Server connection open, the event comes */
} esp_spp_cb_event_t;
@ -95,6 +95,7 @@ typedef union {
struct spp_open_evt_param {
esp_spp_status_t status; /*!< status */
uint32_t handle; /*!< The connection handle */
int fd; /*!< The file descriptor olny for ESP_SPP_MODE_VFS*/
esp_bd_addr_t rem_bda; /*!< The peer address */
} open; /*!< SPP callback param of ESP_SPP_OPEN_EVT */
@ -105,6 +106,7 @@ typedef union {
esp_spp_status_t status; /*!< status */
uint32_t handle; /*!< The connection handle */
uint32_t new_listen_handle; /*!< The new listen handle */
int fd; /*!< The file descriptor olny for ESP_SPP_MODE_VFS*/
esp_bd_addr_t rem_bda; /*!< The peer address */
} srv_open; /*!< SPP callback param of ESP_SPP_SRV_OPEN_EVT */
/**
@ -142,7 +144,6 @@ typedef union {
struct spp_write_evt_param {
esp_spp_status_t status; /*!< status */
uint32_t handle; /*!< The connection handle */
uint32_t req_id; /*!< The req_id in the associated BTA_JvRfcommWrite() */
int len; /*!< The length of the data written. */
bool cong; /*!< congestion status */
} write; /*!< SPP callback param of ESP_SPP_WRITE_EVT */
@ -275,7 +276,7 @@ esp_err_t esp_spp_start_srv(esp_spp_sec_t sec_mask,
/**
* @brief This function closes an SPP connection.
* @brief This function is used to write data, olny for ESP_SPP_MODE_CB.
*
* @param[in] handle: The connection handle.
* @param[in] len: The length of the data written.
@ -287,6 +288,16 @@ esp_err_t esp_spp_start_srv(esp_spp_sec_t sec_mask,
*/
esp_err_t esp_spp_write(uint32_t handle, int len, uint8_t *p_data);
/**
* @brief This function is used to register VFS.
*
* @return
* - ESP_OK: success
* - other: failed
*/
esp_err_t esp_spp_vfs_register(void);
#ifdef __cplusplus
}
#endif

View File

@ -1068,7 +1068,6 @@ tBTA_JV_STATUS BTA_JvRfcommReady(UINT32 handle, UINT32 *p_data_size)
** BTA_JV_FAILURE, otherwise.
**
*******************************************************************************/
// UINT8 spp_data[10] = {1,2,3,4,5,6,7,8,9,0};
tBTA_JV_STATUS BTA_JvRfcommWrite(UINT32 handle, UINT32 req_id, int len, UINT8 *p_data)
{
@ -1093,7 +1092,6 @@ tBTA_JV_STATUS BTA_JvRfcommWrite(UINT32 handle, UINT32 req_id, int len, UINT8 *p
bta_sys_sendmsg(p_msg);
status = BTA_JV_SUCCESS;
}
return (status);
}

View File

@ -26,6 +26,8 @@
#define ESP_SPP_MAX_SESSION BTA_JV_MAX_RFC_SR_SESSION
#define ESP_SPP_SERVER_NAME_MAX 32
#define ESP_SPP_RINGBUF_SIZE 1000
typedef enum {
BTC_SPP_ACT_INIT = 0,
BTC_SPP_ACT_UNINIT,
@ -40,6 +42,7 @@ typedef enum {
typedef union {
//BTC_SPP_ACT_INIT
struct init_arg {
esp_spp_mode_t mode;
} init;
//BTC_SPP_ACT_UNINIT
struct uninit_arg {
@ -84,6 +87,6 @@ void btc_spp_call_handler(btc_msg_t *msg);
void btc_spp_cb_handler(btc_msg_t *msg);
void btc_spp_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
void btc_spp_vfs_register(void);
#endif ///defined BTC_SPP_INCLUDED && BTC_SPP_INCLUDED == TRUE
#endif ///__BTC_SPP_H__

View File

@ -22,11 +22,13 @@
#include "osi/allocator.h"
#include "esp_spp_api.h"
#include "osi/list.h"
#include "freertos/ringbuf.h"
#include "osi/mutex.h"
#include <sys/errno.h>
#include <sys/lock.h>
#include <sys/fcntl.h>
#include "esp_vfs.h"
#include "esp_vfs_dev.h"
#if (defined BTC_SPP_INCLUDED && BTC_SPP_INCLUDED == TRUE)
@ -36,71 +38,127 @@ typedef struct {
bool connected;
uint8_t scn;
uint8_t max_session;
RingbufHandle_t ringbuf_read;
RingbufHandle_t ringbuf_write;
uint32_t id;
uint32_t mtu;//unused
uint32_t sdp_handle;
uint32_t rfc_handle;
uint32_t rfc_port_handle;
int fd;
uint8_t *write_data;
esp_spp_role_t role;
esp_spp_sec_t security;
esp_bd_addr_t addr;
list_t *list;
list_t *incoming_list;
uint8_t service_uuid[16];
char service_name[ESP_SPP_SERVER_NAME_MAX];
} spp_slot_t;
static spp_slot_t *spp_slots[BTA_JV_MAX_RFC_SR_SESSION + 1];
static uint32_t spp_slot_id = 0;
static osi_mutex_t spp_slot_mutex;
static struct spp_local_param_t {
spp_slot_t *spp_slots[BTA_JV_MAX_RFC_SR_SESSION + 1];
uint32_t spp_slot_id;
esp_spp_mode_t spp_mode;
int spp_max_fd;
int spp_min_fd;
int spp_fd;
osi_mutex_t spp_slot_mutex;
} spp_local_param;
static void spp_osi_free(void *p)
{
osi_free(p);
}
static spp_slot_t *malloc_spp_slot(void)
static int find_free_fd()
{
if (++spp_slot_id == 0) {
spp_slot_id = 1;
spp_local_param.spp_fd = 1;
if (spp_local_param.spp_fd < spp_local_param.spp_min_fd || spp_local_param.spp_fd > spp_local_param.spp_max_fd){
spp_local_param.spp_fd = spp_local_param.spp_min_fd;
}
for (size_t i = 1; i <= BTA_JV_MAX_RFC_SR_SESSION; i++) {
if (spp_slots[i] == NULL) {
spp_slots[i] = (spp_slot_t *)osi_malloc(sizeof(spp_slot_t));
if (!spp_slots[i]) {
if (spp_local_param.spp_slots[i] != NULL && spp_local_param.spp_slots[i]->fd == spp_local_param.spp_fd) {
i = 1;
spp_local_param.spp_fd++;
if (spp_local_param.spp_fd < spp_local_param.spp_min_fd || spp_local_param.spp_fd > spp_local_param.spp_max_fd){
spp_local_param.spp_fd = spp_local_param.spp_min_fd;
}
}
}
return spp_local_param.spp_fd++;
}
static spp_slot_t *malloc_spp_slot(void)
{
if (++spp_local_param.spp_slot_id == 0) {
spp_local_param.spp_slot_id = 1;
}
for (size_t i = 1; i <= BTA_JV_MAX_RFC_SR_SESSION; i++) {
if (spp_local_param.spp_slots[i] == NULL) {
spp_local_param.spp_slots[i] = (spp_slot_t *)osi_malloc(sizeof(spp_slot_t));
if (!spp_local_param.spp_slots[i]) {
return NULL;
}
spp_slots[i]->id = spp_slot_id;
spp_slots[i]->serial = i;
spp_slots[i]->connected = FALSE;
spp_slots[i]->list = list_new(spp_osi_free);
return spp_slots[i];
spp_local_param.spp_slots[i]->id = spp_local_param.spp_slot_id;
spp_local_param.spp_slots[i]->serial = i;
spp_local_param.spp_slots[i]->connected = FALSE;
spp_local_param.spp_slots[i]->write_data = NULL;
spp_local_param.spp_slots[i]->list = list_new(spp_osi_free);
if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) {
spp_local_param.spp_slots[i]->incoming_list = list_new(spp_osi_free);
spp_local_param.spp_slots[i]->fd = find_free_fd();
spp_local_param.spp_slots[i]->ringbuf_read = xRingbufferCreate(ESP_SPP_RINGBUF_SIZE, RINGBUF_TYPE_BYTEBUF);
spp_local_param.spp_slots[i]->ringbuf_write = xRingbufferCreate(ESP_SPP_RINGBUF_SIZE, RINGBUF_TYPE_BYTEBUF);
}
return spp_local_param.spp_slots[i];
}
}
return NULL;
}
static spp_slot_t *find_slot_by_id(uint32_t id)
{
for (size_t i = 1; i <= BTA_JV_MAX_RFC_SR_SESSION; i++) {
if (spp_slots[i] != NULL && spp_slots[i]->id == id) {
return spp_slots[i];
if (spp_local_param.spp_slots[i] != NULL && spp_local_param.spp_slots[i]->id == id) {
return spp_local_param.spp_slots[i];
}
}
return NULL;
}
static spp_slot_t *find_slot_by_handle(uint32_t handle)
{
for (size_t i = 1; i <= BTA_JV_MAX_RFC_SR_SESSION; i++) {
if (spp_slots[i] != NULL && spp_slots[i]->rfc_handle == handle) {
return spp_slots[i];
if (spp_local_param.spp_slots[i] != NULL && spp_local_param.spp_slots[i]->rfc_handle == handle) {
return spp_local_param.spp_slots[i];
}
}
return NULL;
}
static spp_slot_t *find_slot_by_fd(int fd)
{
for (size_t i = 1; i <= BTA_JV_MAX_RFC_SR_SESSION; i++) {
if (spp_local_param.spp_slots[i] != NULL && spp_local_param.spp_slots[i]->fd == fd) {
return spp_local_param.spp_slots[i];
}
}
return NULL;
}
static void free_spp_slot(spp_slot_t *slot)
{
if (!slot) {
return;
}
spp_slots[slot->serial] = NULL;
spp_local_param.spp_slots[slot->serial] = NULL;
list_free(slot->list);
if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) {
list_free(slot->incoming_list);
vRingbufferDelete(slot->ringbuf_read);
vRingbufferDelete(slot->ringbuf_write);
}
osi_free(slot);
}
@ -118,6 +176,7 @@ static void btc_create_server_fail_cb(void)
param.start.status = ESP_SPP_FAILURE;
btc_spp_cb_to_app(ESP_SPP_START_EVT, &param);
}
static void btc_disconnect_cb(uint32_t handle)
{
esp_spp_cb_param_t param;
@ -134,8 +193,8 @@ static void *btc_spp_rfcomm_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *u
void *new_user_data = NULL;
uint32_t id = (uintptr_t)user_data;
osi_mutex_lock(&spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
spp_slot_t *slot, *slot_new;
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
switch (event) {
case BTA_JV_RFCOMM_SRV_OPEN_EVT:
slot = find_slot_by_id(id);
@ -190,7 +249,7 @@ static void *btc_spp_rfcomm_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *u
default:
break;
}
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_SPP;
msg.act = event;
@ -214,31 +273,31 @@ static void btc_spp_dm_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_d
spp_slot_t *slot;
switch (event) {
case BTA_JV_GET_SCN_EVT:
osi_mutex_lock(&spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
slot = find_slot_by_id(id);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
break;
}
if (p_data->scn == 0) {
LOG_ERROR("%s unable to get scn, start server fail!", __func__);
btc_create_server_fail_cb();
free_spp_slot(slot);
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
break;
}
slot->scn = p_data->scn;
BTA_JvCreateRecordByUser(slot->service_name, slot->scn, (void *)slot->id);
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
break;
case BTA_JV_CREATE_RECORD_EVT:
osi_mutex_lock(&spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
slot = find_slot_by_id(id);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
break;
}
if (p_data->create_rec.status == BTA_JV_SUCCESS) {
@ -251,7 +310,7 @@ static void btc_spp_dm_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_d
BTA_JvFreeChannel(slot->scn, BTA_JV_CONN_TYPE_RFCOMM);
free_spp_slot(slot);
}
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
break;
default:
msg.sig = BTC_SIG_API_CB;
@ -267,48 +326,52 @@ static void btc_spp_dm_inter_cb(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_d
}
}
static void btc_spp_init(void)
static void btc_spp_init(btc_spp_args_t *arg)
{
if (osi_mutex_new(&spp_slot_mutex) != 0) {
if (osi_mutex_new(&spp_local_param.spp_slot_mutex) != 0) {
LOG_ERROR("%s osi_mutex_new failed\n", __func__);
}
spp_local_param.spp_mode = arg->init.mode;
spp_local_param.spp_slot_id = 0;
BTA_JvEnable((tBTA_JV_DM_CBACK *)btc_spp_dm_inter_cb);
}
static void btc_spp_uninit(void)
{
osi_mutex_lock(&spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
for (size_t i = 1; i <= BTA_JV_MAX_RFC_SR_SESSION; i++) {
if (spp_slots[i] != NULL && spp_slots[i]->connected) {
BTA_JvRfcommClose(spp_slots[i]->rfc_handle, (void *)spp_slots[i]->id);
free_spp_slot(spp_slots[i]);
spp_slots[i] = NULL;
if (spp_local_param.spp_slots[i] != NULL && spp_local_param.spp_slots[i]->connected) {
BTA_JvRfcommClose(spp_local_param.spp_slots[i]->rfc_handle, (void *)spp_local_param.spp_slots[i]->id);
free_spp_slot(spp_local_param.spp_slots[i]);
spp_local_param.spp_slots[i] = NULL;
}
}
for (size_t i = 1; i <= BTA_JV_MAX_RFC_SR_SESSION; i++) {
if (spp_slots[i] != NULL && !(spp_slots[i]->connected)) {
BTA_JvDeleteRecord(spp_slots[i]->sdp_handle);
BTA_JvFreeChannel(spp_slots[i]->scn, BTA_JV_CONN_TYPE_RFCOMM);
free_spp_slot(spp_slots[i]);
spp_slots[i] = NULL;
if (spp_local_param.spp_slots[i] != NULL && !(spp_local_param.spp_slots[i]->connected)) {
BTA_JvDeleteRecord(spp_local_param.spp_slots[i]->sdp_handle);
BTA_JvFreeChannel(spp_local_param.spp_slots[i]->scn, BTA_JV_CONN_TYPE_RFCOMM);
free_spp_slot(spp_local_param.spp_slots[i]);
spp_local_param.spp_slots[i] = NULL;
}
}
BTA_JvDisable();
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_free(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
osi_mutex_free(&spp_local_param.spp_slot_mutex);
}
static void btc_spp_start_discovery(btc_spp_args_t *arg)
{
BTA_JvStartDiscovery(arg->start_discovery.bd_addr, arg->start_discovery.num_uuid, arg->start_discovery.p_uuid_list, NULL);
}
static void btc_spp_connect(btc_spp_args_t *arg)
{
osi_mutex_lock(&spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
spp_slot_t *slot = malloc_spp_slot();
if (!slot) {
LOG_ERROR("%s unable to malloc RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return;
}
slot->security = arg->connect.sec_mask;
@ -317,29 +380,31 @@ static void btc_spp_connect(btc_spp_args_t *arg)
memcpy(slot->addr, arg->connect.peer_bd_addr, ESP_BD_ADDR_LEN);
BTA_JvRfcommConnect(arg->connect.sec_mask, arg->connect.role, arg->connect.remote_scn,
arg->connect.peer_bd_addr, (tBTA_JV_RFCOMM_CBACK *)btc_spp_rfcomm_inter_cb, (void *)slot->id);
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
}
static void btc_spp_disconnect(btc_spp_args_t *arg)
{
osi_mutex_lock(&spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
spp_slot_t *slot = find_slot_by_handle(arg->disconnect.handle);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot! disconnect fail!", __func__);
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return;
}
BTA_JvRfcommClose(arg->disconnect.handle, (void *)slot->id);
btc_disconnect_cb(slot->rfc_handle);
free_spp_slot(slot);
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
}
static void btc_spp_start_srv(btc_spp_args_t *arg)
{
osi_mutex_lock(&spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
spp_slot_t *slot = malloc_spp_slot();
if (!slot) {
LOG_ERROR("%s unable to malloc RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return;
}
slot->security = arg->start_srv.sec_mask;
@ -349,21 +414,34 @@ static void btc_spp_start_srv(btc_spp_args_t *arg)
strcpy(slot->service_name, arg->start_srv.name);
BTA_JvGetChannelId(BTA_JV_CONN_TYPE_RFCOMM, (void *)slot->id, arg->start_srv.local_scn);
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
}
static void btc_spp_write(btc_spp_args_t *arg)
{
osi_mutex_lock(&spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
spp_slot_t *slot = find_slot_by_handle(arg->write.handle);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot! disconnect fail!", __func__);
osi_mutex_unlock(&spp_slot_mutex);
LOG_ERROR("%s unable to find RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return;
}
if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) {
size_t item_size = 0;
if (slot->write_data != NULL) {
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return;
}
uint8_t *data = xRingbufferReceiveUpTo(slot->ringbuf_write, &item_size, 0,990);
if (item_size != 0){
slot->write_data = data;
BTA_JvRfcommWrite(arg->write.handle, slot->id, item_size, data);
}
} else {
list_append(slot->list, arg->write.p_data);
BTA_JvRfcommWrite(arg->write.handle, slot->id, arg->write.len, arg->write.p_data);
osi_mutex_unlock(&spp_slot_mutex);
}
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
}
@ -418,7 +496,7 @@ void btc_spp_call_handler(btc_msg_t *msg)
btc_spp_args_t *arg = (btc_spp_args_t *)(msg->arg);
switch (msg->act) {
case BTC_SPP_ACT_INIT:
btc_spp_init();
btc_spp_init(arg);
break;
case BTC_SPP_ACT_UNINIT:
btc_spp_uninit();
@ -449,6 +527,7 @@ void btc_spp_cb_handler(btc_msg_t *msg)
{
esp_spp_cb_param_t param;
tBTA_JV *p_data = (tBTA_JV *)msg->arg;
spp_slot_t *slot;
switch (msg->act) {
case BTA_JV_ENABLE_EVT:
param.init.status = p_data->status;
@ -468,6 +547,17 @@ void btc_spp_cb_handler(btc_msg_t *msg)
btc_spp_cb_to_app(ESP_SPP_CL_INIT_EVT, &param);
break;
case BTA_JV_RFCOMM_OPEN_EVT:
if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) {
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
slot = find_slot_by_handle(p_data->rfc_open.handle);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
break;
}
param.open.fd = slot->fd;
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
}
param.open.status = p_data->rfc_open.status;
param.open.handle = p_data->rfc_open.handle;
memcpy(param.open.rem_bda, p_data->rfc_open.rem_bda, ESP_BD_ADDR_LEN);
@ -481,6 +571,17 @@ void btc_spp_cb_handler(btc_msg_t *msg)
btc_spp_cb_to_app(ESP_SPP_START_EVT, &param);
break;
case BTA_JV_RFCOMM_SRV_OPEN_EVT:
if (spp_local_param.spp_mode == ESP_SPP_MODE_VFS) {
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
slot = find_slot_by_handle(p_data->rfc_srv_open.handle);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
break;
}
param.srv_open.fd = slot->fd;
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
}
param.srv_open.status = p_data->rfc_srv_open.status;
param.srv_open.handle = p_data->rfc_srv_open.handle;
param.srv_open.new_listen_handle = p_data->rfc_srv_open.new_listen_handle;
@ -488,21 +589,43 @@ void btc_spp_cb_handler(btc_msg_t *msg)
btc_spp_cb_to_app(ESP_SPP_SRV_OPEN_EVT, &param);
break;
case BTA_JV_RFCOMM_WRITE_EVT:
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
slot = find_slot_by_handle(p_data->rfc_write.handle);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
break;
}
if (spp_local_param.spp_mode == ESP_SPP_MODE_CB){
param.write.status = p_data->rfc_write.status;
param.write.handle = p_data->rfc_write.handle;
param.write.req_id = p_data->rfc_write.req_id;
param.write.len = p_data->rfc_write.len;
param.write.cong = p_data->rfc_write.cong;
btc_spp_cb_to_app(ESP_SPP_WRITE_EVT, &param);
osi_mutex_lock(&spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
spp_slot_t *slot = find_slot_by_handle(p_data->rfc_write.handle);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot! disconnect fail!", __func__);
osi_mutex_unlock(&spp_slot_mutex);
list_remove(slot->list, list_front(slot->list));
} else {
if (p_data->rfc_write.status != BTA_JV_SUCCESS) {
if (slot->write_data != NULL){
vRingbufferReturnItem(slot->ringbuf_write,slot->write_data);
slot->write_data = NULL;
}
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
break;
}
list_remove(slot->list, list_front(slot->list));
osi_mutex_unlock(&spp_slot_mutex);
if (p_data->rfc_write.cong == 0) {
if (slot->write_data != NULL){
vRingbufferReturnItem(slot->ringbuf_write,slot->write_data);
slot->write_data = NULL;
}
size_t item_size = 0;
uint8_t *data = xRingbufferReceiveUpTo(slot->ringbuf_write, &item_size, 0,990);
if (item_size != 0){
slot->write_data = data;
BTA_JvRfcommWrite(slot->rfc_handle, slot->id, item_size, data);
}
}
}
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
break;
case BTA_JV_RFCOMM_CLOSE_EVT:
param.close.status = p_data->rfc_close.status;
@ -512,10 +635,33 @@ void btc_spp_cb_handler(btc_msg_t *msg)
btc_spp_cb_to_app(ESP_SPP_CLOSE_EVT, &param);
break;
case BTA_JV_RFCOMM_CONG_EVT:
if (spp_local_param.spp_mode == ESP_SPP_MODE_CB) {
param.cong.status = p_data->rfc_cong.status;
param.cong.handle = p_data->rfc_cong.handle;
param.cong.cong = p_data->rfc_cong.cong;
btc_spp_cb_to_app(ESP_SPP_CONG_EVT, &param);
} else {
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
slot = find_slot_by_handle(p_data->rfc_cong.handle);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
break;
}
if (p_data->rfc_cong.cong == 0) {
if (slot->write_data != NULL){
vRingbufferReturnItem(slot->ringbuf_write,slot->write_data);
slot->write_data = NULL;
}
size_t item_size = 0;
uint8_t *data = xRingbufferReceiveUpTo(slot->ringbuf_write, &item_size, 0,990);
if (item_size != 0){
slot->write_data = data;
BTA_JvRfcommWrite(slot->rfc_handle, slot->id, item_size, data);
}
}
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
}
break;
case BTA_JV_RFCOMM_DATA_IND_EVT:
param.data_ind.status = ESP_SPP_SUCCESS;
@ -538,9 +684,25 @@ void btc_spp_cb_handler(btc_msg_t *msg)
}
/**
* If there is too much data not accepted, wait until accepted,or discard it when timeout!
* @param list list
* @param p_buf incoming data
*/
static void spp_delay_append(list_t *list, BT_HDR *p_buf){
uint8_t count = 0;
while (count++ < 100) {
if (list_length(list) < 200){
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
list_append(list, p_buf);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return;
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
osi_free(p_buf);
LOG_WARN("%s There is too much data not accepted, discard it!", __func__);
}
int bta_co_rfc_data_incoming(void *user_data, BT_HDR *p_buf)
{
@ -552,22 +714,38 @@ int bta_co_rfc_data_incoming(void *user_data, BT_HDR *p_buf)
msg.act = BTA_JV_RFCOMM_DATA_IND_EVT;
uint32_t id = (uintptr_t)user_data;
osi_mutex_lock(&spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
spp_slot_t *slot = find_slot_by_id(id);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_slot_mutex);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return 0;
}
p_data.data_ind.handle = slot->rfc_handle;
if (spp_local_param.spp_mode == ESP_SPP_MODE_CB) {
p_data.data_ind.p_buf = p_buf;
status = btc_transfer_context(&msg, &p_data,
sizeof(tBTA_JV), NULL);
if (status != BT_STATUS_SUCCESS) {
LOG_ERROR("%s btc_transfer_context failed\n", __func__);
}
osi_mutex_unlock(&spp_slot_mutex);
} else {
if (list_is_empty(slot->incoming_list)) {
BaseType_t done = xRingbufferSend(slot->ringbuf_read, p_buf->data + p_buf->offset, p_buf->len, 0);
if (done) {
osi_free (p_buf);
} else {
list_append(slot->incoming_list, p_buf);
}
} else {
list_t *list = slot->incoming_list;
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
spp_delay_append(list,p_buf);
return 1;
}
}
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return 1;
}
int bta_co_rfc_data_outgoing_size(void *user_data, int *size)
@ -579,4 +757,87 @@ int bta_co_rfc_data_outgoing(void *user_data, uint8_t *buf, uint16_t size)
return 1;
}
static ssize_t spp_vfs_write(int fd, const void * data, size_t size)
{
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
spp_slot_t *slot = find_slot_by_fd(fd);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return -1;
}
BaseType_t done = xRingbufferSend(slot->ringbuf_write, (void *)data, size, 0);
esp_spp_write(slot->rfc_handle, 0, NULL);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
if (done){
return size;
} else {
return 0;
}
}
static int spp_vfs_close(int fd)
{
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
spp_slot_t *slot = find_slot_by_fd(fd);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return -1;
}
esp_spp_disconnect(slot->rfc_handle);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return 0;
}
static bool incoming_list_2_ringbuf_read(spp_slot_t *slot)
{
while (!list_is_empty(slot->incoming_list)) {
BT_HDR *p_buf = list_front(slot->incoming_list);
BaseType_t done = xRingbufferSend(slot->ringbuf_read, p_buf->data + p_buf->offset, p_buf->len, 0);
if (done) {
list_remove(slot->incoming_list, p_buf);
} else {
return false;
}
}
return true;
}
static ssize_t spp_vfs_read(int fd, void * dst, size_t size)
{
osi_mutex_lock(&spp_local_param.spp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
spp_slot_t *slot = find_slot_by_fd(fd);
if (!slot) {
LOG_ERROR("%s unable to find RFCOMM slot!", __func__);
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return -1;
}
incoming_list_2_ringbuf_read(slot);
size_t item_size = 0;
uint8_t *data = xRingbufferReceiveUpTo(slot->ringbuf_read, &item_size, 0, size);
if (item_size > 0){
memcpy(dst, data, item_size);
vRingbufferReturnItem(slot->ringbuf_read, data);
}
osi_mutex_unlock(&spp_local_param.spp_slot_mutex);
return item_size;
}
void btc_spp_vfs_register()
{
esp_vfs_t vfs = {
.flags = ESP_VFS_FLAG_DEFAULT | ESP_VFS_FLAG_SHARED_FD_SPACE,
.write = spp_vfs_write,
.open = NULL,
.fstat = NULL,
.close = spp_vfs_close,
.read = spp_vfs_read,
.fcntl = NULL
};
ESP_ERROR_CHECK(esp_vfs_register_socket_space(&vfs, NULL, &spp_local_param.spp_min_fd, &spp_local_param.spp_max_fd));
spp_local_param.spp_fd = spp_local_param.spp_min_fd;
}
#endif ///defined BTC_SPP_INCLUDED && BTC_SPP_INCLUDED == TRUE

View File

@ -2585,7 +2585,7 @@ void l2cu_adjust_out_mps (tL2C_CCB *p_ccb)
if (packet_size <= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD + L2CAP_SDU_LEN_OVERHEAD + L2CAP_FCS_LEN)) {
/* something is very wrong */
L2CAP_TRACE_ERROR ("l2cu_adjust_out_mps bad packet size: %u will use MPS: %u", packet_size, p_ccb->peer_cfg.fcr.mps);
L2CAP_TRACE_DEBUG ("l2cu_adjust_out_mps bad packet size: %u will use MPS: %u", packet_size, p_ccb->peer_cfg.fcr.mps);
p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps;
} else {
packet_size -= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD + L2CAP_SDU_LEN_OVERHEAD + L2CAP_FCS_LEN);

View File

@ -5,6 +5,6 @@
PROJECT_NAME := bt_spp_acceptor_demo
#COMPONENT_ADD_INCLUDEDIRS := components/include
COMPONENT_ADD_INCLUDEDIRS := components/include
include $(IDF_PATH)/make/project.mk

View File

@ -5,6 +5,6 @@
PROJECT_NAME := bt_spp_initiator_demo
#COMPONENT_ADD_INCLUDEDIRS := components/include
COMPONENT_ADD_INCLUDEDIRS := components/include
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,10 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := bt_spp_vfs_acceptor_demo
COMPONENT_ADD_INCLUDEDIRS := components/include
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,14 @@
ESP-IDF BT-SPP-VFS-ACCEPTOR demo
======================
Demo of SPP acceptor role
This is the demo for user to use ESP_APIs to create a SPP acceptor.
Options choose step:
1. make menuconfig.
2. enter menuconfig "Component config", choose "Bluetooth"
3. enter menu Bluetooth, choose "Classic Bluetooth" and "SPP Profile"
4. choose your options.
After the program started, bt_spp_vfs_initator will connect it and send data.

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,150 @@
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/****************************************************************************
*
* This file is for bt_spp_vfs_acceptor demo. It can create servers, wait for connected and receive data.
* run bt_spp_vfs_acceptor demo, the bt_spp_vfs_initiator demo will automatically connect the bt_spp_vfs_acceptor demo,
* then receive data.
*
****************************************************************************/
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "nvs.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_bt_api.h"
#include "esp_bt_device.h"
#include "esp_spp_api.h"
#include "spp_task.h"
#include "time.h"
#include "sys/time.h"
#include "esp_vfs.h"
#include "sys/unistd.h"
#define SPP_TAG "SPP_ACCEPTOR_DEMO"
#define SPP_SERVER_NAME "SPP_SERVER"
#define EXCAMPLE_DEVICE_NAME "ESP_SPP_ACCEPTOR"
static const esp_spp_mode_t esp_spp_mode = ESP_SPP_MODE_VFS;
static const esp_spp_sec_t sec_mask = ESP_SPP_SEC_NONE;
static const esp_spp_role_t role_slave = ESP_SPP_ROLE_SLAVE;
#define SPP_DATA_LEN 100
static uint8_t spp_data[SPP_DATA_LEN];
static void spp_read_handle(void * param)
{
int size = 0;
int fd = (int)param;
do {
size = read (fd, spp_data, SPP_DATA_LEN);
ESP_LOGI(SPP_TAG, "fd = %d data_len = %d", fd, size);
if (size == -1) {
break;
}
esp_log_buffer_hex(SPP_TAG, spp_data, size);
if (size == 0) {
/*read fail due to there is no data, retry after 1s*/
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
} while (1);
spp_wr_task_shut_down();
}
static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
switch (event) {
case ESP_SPP_INIT_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT");
esp_bt_dev_set_device_name(EXCAMPLE_DEVICE_NAME);
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
esp_spp_start_srv(sec_mask,role_slave, 0, SPP_SERVER_NAME);
break;
case ESP_SPP_DISCOVERY_COMP_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT");
break;
case ESP_SPP_OPEN_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_OPEN_EVT");
break;
case ESP_SPP_CLOSE_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT");
break;
case ESP_SPP_START_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_START_EVT");
break;
case ESP_SPP_CL_INIT_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_CL_INIT_EVT");
break;
case ESP_SPP_SRV_OPEN_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_SRV_OPEN_EVT");
spp_wr_task_start_up(spp_read_handle, param->srv_open.fd);
break;
default:
break;
}
}
static void esp_spp_stack_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
spp_task_work_dispatch((spp_task_cb_t)esp_spp_cb, event, param, sizeof(esp_spp_cb_param_t), NULL);
}
void app_main()
{
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
if (esp_bt_controller_init(&bt_cfg) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s initialize controller failed", __func__);
return;
}
if (esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s enable controller failed", __func__);
return;
}
if (esp_bluedroid_init() != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s initialize bluedroid failed", __func__);
return;
}
if (esp_bluedroid_enable() != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s enable bluedroid failed", __func__);
return;
}
if (esp_spp_register_callback(esp_spp_stack_cb) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s spp register failed", __func__);
return;
}
esp_spp_vfs_register();
spp_task_task_start_up();
if (esp_spp_init(esp_spp_mode) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s spp init failed", __func__);
return;
}
}

View File

@ -0,0 +1,128 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "freertos/xtensa_api.h"
#include "freertos/FreeRTOSConfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "spp_task.h"
static void spp_task_task_handler(void *arg);
static bool spp_task_send_msg(spp_task_msg_t *msg);
static void spp_task_work_dispatched(spp_task_msg_t *msg);
static xQueueHandle spp_task_task_queue = NULL;
static xTaskHandle spp_task_task_handle = NULL;
bool spp_task_work_dispatch(spp_task_cb_t p_cback, uint16_t event, void *p_params, int param_len, spp_task_copy_cb_t p_copy_cback)
{
ESP_LOGD(SPP_TASK_TAG, "%s event 0x%x, param len %d", __func__, event, param_len);
spp_task_msg_t msg;
memset(&msg, 0, sizeof(spp_task_msg_t));
msg.sig = SPP_TASK_SIG_WORK_DISPATCH;
msg.event = event;
msg.cb = p_cback;
if (param_len == 0) {
return spp_task_send_msg(&msg);
} else if (p_params && param_len > 0) {
if ((msg.param = malloc(param_len)) != NULL) {
memcpy(msg.param, p_params, param_len);
/* check if caller has provided a copy callback to do the deep copy */
if (p_copy_cback) {
p_copy_cback(&msg, msg.param, p_params);
}
return spp_task_send_msg(&msg);
}
}
return false;
}
static bool spp_task_send_msg(spp_task_msg_t *msg)
{
if (msg == NULL) {
return false;
}
if (xQueueSend(spp_task_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) {
ESP_LOGE(SPP_TASK_TAG, "%s xQueue send failed", __func__);
return false;
}
return true;
}
static void spp_task_work_dispatched(spp_task_msg_t *msg)
{
if (msg->cb) {
msg->cb(msg->event, msg->param);
}
}
static void spp_task_task_handler(void *arg)
{
spp_task_msg_t msg;
for (;;) {
if (pdTRUE == xQueueReceive(spp_task_task_queue, &msg, (portTickType)portMAX_DELAY)) {
ESP_LOGD(SPP_TASK_TAG, "%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event);
switch (msg.sig) {
case SPP_TASK_SIG_WORK_DISPATCH:
spp_task_work_dispatched(&msg);
break;
default:
ESP_LOGW(SPP_TASK_TAG, "%s, unhandled sig: %d", __func__, msg.sig);
break;
}
if (msg.param) {
free(msg.param);
}
}
}
}
void spp_task_task_start_up(void)
{
spp_task_task_queue = xQueueCreate(10, sizeof(spp_task_msg_t));
xTaskCreate(spp_task_task_handler, "SPPAppT", 2048, NULL, 10, spp_task_task_handle);
return;
}
void spp_task_task_shut_down(void)
{
if (spp_task_task_handle) {
vTaskDelete(spp_task_task_handle);
spp_task_task_handle = NULL;
}
if (spp_task_task_queue) {
vQueueDelete(spp_task_task_queue);
spp_task_task_queue = NULL;
}
}
void spp_wr_task_start_up(spp_wr_task_cb_t p_cback, int fd)
{
xTaskCreate(p_cback, "write_read", 2048, (void *)fd, 5, NULL);
}
void spp_wr_task_shut_down(void)
{
vTaskDelete(NULL);
}

View File

@ -0,0 +1,63 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __SPP_TASK_H__
#define __SPP_TASK_H__
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#define SPP_TASK_TAG "SPP_TASK"
#define SPP_TASK_SIG_WORK_DISPATCH (0x01)
/**
* @brief handler for the dispatched work
*/
typedef void (* spp_task_cb_t) (uint16_t event, void *param);
/* message to be sent */
typedef struct {
uint16_t sig; /*!< signal to spp_task_task */
uint16_t event; /*!< message event id */
spp_task_cb_t cb; /*!< context switch callback */
void *param; /*!< parameter area needs to be last */
} spp_task_msg_t;
/**
* @brief parameter deep-copy function to be customized
*/
typedef void (* spp_task_copy_cb_t) (spp_task_msg_t *msg, void *p_dest, void *p_src);
/**
* @brief work dispatcher for the application task
*/
bool spp_task_work_dispatch(spp_task_cb_t p_cback, uint16_t event, void *p_params, int param_len, spp_task_copy_cb_t p_copy_cback);
void spp_task_task_start_up(void);
void spp_task_task_shut_down(void);
/**
* @brief handler for write and read
*/
typedef void (* spp_wr_task_cb_t) (void *fd);
void spp_wr_task_start_up(spp_wr_task_cb_t p_cback, int fd);
void spp_wr_task_shut_down(void);
#endif ///__SPP_TASK_H__

View File

@ -0,0 +1,6 @@
# Override some defaults so BT stack is enabled
# and WiFi disabled by default in this example
CONFIG_BT_ENABLED=y
CONFIG_CLASSIC_BT_ENABLED=y
CONFIG_WIFI_ENABLED=n
CONFIG_BT_SPP_ENABLED=y

View File

@ -0,0 +1,10 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := bt_spp_vfs_initiator_demo
COMPONENT_ADD_INCLUDEDIRS := components/include
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,14 @@
ESP-IDF BT-SPP-VFS-INITATOR demo
======================
Demo of SPP initator role
This is the demo for user to use ESP_APIs to create a SPP initator.
Options choose step:
1. make menuconfig.
2. enter menuconfig "Component config", choose "Bluetooth"
3. enter menu Bluetooth, choose "Classic Bluetooth" and "SPP Profile"
4. choose your options.
After the program started, It will connect to bt_spp_vfs_acceptor and send data.

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,234 @@
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/****************************************************************************
*
* This file is for bt_spp_vfs_initiator demo. It can discovery servers, connect one device and send data.
* run bt_spp_vfs_initiator demo, the bt_spp_vfs_initiator demo will automatically connect the bt_spp_vfs_acceptor demo,
* then send data.
*
****************************************************************************/
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "nvs.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_bt_api.h"
#include "esp_bt_device.h"
#include "esp_spp_api.h"
#include "spp_task.h"
#include "time.h"
#include "sys/time.h"
#include "esp_vfs.h"
#include "sys/unistd.h"
#define SPP_TAG "SPP_INITIATOR_DEMO"
#define EXCAMPLE_DEVICE_NAME "ESP_SPP_INITIATOR"
static const esp_spp_mode_t esp_spp_mode = ESP_SPP_MODE_VFS;
static const esp_spp_sec_t sec_mask = ESP_SPP_SEC_NONE;
static const esp_spp_role_t role_master = ESP_SPP_ROLE_MASTER;
static esp_bd_addr_t peer_bd_addr;
static uint8_t peer_bdname_len;
static char peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1];
static const char remote_device_name[] = "ESP_SPP_ACCEPTOR";
static const esp_bt_inq_mode_t inq_mode = ESP_BT_INQ_MODE_GENERAL_INQUIRY;
static const uint8_t inq_len = 30;
static const uint8_t inq_num_rsps = 0;
#define SPP_DATA_LEN 20
static uint8_t spp_data[SPP_DATA_LEN];
static void spp_write_handle(void * param)
{
int size = 0;
int fd = (int)param;
printf("%s %d %p\n", __func__,fd,param);
do {
size = write (fd, spp_data, SPP_DATA_LEN);
ESP_LOGI(SPP_TAG, "fd = %d data_len = %d",fd, size);
if (size == -1) {
break;
} else if ( size == 0) {
/*write fail due to ringbuf is full, retry after 1s*/
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
} while (1);
spp_wr_task_shut_down();
}
static bool get_name_from_eir(uint8_t *eir, char *bdname, uint8_t *bdname_len)
{
uint8_t *rmt_bdname = NULL;
uint8_t rmt_bdname_len = 0;
if (!eir) {
return false;
}
rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &rmt_bdname_len);
if (!rmt_bdname) {
rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &rmt_bdname_len);
}
if (rmt_bdname) {
if (rmt_bdname_len > ESP_BT_GAP_MAX_BDNAME_LEN) {
rmt_bdname_len = ESP_BT_GAP_MAX_BDNAME_LEN;
}
if (bdname) {
memcpy(bdname, rmt_bdname, rmt_bdname_len);
bdname[rmt_bdname_len] = '\0';
}
if (bdname_len) {
*bdname_len = rmt_bdname_len;
}
return true;
}
return false;
}
static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
switch (event) {
case ESP_SPP_INIT_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT");
esp_bt_dev_set_device_name(EXCAMPLE_DEVICE_NAME);
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
esp_bt_gap_start_discovery(inq_mode, inq_len, inq_num_rsps);
break;
case ESP_SPP_DISCOVERY_COMP_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT status=%d scn_num=%d",param->disc_comp.status, param->disc_comp.scn_num);
if (param->disc_comp.status == ESP_SPP_SUCCESS) {
esp_spp_connect(sec_mask, role_master, param->disc_comp.scn[0], peer_bd_addr);
}
break;
case ESP_SPP_OPEN_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_OPEN_EVT");
spp_wr_task_start_up(spp_write_handle, param->open.fd);
break;
case ESP_SPP_CLOSE_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT");
break;
case ESP_SPP_START_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_START_EVT");
break;
case ESP_SPP_CL_INIT_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_CL_INIT_EVT");
break;
case ESP_SPP_SRV_OPEN_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_SRV_OPEN_EVT");
break;
default:
break;
}
}
static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
switch(event){
case ESP_BT_GAP_DISC_RES_EVT:
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_DISC_RES_EVT");
esp_log_buffer_hex(SPP_TAG, param->disc_res.bda, ESP_BD_ADDR_LEN);
for (int i = 0; i < param->disc_res.num_prop; i++){
if (param->disc_res.prop[i].type == ESP_BT_GAP_DEV_PROP_EIR
&& get_name_from_eir(param->disc_res.prop[i].val, peer_bdname, &peer_bdname_len)){
esp_log_buffer_char(SPP_TAG, peer_bdname, peer_bdname_len);
if (strlen(remote_device_name) == peer_bdname_len
&& strncmp(peer_bdname, remote_device_name, peer_bdname_len) == 0) {
memcpy(peer_bd_addr, param->disc_res.bda, ESP_BD_ADDR_LEN);
esp_spp_start_discovery(peer_bd_addr);
esp_bt_gap_cancel_discovery();
}
}
}
break;
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT:
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_DISC_STATE_CHANGED_EVT");
break;
case ESP_BT_GAP_RMT_SRVCS_EVT:
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_RMT_SRVCS_EVT");
break;
case ESP_BT_GAP_RMT_SRVC_REC_EVT:
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_RMT_SRVC_REC_EVT");
break;
default:
break;
}
}
static void esp_spp_stack_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
spp_task_work_dispatch((spp_task_cb_t)esp_spp_cb, event, param, sizeof(esp_spp_cb_param_t), NULL);
}
void app_main()
{
for (int i = 0; i < SPP_DATA_LEN; ++i) {
spp_data[i] = i;
}
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
if (esp_bt_controller_init(&bt_cfg) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s initialize controller failed", __func__);
return;
}
if (esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s enable controller failed", __func__);
return;
}
if (esp_bluedroid_init() != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s initialize bluedroid failed", __func__);
return;
}
if (esp_bluedroid_enable() != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s enable bluedroid failed", __func__);
return;
}
if (esp_bt_gap_register_callback(esp_bt_gap_cb) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s gap register failed", __func__);
return;
}
if (esp_spp_register_callback(esp_spp_stack_cb) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s spp register failed", __func__);
return;
}
esp_spp_vfs_register();
spp_task_task_start_up();
if (esp_spp_init(esp_spp_mode) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s spp init failed", __func__);
return;
}
}

View File

@ -0,0 +1,128 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "freertos/xtensa_api.h"
#include "freertos/FreeRTOSConfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "spp_task.h"
static void spp_task_task_handler(void *arg);
static bool spp_task_send_msg(spp_task_msg_t *msg);
static void spp_task_work_dispatched(spp_task_msg_t *msg);
static xQueueHandle spp_task_task_queue = NULL;
static xTaskHandle spp_task_task_handle = NULL;
bool spp_task_work_dispatch(spp_task_cb_t p_cback, uint16_t event, void *p_params, int param_len, spp_task_copy_cb_t p_copy_cback)
{
ESP_LOGD(SPP_TASK_TAG, "%s event 0x%x, param len %d", __func__, event, param_len);
spp_task_msg_t msg;
memset(&msg, 0, sizeof(spp_task_msg_t));
msg.sig = SPP_TASK_SIG_WORK_DISPATCH;
msg.event = event;
msg.cb = p_cback;
if (param_len == 0) {
return spp_task_send_msg(&msg);
} else if (p_params && param_len > 0) {
if ((msg.param = malloc(param_len)) != NULL) {
memcpy(msg.param, p_params, param_len);
/* check if caller has provided a copy callback to do the deep copy */
if (p_copy_cback) {
p_copy_cback(&msg, msg.param, p_params);
}
return spp_task_send_msg(&msg);
}
}
return false;
}
static bool spp_task_send_msg(spp_task_msg_t *msg)
{
if (msg == NULL) {
return false;
}
if (xQueueSend(spp_task_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) {
ESP_LOGE(SPP_TASK_TAG, "%s xQueue send failed", __func__);
return false;
}
return true;
}
static void spp_task_work_dispatched(spp_task_msg_t *msg)
{
if (msg->cb) {
msg->cb(msg->event, msg->param);
}
}
static void spp_task_task_handler(void *arg)
{
spp_task_msg_t msg;
for (;;) {
if (pdTRUE == xQueueReceive(spp_task_task_queue, &msg, (portTickType)portMAX_DELAY)) {
ESP_LOGD(SPP_TASK_TAG, "%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event);
switch (msg.sig) {
case SPP_TASK_SIG_WORK_DISPATCH:
spp_task_work_dispatched(&msg);
break;
default:
ESP_LOGW(SPP_TASK_TAG, "%s, unhandled sig: %d", __func__, msg.sig);
break;
}
if (msg.param) {
free(msg.param);
}
}
}
}
void spp_task_task_start_up(void)
{
spp_task_task_queue = xQueueCreate(10, sizeof(spp_task_msg_t));
xTaskCreate(spp_task_task_handler, "SPPAppT", 2048, NULL, 10, &spp_task_task_handle);
return;
}
void spp_task_task_shut_down(void)
{
if (spp_task_task_handle) {
vTaskDelete(spp_task_task_handle);
spp_task_task_handle = NULL;
}
if (spp_task_task_queue) {
vQueueDelete(spp_task_task_queue);
spp_task_task_queue = NULL;
}
}
void spp_wr_task_start_up(spp_wr_task_cb_t p_cback, int fd)
{
xTaskCreate(p_cback, "write_read", 2048, (void *)fd, 5, NULL);
}
void spp_wr_task_shut_down(void)
{
vTaskDelete(NULL);
}

View File

@ -0,0 +1,63 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __SPP_TASK_H__
#define __SPP_TASK_H__
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#define SPP_TASK_TAG "SPP_TASK"
#define SPP_TASK_SIG_WORK_DISPATCH (0x01)
/**
* @brief handler for the dispatched work
*/
typedef void (* spp_task_cb_t) (uint16_t event, void *param);
/* message to be sent */
typedef struct {
uint16_t sig; /*!< signal to spp_task_task */
uint16_t event; /*!< message event id */
spp_task_cb_t cb; /*!< context switch callback */
void *param; /*!< parameter area needs to be last */
} spp_task_msg_t;
/**
* @brief parameter deep-copy function to be customized
*/
typedef void (* spp_task_copy_cb_t) (spp_task_msg_t *msg, void *p_dest, void *p_src);
/**
* @brief work dispatcher for the application task
*/
bool spp_task_work_dispatch(spp_task_cb_t p_cback, uint16_t event, void *p_params, int param_len, spp_task_copy_cb_t p_copy_cback);
void spp_task_task_start_up(void);
void spp_task_task_shut_down(void);
/**
* @brief handler for write and read
*/
typedef void (* spp_wr_task_cb_t) (void *fd);
void spp_wr_task_start_up(spp_wr_task_cb_t p_cback, int fd);
void spp_wr_task_shut_down(void);
#endif ///__SPP_TASK_H__

View File

@ -0,0 +1,6 @@
# Override some defaults so BT stack is enabled
# and WiFi disabled by default in this example
CONFIG_BT_ENABLED=y
CONFIG_CLASSIC_BT_ENABLED=y
CONFIG_WIFI_ENABLED=n
CONFIG_BT_SPP_ENABLED=y