1 // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 
17 #include <protocomm.h>
18 
19 #include "esp_event.h"
20 #include "wifi_provisioning/wifi_config.h"
21 
22 #ifdef __cplusplus
23 extern "C" {
24 #endif
25 
26 ESP_EVENT_DECLARE_BASE(WIFI_PROV_EVENT);
27 
28 /**
29  * @brief   Events generated by manager
30  *
31  * These events are generated in order of declaration and, for the
32  * stretch of time between initialization and de-initialization of
33  * the manager, each event is signaled only once
34  */
35 typedef enum {
36     /**
37      * Emitted when the manager is initialized
38      */
39     WIFI_PROV_INIT,
40 
41     /**
42      * Indicates that provisioning has started
43      */
44     WIFI_PROV_START,
45 
46     /**
47      * Emitted when Wi-Fi AP credentials are received via `protocomm`
48      * endpoint `wifi_config`. The event data in this case is a pointer
49      * to the corresponding `wifi_sta_config_t` structure
50      */
51     WIFI_PROV_CRED_RECV,
52 
53     /**
54      * Emitted when device fails to connect to the AP of which the
55      * credentials were received earlier on event `WIFI_PROV_CRED_RECV`.
56      * The event data in this case is a pointer to the disconnection
57      * reason code with type `wifi_prov_sta_fail_reason_t`
58      */
59     WIFI_PROV_CRED_FAIL,
60 
61     /**
62      * Emitted when device successfully connects to the AP of which the
63      * credentials were received earlier on event `WIFI_PROV_CRED_RECV`
64      */
65     WIFI_PROV_CRED_SUCCESS,
66 
67     /**
68      * Signals that provisioning service has stopped
69      */
70     WIFI_PROV_END,
71 
72     /**
73      * Signals that manager has been de-initialized
74      */
75     WIFI_PROV_DEINIT,
76 } wifi_prov_cb_event_t;
77 
78 typedef void (*wifi_prov_cb_func_t)(void *user_data, wifi_prov_cb_event_t event, void *event_data);
79 
80 /**
81  * @brief   Event handler that is used by the manager while
82  *          provisioning service is active
83  */
84 typedef struct {
85     /**
86      * Callback function to be executed on provisioning events
87      */
88     wifi_prov_cb_func_t event_cb;
89 
90     /**
91      * User context data to pass as parameter to callback function
92      */
93     void *user_data;
94 } wifi_prov_event_handler_t;
95 
96 /**
97  * @brief Event handler can be set to none if not used
98  */
99 #define WIFI_PROV_EVENT_HANDLER_NONE { \
100     .event_cb  = NULL,                 \
101     .user_data = NULL                  \
102 }
103 
104 /**
105  * @brief   Structure for specifying the provisioning scheme to be
106  *          followed by the manager
107  *
108  * @note    Ready to use schemes are available:
109  *              - wifi_prov_scheme_ble     : for provisioning over BLE transport + GATT server
110  *              - wifi_prov_scheme_softap  : for provisioning over SoftAP transport + HTTP server
111  *              - wifi_prov_scheme_console : for provisioning over Serial UART transport + Console (for debugging)
112  */
113 typedef struct wifi_prov_scheme {
114     /**
115      * Function which is to be called by the manager when it is to
116      * start the provisioning service associated with a protocomm instance
117      * and a scheme specific configuration
118      */
119     esp_err_t (*prov_start) (protocomm_t *pc, void *config);
120 
121     /**
122      * Function which is to be called by the manager to stop the
123      * provisioning service previously associated with a protocomm instance
124      */
125     esp_err_t (*prov_stop) (protocomm_t *pc);
126 
127     /**
128      * Function which is to be called by the manager to generate
129      * a new configuration for the provisioning service, that is
130      * to be passed to prov_start()
131      */
132     void *(*new_config) (void);
133 
134     /**
135      * Function which is to be called by the manager to delete a
136      * configuration generated using new_config()
137      */
138     void (*delete_config) (void *config);
139 
140     /**
141      * Function which is to be called by the manager to set the
142      * service name and key values in the configuration structure
143      */
144     esp_err_t (*set_config_service) (void *config, const char *service_name, const char *service_key);
145 
146     /**
147      * Function which is to be called by the manager to set a protocomm endpoint
148      * with an identifying name and UUID in the configuration structure
149      */
150     esp_err_t (*set_config_endpoint) (void *config, const char *endpoint_name, uint16_t uuid);
151 
152     /**
153      * Sets mode of operation of Wi-Fi during provisioning
154      * This is set to :
155      * - WIFI_MODE_APSTA for SoftAP transport
156      * - WIFI_MODE_STA for BLE transport
157      */
158     wifi_mode_t wifi_mode;
159 } wifi_prov_scheme_t;
160 
161 /**
162  * @brief   Structure for specifying the manager configuration
163  */
164 typedef struct {
165     /**
166      * Provisioning scheme to use. Following schemes are already available:
167      *     - wifi_prov_scheme_ble     : for provisioning over BLE transport + GATT server
168      *     - wifi_prov_scheme_softap  : for provisioning over SoftAP transport + HTTP server + mDNS (optional)
169      *     - wifi_prov_scheme_console : for provisioning over Serial UART transport + Console (for debugging)
170      */
171     wifi_prov_scheme_t scheme;
172 
173     /**
174      * Event handler required by the scheme for incorporating scheme specific
175      * behavior while provisioning manager is running. Various options may be
176      * provided by the scheme for setting this field. Use WIFI_PROV_EVENT_HANDLER_NONE
177      * when not used. When using scheme wifi_prov_scheme_ble, the following
178      * options are available:
179      *     - WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM
180      *     - WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BLE
181      *     - WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BT
182      */
183     wifi_prov_event_handler_t scheme_event_handler;
184 
185     /**
186      * Event handler that can be set for the purpose of incorporating application
187      * specific behavior. Use WIFI_PROV_EVENT_HANDLER_NONE when not used.
188      */
189     wifi_prov_event_handler_t app_event_handler;
190 } wifi_prov_mgr_config_t;
191 
192 /**
193  * @brief   Security modes supported by the Provisioning Manager.
194  *
195  * These are same as the security modes provided by protocomm
196  */
197 typedef enum wifi_prov_security {
198     /**
199      * No security (plain-text communication)
200      */
201     WIFI_PROV_SECURITY_0 = 0,
202 
203     /**
204      * This secure communication mode consists of
205      *   X25519 key exchange
206      * + proof of possession (pop) based authentication
207      * + AES-CTR encryption
208      */
209     WIFI_PROV_SECURITY_1
210 } wifi_prov_security_t;
211 
212 /**
213  * @brief   Initialize provisioning manager instance
214  *
215  * Configures the manager and allocates internal resources
216  *
217  * Configuration specifies the provisioning scheme (transport)
218  * and event handlers
219  *
220  * Event WIFI_PROV_INIT is emitted right after initialization
221  * is complete
222  *
223  * @param[in] config Configuration structure
224  *
225  * @return
226  *  - ESP_OK      : Success
227  *  - ESP_FAIL    : Fail
228  */
229 esp_err_t wifi_prov_mgr_init(wifi_prov_mgr_config_t config);
230 
231 /**
232  * @brief   Stop provisioning (if running) and release
233  *          resource used by the manager
234  *
235  * Event WIFI_PROV_DEINIT is emitted right after de-initialization
236  * is finished
237  *
238  * If provisioning service is  still active when this API is called,
239  * it first stops the service, hence emitting WIFI_PROV_END, and
240  * then performs the de-initialization
241  */
242 void wifi_prov_mgr_deinit(void);
243 
244 /**
245  * @brief   Checks if device is provisioned
246  *
247  * This checks if Wi-Fi credentials are present on the NVS
248  *
249  * The Wi-Fi credentials are assumed to be kept in the same
250  * NVS namespace as used by esp_wifi component
251  *
252  * If one were to call esp_wifi_set_config() directly instead
253  * of going through the provisioning process, this function will
254  * still yield true (i.e. device will be found to be provisioned)
255  *
256  * @note    Calling wifi_prov_mgr_start_provisioning() automatically
257  *          resets the provision state, irrespective of what the
258  *          state was prior to making the call.
259  *
260  * @param[out] provisioned  True if provisioned, else false
261  *
262  * @return
263  *  - ESP_OK      : Retrieved provision state successfully
264  *  - ESP_FAIL    : Wi-Fi not initialized
265  *  - ESP_ERR_INVALID_ARG   : Null argument supplied
266  *  - ESP_ERR_INVALID_STATE : Manager not initialized
267  */
268 esp_err_t wifi_prov_mgr_is_provisioned(bool *provisioned);
269 
270 /**
271  * @brief   Start provisioning service
272  *
273  * This starts the provisioning service according to the scheme
274  * configured at the time of initialization. For scheme :
275  * - wifi_prov_scheme_ble : This starts protocomm_ble, which internally initializes
276  *                          BLE transport and starts GATT server for handling
277  *                          provisioning requests
278  * - wifi_prov_scheme_softap : This activates SoftAP mode of Wi-Fi and starts
279  *                          protocomm_httpd, which internally starts an HTTP
280  *                          server for handling provisioning requests (If mDNS is
281  *                          active it also starts advertising service with type
282  *                          _esp_wifi_prov._tcp)
283  *
284  * Event WIFI_PROV_START is emitted right after provisioning starts without failure
285  *
286  * @note   This API will start provisioning service even if device is found to be
287  *         already provisioned, i.e. wifi_prov_mgr_is_provisioned() yields true
288  *
289  * @param[in] security      Specify which protocomm security scheme to use :
290  *                              - WIFI_PROV_SECURITY_0 : For no security
291  *                              - WIFI_PROV_SECURITY_1 : x25519 secure handshake for session
292  *                                establishment followed by AES-CTR encryption of provisioning messages
293  * @param[in] pop           Pointer to proof of possession string (NULL if not needed). This
294  *                          is relevant only for protocomm security 1, in which case it is used
295  *                          for authenticating secure session
296  * @param[in] service_name  Unique name of the service. This translates to:
297  *                              - Wi-Fi SSID when provisioning mode is softAP
298  *                              - Device name when provisioning mode is BLE
299  * @param[in] service_key   Key required by client to access the service (NULL if not needed).
300  *                          This translates to:
301  *                              - Wi-Fi password when provisioning mode is softAP
302  *                              - ignored when provisioning mode is BLE
303  *
304  * @return
305  *  - ESP_OK      : Provisioning started successfully
306  *  - ESP_FAIL    : Failed to start provisioning service
307  *  - ESP_ERR_INVALID_STATE : Provisioning manager not initialized or already started
308  */
309 esp_err_t wifi_prov_mgr_start_provisioning(wifi_prov_security_t security, const char *pop,
310                                            const char *service_name, const char *service_key);
311 
312 /**
313  * @brief   Stop provisioning service
314  *
315  * If provisioning service is active, this API will initiate a process to stop
316  * the service and return. Once the service actually stops, the event WIFI_PROV_END
317  * will be emitted.
318  *
319  * If wifi_prov_mgr_deinit() is called without calling this API first, it will
320  * automatically stop the provisioning service and emit the WIFI_PROV_END, followed
321  * by WIFI_PROV_DEINIT, before returning.
322  *
323  * This API will generally be used along with wifi_prov_mgr_disable_auto_stop()
324  * in the scenario when the main application has registered its own endpoints,
325  * and wishes that the provisioning service is stopped only when some protocomm
326  * command from the client side application is received.
327  *
328  * Calling this API inside an endpoint handler, with sufficient cleanup_delay,
329  * will allow the response / acknowledgment to be sent successfully before the
330  * underlying protocomm service is stopped.
331  *
332  * Cleaup_delay is set when calling wifi_prov_mgr_disable_auto_stop().
333  * If not specified, it defaults to 1000ms.
334  *
335  * For straightforward cases, using this API is usually not necessary as
336  * provisioning is stopped automatically once WIFI_PROV_CRED_SUCCESS is emitted.
337  * Stopping is delayed (maximum 30 seconds) thus allowing the client side
338  * application to query for Wi-Fi state, i.e. after receiving the first query
339  * and sending `Wi-Fi state connected` response the service is stopped immediately.
340  */
341 void wifi_prov_mgr_stop_provisioning(void);
342 
343 /**
344  * @brief   Wait for provisioning service to finish
345  *
346  * Calling this API will block until provisioning service is stopped
347  * i.e. till event WIFI_PROV_END is emitted.
348  *
349  * This will not block if provisioning is not started or not initialized.
350  */
351 void wifi_prov_mgr_wait(void);
352 
353 /**
354  * @brief   Disable auto stopping of provisioning service upon completion
355  *
356  * By default, once provisioning is complete, the provisioning service is automatically
357  * stopped, and all endpoints (along with those registered by main application) are
358  * deactivated.
359  *
360  * This API is useful in the case when main application wishes to close provisioning service
361  * only after it receives some protocomm command from the client side app. For example, after
362  * connecting to Wi-Fi, the device may want to connect to the cloud, and only once that is
363  * successfully, the device is said to be fully configured. But, then it is upto the main
364  * application to explicitly call wifi_prov_mgr_stop_provisioning() later when the device is
365  * fully configured and the provisioning service is no longer required.
366  *
367  * @note    This must be called before executing wifi_prov_mgr_start_provisioning()
368  *
369  * @param[in] cleanup_delay Sets the delay after which the actual cleanup of transport related
370  *                          resources is done after a call to wifi_prov_mgr_stop_provisioning()
371  *                          returns. Minimum allowed value is 100ms. If not specified, this will
372  *                          default to 1000ms.
373  *
374  * @return
375  *  - ESP_OK : Success
376  *  - ESP_ERR_INVALID_STATE : Manager not initialized or
377  *                            provisioning service already started
378  */
379 esp_err_t wifi_prov_mgr_disable_auto_stop(uint32_t cleanup_delay);
380 
381 /**
382  * @brief   Set application version and capabilities in the JSON data returned by
383  *          proto-ver endpoint
384  *
385  * This function can be called multiple times, to specify information about the various
386  * application specific services running on the device, identified by unique labels.
387  *
388  * The provisioning service itself registers an entry in the JSON data, by the label "prov",
389  * containing only provisioning service version and capabilities. Application services should
390  * use a label other than "prov" so as not to overwrite this.
391  *
392  * @note    This must be called before executing wifi_prov_mgr_start_provisioning()
393  *
394  * @param[in] label   String indicating the application name.
395  *
396  * @param[in] version String indicating the application version.
397  *                    There is no constraint on format.
398  *
399  * @param[in] capabilities  Array of strings with capabilities.
400  *                          These could be used by the client side app to know
401  *                          the application registered endpoint capabilities
402  *
403  * @param[in] total_capabilities  Size of capabilities array
404  *
405  * @return
406  *  - ESP_OK : Success
407  *  - ESP_ERR_INVALID_STATE : Manager not initialized or
408  *                            provisioning service already started
409  *  - ESP_ERR_NO_MEM : Failed to allocate memory for version string
410  *  - ESP_ERR_INVALID_ARG : Null argument
411  */
412 esp_err_t wifi_prov_mgr_set_app_info(const char *label, const char *version,
413                                      const char**capabilities, size_t total_capabilities);
414 
415 /**
416  * @brief   Create an additional endpoint and allocate internal resources for it
417  *
418  * This API is to be called by the application if it wants to create an additional
419  * endpoint. All additional endpoints will be assigned UUIDs starting from 0xFF54
420  * and so on in the order of execution.
421  *
422  * protocomm handler for the created endpoint is to be registered later using
423  * wifi_prov_mgr_endpoint_register() after provisioning has started.
424  *
425  * @note    This API can only be called BEFORE provisioning is started
426  *
427  * @note    Additional endpoints can be used for configuring client provided
428  *          parameters other than Wi-Fi credentials, that are necessary for the
429  *          main application and hence must be set prior to starting the application
430  *
431  * @note    After session establishment, the additional endpoints must be targeted
432  *          first by the client side application before sending Wi-Fi configuration,
433  *          because once Wi-Fi configuration finishes the provisioning service is
434  *          stopped and hence all endpoints are unregistered
435  *
436  * @param[in] ep_name  unique name of the endpoint
437  *
438  * @return
439  *  - ESP_OK      : Success
440  *  - ESP_FAIL    : Failure
441  */
442 esp_err_t wifi_prov_mgr_endpoint_create(const char *ep_name);
443 
444 /**
445  * @brief   Register a handler for the previously created endpoint
446  *
447  * This API can be called by the application to register a protocomm handler
448  * to any endpoint that was created using wifi_prov_mgr_endpoint_create().
449  *
450  * @note    This API can only be called AFTER provisioning has started
451  *
452  * @note    Additional endpoints can be used for configuring client provided
453  *          parameters other than Wi-Fi credentials, that are necessary for the
454  *          main application and hence must be set prior to starting the application
455  *
456  * @note    After session establishment, the additional endpoints must be targeted
457  *          first by the client side application before sending Wi-Fi configuration,
458  *          because once Wi-Fi configuration finishes the provisioning service is
459  *          stopped and hence all endpoints are unregistered
460  *
461  * @param[in] ep_name   Name of the endpoint
462  * @param[in] handler   Endpoint handler function
463  * @param[in] user_ctx  User data
464  *
465  * @return
466  *  - ESP_OK      : Success
467  *  - ESP_FAIL    : Failure
468  */
469 esp_err_t wifi_prov_mgr_endpoint_register(const char *ep_name,
470                                           protocomm_req_handler_t handler,
471                                           void *user_ctx);
472 
473 /**
474  * @brief   Unregister the handler for an endpoint
475  *
476  * This API can be called if the application wants to selectively
477  * unregister the handler of an endpoint while the provisioning
478  * is still in progress.
479  *
480  * All the endpoint handlers are unregistered automatically when
481  * the provisioning stops.
482  *
483  * @param[in] ep_name  Name of the endpoint
484  */
485 void wifi_prov_mgr_endpoint_unregister(const char *ep_name);
486 
487 /**
488  * @brief   Event handler for provisioning manager
489  *
490  * This is called from the main event handler and controls the
491  * provisioning manager's internal state machine depending on
492  * incoming Wi-Fi events
493  *
494  * @note : This function is DEPRECATED, because events are now
495  * handled internally using the event loop library, esp_event.
496  * Calling this will do nothing and simply return ESP_OK.
497  *
498  * @param[in] ctx   Event context data
499  * @param[in] event Event info
500  *
501  * @return
502  *  - ESP_OK : Event handled successfully
503  */
504 esp_err_t wifi_prov_mgr_event_handler(void *ctx, system_event_t *event) __attribute__ ((deprecated));
505 
506 /**
507  * @brief   Get state of Wi-Fi Station during provisioning
508  *
509  * @param[out] state    Pointer to wifi_prov_sta_state_t
510  *                      variable to be filled
511  *
512  * @return
513  *  - ESP_OK    : Successfully retrieved Wi-Fi state
514  *  - ESP_FAIL  : Provisioning app not running
515  */
516 esp_err_t wifi_prov_mgr_get_wifi_state(wifi_prov_sta_state_t *state);
517 
518 /**
519  * @brief   Get reason code in case of Wi-Fi station
520  *          disconnection during provisioning
521  *
522 * @param[out] reason    Pointer to wifi_prov_sta_fail_reason_t
523 *                       variable to be filled
524  *
525  * @return
526  *  - ESP_OK    : Successfully retrieved Wi-Fi disconnect reason
527  *  - ESP_FAIL  : Provisioning app not running
528  */
529 esp_err_t wifi_prov_mgr_get_wifi_disconnect_reason(wifi_prov_sta_fail_reason_t *reason);
530 
531 /**
532  * @brief   Runs Wi-Fi as Station with the supplied configuration
533  *
534  * Configures the Wi-Fi station mode to connect to the AP with
535  * SSID and password specified in config structure and sets
536  * Wi-Fi to run as station.
537  *
538  * This is automatically called by provisioning service upon
539  * receiving new credentials.
540  *
541  * If credentials are to be supplied to the manager via a
542  * different mode other than through protocomm, then this
543  * API needs to be called.
544  *
545  * Event WIFI_PROV_CRED_RECV is emitted after credentials have
546  * been applied and Wi-Fi station started
547  *
548  * @param[in] wifi_cfg  Pointer to Wi-Fi configuration structure
549  *
550  * @return
551  *  - ESP_OK      : Wi-Fi configured and started successfully
552  *  - ESP_FAIL    : Failed to set configuration
553  */
554 esp_err_t wifi_prov_mgr_configure_sta(wifi_config_t *wifi_cfg);
555 
556 /**
557  * @brief   Reset Wi-Fi provisioning config
558  *
559  * Calling this API will restore WiFi stack persistent settings to default values.
560  *
561  * @return
562  *  - ESP_OK      : Reset provisioning config successfully
563  *  - ESP_FAIL    : Failed to reset provisioning config
564  */
565 esp_err_t wifi_prov_mgr_reset_provisioning(void);
566 
567 /**
568  * @brief   Reset internal state machine and clear provisioned credentials.
569  *
570  * This API can be used to restart provisioning in case invalid credentials are entered.
571  *
572  * @return
573  *  - ESP_OK      : Reset provisioning state machine successfully
574  *  - ESP_FAIL    : Failed to reset provisioning state machine
575  *  - ESP_ERR_INVALID_STATE : Manager not initialized
576  */
577 esp_err_t wifi_prov_mgr_reset_sm_state_on_failure(void);
578 
579 #ifdef __cplusplus
580 }
581 #endif
582