You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@mynewt.apache.org by Wayne Keenan <wa...@gmail.com> on 2017/05/02 13:26:57 UTC

Re: Question about BLE GATT Services

Apologies, I missed this (email and generally in the 1.0 dev) but please do
support this, as it's pretty much a mandatory requirement for being able to
create GATT services (and set-up advertising data) after the OS started,
using an embedded scripting language.



Thanks,
Wayne

On 28 April 2017 at 00:05, Christopher Collins <ch...@runtime.io> wrote:

> Hi Pritish,
>
> On Thu, Apr 27, 2017 at 03:49:34PM -0700, Pritish Gandhi wrote:
> > Hi All,
> > I have a generic question about the nimBLE capabilities. I'm trying to
> > build a BLE GATT server which has a set of services (say services A and
> B).
> > However, these services need to be registered dynamically. What I mean is
> > that when I boot up I won't know which services I need to register with
> the
> > GATT server. I'd like to boot up, probe around a bit, and then make a
> > decision on whether I want to register and then advertise either service
> A
> > only, service B only, or both.
> >
> > However I'm not quite sure I see a way to do that. It seems like services
> > need to be added and registered before the os runs.
> >
> > Here's what the comment says in ble_gatts.c
> > /**
> >  * Queues a set of service definitions for registration.  All services
> > queued
> >  * in this manner get registered when ble_hs_init() is called.
> >  *
> >  * @param svcs                  An array of service definitions to queue
> for
> >  *                                  registration.  This array must be
> >  *                                  terminated with an entry whose 'type'
> >  *                                  equals 0.
> >  *
> >  * @return                      0 on success;
> >  *                              BLE_HS_ENOMEM on heap exhaustion.
> >  */
> >
> > Is what I'm expecting to do not possible?
>
> What you're expecting is almost possible, but it is not a use case that
> the host API allows.  The issue is that host starts itself as soon as
> the OS is started, at which point it is too late to register additional
> GATT services.
>
> There is a fairly simple hack that I believe should make this possible:
>
> 1. Modify ble_hs_event_start() (ble_hs.c) such that it is a no-op:
>
>     --- a/net/nimble/host/src/ble_hs.c
>     +++ b/net/nimble/host/src/ble_hs.c
>     @@ -388,10 +388,12 @@ ble_hs_event_reset(struct os_event *ev)
>      static void
>      ble_hs_event_start(struct os_event *ev)
>      {
>     +#if 0
>          int rc;
>
>          rc = ble_hs_start();
>          assert(rc == 0);
>     +#endif
>      }
>
> 2. Call ble_hs_start() from your application after you have finished
> registering services.
>
> This seems like a pretty useful behavior, so it might be worth making
> this configurable in future versions.
>
> Chris
>

Re: Question about BLE GATT Services

Posted by Pritish Gandhi <pr...@aylanetworks.com>.
Hi Chris,
Sorry I previous e-mail was a bit premature. The proposed solution is not
complete since once GATT services and characteristic attributes have been
registered with ble_att_scr_register() there is no way to unregister them.
I eventually get ENOMEM errors when I do the ble_gatts_start() the second
time around after adding my new services with ble_gatts_add_svcs().

The issue seems to be in
-> ble_att_svr_register()
  -> ble_att_svr_entry_alloc()
    -> os_memblock_get()

We never go through and call os_memblock_put() on the previous list of
attributes allocated for the old GATT service and so we run out of them.

Its starting to seem to me that this particular feature requires more
cleanup work than anticipated.
Do let me know your thoughts.
Thanks,
Pritish

On Wed, May 10, 2017 at 11:35 AM, Pritish Gandhi <pr...@aylanetworks.com>
wrote:

