From dd288f2b76c447192f8eabab42c89c3de4173d26 Mon Sep 17 00:00:00 2001 From: Roshan Bangar Date: Thu, 20 Jul 2023 21:59:24 +0530 Subject: [PATCH] Feature : HID service support --- .../dis/include/services/dis/ble_svc_dis.h | 25 +- nimble/host/services/dis/src/ble_svc_dis.c | 34 +- .../hid/include/services/hid/ble_svc_hid.h | 112 +++ nimble/host/services/hid/pkg.yml | 34 + nimble/host/services/hid/src/ble_svc_hid.c | 694 ++++++++++++++++++ .../sps/include/services/sps/ble_svc_sps.h | 32 + nimble/host/services/sps/pkg.yml | 34 + nimble/host/services/sps/src/ble_svc_sps.c | 140 ++++ nimble/host/src/ble_gatts.c | 6 + .../examples/linux/include/syscfg/syscfg.h | 9 + .../linux_blemesh/include/syscfg/syscfg.h | 9 + .../examples/nuttx/include/syscfg/syscfg.h | 9 + porting/nimble/include/syscfg/syscfg.h | 9 + 13 files changed, 1138 insertions(+), 9 deletions(-) create mode 100644 nimble/host/services/hid/include/services/hid/ble_svc_hid.h create mode 100644 nimble/host/services/hid/pkg.yml create mode 100644 nimble/host/services/hid/src/ble_svc_hid.c create mode 100644 nimble/host/services/sps/include/services/sps/ble_svc_sps.h create mode 100644 nimble/host/services/sps/pkg.yml create mode 100644 nimble/host/services/sps/src/ble_svc_sps.c diff --git a/nimble/host/services/dis/include/services/dis/ble_svc_dis.h b/nimble/host/services/dis/include/services/dis/ble_svc_dis.h index d095e959a..aa70d2e63 100644 --- a/nimble/host/services/dis/include/services/dis/ble_svc_dis.h +++ b/nimble/host/services/dis/include/services/dis/ble_svc_dis.h @@ -34,14 +34,15 @@ * */ -#define BLE_SVC_DIS_UUID16 0x180A -#define BLE_SVC_DIS_CHR_UUID16_SYSTEM_ID 0x2A23 -#define BLE_SVC_DIS_CHR_UUID16_MODEL_NUMBER 0x2A24 -#define BLE_SVC_DIS_CHR_UUID16_SERIAL_NUMBER 0x2A25 -#define BLE_SVC_DIS_CHR_UUID16_FIRMWARE_REVISION 0x2A26 -#define BLE_SVC_DIS_CHR_UUID16_HARDWARE_REVISION 0x2A27 -#define BLE_SVC_DIS_CHR_UUID16_SOFTWARE_REVISION 0x2A28 -#define BLE_SVC_DIS_CHR_UUID16_MANUFACTURER_NAME 0x2A29 +#define BLE_SVC_DIS_UUID16 0x180A +#define BLE_SVC_DIS_CHR_UUID16_SYSTEM_ID 0x2A23 +#define BLE_SVC_DIS_CHR_UUID16_MODEL_NUMBER 0x2A24 +#define BLE_SVC_DIS_CHR_UUID16_SERIAL_NUMBER 0x2A25 +#define BLE_SVC_DIS_CHR_UUID16_FIRMWARE_REVISION 0x2A26 +#define BLE_SVC_DIS_CHR_UUID16_HARDWARE_REVISION 0x2A27 +#define BLE_SVC_DIS_CHR_UUID16_SOFTWARE_REVISION 0x2A28 +#define BLE_SVC_DIS_CHR_UUID16_MANUFACTURER_NAME 0x2A29 +#define BLE_SVC_DIS_CHR_UUID16_PNP_ID 0x2A50 /** * Structure holding data for the main characteristics @@ -82,6 +83,12 @@ struct ble_svc_dis_data { * Represent the System Id of the device. */ const char *system_id; + + /** + * PNP ID. + * Represent the PNP Id of the device. + */ + const char *pnp_id; }; /** @@ -109,5 +116,7 @@ const char *ble_svc_dis_manufacturer_name(void); int ble_svc_dis_manufacturer_name_set(const char *value); const char *ble_svc_dis_system_id(void); int ble_svc_dis_system_id_set(const char *value); +int ble_svc_dis_pnp_id_set(const char *value); +const char *ble_svc_dis_pnp_id(void); #endif diff --git a/nimble/host/services/dis/src/ble_svc_dis.c b/nimble/host/services/dis/src/ble_svc_dis.c index 0079a04c9..9066bc8e5 100644 --- a/nimble/host/services/dis/src/ble_svc_dis.c +++ b/nimble/host/services/dis/src/ble_svc_dis.c @@ -41,7 +41,8 @@ struct ble_svc_dis_data ble_svc_dis_data = { (MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0) || \ (MYNEWT_VAL(BLE_SVC_DIS_SOFTWARE_REVISION_READ_PERM) >= 0) || \ (MYNEWT_VAL(BLE_SVC_DIS_MANUFACTURER_NAME_READ_PERM) >= 0) || \ - (MYNEWT_VAL(BLE_SVC_DIS_SYSTEM_ID_READ_PERM) >= 0) + (MYNEWT_VAL(BLE_SVC_DIS_SYSTEM_ID_READ_PERM) >= 0) || \ + (MYNEWT_VAL(BLE_SVC_DIS_PNP_ID_READ_PERM) >= 0) static int ble_svc_dis_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); @@ -108,6 +109,14 @@ static const struct ble_gatt_svc_def ble_svc_dis_defs[] = { MYNEWT_VAL(BLE_SVC_DIS_SYSTEM_ID_READ_PERM), }, { #endif +#if (MYNEWT_VAL(BLE_SVC_DIS_PNP_ID_READ_PERM) >= 0) + /*** Characteristic: PNP Id */ + .uuid = BLE_UUID16_DECLARE(BLE_SVC_DIS_CHR_UUID16_PNP_ID), + .access_cb = ble_svc_dis_access, + .flags = BLE_GATT_CHR_F_READ | + MYNEWT_VAL(BLE_SVC_DIS_PNP_ID_READ_PERM), + }, { +#endif 0, /* No more characteristics in this service */ }, } @@ -206,6 +215,16 @@ ble_svc_dis_access(uint16_t conn_handle, uint16_t attr_handle, } #endif break; +#endif +#if (MYNEWT_VAL(BLE_SVC_DIS_PNP_ID_READ_PERM) >= 0) + case BLE_SVC_DIS_CHR_UUID16_PNP_ID: + info = ble_svc_dis_data.pnp_id; +#ifdef MYNEWT_VAL_BLE_SVC_PNP_SYSTEM_ID_DEFAULT + if (info == NULL) { + info = MYNEWT_VAL(BLE_SVC_PNP_SYSTEM_ID_DEFAULT); + } +#endif + break; #endif default: assert(0); @@ -312,6 +331,19 @@ ble_svc_dis_system_id_set(const char *value) return 0; } +const char * +ble_svc_dis_pnp_id(void) +{ + return ble_svc_dis_data.pnp_id; +} + +int +ble_svc_dis_pnp_id_set(const char *value) +{ + ble_svc_dis_data.pnp_id = value; + return 0; +} + /** * Initialize the DIS package. */ diff --git a/nimble/host/services/hid/include/services/hid/ble_svc_hid.h b/nimble/host/services/hid/include/services/hid/ble_svc_hid.h new file mode 100644 index 000000000..c2d0cd905 --- /dev/null +++ b/nimble/host/services/hid/include/services/hid/ble_svc_hid.h @@ -0,0 +1,112 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#if MYNEWT_VAL(BLE_SVC_HID_SERVICE) +#ifndef H_BLE_SVC_HID_ +#define H_BLE_SVC_HID_ + +/* 16 Bit Battery Service UUID */ +#define BLE_SVC_HID_UUID16 0x1812 + +/* 16 Bit HID Service Characteristic UUIDs */ +#define BLE_SVC_HID_CHR_UUID16_REPORT_MAP 0x2A4B +#define BLE_SVC_HID_CHR_UUID16_HID_INFO 0x2A4A +#define BLE_SVC_HID_CHR_UUID16_HID_CTRL_PT 0x2A4C +#define BLE_SVC_HID_DSC_UUID16_EXT_RPT_REF 0x2907 +#define BLE_SVC_HID_CHR_UUID16_RPT 0x2A4D +#define BLE_SVC_HID_DSC_UUID16_RPT_REF 0x2908 +#define BLE_SVC_HID_CHR_UUID16_PROTOCOL_MODE 0x2A4E +#define BLE_SVC_HID_CHR_UUID16_BOOT_KBD_INP 0x2A22 +#define BLE_SVC_HID_CHR_UUID16_BOOT_KBD_OUT 0x2A32 +#define BLE_SVC_HID_CHR_UUID16_BOOT_MOUSE_INP 0x2A33 + +/* Report type values */ +#define BLE_SVC_HID_RPT_TYPE_INPUT 0x01 +#define BLE_SVC_HID_RPT_TYPE_OUTPUT 0x02 +#define BLE_SVC_HID_RPT_TYPE_FEATURE 0x03 + +/* Protocol Mode values */ +#define BLE_SVC_HID_PROTO_MODE_BOOT 0x00 +#define BLE_SVC_HID_PROTO_MODE_REPORT 0x01 + +#define REPORT_MAP_SIZE 512 +#define RPT_MAX_LEN 256 +#define MAX_REPORTS MYNEWT_VAL(BLE_SVC_HID_MAX_RPTS) +#define MOUSE_INP_RPT_SIZE 8 +#define KBD_INP_RPT_SIZE 8 + + +struct report { + uint8_t data[RPT_MAX_LEN]; + uint8_t len; + uint8_t type; + uint8_t id; + uint16_t handle; +}; + +struct ble_svc_hid_params{ + unsigned int proto_mode_present : 1; + unsigned int kbd_inp_present : 1; + unsigned int kbd_out_present : 1; + unsigned int mouse_inp_present : 1; + /* protocol mode char */ + uint8_t proto_mode; + uint16_t proto_mode_handle; + + /* boot keyboard input char */ + uint8_t kbd_inp_rpt[KBD_INP_RPT_SIZE]; + uint16_t kbd_inp_handle; + + /* boot keyboard output char */ + uint8_t kbd_out_rpt; + uint16_t kbd_out_handle; + + /* boot mouse input char */ + /* NOTE : size of mouse inp report + upto byte 2 is mandatory + from byte 3 rest is device specific */ + uint8_t mouse_inp_rpt[MOUSE_INP_RPT_SIZE]; + uint8_t mouse_inp_rpt_len; + uint16_t mouse_inp_handle; + + /* report char */ + struct report rpts[MAX_REPORTS]; + uint8_t rpts_len; + + /* report map char */ + uint8_t report_map[REPORT_MAP_SIZE]; + uint16_t report_map_handle; + uint16_t external_rpt_ref; + uint8_t report_map_len; + + /* hid info char */ + uint32_t hid_info; + uint16_t hid_info_handle; + + /* hid control point char */ + uint8_t ctrl_pt; + uint16_t ctrl_pt_handle; +}; + +void ble_svc_hid_init(); +int ble_svc_hid_add(struct ble_svc_hid_params params); +void ble_svc_hid_reset(); + +#endif +#endif // CONFIG_BT_NIMBLE_HID_SERVICE diff --git a/nimble/host/services/hid/pkg.yml b/nimble/host/services/hid/pkg.yml new file mode 100644 index 000000000..cd0c6bdde --- /dev/null +++ b/nimble/host/services/hid/pkg.yml @@ -0,0 +1,34 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +pkg.name: nimble/host/services/bas +pkg.description: Battery Service +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + - hid + - nimble + +pkg.deps: + - nimble/host + +pkg.init: + ble_svc_hid_init: 'MYNEWT_VAL(BLE_SVC_HID_SYSINIT_STAGE)' diff --git a/nimble/host/services/hid/src/ble_svc_hid.c b/nimble/host/services/hid/src/ble_svc_hid.c new file mode 100644 index 000000000..d16ace829 --- /dev/null +++ b/nimble/host/services/hid/src/ble_svc_hid.c @@ -0,0 +1,694 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 +#include + +#include "sysinit/sysinit.h" +#include "syscfg/syscfg.h" + +#if MYNEWT_VAL(BLE_SVC_HID_SERVICE) +#include "host/ble_hs.h" +#include "host/ble_gap.h" +#include "services/hid/ble_svc_hid.h" + +/* 1 more instance for empty service */ +#define HID_MAX_SVC_INSTANCES (MYNEWT_VAL(BLE_SVC_HID_MAX_SVC_INSTANCES) + 1) + +/* maximum 7 characteristics except report characteristic */ +#define HID_MAX_CHRS (HID_MAX_SVC_INSTANCES * \ + ((MYNEWT_VAL(BLE_SVC_HID_MAX_RPTS) + 7))) +/* 16 bit UUIDs */ +static ble_uuid_t *uuid_ext_rpt_ref = BLE_UUID16_DECLARE(BLE_SVC_HID_DSC_UUID16_EXT_RPT_REF); +static ble_uuid_t *uuid_report_map = BLE_UUID16_DECLARE(BLE_SVC_HID_CHR_UUID16_REPORT_MAP); +static ble_uuid_t *uuid_rpt_ref = BLE_UUID16_DECLARE(BLE_SVC_HID_DSC_UUID16_RPT_REF); +static ble_uuid_t *uuid_report = BLE_UUID16_DECLARE(BLE_SVC_HID_CHR_UUID16_RPT); +static ble_uuid_t *uuid_hid_info = BLE_UUID16_DECLARE(BLE_SVC_HID_CHR_UUID16_HID_INFO); +static ble_uuid_t *uuid_hid_ctrl_pt = BLE_UUID16_DECLARE(BLE_SVC_HID_CHR_UUID16_HID_CTRL_PT); +static ble_uuid_t *uuid_proto_mode = BLE_UUID16_DECLARE(BLE_SVC_HID_CHR_UUID16_PROTOCOL_MODE); +static ble_uuid_t *uuid_boot_kbd_inp = BLE_UUID16_DECLARE(BLE_SVC_HID_CHR_UUID16_BOOT_KBD_INP); +static ble_uuid_t *uuid_boot_kbd_out = BLE_UUID16_DECLARE(BLE_SVC_HID_CHR_UUID16_BOOT_KBD_OUT); +static ble_uuid_t *uuid_boot_mouse_inp = BLE_UUID16_DECLARE(BLE_SVC_HID_CHR_UUID16_BOOT_MOUSE_INP); +static ble_uuid_t *uuid_hid_svc = BLE_UUID16_DECLARE(BLE_SVC_HID_UUID16); + +uint8_t ble_svc_hid_rpt_val[RPT_MAX_LEN]; +uint8_t ble_svc_hid_rpt_len; +static struct ble_svc_hid_params hid_instances[HID_MAX_SVC_INSTANCES]; + +/* Access function */ +static int +ble_svc_hid_access(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); +static struct ble_gatt_dsc_def ble_svc_hid_dscs[HID_MAX_CHRS]; +static uint8_t ble_svc_hid_dsc_index = 0; // used to store the current index in the dscs array +static struct ble_gatt_chr_def ble_svc_hid_chrs[HID_MAX_CHRS]; +static uint8_t ble_svc_hid_chr_index = 0; // used to store the current index in the chrs array +static uint8_t ble_svc_hid_svc_index = 0; // used to store the current index in the svcs array + +static struct ble_gatt_svc_def ble_svc_hid_defs[HID_MAX_SVC_INSTANCES]; + +static int +ble_svc_hid_chr_write(struct os_mbuf *om, uint16_t min_len, + uint16_t max_len, void *dst, + uint16_t *len) +{ + uint16_t om_len; + int rc; + + om_len = OS_MBUF_PKTLEN(om); + if (om_len < min_len || om_len > max_len) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + rc = ble_hs_mbuf_to_flat(om, dst, max_len, len); + if (rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + + return 0; +} + +static struct ble_gatt_dsc_def* +ble_svc_hid_get_dsc(uint8_t num) +{ + if (ble_svc_hid_dsc_index + num - 1 >= HID_MAX_CHRS) { + return NULL; + } + ble_svc_hid_dsc_index = ble_svc_hid_dsc_index + num; + return &ble_svc_hid_dscs[ble_svc_hid_dsc_index - num]; +} + +static struct ble_gatt_chr_def* +ble_svc_hid_get_chr_block() +{ + if (ble_svc_hid_chr_index >= HID_MAX_CHRS) { + return NULL; + } + ble_svc_hid_chr_index = ble_svc_hid_chr_index + 1; + return &ble_svc_hid_chrs[ble_svc_hid_chr_index - 1]; +} + +/*returns current chr index */ +static uint8_t +ble_svc_hid_get_curr_chr_idx() +{ + return ble_svc_hid_chr_index; +} + +/*returns current svc index */ +static uint8_t +get_curr_svc_idx() +{ + return ble_svc_hid_svc_index; +} + +struct report * +find_rpt_by_handle(uint16_t handle) +{ + uint8_t instance, instances; + int i; + + instances = get_curr_svc_idx(); + + for (instance = 0; instance < instances; instance++) { + for (i = 0; i < hid_instances[instance].rpts_len; i++) { + if (hid_instances[instance].rpts[i].handle == handle) { + return &hid_instances[instance].rpts[i]; + } + } + } + /* return some non-zero value */ + return NULL; +} + +static struct ble_gatt_svc_def* +ble_svc_get_svc_block() +{ + if (ble_svc_hid_svc_index >= HID_MAX_SVC_INSTANCES) { + return NULL; + } + ble_svc_hid_svc_index = ble_svc_hid_svc_index + 1; + return &ble_svc_hid_defs[ble_svc_hid_svc_index - 1]; +} + +/* fill protocol mode char */ +static void +fill_proto_mode(uint8_t instance) +{ + struct ble_gatt_chr_def *chr, demo_chr; + + if (!hid_instances[instance].proto_mode_present) { + return; + } + demo_chr = (struct ble_gatt_chr_def) { + /*** Report Map characteristic */ + .uuid = uuid_proto_mode, + .access_cb = ble_svc_hid_access, + .val_handle = &hid_instances[instance].proto_mode_handle, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE_NO_RSP | +#if MYNEWT_VAL(BLE_SM_LVL) == 2 + BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC | +#elif MYNEWT_VAL(BLE_SM_LVL) == 3 + BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_WRITE_AUTHEN | + BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC | +#endif + 0, + }; + + chr = ble_svc_hid_get_chr_block(); + memcpy(chr, &demo_chr, sizeof(struct ble_gatt_chr_def)); +} + +/* fill boot keyboard inp char */ +void +fill_boot_kbd_inp(uint8_t instance) +{ + struct ble_gatt_chr_def *chr, demo_chr; + + if (!hid_instances[instance].kbd_inp_present) { + return; + } + demo_chr = (struct ble_gatt_chr_def) { + /*** Report Map characteristic */ + .uuid = uuid_boot_kbd_inp, + .access_cb = ble_svc_hid_access, + .val_handle = &hid_instances[instance].kbd_inp_handle, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY | + BLE_GATT_CHR_F_WRITE | +#if MYNEWT_VAL(BLE_SM_LVL) == 2 + BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC | +#elif MYNEWT_VAL(BLE_SM_LVL) == 3 + BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_WRITE_AUTHEN | + BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC | +#endif + 0, + }; + + chr = ble_svc_hid_get_chr_block(); + memcpy(chr, &demo_chr, sizeof(struct ble_gatt_chr_def)); +} + +/* fill boot keyboard out char */ +void +fill_boot_kbd_out(uint8_t instance) +{ + struct ble_gatt_chr_def *chr, demo_chr; + + if (!hid_instances[instance].kbd_out_present) { + return; + } + demo_chr = (struct ble_gatt_chr_def) { + /*** Report Map characteristic */ + .uuid = uuid_boot_kbd_out, + .access_cb = ble_svc_hid_access, + .val_handle = &hid_instances[instance].kbd_out_handle, + .flags = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_WRITE | +#if MYNEWT_VAL(BLE_SM_LVL) == 2 + BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC | +#elif MYNEWT_VAL(BLE_SM_LVL) == 3 + BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_WRITE_AUTHEN | + BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC | +#endif + 0, + }; + + chr = ble_svc_hid_get_chr_block(); + memcpy(chr, &demo_chr, sizeof(struct ble_gatt_chr_def)); +} +/* fill boot mouse inp char */ +void +fill_boot_mouse_inp(uint8_t instance) +{ + struct ble_gatt_chr_def *chr, demo_chr; + + if (!hid_instances[instance].mouse_inp_present) { + return; + } + demo_chr = (struct ble_gatt_chr_def) { + /*** Report Map characteristic */ + .uuid = uuid_boot_mouse_inp, + .access_cb = ble_svc_hid_access, + .val_handle = &hid_instances[instance].mouse_inp_handle, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY | + BLE_GATT_CHR_F_WRITE | +#if MYNEWT_VAL(BLE_SM_LVL) == 2 + BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC | +#elif MYNEWT_VAL(BLE_SM_LVL) == 3 + BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_WRITE_AUTHEN | + BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC | +#endif + 0, + }; + + chr = ble_svc_hid_get_chr_block(); + memcpy(chr, &demo_chr, sizeof(struct ble_gatt_chr_def)); +} +/* create report map char */ +static void +fill_rpt_map(uint8_t instance) +{ + struct ble_gatt_chr_def *chr, demo_chr; + struct ble_gatt_dsc_def *dsc; + struct ble_gatt_dsc_def *demo_dsc = (struct ble_gatt_dsc_def[]) { + { + /* External Report Reference descriptor */ + .uuid = uuid_ext_rpt_ref, + .access_cb = ble_svc_hid_access, + .att_flags = BLE_ATT_F_READ, + .arg = &hid_instances[instance].report_map_handle, + }, { + 0, + } + }; + demo_chr = (struct ble_gatt_chr_def) { + /*** Report Map characteristic */ + .uuid = uuid_report_map, + .access_cb = ble_svc_hid_access, + .val_handle = &hid_instances[instance].report_map_handle, + .flags = BLE_GATT_CHR_F_READ | +#if MYNEWT_VAL(BLE_SM_LVL) == 2 + BLE_GATT_CHR_F_READ_ENC | +#elif MYNEWT_VAL(BLE_SM_LVL) == 3 + BLE_GATT_CHR_F_READ_AUTHEN | + BLE_GATT_CHR_F_READ_ENC | +#endif + 0, + }; + /* logic : allocate the discriptor needed and then allocate one more and assign 0 to it, + this is done to indicate there are no more descriptors */ + dsc = ble_svc_hid_get_dsc(2); + memcpy(dsc, demo_dsc, 2 * sizeof(struct ble_gatt_dsc_def)); + + demo_chr.descriptors = dsc, + chr = ble_svc_hid_get_chr_block(); + memcpy(chr, &demo_chr, sizeof(struct ble_gatt_chr_def)); +} + +/* create report chars */ +static void +fill_reports(uint8_t instance) +{ + struct ble_gatt_chr_def *chr, demo_chr; + struct ble_gatt_dsc_def *dsc; + int i; + struct ble_gatt_dsc_def *demo_dsc = (struct ble_gatt_dsc_def[]) { + { + /* Report Reference descriptor */ + .uuid = uuid_rpt_ref, + .access_cb = ble_svc_hid_access, + .att_flags = BLE_ATT_F_READ + }, { + 0, + } + }; + demo_chr = (struct ble_gatt_chr_def) { + /*** Report characteristic */ + .uuid = uuid_report, + .access_cb = ble_svc_hid_access, + }; + /* Multiple instances of this characteristic are allowed*/ + for (i = 0; i < hid_instances[instance].rpts_len; i++) { + /* logic : allocate the discriptor needed and then allocate one more and assign 0 to it, + this is done to indicate there are no more descriptors */ + dsc = ble_svc_hid_get_dsc(2); + demo_dsc[0].arg = &hid_instances[instance].rpts[i].handle; + memcpy(dsc, demo_dsc, 2 * sizeof(struct ble_gatt_dsc_def)); + + switch (hid_instances[instance].rpts[i].type) { + case BLE_SVC_HID_RPT_TYPE_INPUT: + demo_chr.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE; + break; + case BLE_SVC_HID_RPT_TYPE_OUTPUT: + demo_chr.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_WRITE; + break; + case BLE_SVC_HID_RPT_TYPE_FEATURE: + demo_chr.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE; + break; + default : + assert(0); + break; + } + demo_chr.flags |= ( +#if MYNEWT_VAL(BLE_SM_LVL) == 2 + BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC | +#elif MYNEWT_VAL(BLE_SM_LVL) == 3 + BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_WRITE_AUTHEN | + BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC | +#endif + 0); + demo_chr.val_handle = &hid_instances[instance].rpts[i].handle; + demo_chr.descriptors = dsc; + + chr = ble_svc_hid_get_chr_block(); + memcpy(chr, &demo_chr, sizeof(struct ble_gatt_chr_def)); + } +} + +static void +fill_hid_info(uint8_t instance) +{ + struct ble_gatt_chr_def *chr, demo_chr; + + demo_chr = (struct ble_gatt_chr_def) { + /*** HID Information Characteristic */ + .uuid = uuid_hid_info, + .access_cb = ble_svc_hid_access, + .val_handle = &hid_instances[instance].hid_info_handle, + .flags = BLE_GATT_CHR_F_READ | +#if MYNEWT_VAL(BLE_SM_LVL) == 2 + BLE_GATT_CHR_F_READ_ENC | +#elif MYNEWT_VAL(BLE_SM_LVL) == 3 + BLE_GATT_CHR_F_READ_AUTHEN | + BLE_GATT_CHR_F_READ_ENC | +#endif + 0, + }; + chr = ble_svc_hid_get_chr_block(); + memcpy(chr, &demo_chr, sizeof(struct ble_gatt_chr_def)); +} + +static void +fill_ctrl_pt(uint8_t instance) +{ + struct ble_gatt_chr_def *chr, demo_chr; + + demo_chr = (struct ble_gatt_chr_def) { + /*** HID Control Point Characteristic */ + .uuid = uuid_hid_ctrl_pt, + .access_cb = ble_svc_hid_access, + .val_handle = &hid_instances[instance].ctrl_pt_handle, + .flags = BLE_GATT_CHR_F_WRITE_NO_RSP | +#if MYNEWT_VAL(BLE_SM_LVL) == 2 + BLE_GATT_CHR_F_WRITE_ENC | +#elif MYNEWT_VAL(BLE_SM_LVL) == 3 + BLE_GATT_CHR_F_WRITE_AUTHEN | + BLE_GATT_CHR_F_WRITE_ENC | +#endif + 0, + }; + chr = ble_svc_hid_get_chr_block(); + memcpy(chr, &demo_chr, sizeof(struct ble_gatt_chr_def)); +} + +/** + * allocating one more chr block with + * value set to zero + */ +static void +ble_svc_hid_end_chrs() +{ + struct ble_gatt_chr_def *chr; + chr = ble_svc_hid_get_chr_block(); + memset(chr, 0, sizeof(struct ble_gatt_chr_def)); +} +/** + * HID access function + */ +static int +ble_svc_hid_access(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + uint16_t uuid16 = ble_uuid_u16(ctxt->chr->uuid); + int rc; + struct report *rpt; + uint16_t rpt_ref_val = 0; + uint16_t out_rpt_len = 0; + uint8_t instances = get_curr_svc_idx(); + uint16_t handle; + uint8_t val; + + for (int instance = 0; instance < instances; instance++) { + switch (uuid16) { + case BLE_SVC_HID_CHR_UUID16_REPORT_MAP: + if (hid_instances[instance].report_map_handle != attr_handle) { + continue; + } + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + rc = os_mbuf_append(ctxt->om, &hid_instances[instance].report_map, + hid_instances[instance].report_map_len); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + case BLE_SVC_HID_DSC_UUID16_EXT_RPT_REF: + handle = *(uint16_t*)(ctxt->dsc->arg); + if (hid_instances[instance].report_map_handle != handle) { + continue; + } + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC); + rc = os_mbuf_append(ctxt->om, &hid_instances[instance].external_rpt_ref, + sizeof hid_instances[instance].external_rpt_ref); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + case BLE_SVC_HID_DSC_UUID16_RPT_REF: + /* this will work without having any instance check + because find_rpt_by_handle already checks for the instance */ + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC); + rpt = find_rpt_by_handle(*(uint16_t *)ctxt->dsc->arg); + rpt_ref_val = (0x00FF & rpt->id) | ((0x00FF & (uint16_t)rpt->type) << 8); /* check if this should be opposite */ + rc = os_mbuf_append(ctxt->om, &rpt_ref_val, + sizeof rpt_ref_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case BLE_SVC_HID_CHR_UUID16_HID_INFO: + if (hid_instances[instance].hid_info_handle != attr_handle) { + continue; + } + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + rc = os_mbuf_append(ctxt->om, &hid_instances[instance].hid_info, + sizeof hid_instances[instance].hid_info); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case BLE_SVC_HID_CHR_UUID16_HID_CTRL_PT: + if (hid_instances[instance].ctrl_pt_handle != attr_handle) { + continue; + } + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR); + /* check if the value is correct */ + rc = ble_hs_mbuf_to_flat(ctxt->om, &val, sizeof(uint8_t), NULL); + if(rc != 0) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + if(val == 0 || val == 1) { + rc = ble_svc_hid_chr_write(ctxt->om, 0, sizeof hid_instances[instance].ctrl_pt, &hid_instances[instance].ctrl_pt, NULL); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + return BLE_ATT_ERR_UNLIKELY; + + case BLE_SVC_HID_CHR_UUID16_BOOT_KBD_OUT: + if (hid_instances[instance].kbd_out_handle != attr_handle) { + continue; + } + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR || ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &hid_instances[instance].kbd_out_rpt, + sizeof(hid_instances[instance].kbd_out_rpt)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = ble_svc_hid_chr_write(ctxt->om, 0, sizeof(hid_instances[instance].kbd_out_rpt), &hid_instances[instance].kbd_out_rpt, NULL); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + return 0; + case BLE_SVC_HID_CHR_UUID16_BOOT_KBD_INP: + if (hid_instances[instance].kbd_inp_handle != attr_handle) { + continue; + } + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR || ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &hid_instances[instance].kbd_inp_rpt, + sizeof(hid_instances[instance].kbd_inp_rpt)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = ble_svc_hid_chr_write(ctxt->om, 0, sizeof(hid_instances[instance].kbd_inp_rpt), hid_instances[instance].kbd_inp_rpt, NULL); + if (ctxt->chr->flags & BLE_GATT_CHR_F_NOTIFY) { + ble_gatts_chr_updated(*(ctxt->chr->val_handle)); + } + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + return 0; + case BLE_SVC_HID_CHR_UUID16_BOOT_MOUSE_INP: + if (hid_instances[instance].mouse_inp_handle != attr_handle) { + continue; + } + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR || ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &hid_instances[instance].mouse_inp_rpt, + hid_instances[instance].mouse_inp_rpt_len); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = ble_svc_hid_chr_write(ctxt->om, 0, sizeof(hid_instances[instance].mouse_inp_rpt), hid_instances[instance].mouse_inp_rpt, &out_rpt_len); + if (rc != 0) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + hid_instances[instance].mouse_inp_rpt_len = out_rpt_len; + if (ctxt->chr->flags & BLE_GATT_CHR_F_NOTIFY) { + ble_gatts_chr_updated(*(ctxt->chr->val_handle)); + } + return 0; + } + return 0; + case BLE_SVC_HID_CHR_UUID16_PROTOCOL_MODE: + if (hid_instances[instance].proto_mode_handle != attr_handle) { + continue; + } + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR || ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, &hid_instances[instance].proto_mode, + sizeof(hid_instances[instance].proto_mode)); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + /* check if the value is correct */ + rc = ble_hs_mbuf_to_flat(ctxt->om, &val, sizeof(uint8_t), NULL); + if(rc != 0) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + if(val == 0 || val == 1) { + rc = ble_svc_hid_chr_write(ctxt->om, 0, sizeof(hid_instances[instance].proto_mode), &hid_instances[instance].proto_mode, NULL); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + return BLE_ATT_ERR_UNLIKELY; + } + return 0; + case BLE_SVC_HID_CHR_UUID16_RPT: + /* this will work without any check of instance */ + rpt = find_rpt_by_handle(*(ctxt->chr->val_handle)); + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR || ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR); + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + rc = os_mbuf_append(ctxt->om, rpt->data, + rpt->len); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } else if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + rc = ble_svc_hid_chr_write(ctxt->om, 0, RPT_MAX_LEN, &(rpt->data), &out_rpt_len); + if (rc != 0) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + rpt->len = out_rpt_len; + if (ctxt->chr->flags & BLE_GATT_CHR_F_NOTIFY) { + ble_gatts_chr_updated(*(ctxt->chr->val_handle)); + } + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + return 0; + + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } + } + return BLE_ATT_ERR_UNLIKELY; +} + +/* can be called multiple times */ +int +ble_svc_hid_add(struct ble_svc_hid_params params) +{ + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + uint8_t svc_idx; + int rc = 0; + struct ble_gatt_svc_def *svc; + uint8_t chr_idx; + + svc_idx = get_curr_svc_idx(); + /* one instance is required for empty service */ + if (HID_MAX_SVC_INSTANCES - 1 <= svc_idx) { + /* increase instances count */ + return BLE_HS_ENOMEM; + } + + memcpy(&hid_instances[svc_idx], ¶ms, sizeof(struct ble_svc_hid_params)); + + /* get the pointer to the first characteristic */ + chr_idx = ble_svc_hid_get_curr_chr_idx(); + + /* Fill protocol mode characteristic */ + fill_proto_mode(svc_idx); + /* Fill report map characteristic */ + fill_rpt_map(svc_idx); + /* Fill report characteristics */ + fill_reports(svc_idx); + /* Fill the boot keyboard input characteristic */ + fill_boot_kbd_inp(svc_idx); + /* Fill the boot keyboard output characteristic */ + fill_boot_kbd_out(svc_idx); + /* Fill the boot mouse input characteristic */ + fill_boot_mouse_inp(svc_idx); + /* Fill the hid info characteristic */ + fill_hid_info(svc_idx); + /* Fill the control point characteristic */ + fill_ctrl_pt(svc_idx); + /* End the characteristics with the characteristic with empty block */ + ble_svc_hid_end_chrs(); + + svc = ble_svc_get_svc_block(); + svc->type = BLE_GATT_SVC_TYPE_PRIMARY; + svc->uuid = uuid_hid_svc; + svc->characteristics = ble_svc_hid_chrs + chr_idx; + return rc; +} + +/** + * Allocating one more svc block + * with value set to 0 + */ +static int +ble_svc_hid_end() +{ + struct ble_gatt_svc_def *svc; + + svc = ble_svc_get_svc_block(); + if (svc == NULL) { + return BLE_HS_ENOMEM; + } + memset(svc, 0, sizeof(struct ble_gatt_svc_def)); + return 0; +} + +/** + * If the ble_gatts_reset() is called after ble_svc_hid_init(), + call ble_svc_hid_reset() to reinitialize the service. + */ +void +ble_svc_hid_reset() +{ + ble_svc_hid_dsc_index = 0; + ble_svc_hid_chr_index = 0; + ble_svc_hid_svc_index = 0; +} + +/** + * Initialize the HID Service. + */ +void +ble_svc_hid_init() +{ + int rc; + + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + rc = ble_svc_hid_end(); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = ble_gatts_count_cfg(ble_svc_hid_defs); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = ble_gatts_add_svcs(ble_svc_hid_defs); + SYSINIT_PANIC_ASSERT(rc == 0); +} +#endif // CONFIG_BT_NIMBLE_HID_SERVICE diff --git a/nimble/host/services/sps/include/services/sps/ble_svc_sps.h b/nimble/host/services/sps/include/services/sps/ble_svc_sps.h new file mode 100644 index 000000000..72d4f4aa6 --- /dev/null +++ b/nimble/host/services/sps/include/services/sps/ble_svc_sps.h @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/* Scan Parameters Service */ + +#ifndef H_BLE_SVC_SPS_ +#define H_BLE_SVC_SPS_ + + +#define BLE_SVC_SPS_UUID16 0x1813 +#define BLE_SVC_SPS_CHR_UUID16_SCAN_ITVL_WINDOW 0x2A23 +#define BLE_SVC_SPS_CHR_UUID16_SCAN_REFRESH 0x2A31 + +void ble_svc_sps_scan_refresh(void); +void ble_svc_sps_init(uint16_t scan_itvl, uint16_t scan_window); +#endif diff --git a/nimble/host/services/sps/pkg.yml b/nimble/host/services/sps/pkg.yml new file mode 100644 index 000000000..8ac1fe755 --- /dev/null +++ b/nimble/host/services/sps/pkg.yml @@ -0,0 +1,34 @@ + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +pkg.name: nimble/host/services/dis +pkg.description: Device Information Service Implementation. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + - sps + - nimble + +pkg.deps: + - nimble/host + +pkg.init: + ble_svc_dis_init: 'MYNEWT_VAL(BLE_SVC_SPS_SYSINIT_STAGE)' diff --git a/nimble/host/services/sps/src/ble_svc_sps.c b/nimble/host/services/sps/src/ble_svc_sps.c new file mode 100644 index 000000000..f7cbd1978 --- /dev/null +++ b/nimble/host/services/sps/src/ble_svc_sps.c @@ -0,0 +1,140 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 +#include +#include "sysinit/sysinit.h" +#include "host/ble_hs.h" +#include "services/sps/ble_svc_sps.h" + +static uint16_t ble_scan_itvl; +static uint16_t ble_scan_window; +static uint8_t ble_scan_refresh; +static uint16_t ble_scan_itvl_handle; +static uint16_t ble_scan_refresh_handle; + + +/* Access function */ +static int +ble_svc_sps_access(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static const struct ble_gatt_svc_def ble_svc_sps_defs[] = { + { /*** Service: Device Information Service (SPS). */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(BLE_SVC_SPS_UUID16), + .characteristics = (struct ble_gatt_chr_def[]) { { + /*** Characteristic: Scan Interval */ + .uuid = BLE_UUID16_DECLARE(BLE_SVC_SPS_CHR_UUID16_SCAN_ITVL_WINDOW), + .access_cb = ble_svc_sps_access, + .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, + .val_handle = &ble_scan_itvl_handle, + }, { + /*** Characteristic: Scan Refresh */ + .uuid = BLE_UUID16_DECLARE(BLE_SVC_SPS_CHR_UUID16_SCAN_REFRESH), + .access_cb = ble_svc_sps_access, + .flags = BLE_GATT_CHR_F_NOTIFY, + .val_handle = &ble_scan_refresh_handle, + }, { + 0, /* No more characteristics in this service */ + }, } + }, + + { + 0, /* No more services. */ + }, +}; + +static int +ble_svc_sps_chr_write(struct os_mbuf *om, uint16_t min_len, + uint16_t max_len, void *dst, + uint16_t *len) +{ + uint16_t om_len; + int rc; + + om_len = OS_MBUF_PKTLEN(om); + if (om_len < min_len || om_len > max_len) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + rc = ble_hs_mbuf_to_flat(om, dst, max_len, len); + if (rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + + return 0; +} + +void ble_svc_sps_scan_refresh() { + /* spec allows only value 0 to send */ + ble_scan_refresh = 0; + ble_gatts_chr_updated(ble_scan_refresh_handle); +} + +static int +ble_svc_sps_access(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + uint16_t uuid = ble_uuid_u16(ctxt->chr->uuid); + uint32_t write_val; + int rc; + + switch(uuid) { + case BLE_SVC_SPS_CHR_UUID16_SCAN_ITVL_WINDOW: + assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR); + rc = ble_svc_sps_chr_write(ctxt->om, 0, sizeof(ble_scan_itvl) + sizeof(ble_scan_window), &write_val, NULL); + if(rc != 0) { + ble_scan_itvl = (write_val & 0xffff0000) >> 16; + ble_scan_window = (write_val && 0x0000ffff); + } + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + case BLE_SVC_SPS_CHR_UUID16_SCAN_REFRESH: + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR && conn_handle == BLE_HS_CONN_HANDLE_NONE); + rc = os_mbuf_append(ctxt->om, &ble_scan_refresh, + sizeof ble_scan_refresh); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } + + return 0; +} + + +/** + * Initialize the SPS package. + */ +void +ble_svc_sps_init(uint16_t scan_itvl, uint16_t scan_window) +{ + int rc; + + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + ble_scan_itvl = scan_itvl; + ble_scan_window = scan_window; + rc = ble_gatts_count_cfg(ble_svc_sps_defs); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = ble_gatts_add_svcs(ble_svc_sps_defs); + SYSINIT_PANIC_ASSERT(rc == 0); +} diff --git a/nimble/host/src/ble_gatts.c b/nimble/host/src/ble_gatts.c index e382719fe..fcbafdf4f 100644 --- a/nimble/host/src/ble_gatts.c +++ b/nimble/host/src/ble_gatts.c @@ -29,6 +29,9 @@ #if MYNEWT_VAL(BLE_DYNAMIC_SERVICE) #include "services/gatt/ble_svc_gatt.h" #endif +#if MYNEWT_VAL(BLE_SVC_HID_SERVICE) +#include "services/hid/ble_svc_hid.h" +#endif #define BLE_GATTS_INCLUDE_SZ 6 #define BLE_GATTS_CHR_MAX_SZ 19 @@ -2808,6 +2811,9 @@ ble_gatts_reset(void) /* Note: gatts memory gets freed on next call to ble_gatts_start(). */ } +#if MYNEWT_VAL(BLE_SVC_HID_SERVICE) + ble_svc_hid_reset(); +#endif ble_hs_unlock(); return rc; diff --git a/porting/examples/linux/include/syscfg/syscfg.h b/porting/examples/linux/include/syscfg/syscfg.h index 48ad71096..cbcb719c0 100644 --- a/porting/examples/linux/include/syscfg/syscfg.h +++ b/porting/examples/linux/include/syscfg/syscfg.h @@ -924,6 +924,15 @@ #define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM (-1) #endif +#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT +#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT (NULL) +#endif + +/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ +#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM +#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM (-1) +#endif + /*** @apache-mynewt-nimble/nimble/host/services/gap */ #ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE #define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE (0) diff --git a/porting/examples/linux_blemesh/include/syscfg/syscfg.h b/porting/examples/linux_blemesh/include/syscfg/syscfg.h index 98ca8c9cc..1dfe0d2e4 100644 --- a/porting/examples/linux_blemesh/include/syscfg/syscfg.h +++ b/porting/examples/linux_blemesh/include/syscfg/syscfg.h @@ -1494,11 +1494,20 @@ #define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_DEFAULT (NULL) #endif +#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT +#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT (NULL) +#endif + /* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ #ifndef MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM #define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM (-1) #endif +/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ +#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM +#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM (-1) +#endif + /*** @apache-mynewt-nimble/nimble/host/services/gap */ #ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE #define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE (0) diff --git a/porting/examples/nuttx/include/syscfg/syscfg.h b/porting/examples/nuttx/include/syscfg/syscfg.h index 93646a6f3..55d38b66c 100644 --- a/porting/examples/nuttx/include/syscfg/syscfg.h +++ b/porting/examples/nuttx/include/syscfg/syscfg.h @@ -921,11 +921,20 @@ #define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_DEFAULT (NULL) #endif +#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT +#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT (NULL) +#endif + /* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ #ifndef MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM #define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM (-1) #endif +/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ +#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM +#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM (-1) +#endif + /*** @apache-mynewt-nimble/nimble/host/services/gap */ #ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE #define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE (0) diff --git a/porting/nimble/include/syscfg/syscfg.h b/porting/nimble/include/syscfg/syscfg.h index 2f39680ca..75a5e9ab1 100644 --- a/porting/nimble/include/syscfg/syscfg.h +++ b/porting/nimble/include/syscfg/syscfg.h @@ -1234,11 +1234,20 @@ #define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_DEFAULT (NULL) #endif +#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT +#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT (NULL) +#endif + /* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ #ifndef MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM #define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM (-1) #endif +/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */ +#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM +#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM (-1) +#endif + /*** @apache-mynewt-nimble/nimble/host/services/gap */ #ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE #define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE (0)