Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,28 @@ Wi-Fi credentials itself. It SHALL judge success by the network coming up
- **THEN** the application joins the network and stores the credentials in
its own way, and the library reports "provisioned" when the network is up

### Requirement: The Zephyr port provides default hooks

The Zephyr port SHALL provide `tedge_provisioning_zephyr_init()`, which
starts the library with the port's hooks: Wi-Fi through Zephyr's Wi-Fi
credentials store, the scan, identify on the devicetree's `led0` or
`led-strip`, and `CONFIG_SOC` as the chip. Each hook the application passes
SHALL replace the port's. The port's Wi-Fi hook SHALL store a network only
once the device has an address on it, and SHALL rejoin the previously
stored network when the new one cannot be joined.

#### Scenario: Application with its own network management

- **WHEN** an application passes its own Wi-Fi hook to
`tedge_provisioning_zephyr_init()`
- **THEN** the library uses that hook, and the port's scan, identify and
chip

#### Scenario: Wrong password with a network already stored

- **WHEN** the port's Wi-Fi hook cannot join the new network
- **THEN** the stored network is unchanged, and the device joins it again

### Requirement: The tiny-edge info command reports what onboarding needs

The library SHALL answer a tiny-edge info command (0xE0) with the
Expand Down
2 changes: 2 additions & 0 deletions ports/zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ if(CONFIG_TEDGE)
zephyr_library_sources_ifdef(CONFIG_TEDGE_AUTH_C8Y_CA src/tedge_port_crypto.c)
zephyr_library_sources_ifdef(CONFIG_TEDGE_PROVISIONING_SERIAL
src/tedge_provisioning_console.c)
zephyr_library_sources_ifdef(CONFIG_TEDGE_PROVISIONING
src/tedge_provisioning_zephyr.c)
if(CONFIG_TEDGE_PROVISIONING AND CONFIG_WIFI)
zephyr_library_sources(src/tedge_provisioning_wifi.c)
endif()
Expand Down
36 changes: 36 additions & 0 deletions ports/zephyr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,42 @@ DNS from DHCP or runs the mDNS responder
It needs nothing from the application beyond UDP; each lookup holds one
socket while it runs.

### Setting a device up over USB (tedge-onboard)

With `CONFIG_TEDGE_PROVISIONING=y`, a device can be set up from a computer
it is plugged into: [`tedge-onboard`](../../tools/onboard/README.md) gives it
a Wi-Fi network, registers it in Cumulocity, and passes it the tenant and a
one-time password. The library (`include/tedge/provisioning.h`) answers the
Improv Wi-Fi protocol on the console; with the shell on the console
(`CONFIG_SHELL_BACKEND_SERIAL`) the tool borrows it with `tedge improv`,
otherwise the console speaks Improv from boot.

Start it after `tedge_init()`, with the port's ready-made hooks:

```c
#include <tedge/provisioning_zephyr.h>

tedge_init(NULL, &hooks);
tedge_provisioning_zephyr_init(NULL);
tedge_start();
```

| Hook | The port's default |
|---|---|
| Wi-Fi | Joins the network, keeps it in Zephyr's Wi-Fi credentials store (`CONFIG_WIFI_CREDENTIALS`) only once the device has an address, and goes back to the previous network otherwise |
| Scan | The networks the first Wi-Fi interface sees |
| Identify | Blinks the devicetree's `led0`, or the first pixel of its `led-strip` |
| Chip | `CONFIG_SOC` |

An application that manages its network itself (its own reconnect logic),
or has a better light, passes the hooks it wants to replace:
`tedge_provisioning_zephyr_init(&mine)`. Joining a stored network at boot
stays the application's: the minimal sample does it with
`NET_REQUEST_WIFI_CONNECT_STORED` (`samples/minimal/overlay-onboarding.conf`).