> Hey Chris,
>
>
>
> On Wed, May 3, 2017 at 12:16 PM, Christopher Collins <ch...@runtime.io>
> wrote:
>
>> On Wed, May 03, 2017 at 12:10:28PM -0700, Christopher Collins wrote:
>> > On Wed, May 03, 2017 at 10:59:59AM -0700, Pritish Gandhi wrote:
>> > > Hi All,
>> > > Just wanted to give an update to the community on the status of
>> trying to
>> > > start the GATT server after the OS is initialized.
>> > >
>> > > to ble_hs.c
>> > > @@ -606,5 +603,7 @@ ble_hs_init(void)
>> > >      /* Configure the HCI transport to communicate with a host. */
>> > >      ble_hci_trans_cfg_hs(ble_hs_hci_rx_evt, NULL, ble_hs_rx_data,
>> NULL);
>> > >
>> > > +#if 0 /* Let the Application set the queue and start the BLE stack */
>> > >      ble_hs_evq_set(os_eventq_dflt_get());
>> > > +#endif
>> > >  }
>> >
>> > Is there a particular reason you removed the call to ble_hs_evq_set(),
>> > rather than the call to ble_hs_start()?  I think it is preferable to
>> > keep ble_hs_evq_set() here, and just hold off on starting the host.  If
>> > some other package tries to use the host before its eventq is set, it
>> > will result in a null pointer dereference.
>>
> My application was setting the ble evq anyways
> and I thought it might be cleaner to just let the applications decide
> which queue
> to use and when to start it.
>
> But I didn't have a good reason, I guess I didn't expect anything to use
> the host before
> the application set the queue and started the stack since the way I
> understand it is that
> no one should call into the host without getting an on_sync callback which
> won't happen
> until ble_hs_evq_set().
>
> >
>> > > Then, I just have to call ble_hs_evq_set() with my ble thread's queue
>> after
>> > > registering my GATT services.
>> > >
>> > > The next thing I need to figure out though, is how to remove
>> registered
>> > > GATT services and add new ones after the GATT server has started.
>> > > I think ble_hs_sched_reset() might work, if I move its function
>> definition
>> > > from src/ble_hs_priv.h to include/ble_hs.h.
>> > > I haven't tried this out yet though. Will get to it soon.
>> >
>> > Unfortunately, I don't think ble_hs_sched_reset() will work here.  That
>> > function just resets the controller and connection / procedure lists,
>> > but it does not have any effect on the registered GATT services.]
>> >
>> > I haven't tried it, but I think the following sequence should do the
>> > trick:
>> >
>> > 1. Register your new services with ble_gatts_add_svcs().
>> > 2. Call ble_gatts_start()
>> >
>> > ble_gatts_start() should already get called at startup as soon as the
>> > host starts.  When this happens, the registered services get exposed to
>> > BLE peers, and the "working set" of services is cleared.  This is why
>> > you can just start adding new services in step 1; the working set has
>> > been cleared, so you are starting from scratch.  When you call
>> > ble_gatts_start() in step 2, the previously-exported services get
>> > replaced with the new ones.
>>
>> Actually, there is one more thing that you'll need to do here: set
>> ble_gatts_num_cfgable_chrs to 0.  This is a static variable, so you'll
>> need to hack the code a bit to get access to it.  So the sequence should
>> be:
>>
>> 1. ble_gatts_num_cfgable_chrs = 0;
>> 2. Register your new services with ble_gatts_add_svcs().
>> 3. Call ble_gatts_start()
>>
>> Without this step, the GATT server will try to allocate too many CCCD
>> entries, which could cause the ble_gatts_start() call to fail.
>>
>> Chris
>>
>
> This worked! I can now remove old GATT services and register new ones on
> the fly.
> Thanks,
> Pritish
>

Re: Question about BLE GATT Services

Posted by Pritish Gandhi <pr...@aylanetworks.com>.
Hey Chris,



On Wed, May 3, 2017 at 12:16 PM, Christopher Collins <ch...@runtime.io>
wrote:

