Hi On Fri, 7 Sep 2018 at 21:12, Andrzej Kaczmarek < andrzej.kaczma...@codecoup.pl> wrote:
> Hi Chris, > > On Fri, Sep 7, 2018 at 8:00 PM Christopher Collins <ch...@runtime.io> > wrote: > > > Hello all, > > > > TL;DR: Proposal for registration of GAP event listeners in the NimBLE > > host. Currently, GAP event callbacks are specified by the code which > > creates a connection. This proposal allows code to listen for GAP > > events without creating a connection. > > > > The NimBLE host allows the application to associate callbacks with GAP > > events. Callbacks are configured *per connection*. When the > > application initiates a connection, it specifies a single callback to > > be executed for all GAP events involving the connection. Similarly, > > when the application starts advertising, it specifies the callback to > > use with the connection that may result. > > > > This design provides some degree of modularity. If a package > > independently creates a connection, it can handle all the relevant GAP > > events without involving other packages in the system. However, there > > is a type of modularity that this does not provide: *per event* > > callbacks. If a package doesn't want to initiate any GAP events, but > > it wants to be notified every time a connection is established (for > > example), there is no way to achieve this. The problem here is that > > there is only a single callback associated with each connection. What > > we really need is a list of callbacks that all get called whenever a > > GAP event occurs. > > > > My proposal is to add the following to the NimBLE host API: > > > > struct ble_gap_listener { > > /*** Public. */ > > ble_gap_event_fn *fn; > > void *arg; > > > > /*** Internal. */ > > STAILQ_ENTRY(ble_gap_listener) next; > > }; > > > > /** > > * @brief Registers a BLE GAP listener. > > * > > * When a GAP event occurs, callbacks are executed in the following > > * order: > > * 1. Connection-specific GAP event callback. > > * 2. Each registered listener, in the order they were registered. > > * > > * @param listener The listener to register. > > */ > > void ble_gap_register(struct ble_gap_listener *listener); > > > > /** > > * @brief Unregisters a BLE GAP listener. > > * > > * @param listener The listener to unregister. > > * > > * @return 0 on success; > > * BLE_HS_ENOENT if the listener is > > * not registered. > > */ > > int ble_gap_unregister(struct ble_gap_listener *listener); > > > > Initially, I thought this functionality could be achieved with a new > > package implemented on top of the host (call it `blemux`). > > Unfortunately, I think there are some issues that make such an approach > > unwieldy for the user. I imagined such a package would be used as > > follows: > > > > 1. The `blemux` package exposes a GAP callback (call it > > `blemux_gap_event`). > > 2. Elsewhere in the sytem, for each GAP function call, the caller > > specifies `blemux_gap_event` as the callback. > > 3. Each package that is interested in GAP events registers one or > > more listeners via `blemux_register()`. > > 4. When a GAP event occurs, `blemux_gap_event` is executed. This > > callback executes every registered listener. > > > > The problem is that packages have no guarantee that the app is using > > blemux. A package can depend on `blemux` and can register listeners, > > but it really only works if every other BLE-aware package in the system > > also uses `blemux`. If any package doesn't use `blemux`, then the GAP > > procedures that it initiates won't be reported to the `blemux` > > listeners. A secondary problem is that this just feels like a clumsy > > API. It is confusing and error prone to need to specify > > `blemux_gap_event` as the GAP event callback. > > > > So, I think this functionality should be implemented directly in the > > host. > > > > Like the idea. We could use it for mesh as well - for now we have dedicated API for Mesh to register for GAP events. With this change we could get rid of it. > Sounds exactly like something I wrote some time ago, so I can only agree > with above :-) > Typical scenario here (and what I was doing) is that external package, like > service, wants to be notified about connection or characteristic > subscription state. Currently we do this by exposing extra API from package > which app needs to call from GAP callbacks in order to everything work > properly. It works, but it's really clunky. Hooks provided by host are imo > good way to handle this. > > I did not finish my service so forgot to send a PR with NimBLE changes, but > here they are for reference - I can create PR if they look ok. > > https://github.com/andrzej-kaczmarek/apache-mynewt-nimble/commit/6cc902821e6d65fbd5f72fa89e7219915afca38d Looks it does what it should, however ble_gap_register() sounds like a better name for API to me imho. > > The API is pretty much the same as proposed. The difference is that > listener struct is opaque and fields are initialized when listener is > registered. > Notified events are the ones which are notifications, i.e. do not require > interaction from app (so e.g. pairing event does not call listeners since > it does not make sense to do so). > > > Thoughts? > > > Thanks, > > Chris > > > > Best Łukasz > Best, > Andrzej >