The library adds about 5 KB of flash and 5 KB of RAM (4 KB of it the
worker's stack).

### Security limitations

- The device key is stored in PSA ITS, encrypted with a key derived from the
Expand Down
44 changes: 35 additions & 9 deletions ports/zephyr/include/tedge/provisioning_zephyr.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,34 @@
* @file
* @brief Ready-made provisioning hooks for Zephyr applications.
*
* Scanning for Wi-Fi networks is the same in every Zephyr application, so
* the port provides the hook (tedge_provisioning_hooks::wifi_scan).
* Joining and storing a network stays the application's, since only it
* knows how it keeps its credentials:
* Most of what the provisioning library asks of an application is the same
* in every Zephyr application, so the port provides it:
*
* - Wi-Fi: join the network, keep it in Zephyr's Wi-Fi credentials store
* (CONFIG_WIFI_CREDENTIALS) only once the device has an address, and go
* back to the previous network otherwise;
* - the scan, on the first Wi-Fi interface;
* - identify: blink the devicetree's led0, or the first pixel of its
* led-strip;
* - the chip: CONFIG_SOC.
*
* One call starts the library with them:
*
* @code
* tedge_init(NULL, &hooks);
* tedge_provisioning_zephyr_init(NULL);
* tedge_start();
* @endcode
*
* An application that manages the network itself, or has a better light,
* passes its own hooks; each one it sets replaces the port's:
*
* @code
* static const struct tedge_provisioning_hooks hooks = {
* .wifi_connect = my_join_and_store,
* .wifi_scan = tedge_provisioning_wifi_scan,
* .chip = CONFIG_SOC,
* static const struct tedge_provisioning_hooks mine = {
* .wifi_connect = my_network_switch,
* .identify = my_status_led_blink,
* };
* tedge_provisioning_zephyr_init(&mine);
* @endcode
*/

Expand All @@ -23,7 +40,16 @@

#include <tedge/provisioning.h>

/** Scan on the first Wi-Fi interface (needs CONFIG_WIFI). */
/**
* Start the provisioning library with the port's hooks, replaced by any
* member of @p overrides that is set (NULL keeps them all). Call after
* tedge_init().
*
* @return 0, or a negative errno.
*/
int tedge_provisioning_zephyr_init(const struct tedge_provisioning_hooks *overrides);

/** The port's scan hook, on the first Wi-Fi interface (needs CONFIG_WIFI). */
int tedge_provisioning_wifi_scan(struct tedge_provisioning_network *out,
size_t max, void *ctx);

Expand Down
77 changes: 4 additions & 73 deletions ports/zephyr/samples/minimal/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
#include <string.h>

#if defined(CONFIG_TEDGE_PROVISIONING)
/* Setting the device up with a tool (tedge-onboard) over the console: the
* port's hooks join and store the network it is given, so the next boot
* joins it by itself. */
#include <tedge/provisioning_zephyr.h>
#include <zephyr/net/net_mgmt.h>
#include <zephyr/net/wifi_credentials.h>
#endif

Expand Down Expand Up @@ -67,82 +69,11 @@ static int wifi_connect(void)
}
#endif

#if defined(CONFIG_TEDGE_PROVISIONING)
/* Setting the device up with a tool (tedge-onboard) over the console: the
* library hands this application the Wi-Fi network to join, and the client
* the Cumulocity tenant. The network is stored with Zephyr's Wi-Fi
* credentials, so the next boot joins it by itself. */

static K_SEM_DEFINE(wifi_done, 0, 1);
static int wifi_status;
static struct net_mgmt_event_callback wifi_cb;

static void wifi_event(struct net_mgmt_event_callback *cb, uint64_t event,
struct net_if *iface)
{
ARG_UNUSED(iface);
if (event == NET_EVENT_WIFI_CONNECT_RESULT) {
const struct wifi_status *status = cb->info;

wifi_status = status->status;
k_sem_give(&wifi_done);
}
}

static int provision_wifi(const char *ssid, const char *password, void *ctx)
{
struct net_if *iface = net_if_get_first_wifi();
enum wifi_security_type security = password[0] != '\0'
? WIFI_SECURITY_TYPE_PSK
: WIFI_SECURITY_TYPE_NONE;
struct wifi_connect_req_params p = {
.ssid = (const uint8_t *)ssid,
.ssid_length = strlen(ssid),
.psk = (const uint8_t *)password,
.psk_length = strlen(password),
.security = security,
.channel = WIFI_CHANNEL_ANY,
.band = WIFI_FREQ_BAND_2_4_GHZ,
};
int rc;

ARG_UNUSED(ctx);
if (iface == NULL) {
return -ENODEV;
}
(void)net_mgmt(NET_REQUEST_WIFI_DISCONNECT, iface, NULL, 0);
k_sem_reset(&wifi_done);
rc = net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, &p, sizeof(p));
if (rc != 0) {
return rc;
}
if (k_sem_take(&wifi_done, K_SECONDS(30)) != 0) {
return -ETIMEDOUT;
}
if (wifi_status != 0) {
return -ECONNREFUSED;
}
/* Joined: keep it, and only it, for the next boot. */
(void)wifi_credentials_delete_all();
return wifi_credentials_set_personal(ssid, strlen(ssid), security, NULL, 0,
password, strlen(password), 0, 0, 0);
}

static const struct tedge_provisioning_hooks provisioning_hooks = {
.wifi_connect = provision_wifi,
.wifi_scan = tedge_provisioning_wifi_scan,
.chip = CONFIG_SOC,
};
#endif

int main(void)
{
LOG_INF("tedge-zephyr %s", tedge_version());

#if defined(CONFIG_TEDGE_PROVISIONING)
net_mgmt_init_event_callback(&wifi_cb, wifi_event,
NET_EVENT_WIFI_CONNECT_RESULT);
net_mgmt_add_event_callback(&wifi_cb);
if (!wifi_credentials_is_empty()) {
/* Set up before: join the network it was given. */
(void)net_mgmt(NET_REQUEST_WIFI_CONNECT_STORED,
Expand All @@ -161,7 +92,7 @@ int main(void)

if (tedge_init(NULL, &hooks) == 0) {
#if defined(CONFIG_TEDGE_PROVISIONING)
(void)tedge_provisioning_init(&provisioning_hooks);
(void)tedge_provisioning_zephyr_init(NULL);
#endif
(void)tedge_start();
}
Expand Down
Loading
Loading