> On Wed, May 03, 2017 at 12:10:28PM -0700, Christopher Collins wrote:
> > On Wed, May 03, 2017 at 10:59:59AM -0700, Pritish Gandhi wrote:
> > > Hi All,
> > > Just wanted to give an update to the community on the status of trying
> to
> > > start the GATT server after the OS is initialized.
> > >
> > > to ble_hs.c
> > > @@ -606,5 +603,7 @@ ble_hs_init(void)
> > >      /* Configure the HCI transport to communicate with a host. */
> > >      ble_hci_trans_cfg_hs(ble_hs_hci_rx_evt, NULL, ble_hs_rx_data,
> NULL);
> > >
> > > +#if 0 /* Let the Application set the queue and start the BLE stack */
> > >      ble_hs_evq_set(os_eventq_dflt_get());
> > > +#endif
> > >  }
> >
> > Is there a particular reason you removed the call to ble_hs_evq_set(),
> > rather than the call to ble_hs_start()?  I think it is preferable to
> > keep ble_hs_evq_set() here, and just hold off on starting the host.  If
> > some other package tries to use the host before its eventq is set, it
> > will result in a null pointer dereference.
>
My application was setting the ble evq anyways
and I thought it might be cleaner to just let the applications decide which
queue
to use and when to start it.

But I didn't have a good reason, I guess I didn't expect anything to use
the host before
the application set the queue and started the stack since the way I
understand it is that
no one should call into the host without getting an on_sync callback which
won't happen
until ble_hs_evq_set().

>
> > > Then, I just have to call ble_hs_evq_set() with my ble thread's queue
> after
> > > registering my GATT services.
> > >
> > > The next thing I need to figure out though, is how to remove registered
> > > GATT services and add new ones after the GATT server has started.
> > > I think ble_hs_sched_reset() might work, if I move its function
> definition
> > > from src/ble_hs_priv.h to include/ble_hs.h.
> > > I haven't tried this out yet though. Will get to it soon.
> >
> > Unfortunately, I don't think ble_hs_sched_reset() will work here.  That
> > function just resets the controller and connection / procedure lists,
> > but it does not have any effect on the registered GATT services.]
> >
> > I haven't tried it, but I think the following sequence should do the
> > trick:
> >
> > 1. Register your new services with ble_gatts_add_svcs().
> > 2. Call ble_gatts_start()
> >
> > ble_gatts_start() should already get called at startup as soon as the
> > host starts.  When this happens, the registered services get exposed to
> > BLE peers, and the "working set" of services is cleared.  This is why
> > you can just start adding new services in step 1; the working set has
> > been cleared, so you are starting from scratch.  When you call
> > ble_gatts_start() in step 2, the previously-exported services get
> > replaced with the new ones.
>
> Actually, there is one more thing that you'll need to do here: set
> ble_gatts_num_cfgable_chrs to 0.  This is a static variable, so you'll
> need to hack the code a bit to get access to it.  So the sequence should
> be:
>
> 1. ble_gatts_num_cfgable_chrs = 0;
> 2. Register your new services with ble_gatts_add_svcs().
> 3. Call ble_gatts_start()
>
> Without this step, the GATT server will try to allocate too many CCCD
> entries, which could cause the ble_gatts_start() call to fail.
>
> Chris
>

This worked! I can now remove old GATT services and register new ones on
the fly.
Thanks,
Pritish

Re: Question about BLE GATT Services

Posted by Christopher Collins <ch...@runtime.io>.
On Wed, May 03, 2017 at 12:10:28PM -0700, Christopher Collins wrote:
> On Wed, May 03, 2017 at 10:59:59AM -0700, Pritish Gandhi wrote:
> > Hi All,
> > Just wanted to give an update to the community on the status of trying to
> > start the GATT server after the OS is initialized.
> > 
> > to ble_hs.c
> > @@ -606,5 +603,7 @@ ble_hs_init(void)
> >      /* Configure the HCI transport to communicate with a host. */
> >      ble_hci_trans_cfg_hs(ble_hs_hci_rx_evt, NULL, ble_hs_rx_data, NULL);
> > 
> > +#if 0 /* Let the Application set the queue and start the BLE stack */
> >      ble_hs_evq_set(os_eventq_dflt_get());
> > +#endif
> >  }
> 
> Is there a particular reason you removed the call to ble_hs_evq_set(),
> rather than the call to ble_hs_start()?  I think it is preferable to
> keep ble_hs_evq_set() here, and just hold off on starting the host.  If
> some other package tries to use the host before its eventq is set, it
> will result in a null pointer dereference.
> 
> > Then, I just have to call ble_hs_evq_set() with my ble thread's queue after
> > registering my GATT services.
> > 
> > The next thing I need to figure out though, is how to remove registered
> > GATT services and add new ones after the GATT server has started.
> > I think ble_hs_sched_reset() might work, if I move its function definition
> > from src/ble_hs_priv.h to include/ble_hs.h.
> > I haven't tried this out yet though. Will get to it soon.
> 
> Unfortunately, I don't think ble_hs_sched_reset() will work here.  That
> function just resets the controller and connection / procedure lists,
> but it does not have any effect on the registered GATT services.]
> 
> I haven't tried it, but I think the following sequence should do the
> trick:
> 
> 1. Register your new services with ble_gatts_add_svcs().
> 2. Call ble_gatts_start()
> 
> ble_gatts_start() should already get called at startup as soon as the
> host starts.  When this happens, the registered services get exposed to
> BLE peers, and the "working set" of services is cleared.  This is why
> you can just start adding new services in step 1; the working set has
> been cleared, so you are starting from scratch.  When you call
> ble_gatts_start() in step 2, the previously-exported services get
> replaced with the new ones.

Actually, there is one more thing that you'll need to do here: set
ble_gatts_num_cfgable_chrs to 0.  This is a static variable, so you'll
need to hack the code a bit to get access to it.  So the sequence should be:

1. ble_gatts_num_cfgable_chrs = 0;
2. Register your new services with ble_gatts_add_svcs().
3. Call ble_gatts_start()

Without this step, the GATT server will try to allocate too many CCCD
entries, which could cause the ble_gatts_start() call to fail.

Chris

Re: Question about BLE GATT Services

Posted by Christopher Collins <ch...@runtime.io>.
On Wed, May 03, 2017 at 10:59:59AM -0700, Pritish Gandhi wrote:
> Hi All,
> Just wanted to give an update to the community on the status of trying to
> start the GATT server after the OS is initialized.
> 
> to ble_hs.c
> @@ -606,5 +603,7 @@ ble_hs_init(void)
>      /* Configure the HCI transport to communicate with a host. */
>      ble_hci_trans_cfg_hs(ble_hs_hci_rx_evt, NULL, ble_hs_rx_data, NULL);
> 
> +#if 0 /* Let the Application set the queue and start the BLE stack */
>      ble_hs_evq_set(os_eventq_dflt_get());
> +#endif
>  }

Is there a particular reason you removed the call to ble_hs_evq_set(),
rather than the call to ble_hs_start()?  I think it is preferable to
keep ble_hs_evq_set() here, and just hold off on starting the host.  If
some other package tries to use the host before its eventq is set, it
will result in a null pointer dereference.

> Then, I just have to call ble_hs_evq_set() with my ble thread's queue after
> registering my GATT services.
> 
> The next thing I need to figure out though, is how to remove registered
> GATT services and add new ones after the GATT server has started.
> I think ble_hs_sched_reset() might work, if I move its function definition
> from src/ble_hs_priv.h to include/ble_hs.h.
> I haven't tried this out yet though. Will get to it soon.

Unfortunately, I don't think ble_hs_sched_reset() will work here.  That
function just resets the controller and connection / procedure lists,
but it does not have any effect on the registered GATT services.]

I haven't tried it, but I think the following sequence should do the
trick:

1. Register your new services with ble_gatts_add_svcs().
2. Call ble_gatts_start()

ble_gatts_start() should already get called at startup as soon as the
host starts.  When this happens, the registered services get exposed to
BLE peers, and the "working set" of services is cleared.  This is why
you can just start adding new services in step 1; the working set has
been cleared, so you are starting from scratch.  When you call
ble_gatts_start() in step 2, the previously-exported services get
replaced with the new ones.

Chris

Re: Question about BLE GATT Services

Posted by Pritish Gandhi <pr...@aylanetworks.com>.
Hi All,
Just wanted to give an update to the community on the status of trying to
start the GATT server after the OS is initialized.

to ble_hs.c
@@ -606,5 +603,7 @@ ble_hs_init(void)
     /* Configure the HCI transport to communicate with a host. */
     ble_hci_trans_cfg_hs(ble_hs_hci_rx_evt, NULL, ble_hs_rx_data, NULL);

+#if 0 /* Let the Application set the queue and start the BLE stack */
     ble_hs_evq_set(os_eventq_dflt_get());
+#endif
 }

Then, I just have to call ble_hs_evq_set() with my ble thread's queue after
registering my GATT services.

The next thing I need to figure out though, is how to remove registered
GATT services and add new ones after the GATT server has started.
I think ble_hs_sched_reset() might work, if I move its function definition
from src/ble_hs_priv.h to include/ble_hs.h.
I haven't tried this out yet though. Will get to it soon.
Thanks,
Pritish

On Tue, May 2, 2017 at 6:26 AM, Wayne Keenan <wa...@gmail.com> wrote:

> Apologies, I missed this (email and generally in the 1.0 dev) but please do
> support this, as it's pretty much a mandatory requirement for being able to
> create GATT services (and set-up advertising data) after the OS started,
> using an embedded scripting language.
>
>
>
> Thanks,
> Wayne
>
> On 28 April 2017 at 00:05, Christopher Collins <ch...@runtime.io> wrote:
>
> > Hi Pritish,
> >
> > On Thu, Apr 27, 2017 at 03:49:34PM -0700, Pritish Gandhi wrote:
> > > Hi All,
> > > I have a generic question about the nimBLE capabilities. I'm trying to
> > > build a BLE GATT server which has a set of services (say services A and
> > B).
> > > However, these services need to be registered dynamically. What I mean
> is
> > > that when I boot up I won't know which services I need to register with
> > the
> > > GATT server. I'd like to boot up, probe around a bit, and then make a
> > > decision on whether I want to register and then advertise either
> service
> > A
> > > only, service B only, or both.
> > >
> > > However I'm not quite sure I see a way to do that. It seems like
> services
> > > need to be added and registered before the os runs.
> > >
> > > Here's what the comment says in ble_gatts.c
> > > /**
> > >  * Queues a set of service definitions for registration.  All services
> > > queued
> > >  * in this manner get registered when ble_hs_init() is called.
> > >  *
> > >  * @param svcs                  An array of service definitions to
> queue
> > for
> > >  *                                  registration.  This array must be
> > >  *                                  terminated with an entry whose
> 'type'
> > >  *                                  equals 0.
> > >  *
> > >  * @return                      0 on success;
> > >  *                              BLE_HS_ENOMEM on heap exhaustion.
> > >  */
> > >
> > > Is what I'm expecting to do not possible?
> >
> > What you're expecting is almost possible, but it is not a use case that
> > the host API allows.  The issue is that host starts itself as soon as
> > the OS is started, at which point it is too late to register additional
> > GATT services.
> >
> > There is a fairly simple hack that I believe should make this possible:
> >
> > 1. Modify ble_hs_event_start() (ble_hs.c) such that it is a no-op:
> >
> >     --- a/net/nimble/host/src/ble_hs.c
> >     +++ b/net/nimble/host/src/ble_hs.c
> >     @@ -388,10 +388,12 @@ ble_hs_event_reset(struct os_event *ev)
> >      static void
> >      ble_hs_event_start(struct os_event *ev)
> >      {
> >     +#if 0
> >          int rc;
> >
> >          rc = ble_hs_start();
> >          assert(rc == 0);
> >     +#endif
> >      }
> >
> > 2. Call ble_hs_start() from your application after you have finished
> > registering services.
> >
> > This seems like a pretty useful behavior, so it might be worth making
> > this configurable in future versions.
> >
> > Chris
> >
>