diff options
Diffstat (limited to 'libs')
57 files changed, 3374 insertions, 632 deletions
diff --git a/libs/bleuart/include/bleuart/bleuart.h b/libs/bleuart/include/bleuart/bleuart.h new file mode 100644 index 00000000..14ae9306 --- /dev/null +++ b/libs/bleuart/include/bleuart/bleuart.h @@ -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. + */ + +#ifndef _BLEUART_H_ +#define _BLEUART_H_ + +int +bleuart_init(int max_input); +int +bleuart_svc_register(void); +int +bleuart_gatt_svr_init(struct ble_hs_cfg *cfg); +void +bleuart_set_conn_handle(uint16_t conn_handle); + +extern const uint8_t gatt_svr_svc_uart[16]; + +#endif /* _BLEUART_H */ diff --git a/libs/bleuart/pkg.yml b/libs/bleuart/pkg.yml new file mode 100644 index 00000000..5173293b --- /dev/null +++ b/libs/bleuart/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: libs/bleuart +pkg.description: BLE uart service. +pkg.author: "Apache Mynewt <dev@mynewt.incubator.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + - uart + +pkg.deps: + - libs/os + - net/nimble/host + +pkg.req_apis: + - console diff --git a/libs/bleuart/src/bleuart.c b/libs/bleuart/src/bleuart.c new file mode 100644 index 00000000..81e433ca --- /dev/null +++ b/libs/bleuart/src/bleuart.c @@ -0,0 +1,204 @@ +/** + * 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 <assert.h> +#include <stdio.h> +#include <string.h> +#include "host/ble_hs.h" +#include <bleuart/bleuart.h> +#include <os/endian.h> +#include <console/console.h> + +/* ble uart attr read handle */ +uint16_t g_bleuart_attr_read_handle; + +/* ble uart attr write handle */ +uint16_t g_bleuart_attr_write_handle; + +/* Pointer to a console buffer */ +char *console_buf; + +/* Console max input */ +uint16_t console_max_input; + +uint16_t g_console_conn_handle; +/** + * The vendor specific "bleuart" service consists of one write no-rsp characteristic + * and one notification only read charateristic + * o "write no-rsp": a single-byte characteristic that can be written only + * over a non-encrypted connection + * o "read": a single-byte characteristic that can always be read only via + * notifications + */ + +/* {6E400001-B5A3-F393-E0A9-E50E24DCCA9E} */ +const uint8_t gatt_svr_svc_uart[16] = { + 0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0, + 0x93, 0xf3, 0xa3, 0xb5, 0x01, 0x00, 0x40, 0x6e +}; + +/* {6E400002-B5A3-F393-E0A9-E50E24DCCA9E} */ +const uint8_t gatt_svr_chr_uart_write[16] = { + 0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0, + 0x93, 0xf3, 0xa3, 0xb5, 0x02, 0x00, 0x40, 0x6e +}; + + +/* {6E400003-B5A3-F393-E0A9-E50E24DCCA9E} */ +const uint8_t gatt_svr_chr_uart_read[16] = { + 0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0, + 0x93, 0xf3, 0xa3, 0xb5, 0x03, 0x00, 0x40, 0x6e +}; + +static int +gatt_svr_chr_access_uart_write(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /* Service: uart */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid128 = (void *)gatt_svr_svc_uart, + .characteristics = (struct ble_gatt_chr_def[]) { { + .uuid128 = gatt_svr_chr_uart_read, + .val_handle = &g_bleuart_attr_read_handle, + .access_cb = gatt_svr_chr_access_uart_write, + .flags = BLE_GATT_CHR_F_NOTIFY, + }, { + /* Characteristic: Write */ + .uuid128 = (void *)gatt_svr_chr_uart_write, + .access_cb = gatt_svr_chr_access_uart_write, + .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_NO_RSP, + .val_handle = &g_bleuart_attr_write_handle, + }, { + 0, /* No more characteristics in this service */ + } }, + }, + + { + 0, /* No more services */ + }, +}; + +static int +gatt_svr_chr_access_uart_write(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + struct os_mbuf *om = ctxt->om; + switch (ctxt->op) { + case BLE_GATT_ACCESS_OP_WRITE_CHR: + while(om) { + console_write((char *)om->om_data, om->om_len); + om = SLIST_NEXT(om, om_next); + } + console_write("\n", 1); + return 0; + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +/** + * bleuart GATT server initialization + * + * @param eventq + * @return 0 on success; non-zero on failure + */ +int +bleuart_gatt_svr_init(struct ble_hs_cfg *cfg) +{ + int rc; + + rc = ble_gatts_count_cfg(gatt_svr_svcs, cfg); + if (rc != 0) { + goto err; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + +err: + return rc; +} + +/** + * Reads console and sends data over BLE + */ +static void +bleuart_uart_read(void) +{ + int rc; + int off; + int full_line; + struct os_mbuf *om; + + off = 0; + while (1) { + rc = console_read(console_buf + off, + console_max_input - off, &full_line); + if (rc <= 0 && !full_line) { + continue; + } + off += rc; + if (!full_line) { + continue; + } + + om = ble_hs_mbuf_from_flat(console_buf, off); + if (!om) { + return; + } + ble_gattc_notify_custom(g_console_conn_handle, + g_bleuart_attr_read_handle, om); + off = 0; + break; + } +} + +/** + * Sets the global connection handle + * + * @param connection handle + */ +void +bleuart_set_conn_handle(uint16_t conn_handle) { + g_console_conn_handle = conn_handle; +} + +/** + * BLEuart console initialization + * + * @param Maximum input + */ +int +bleuart_init(int max_input) +{ + int rc; + + rc = console_init(bleuart_uart_read); + assert(rc == 0); + + console_buf = malloc(max_input); + console_max_input = max_input; + assert(console_buf); + return 0; +} diff --git a/libs/boot_serial/src/test/boot_test.c b/libs/boot_serial/src/test/boot_test.c index e11a6492..8fe3425a 100644 --- a/libs/boot_serial/src/test/boot_test.c +++ b/libs/boot_serial/src/test/boot_test.c @@ -25,6 +25,7 @@ #include <inttypes.h> #include <util/base64.h> #include <util/crc16.h> +#include <os/endian.h> #include <testutil/testutil.h> #include <hal/hal_flash.h> #include <hal/flash_map.h> diff --git a/libs/bootutil/include/bootutil/bootutil_misc.h b/libs/bootutil/include/bootutil/bootutil_misc.h index ff42ac85..2e3049d3 100644 --- a/libs/bootutil/include/bootutil/bootutil_misc.h +++ b/libs/bootutil/include/bootutil/bootutil_misc.h @@ -20,12 +20,9 @@ #ifndef __BOOTUTIL_MISC_H_ #define __BOOTUTIL_MISC_H_ -struct image_version; -int boot_vect_read_test(struct image_version *out_ver); -int boot_vect_read_main(struct image_version *out_ver); -int boot_vect_write_test(struct image_version *ver); -int boot_vect_write_main(struct image_version *ver); - -void bootutil_cfg_register(void); +int boot_vect_read_test(int *slot); +int boot_vect_read_main(int *slot); +int boot_vect_write_test(int slot); +int boot_vect_write_main(void); #endif /* __BOOTUTIL_MISC_H_ */ diff --git a/libs/bootutil/include/bootutil/loader.h b/libs/bootutil/include/bootutil/loader.h index 86f06bad..57a9eef2 100644 --- a/libs/bootutil/include/bootutil/loader.h +++ b/libs/bootutil/include/bootutil/loader.h @@ -46,6 +46,9 @@ struct boot_req { /** The area to use as the image scratch area, index is index to br_area_descs array, of the */ uint8_t br_scratch_area_idx; + + /** Size of the image slot */ + uint32_t br_img_sz; }; /** diff --git a/libs/bootutil/src/bootutil_misc.c b/libs/bootutil/src/bootutil_misc.c index dd4c9036..6539bdd1 100644 --- a/libs/bootutil/src/bootutil_misc.c +++ b/libs/bootutil/src/bootutil_misc.c @@ -19,132 +19,94 @@ #include <string.h> #include <inttypes.h> +#include <assert.h> #include <hal/hal_flash.h> -#include <config/config.h> +#include <hal/flash_map.h> +#include <hal/hal_bsp.h> #include <os/os.h> #include "bootutil/image.h" +#include "bootutil/bootutil_misc.h" #include "bootutil_priv.h" -#ifdef USE_STATUS_FILE -#include <fs/fs.h> -#include <fs/fsutil.h> -#endif - -static int boot_conf_set(int argc, char **argv, char *val); - -static struct image_version boot_main; -static struct image_version boot_test; -#ifndef USE_STATUS_FILE -static struct boot_status boot_saved; -#endif - -static struct conf_handler boot_conf_handler = { - .ch_name = "boot", - .ch_get = NULL, - .ch_set = boot_conf_set, - .ch_commit = NULL, - .ch_export = NULL, -}; - +/* + * Read the image trailer from a given slot. + */ static int -boot_conf_set(int argc, char **argv, char *val) +boot_vect_read_img_trailer(int slot, struct boot_img_trailer *bit) { int rc; - int len; + const struct flash_area *fap; + uint32_t off; - if (argc == 1) { - if (!strcmp(argv[0], "main")) { - len = sizeof(boot_main); - if (val) { - rc = conf_bytes_from_str(val, &boot_main, &len); - } else { - memset(&boot_main, 0, len); - rc = 0; - } - } else if (!strcmp(argv[0], "test")) { - len = sizeof(boot_test); - if (val) { - rc = conf_bytes_from_str(val, &boot_test, &len); - } else { - memset(&boot_test, 0, len); - rc = 0; - } -#ifndef USE_STATUS_FILE - } else if (!strcmp(argv[0], "status")) { - if (!val) { - boot_saved.state = 0; - rc = 0; - } else { - rc = conf_value_from_str(val, CONF_INT32, - &boot_saved.state, sizeof(boot_saved.state)); - } - } else if (!strcmp(argv[0], "len")) { - conf_value_from_str(val, CONF_INT32, &boot_saved.length, - sizeof(boot_saved.length)); - rc = 0; -#endif - } else { - rc = OS_ENOENT; - } - } else { - rc = OS_ENOENT; + rc = flash_area_open(slot, &fap); + if (rc) { + return rc; } - return rc; -} + off = fap->fa_size - sizeof(struct boot_img_trailer); + rc = flash_area_read(fap, off, bit, sizeof(*bit)); + flash_area_close(fap); -static int -boot_vect_read_one(struct image_version *dst, struct image_version *src) -{ - if (src->iv_major == 0 && src->iv_minor == 0 && - src->iv_revision == 0 && src->iv_build_num == 0) { - return BOOT_EBADVECT; - } - memcpy(dst, src, sizeof(*dst)); - return 0; + return rc; } /** - * Retrieves from the boot vector the version number of the test image (i.e., + * Retrieves from the slot number of the test image (i.e., * the image that has not been proven stable, and which will only run once). * - * @param out_ver On success, the test version gets written here. + * @param slot On success, the slot number of image to boot. * * @return 0 on success; nonzero on failure. */ int -boot_vect_read_test(struct image_version *out_ver) +boot_vect_read_test(int *slot) { - return boot_vect_read_one(out_ver, &boot_test); + struct boot_img_trailer bit; + int i; + int rc; + + for (i = FLASH_AREA_IMAGE_0; i <= FLASH_AREA_IMAGE_1; i++) { + if (i == bsp_imgr_current_slot()) { + continue; + } + rc = boot_vect_read_img_trailer(i, &bit); + if (rc) { + continue; + } + if (bit.bit_copy_start == BOOT_IMG_MAGIC) { + *slot = i; + return 0; + } + } + return -1; } /** - * Retrieves from the boot vector the version number of the main image. + * Retrieves from the slot number of the main image. If this is + * different from test image slot, next restart will revert to main. * * @param out_ver On success, the main version gets written here. * * @return 0 on success; nonzero on failure. */ int -boot_vect_read_main(struct image_version *out_ver) +boot_vect_read_main(int *slot) { - return boot_vect_read_one(out_ver, &boot_main); -} + int rc; + struct boot_img_trailer bit; -static int -boot_vect_write_one(const char *name, struct image_version *ver) -{ - char str[CONF_STR_FROM_BYTES_LEN(sizeof(struct image_version))]; - char *to_store; + rc = boot_vect_read_img_trailer(FLASH_AREA_IMAGE_0, &bit); + assert(rc == 0); - if (!ver) { - to_store = NULL; + if (bit.bit_copy_start != BOOT_IMG_MAGIC || bit.bit_img_ok != 0xff) { + /* + * If there never was copy that took place, or if the current + * image has been marked good, we'll keep booting it. + */ + *slot = FLASH_AREA_IMAGE_0; } else { - if (!conf_str_from_bytes(ver, sizeof(*ver), str, sizeof(str))) { - return -1; - } - to_store = str; + *slot = FLASH_AREA_IMAGE_1; } - return conf_save_one(name, to_store); + return 0; } /** @@ -153,56 +115,63 @@ boot_vect_write_one(const char *name, struct image_version *ver) * @return 0 on success; nonzero on failure. */ int -boot_vect_write_test(struct image_version *ver) +boot_vect_write_test(int slot) { - if (!ver) { - memset(&boot_test, 0, sizeof(boot_test)); - return boot_vect_write_one("boot/test", NULL); - } else { - memcpy(&boot_test, ver, sizeof(boot_test)); - return boot_vect_write_one("boot/test", &boot_test); + const struct flash_area *fap; + uint32_t off; + uint32_t magic; + int rc; + + rc = flash_area_open(slot, &fap); + if (rc) { + return rc; } + + off = fap->fa_size - sizeof(struct boot_img_trailer); + magic = BOOT_IMG_MAGIC; + + rc = flash_area_write(fap, off, &magic, sizeof(magic)); + flash_area_close(fap); + + return rc; } /** * Deletes the main image version number from the boot vector. + * This must be called by the app to confirm that it is ok to keep booting + * to this image. * * @return 0 on success; nonzero on failure. */ int -boot_vect_write_main(struct image_version *ver) -{ - if (!ver) { - memset(&boot_main, 0, sizeof(boot_main)); - return boot_vect_write_one("boot/main", NULL); - } else { - memcpy(&boot_main, ver, sizeof(boot_main)); - return boot_vect_write_one("boot/main", &boot_main); - } -} - -static int -boot_read_image_header(struct image_header *out_hdr, - const struct boot_image_location *loc) +boot_vect_write_main(void) { + const struct flash_area *fap; + uint32_t off; int rc; + uint8_t val; - rc = hal_flash_read(loc->bil_flash_id, loc->bil_address, out_hdr, - sizeof *out_hdr); - if (rc != 0) { - return BOOT_EFLASH; + /* + * Write to slot 0. + */ + rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap); + if (rc) { + return rc; } - if (out_hdr->ih_magic != IMAGE_MAGIC) { - return BOOT_EBADIMAGE; + off = fap->fa_size - sizeof(struct boot_img_trailer); + off += (sizeof(uint32_t) + sizeof(uint8_t)); + rc = flash_area_read(fap, off, &val, sizeof(val)); + if (!rc && val == 0xff) { + val = 0; + rc = flash_area_write(fap, off, &val, sizeof(val)); } - - return 0; + return rc; } /** - * Reads the header of each image present in flash. Headers corresponding to - * empty image slots are filled with 0xff bytes. + * Reads the header of image present in flash. Header corresponding to + * empty image slot is filled with 0xff bytes. * * @param out_headers Points to an array of image headers. Each * element is filled with the header of the @@ -213,118 +182,135 @@ boot_read_image_header(struct image_header *out_hdr, * also be equal to the lengths of the * out_headers and addresses arrays. */ -void -boot_read_image_headers(struct image_header *out_headers, - const struct boot_image_location *addresses, - int num_addresses) +int +boot_read_image_header(struct boot_image_location *loc, + struct image_header *out_hdr) { - struct image_header *hdr; int rc; - int i; - for (i = 0; i < num_addresses; i++) { - hdr = out_headers + i; - rc = boot_read_image_header(hdr, &addresses[i]); - if (rc != 0 || hdr->ih_magic != IMAGE_MAGIC) { - memset(hdr, 0xff, sizeof *hdr); - } + rc = hal_flash_read(loc->bil_flash_id, loc->bil_address, out_hdr, + sizeof *out_hdr); + if (rc != 0) { + rc = BOOT_EFLASH; + } else if (out_hdr->ih_magic != IMAGE_MAGIC) { + rc = BOOT_EBADIMAGE; } + + if (rc) { + memset(out_hdr, 0xff, sizeof(*out_hdr)); + } + return rc; } -void -bootutil_cfg_register(void) +/* + * How far has the copy progressed? + */ +static void +boot_read_status_bytes(struct boot_status *bs, uint8_t flash_id, uint32_t off) { - conf_register(&boot_conf_handler); + uint8_t status; + + assert(bs->elem_sz); + off -= bs->elem_sz * 2; + while (1) { + hal_flash_read(flash_id, off, &status, sizeof(status)); + if (status == 0xff) { + break; + } + off -= bs->elem_sz; + if (bs->state == 2) { + bs->idx++; + bs->state = 0; + } else { + bs->state++; + } + } } -#ifndef USE_STATUS_FILE +/** + * Reads the boot status from the flash. The boot status contains + * the current state of an interrupted image copy operation. If the boot + * status is not present, or it indicates that previous copy finished, + * there is no operation in progress. + */ int boot_read_status(struct boot_status *bs) { - conf_load(); + struct boot_img_trailer bit; + uint8_t flash_id; + uint32_t off; - *bs = boot_saved; - return (boot_saved.state != 0); + /* + * Check if boot_img_trailer is in scratch, or at the end of slot0. + */ + boot_slot_magic(0, &bit); + if (bit.bit_copy_start == BOOT_IMG_MAGIC && bit.bit_copy_done == 0xff) { + boot_magic_loc(0, &flash_id, &off); + boot_read_status_bytes(bs, flash_id, off); + return 1; + } + boot_scratch_magic(&bit); + if (bit.bit_copy_start == BOOT_IMG_MAGIC && bit.bit_copy_done == 0xff) { + boot_scratch_loc(&flash_id, &off); + boot_read_status_bytes(bs, flash_id, off); + return 1; + } + return 0; } + /** * Writes the supplied boot status to the flash file system. The boot status * contains the current state of an in-progress image copy operation. * - * @param status The boot status base to write. + * @param bs The boot status to write. * * @return 0 on success; nonzero on failure. */ int boot_write_status(struct boot_status *bs) { - char str[12]; - int rc; - - rc = conf_save_one("boot/status", - conf_str_from_value(CONF_INT32, &bs->state, str, sizeof(str))); - if (rc) { - return rc; + uint32_t off; + uint8_t flash_id; + uint8_t val; + + if (bs->idx == 0) { + /* + * Write to scratch + */ + boot_scratch_loc(&flash_id, &off); + } else { + /* + * Write to slot 0; + */ + boot_magic_loc(0, &flash_id, &off); } - return conf_save_one("boot/len", - conf_str_from_value(CONF_INT32, &bs->length, str, sizeof(str))); + off -= ((3 * bs->elem_sz) * bs->idx + bs->elem_sz * (bs->state + 1)); + + val = bs->state; + hal_flash_write(flash_id, off, &val, sizeof(val)); + + return 0; } /** - * Erases the boot status from the flash file system. The boot status + * Finalizes the copy-in-progress status on the flash. The boot status * contains the current state of an in-progress image copy operation. By - * erasing the boot status, it is implied that there is no copy operation in + * clearing this, it is implied that there is no copy operation in * progress. */ void boot_clear_status(void) { - conf_save_one("boot/status", NULL); -} - -#else - -/** - * Reads the boot status from the flash file system. The boot status contains - * the current state of an interrupted image copy operation. If the boot - * status is not present in the file system, the implication is that there is - * no copy operation in progress. - */ -int -boot_read_status(struct boot_status *bs) -{ - int rc; - uint32_t bytes_read; - - conf_load(); - - rc = fsutil_read_file(BOOT_PATH_STATUS, 0, sizeof(*bs), - bs, &bytes_read); - if (rc || bytes_read != sizeof(*bs)) { - memset(bs, 0, sizeof(*bs)); - return 0; - } - return 1; -} - -int -boot_write_status(struct boot_status *bs) -{ - int rc; + uint32_t off; + uint8_t val = 0; + uint8_t flash_id; /* - * XXX point of failure. + * Write to slot 0; boot_img_trailer is the 8 bytes within image slot. + * Here we say that copy operation was finished. */ - rc = fsutil_write_file(BOOT_PATH_STATUS, bs, sizeof(*bs)); - if (rc) { - rc = BOOT_EFILE; - } - return rc; -} - -void -boot_clear_status(void) -{ - fs_unlink(BOOT_PATH_STATUS); + boot_magic_loc(0, &flash_id, &off); + off += sizeof(uint32_t); + hal_flash_write(flash_id, off, &val, sizeof(val)); } -#endif diff --git a/libs/bootutil/src/bootutil_priv.h b/libs/bootutil/src/bootutil_priv.h index 98aa29f0..7fe6eace 100644 --- a/libs/bootutil/src/bootutil_priv.h +++ b/libs/bootutil/src/bootutil_priv.h @@ -6,7 +6,7 @@ * 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, @@ -21,7 +21,6 @@ #define H_BOOTUTIL_PRIV_ #include "bootutil/image.h" -struct image_header; #define BOOT_EFLASH 1 #define BOOT_EFILE 2 @@ -30,37 +29,49 @@ struct image_header; #define BOOT_EBADSTATUS 5 #define BOOT_ENOMEM 6 -#define BOOT_IMAGE_NUM_NONE 0xff - -#define BOOT_PATH_STATUS "/cfg/bst" - #define BOOT_TMPBUF_SZ 256 -struct boot_status { - uint32_t length; - uint32_t state; -}; - -/** - * The boot status header read from the file system, or generated if not - * present on disk. The boot status indicates the state of the image slots in - * case the system was restarted while images were being moved in flash. - */ - struct boot_image_location { uint8_t bil_flash_id; uint32_t bil_address; }; -void boot_read_image_headers(struct image_header *out_headers, - const struct boot_image_location *addresses, - int num_addresses); -int boot_read_status(struct boot_status *); -int boot_write_status(struct boot_status *); -void boot_clear_status(void); +/* + * Maintain state of copy progress. + */ +struct boot_status { + uint32_t idx; /* Which area we're operating on */ + uint8_t elem_sz; /* Size of the status element to write in bytes */ + uint8_t state; /* Which part of the swapping process are we at */ +}; + +/* + * End-of-image slot data structure. + */ +#define BOOT_IMG_MAGIC 0x12344321 +struct boot_img_trailer { + uint32_t bit_copy_start; + uint8_t bit_copy_done; + uint8_t bit_img_ok; + uint16_t _pad; +}; int bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, int slen, uint8_t key_id); +int boot_read_image_header(struct boot_image_location *loc, + struct image_header *out_hdr); +int boot_write_status(struct boot_status *bs); +int boot_read_status(struct boot_status *bs); +void boot_clear_status(void); + +void boot_magic_loc(int slot_num, uint8_t *flash_id, uint32_t *off); +void boot_scratch_loc(uint8_t *flash_id, uint32_t *off); +void boot_slot_magic(int slot_num, struct boot_img_trailer *bit); +void boot_scratch_magic(struct boot_img_trailer *bit); + +struct boot_req; +void boot_req_set(struct boot_req *req); + #endif diff --git a/libs/bootutil/src/loader.c b/libs/bootutil/src/loader.c index bd7fbd8f..8037c98c 100644 --- a/libs/bootutil/src/loader.c +++ b/libs/bootutil/src/loader.c @@ -35,94 +35,136 @@ /** The request object provided by the client. */ static const struct boot_req *boot_req; -/** Image headers read from flash. */ -struct image_header boot_img_hdrs[2]; +/** Info about image slots. */ +static struct boot_img { + struct image_header hdr; + struct boot_image_location loc; + uint32_t area; +} boot_img[BOOT_NUM_SLOTS]; static struct boot_status boot_state; -#define BOOT_PERSIST(idx, st) (((idx) << 8) | (0xff & (st))) -#define BOOT_PERSIST_IDX(st) (((st) >> 8) & 0xffffff) -#define BOOT_PERSIST_ST(st) ((st) & 0xff) +static int boot_erase_area(int area_idx, uint32_t sz); +static uint32_t boot_copy_sz(int max_idx, int *cnt); + +void +boot_req_set(struct boot_req *req) +{ + boot_req = req; +} /** * Calculates the flash offset of the specified image slot. * * @param slot_num The number of the slot to calculate. + * @param loc The flash location of the slot. * - * @return The flash offset of the image slot. */ static void -boot_slot_addr(int slot_num, uint8_t *flash_id, uint32_t *address) +boot_slot_addr(int slot_num, struct boot_image_location *loc) { const struct flash_area *area_desc; uint8_t area_idx; - assert(slot_num >= 0 && slot_num < BOOT_NUM_SLOTS); - area_idx = boot_req->br_slot_areas[slot_num]; area_desc = boot_req->br_area_descs + area_idx; - *flash_id = area_desc->fa_flash_id; - *address = area_desc->fa_off; + loc->bil_flash_id = area_desc->fa_flash_id; + loc->bil_address = area_desc->fa_off; } -/** - * Searches flash for an image with the specified version number. +/* + * Status about copy-in-progress is either in slot0 (target slot) or + * in scratch area. It is in scratch area if the process is currently + * moving the last sector within image. * - * @param ver The version number to search for. + * If the copy-in-progress status is within the image slot, it will + * be at the end of the area. + * If the sector containing the boot copy status is in scratch, it's + * offset from beginning of scratch depends on how much of the image + * fits inside the scratch area. * - * @return The image slot containing the specified image - * on success; -1 on failure. + * We start copy from the end of image, so boot-copy-status is in + * scratch when the first area is being moved. Otherwise it will be + * in slot 0. */ -static int -boot_find_image_slot(const struct image_version *ver) +void +boot_magic_loc(int slot_num, uint8_t *flash_id, uint32_t *off) { - int i; + struct boot_img *b; - for (i = 0; i < 2; i++) { - if (memcmp(&boot_img_hdrs[i].ih_ver, ver, sizeof *ver) == 0) { - return i; - } - } + b = &boot_img[slot_num]; + *flash_id = b->loc.bil_flash_id; + *off = b->area + b->loc.bil_address - sizeof(struct boot_img_trailer); +} + +void +boot_scratch_loc(uint8_t *flash_id, uint32_t *off) +{ + struct flash_area *scratch; + int cnt; + + scratch = &boot_req->br_area_descs[boot_req->br_scratch_area_idx]; + *flash_id = scratch->fa_flash_id; - return -1; + /* + * Calculate where the boot status would be, if it was copied to scratch. + */ + *off = boot_copy_sz(boot_req->br_slot_areas[1], &cnt); + *off += (scratch->fa_off - sizeof(struct boot_img_trailer)); } -/** - * Selects a slot number to boot from, based on the contents of the boot - * vector. - * - * @return The slot number to boot from on success; - * -1 if an appropriate slot could not be - * determined. +void +boot_slot_magic(int slot_num, struct boot_img_trailer *bit) +{ + uint32_t off; + uint8_t flash_id; + + boot_magic_loc(slot_num, &flash_id, &off); + memset(bit, 0xff, sizeof(*bit)); + hal_flash_read(flash_id, off, bit, sizeof(*bit)); +} + +void +boot_scratch_magic(struct boot_img_trailer *bit) +{ + uint32_t off; + uint8_t flash_id; + + boot_scratch_loc(&flash_id, &off); + memset(bit, 0xff, sizeof(*bit)); + hal_flash_read(flash_id, off, bit, sizeof(*bit)); +} + +/* + * Gather info about image in a given slot. */ -static int -boot_select_image_slot(void) +void +boot_image_info(void) { - struct image_version ver; - int slot; - int rc; + int i; + struct boot_img *b; + struct flash_area *scratch; - rc = boot_vect_read_test(&ver); - if (rc == 0) { - slot = boot_find_image_slot(&ver); - if (slot == -1) { - boot_vect_write_test(NULL); - } else { - return slot; - } + for (i = 0; i < BOOT_NUM_SLOTS; i++) { + b = &boot_img[i]; + boot_slot_addr(i, &b->loc); + boot_read_image_header(&b->loc, &b->hdr); + b->area = boot_req->br_img_sz; } - rc = boot_vect_read_main(&ver); - if (rc == 0) { - slot = boot_find_image_slot(&ver); - if (slot == -1) { - boot_vect_write_main(NULL); - } else { - return slot; - } - } + /* + * Figure out what size to write update status update as. + * The size depends on what the minimum write size is for scratch + * area, active image slot. We need to use the bigger of those 2 + * values. + */ + boot_state.elem_sz = hal_flash_align(boot_img[0].loc.bil_flash_id); - return -1; + scratch = &boot_req->br_area_descs[boot_req->br_scratch_area_idx]; + i = hal_flash_align(scratch->fa_flash_id); + if (i > boot_state.elem_sz) { + boot_state.elem_sz = i; + } } /* @@ -146,12 +188,63 @@ boot_image_check(struct image_header *hdr, struct boot_image_location *loc) return 0; } +/** + * Selects a slot number to boot from. + * + * @return The slot number to boot from on success; + * -1 if an appropriate slot could not be + * determined. + */ +static int +boot_select_image_slot(void) +{ + /* + * Check for swap magic. Check the integrity of the suggested image. + */ + int rc; + int i; + struct boot_img *b; + struct boot_img_trailer bit; + + boot_slot_magic(0, &bit); + if (bit.bit_copy_start == BOOT_IMG_MAGIC && bit.bit_copy_done != 0xff && + bit.bit_img_ok == 0xff) { + /* + * Copied the image successfully, but image was not confirmed as good. + * We need to go back to another image. + */ + boot_vect_write_test(FLASH_AREA_IMAGE_1); + } + for (i = 1; i < BOOT_NUM_SLOTS; i++) { + b = &boot_img[i]; + boot_slot_magic(i, &bit); + if (bit.bit_copy_start == BOOT_IMG_MAGIC) { + rc = boot_image_check(&b->hdr, &b->loc); + if (rc) { + /* + * Image fails integrity check. Erase it. + */ + boot_erase_area(boot_req->br_slot_areas[i], b->area); + } else { + return i; + } + } + } + return 0; +} + +static int +boot_status_sz(void) +{ + return sizeof(struct boot_img_trailer) + 32 * sizeof(uint32_t); +} + /* * How many sectors starting from sector[idx] can fit inside scratch. * */ static uint32_t -boot_copy_sz(int idx, int max_idx, int *cnt) +boot_copy_sz(int max_idx, int *cnt) { int i; uint32_t sz; @@ -166,7 +259,7 @@ boot_copy_sz(int idx, int max_idx, int *cnt) } sz = 0; *cnt = 0; - for (i = idx; i < max_idx; i++) { + for (i = max_idx - 1; i >= 0; i--) { if (sz + boot_req->br_area_descs[i].fa_size > scratch_sz) { break; } @@ -176,7 +269,15 @@ boot_copy_sz(int idx, int max_idx, int *cnt) return sz; } - +/** + * Erase one area. The destination area must + * be erased prior to this function being called. + * + * @param area_idx The index of the area. + * @param sz The number of bytes to erase. + * + * @return 0 on success; nonzero on failure. + */ static int boot_erase_area(int area_idx, uint32_t sz) { @@ -248,22 +349,23 @@ boot_copy_area(int from_area_idx, int to_area_idx, uint32_t sz) } /** - * Swaps the contents of two flash areas. + * Swaps the contents of two flash areas belonging to images. * - * @param area_idx_1 The index of one area to swap. This area + * @param area_idx The index of first slot to exchange. This area * must be part of the first image slot. - * @param area_idx_2 The index of the other area to swap. This - * area must be part of the second image - * slot. + * @param sz The number of bytes swap. + * + * @param end_area Boolean telling whether this includes this + * area has last slots. + * * @return 0 on success; nonzero on failure. */ static int -boot_swap_areas(int idx, uint32_t sz) +boot_swap_areas(int idx, uint32_t sz, int end_area) { int area_idx_1; int area_idx_2; int rc; - int state; area_idx_1 = boot_req->br_slot_areas[0] + idx; area_idx_2 = boot_req->br_slot_areas[1] + idx; @@ -271,8 +373,7 @@ boot_swap_areas(int idx, uint32_t sz) assert(area_idx_1 != boot_req->br_scratch_area_idx); assert(area_idx_2 != boot_req->br_scratch_area_idx); - state = BOOT_PERSIST_ST(boot_state.state); - if (state == 0) { + if (boot_state.state == 0) { rc = boot_erase_area(boot_req->br_scratch_area_idx, sz); if (rc != 0) { return rc; @@ -283,26 +384,25 @@ boot_swap_areas(int idx, uint32_t sz) return rc; } - boot_state.state = BOOT_PERSIST(idx, 1); + boot_state.state = 1; (void)boot_write_status(&boot_state); - state = 1; } - if (state == 1) { + if (boot_state.state == 1) { rc = boot_erase_area(area_idx_2, sz); if (rc != 0) { return rc; } - rc = boot_copy_area(area_idx_1, area_idx_2, sz); + rc = boot_copy_area(area_idx_1, area_idx_2, + end_area ? (sz - boot_status_sz()) : sz); if (rc != 0) { return rc; } - boot_state.state = BOOT_PERSIST(idx, 2); + boot_state.state = 2; (void)boot_write_status(&boot_state); - state = 2; } - if (state == 2) { + if (boot_state.state == 2) { rc = boot_erase_area(area_idx_1, sz); if (rc != 0) { return rc; @@ -313,9 +413,9 @@ boot_swap_areas(int idx, uint32_t sz) return rc; } - boot_state.state = BOOT_PERSIST(idx + 1, 0); + boot_state.idx++; + boot_state.state = 0; (void)boot_write_status(&boot_state); - state = 3; } return 0; } @@ -324,66 +424,31 @@ boot_swap_areas(int idx, uint32_t sz) * Swaps the two images in flash. If a prior copy operation was interrupted * by a system reset, this function completes that operation. * - * @param img1_length The length, in bytes, of the slot 1 image. - * @param img2_length The length, in bytes, of the slot 2 image. - * * @return 0 on success; nonzero on failure. */ static int boot_copy_image(void) { - uint32_t off; uint32_t sz; int i; + int end_area = 1; int cnt; - int rc; - int state_idx; - - state_idx = BOOT_PERSIST_IDX(boot_state.state); - for (off = 0, i = 0; off < boot_state.length; off += sz, i += cnt) { - sz = boot_copy_sz(i, boot_req->br_slot_areas[1], &cnt); - if (i >= state_idx) { - rc = boot_swap_areas(i, sz); - assert(rc == 0); + int cur_idx; + + for (i = boot_req->br_slot_areas[1], cur_idx = 0; i > 0; cur_idx++) { + sz = boot_copy_sz(i, &cnt); + i -= cnt; + if (cur_idx >= boot_state.idx) { + boot_swap_areas(i, sz, end_area); } + end_area = 0; } + boot_clear_status(); return 0; } /** - * Builds a default boot status corresponding to all images being fully present - * in their slots. This function is used when a boot status is not present in - * flash (i.e., in the usual case when the previous boot operation ran to - * completion). - */ -static void -boot_build_status(void) -{ - uint32_t len1; - uint32_t len2; - - if (boot_img_hdrs[0].ih_magic == IMAGE_MAGIC) { - len1 = boot_img_hdrs[0].ih_hdr_size + boot_img_hdrs[0].ih_img_size + - boot_img_hdrs[0].ih_tlv_size; - } else { - len1 = 0; - } - - if (boot_img_hdrs[1].ih_magic == IMAGE_MAGIC) { - len2 = boot_img_hdrs[1].ih_hdr_size + boot_img_hdrs[1].ih_img_size + - boot_img_hdrs[0].ih_tlv_size; - } else { - len2 = 0; - } - boot_state.length = len1; - if (len1 < len2) { - boot_state.length = len2; - } - boot_state.state = 0; -} - -/** * Prepares the booting process. Based on the information provided in the * request object, this function moves images around in flash as appropriate, * and tells you what address to boot from. @@ -396,23 +461,23 @@ boot_build_status(void) int boot_go(const struct boot_req *req, struct boot_rsp *rsp) { - struct boot_image_location image_addrs[BOOT_NUM_SLOTS]; int slot; int rc; - int i; /* Set the global boot request object. The remainder of the boot process * will reference the global. */ boot_req = req; + /* Attempt to read an image header from each slot. */ + boot_image_info(); + /* Read the boot status to determine if an image copy operation was * interrupted (i.e., the system was reset before the boot loader could * finish its task last time). */ if (boot_read_status(&boot_state)) { /* We are resuming an interrupted image copy. */ - /* XXX if copy has not actually started yet, validate image */ rc = boot_copy_image(); if (rc != 0) { /* We failed to put the images back together; there is really no @@ -422,80 +487,28 @@ boot_go(const struct boot_req *req, struct boot_rsp *rsp) } } - /* Cache the flash address of each image slot. */ - for (i = 0; i < BOOT_NUM_SLOTS; i++) { - boot_slot_addr(i, &image_addrs[i].bil_flash_id, - &image_addrs[i].bil_address); - } - - /* Attempt to read an image header from each slot. */ - boot_read_image_headers(boot_img_hdrs, image_addrs, BOOT_NUM_SLOTS); - - /* Build a boot status structure indicating the flash location of each - * image part. This structure will need to be used if an image copy - * operation is required. + /* + * Check if we should initiate copy, or revert back to earlier image. + * */ - boot_build_status(); - - /* Determine which image the user wants to run, and where it is located. */ slot = boot_select_image_slot(); if (slot == -1) { - /* Either there is no image vector, or none of the requested images are - * present. Just try booting from the first image slot. - */ - if (boot_img_hdrs[0].ih_magic != IMAGE_MAGIC_NONE) { - slot = 0; - } else if (boot_img_hdrs[1].ih_magic != IMAGE_MAGIC_NONE) { - slot = 1; - } else { - /* No images present. */ - return BOOT_EBADIMAGE; - } + return BOOT_EBADIMAGE; } - /* - * If the selected image fails integrity check, try the other one. - */ - if (boot_image_check(&boot_img_hdrs[slot], &image_addrs[slot])) { - slot ^= 1; - if (boot_image_check(&boot_img_hdrs[slot], &image_addrs[slot])) { - return BOOT_EBADIMAGE; - } - } - switch (slot) { - case 0: - rsp->br_hdr = &boot_img_hdrs[0]; - break; - - case 1: - /* The user wants to run the image in the secondary slot. The contents - * of this slot need to moved to the primary slot. - */ + if (slot) { + boot_state.idx = 0; + boot_state.state = 0; rc = boot_copy_image(); - if (rc != 0) { - /* We failed to put the images back together; there is really no - * solution here. - */ + if (rc) { return rc; } - - rsp->br_hdr = &boot_img_hdrs[1]; - break; - - default: - assert(0); - break; } /* Always boot from the primary slot. */ - rsp->br_flash_id = image_addrs[0].bil_flash_id; - rsp->br_image_addr = image_addrs[0].bil_address; - - /* After successful boot, there should not be a status file. */ - boot_clear_status(); - - /* If an image is being tested, it should only be booted into once. */ - boot_vect_write_test(NULL); + rsp->br_flash_id = boot_img[0].loc.bil_flash_id; + rsp->br_image_addr = boot_img[0].loc.bil_address; + rsp->br_hdr = &boot_img[slot].hdr; return 0; } diff --git a/libs/bootutil/src/test/boot_test.c b/libs/bootutil/src/test/boot_test.c index e78d2e4c..c1dcb836 100644 --- a/libs/bootutil/src/test/boot_test.c +++ b/libs/bootutil/src/test/boot_test.c @@ -47,13 +47,7 @@ static struct flash_area boot_test_area_descs[] = { [4] = { .fa_off = 0x000a0000, .fa_size = 128 * 1024 }, [5] = { .fa_off = 0x000c0000, .fa_size = 128 * 1024 }, [6] = { .fa_off = 0x000e0000, .fa_size = 128 * 1024 }, -}; - -static const struct flash_area boot_test_format_descs[] = { - [0] = { .fa_off = 0x00004000, .fa_size = 16 * 1024 }, - [1] = { .fa_off = 0x00008000, .fa_size = 16 * 1024 }, - [2] = { .fa_off = 0x0000c000, .fa_size = 16 * 1024 }, - [3] = { .fa_off = 0, .fa_size = 0 }, + [7] = { 0 }, }; /** Areas representing the beginning of image slots. */ @@ -342,7 +336,9 @@ boot_test_util_verify_area(const struct flash_area *area_desc, TEST_ASSERT(buf[i] == boot_test_util_byte_at(img_msb, img_off + i)); } else if (past_image) { +#if 0 TEST_ASSERT(buf[i] == 0xff); +#endif } } @@ -353,45 +349,18 @@ boot_test_util_verify_area(const struct flash_area *area_desc, static void boot_test_util_verify_status_clear(void) { - struct fs_file *file; + struct boot_img_trailer bit; + const struct flash_area *fap; int rc; - int empty = 1; - char *needle = "boot/status="; - int nlen = strlen(needle); - uint32_t len, hlen; - char *haystack, *ptr; - - rc = fs_open(MY_CONF_PATH, FS_ACCESS_READ, &file); - if (rc != 0) { - return; - } - rc = fs_filelen(file, &len); - TEST_ASSERT(rc == 0); - - haystack = malloc(len + 1); - TEST_ASSERT(haystack); - rc = fs_read(file, len, haystack, &hlen); + rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap); TEST_ASSERT(rc == 0); - TEST_ASSERT(hlen == len); - haystack[len] = '\0'; - fs_close(file); - - ptr = haystack; - while ((ptr = strstr(ptr, needle))) { - if (ptr[nlen] == '\n') { - empty = 1; - } else { - empty = 0; - } - ptr += nlen; - } - TEST_ASSERT(empty == 1); - free(haystack); + rc = flash_area_read(fap, fap->fa_size - sizeof(bit), &bit, sizeof(bit)); + TEST_ASSERT(rc == 0); - rc = fs_open(BOOT_PATH_STATUS, FS_ACCESS_READ, &file); - TEST_ASSERT(rc == FS_ENOENT); + TEST_ASSERT(bit.bit_copy_start != BOOT_IMG_MAGIC || + bit.bit_copy_done != 0xff); } static void @@ -436,7 +405,6 @@ TEST_CASE(boot_test_setup) rc = conf_file_dst(&my_conf); assert(rc == 0); - bootutil_cfg_register(); } TEST_CASE(boot_test_nv_ns_10) @@ -458,6 +426,7 @@ TEST_CASE(boot_test_nv_ns_10) .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); @@ -495,12 +464,14 @@ TEST_CASE(boot_test_nv_ns_01) .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); boot_test_util_write_image(&hdr, 1); boot_test_util_write_hash(&hdr, 1); + boot_vect_write_test(FLASH_AREA_IMAGE_1); rc = boot_go(&req, &rsp); TEST_ASSERT(rc == 0); @@ -540,6 +511,7 @@ TEST_CASE(boot_test_nv_ns_11) .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); @@ -579,15 +551,13 @@ TEST_CASE(boot_test_vm_ns_10) .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); boot_test_util_write_image(&hdr, 0); boot_test_util_write_hash(&hdr, 0); - rc = boot_vect_write_main(&hdr.ih_ver); - TEST_ASSERT(rc == 0); - rc = boot_go(&req, &rsp); TEST_ASSERT(rc == 0); @@ -618,13 +588,14 @@ TEST_CASE(boot_test_vm_ns_01) .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); boot_test_util_write_image(&hdr, 1); boot_test_util_write_hash(&hdr, 1); - rc = boot_vect_write_main(&hdr.ih_ver); + rc = boot_vect_write_test(FLASH_AREA_IMAGE_1); TEST_ASSERT(rc == 0); rc = boot_go(&req, &rsp); @@ -666,6 +637,7 @@ TEST_CASE(boot_test_vm_ns_11_a) .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); @@ -674,9 +646,6 @@ TEST_CASE(boot_test_vm_ns_11_a) boot_test_util_write_image(&hdr1, 1); boot_test_util_write_hash(&hdr1, 1); - rc = boot_vect_write_main(&hdr0.ih_ver); - TEST_ASSERT(rc == 0); - rc = boot_go(&req, &rsp); TEST_ASSERT(rc == 0); @@ -716,6 +685,7 @@ TEST_CASE(boot_test_vm_ns_11_b) .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); @@ -724,7 +694,7 @@ TEST_CASE(boot_test_vm_ns_11_b) boot_test_util_write_image(&hdr1, 1); boot_test_util_write_hash(&hdr1, 1); - rc = boot_vect_write_main(&hdr1.ih_ver); + rc = boot_vect_write_test(FLASH_AREA_IMAGE_1); TEST_ASSERT(rc == 0); rc = boot_go(&req, &rsp); @@ -766,6 +736,7 @@ TEST_CASE(boot_test_vm_ns_11_2areas) .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); @@ -774,7 +745,7 @@ TEST_CASE(boot_test_vm_ns_11_2areas) boot_test_util_write_image(&hdr1, 1); boot_test_util_write_hash(&hdr1, 1); - rc = boot_vect_write_main(&hdr1.ih_ver); + rc = boot_vect_write_test(FLASH_AREA_IMAGE_1); TEST_ASSERT(rc == 0); rc = boot_go(&req, &rsp); @@ -808,6 +779,7 @@ TEST_CASE(boot_test_nv_bs_10) .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); @@ -815,14 +787,16 @@ TEST_CASE(boot_test_nv_bs_10) boot_test_util_write_hash(&hdr, 0); boot_test_util_swap_areas(boot_test_slot_areas[1], BOOT_TEST_AREA_IDX_SCRATCH); - +#if 0 status.length = hdr.ih_hdr_size + hdr.ih_img_size + hdr.ih_tlv_size; status.state = 1; rc = boot_write_status(&status); TEST_ASSERT(rc == 0); conf_load(); - +#else + (void)status; +#endif rc = boot_go(&req, &rsp); TEST_ASSERT(rc == 0); @@ -838,7 +812,6 @@ TEST_CASE(boot_test_nv_bs_11) { struct boot_status status; struct boot_rsp rsp; - int len; int rc; struct image_header hdr0 = { @@ -864,6 +837,7 @@ TEST_CASE(boot_test_nv_bs_11) .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); @@ -871,14 +845,13 @@ TEST_CASE(boot_test_nv_bs_11) boot_test_util_write_hash(&hdr0, 0); boot_test_util_write_image(&hdr1, 1); boot_test_util_write_hash(&hdr1, 1); - boot_test_util_copy_area(boot_test_slot_areas[1], + rc = boot_vect_write_test(FLASH_AREA_IMAGE_1); + boot_test_util_copy_area(5, BOOT_TEST_AREA_IDX_SCRATCH); - status.length = hdr0.ih_hdr_size + hdr0.ih_img_size + hdr0.ih_tlv_size; - len = hdr1.ih_hdr_size + hdr1.ih_img_size + hdr1.ih_tlv_size; - if (len > status.length) { - status.length = len; - } + boot_req_set(&req); + status.idx = 0; + status.elem_sz = 1; status.state = 1; rc = boot_write_status(&status); @@ -900,7 +873,6 @@ TEST_CASE(boot_test_nv_bs_11_2areas) struct boot_status status; struct boot_rsp rsp; int rc; - int len; struct image_header hdr0 = { .ih_magic = IMAGE_MAGIC, @@ -925,6 +897,7 @@ TEST_CASE(boot_test_nv_bs_11_2areas) .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); @@ -932,15 +905,13 @@ TEST_CASE(boot_test_nv_bs_11_2areas) boot_test_util_write_hash(&hdr0, 0); boot_test_util_write_image(&hdr1, 1); boot_test_util_write_hash(&hdr1, 1); - boot_test_util_swap_areas(boot_test_slot_areas[0], - boot_test_slot_areas[1]); + rc = boot_vect_write_test(FLASH_AREA_IMAGE_1); - status.length = hdr0.ih_hdr_size + hdr0.ih_img_size + hdr0.ih_tlv_size; - len = hdr1.ih_hdr_size + hdr1.ih_img_size + hdr1.ih_tlv_size; - if (len > status.length) { - status.length = len; - } - status.state = 1 << 8; + boot_test_util_swap_areas(2, 5); + + status.idx = 1; + status.elem_sz = 1; + status.state = 0; rc = boot_write_status(&status); TEST_ASSERT(rc == 0); @@ -958,6 +929,8 @@ TEST_CASE(boot_test_nv_bs_11_2areas) TEST_CASE(boot_test_vb_ns_11) { + const struct flash_area *fap; + struct boot_img_trailer bit; struct boot_rsp rsp; int rc; int i; @@ -985,18 +958,27 @@ TEST_CASE(boot_test_vb_ns_11) .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); boot_test_util_write_image(&hdr0, 0); - boot_test_util_write_image(&hdr1, 1); boot_test_util_write_hash(&hdr0, 0); + boot_test_util_write_image(&hdr1, 1); boot_test_util_write_hash(&hdr1, 1); - rc = boot_vect_write_main(&hdr0.ih_ver); + rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap); + TEST_ASSERT(rc == 0); + + memset(&bit, 0xff, sizeof(bit)); + bit.bit_copy_start = BOOT_IMG_MAGIC; + bit.bit_copy_done = 0; + bit.bit_img_ok = 1; + + rc = flash_area_write(fap, fap->fa_size - sizeof(bit), &bit, sizeof(bit)); TEST_ASSERT(rc == 0); - rc = boot_vect_write_test(&hdr1.ih_ver); + rc = boot_vect_write_test(FLASH_AREA_IMAGE_1); TEST_ASSERT(rc == 0); /* First boot should use the test image. */ @@ -1021,6 +1003,7 @@ TEST_CASE(boot_test_vb_ns_11) boot_test_util_verify_flash(&hdr0, 0, &hdr1, 1); boot_test_util_verify_status_clear(); + boot_vect_write_main(); } } @@ -1029,29 +1012,45 @@ TEST_CASE(boot_test_no_hash) struct boot_rsp rsp; int rc; - struct image_header hdr = { + struct image_header hdr0 = { .ih_magic = IMAGE_MAGIC, - .ih_tlv_size = 0, + .ih_tlv_size = 4 + 32, .ih_hdr_size = BOOT_TEST_HEADER_SIZE, .ih_img_size = 12 * 1024, - .ih_flags = 0, + .ih_flags = IMAGE_F_SHA256, .ih_ver = { 0, 2, 3, 4 }, }; + struct image_header hdr1 = { + .ih_magic = IMAGE_MAGIC, + .ih_tlv_size = 0, + .ih_hdr_size = BOOT_TEST_HEADER_SIZE, + .ih_img_size = 32 * 1024, + .ih_flags = 0, + .ih_ver = { 1, 2, 3, 432 }, + }; struct boot_req req = { .br_area_descs = boot_test_area_descs, .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); - boot_test_util_write_image(&hdr, 0); + boot_test_util_write_image(&hdr0, 0); + boot_test_util_write_hash(&hdr0, 0); + boot_test_util_write_image(&hdr1, 1); + + rc = boot_vect_write_test(FLASH_AREA_IMAGE_1); + TEST_ASSERT(rc == 0); rc = boot_go(&req, &rsp); - TEST_ASSERT(rc != 0); + TEST_ASSERT(rc == 0); - boot_test_util_verify_flash(&hdr, 0, NULL, 0xff); + TEST_ASSERT(memcmp(rsp.br_hdr, &hdr0, sizeof hdr0) == 0); + + boot_test_util_verify_flash(&hdr0, 0, NULL, 0xff); boot_test_util_verify_status_clear(); } @@ -1060,30 +1059,46 @@ TEST_CASE(boot_test_no_flag_has_hash) struct boot_rsp rsp; int rc; - struct image_header hdr = { + struct image_header hdr0 = { .ih_magic = IMAGE_MAGIC, .ih_tlv_size = 4 + 32, .ih_hdr_size = BOOT_TEST_HEADER_SIZE, .ih_img_size = 12 * 1024, - .ih_flags = 0, + .ih_flags = IMAGE_F_SHA256, .ih_ver = { 0, 2, 3, 4 }, }; + struct image_header hdr1 = { + .ih_magic = IMAGE_MAGIC, + .ih_tlv_size = 4 + 32, + .ih_hdr_size = BOOT_TEST_HEADER_SIZE, + .ih_img_size = 32 * 1024, + .ih_flags = 0, + .ih_ver = { 1, 2, 3, 432 }, + }; struct boot_req req = { .br_area_descs = boot_test_area_descs, .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; boot_test_util_init_flash(); - boot_test_util_write_image(&hdr, 0); - boot_test_util_write_hash(&hdr, 0); + boot_test_util_write_image(&hdr0, 0); + boot_test_util_write_hash(&hdr0, 0); + boot_test_util_write_image(&hdr1, 1); + boot_test_util_write_hash(&hdr1, 1); + + rc = boot_vect_write_test(FLASH_AREA_IMAGE_1); + TEST_ASSERT(rc == 0); rc = boot_go(&req, &rsp); - TEST_ASSERT(rc != 0); + TEST_ASSERT(rc == 0); - boot_test_util_verify_flash(&hdr, 0, NULL, 0xff); + TEST_ASSERT(memcmp(rsp.br_hdr, &hdr0, sizeof hdr0) == 0); + + boot_test_util_verify_flash(&hdr0, 0, NULL, 0xff); boot_test_util_verify_status_clear(); } @@ -1092,7 +1107,7 @@ TEST_CASE(boot_test_invalid_hash) struct boot_rsp rsp; int rc; - struct image_header hdr = { + struct image_header hdr0 = { .ih_magic = IMAGE_MAGIC, .ih_tlv_size = 4 + 32, .ih_hdr_size = BOOT_TEST_HEADER_SIZE, @@ -1100,12 +1115,21 @@ TEST_CASE(boot_test_invalid_hash) .ih_flags = IMAGE_F_SHA256, .ih_ver = { 0, 2, 3, 4 }, }; + struct image_header hdr1 = { + .ih_magic = IMAGE_MAGIC, + .ih_tlv_size = 4 + 32, + .ih_hdr_size = BOOT_TEST_HEADER_SIZE, + .ih_img_size = 32 * 1024, + .ih_flags = 0, + .ih_ver = { 1, 2, 3, 432 }, + }; struct boot_req req = { .br_area_descs = boot_test_area_descs, .br_slot_areas = boot_test_slot_areas, .br_num_image_areas = BOOT_TEST_AREA_IDX_SCRATCH + 1, .br_scratch_area_idx = BOOT_TEST_AREA_IDX_SCRATCH, + .br_img_sz = (384 * 1024), }; struct image_tlv tlv = { @@ -1113,16 +1137,23 @@ TEST_CASE(boot_test_invalid_hash) .it_len = 32 }; boot_test_util_init_flash(); - boot_test_util_write_image(&hdr, 0); - rc = hal_flash_write(boot_test_img_addrs[0].flash_id, - boot_test_img_addrs[0].address + hdr.ih_hdr_size + hdr.ih_img_size, + boot_test_util_write_image(&hdr0, 0); + boot_test_util_write_hash(&hdr0, 0); + boot_test_util_write_image(&hdr1, 1); + rc = hal_flash_write(boot_test_img_addrs[1].flash_id, + boot_test_img_addrs[1].address + hdr1.ih_hdr_size + hdr1.ih_img_size, &tlv, sizeof(tlv)); TEST_ASSERT(rc == 0); + rc = boot_vect_write_test(FLASH_AREA_IMAGE_1); + TEST_ASSERT(rc == 0); + rc = boot_go(&req, &rsp); - TEST_ASSERT(rc != 0); + TEST_ASSERT(rc == 0); - boot_test_util_verify_flash(&hdr, 0, NULL, 0xff); + TEST_ASSERT(memcmp(rsp.br_hdr, &hdr0, sizeof hdr0) == 0); + + boot_test_util_verify_flash(&hdr0, 0, NULL, 0xff); boot_test_util_verify_status_clear(); } @@ -1156,9 +1187,11 @@ boot_test_all(void) #ifdef MYNEWT_SELFTEST int -main(void) +main(int argc, char **argv) { tu_config.tc_print_results = 1; + tu_parse_args(argc, argv); + tu_init(); boot_test_all(); diff --git a/libs/console/full/src/cons_tty.c b/libs/console/full/src/cons_tty.c index 7dfd0b70..9c576e23 100644 --- a/libs/console/full/src/cons_tty.c +++ b/libs/console/full/src/cons_tty.c @@ -165,6 +165,9 @@ console_file_write(void *arg, const char *str, size_t cnt) struct console_tty *ct = &console_tty; int i; + if (!ct->ct_write_char) { + return cnt; + } for (i = 0; i < cnt; i++) { if (str[i] == '\n') { ct->ct_write_char('\r'); @@ -200,13 +203,13 @@ console_read(char *str, int cnt, int *newline) break; } - if ((i & (CONSOLE_RX_CHUNK - 1)) == (CONSOLE_RX_CHUNK - 1)) { - /* - * Make a break from blocking interrupts during the copy. - */ - OS_EXIT_CRITICAL(sr); - OS_ENTER_CRITICAL(sr); - } + if ((i & (CONSOLE_RX_CHUNK - 1)) == (CONSOLE_RX_CHUNK - 1)) { + /* + * Make a break from blocking interrupts during the copy. + */ + OS_EXIT_CRITICAL(sr); + OS_ENTER_CRITICAL(sr); + } ch = console_pull_char(cr); if (ch == '\n') { diff --git a/libs/imgmgr/include/imgmgr/imgmgr.h b/libs/imgmgr/include/imgmgr/imgmgr.h index d007e7f2..17c9e7f4 100644 --- a/libs/imgmgr/include/imgmgr/imgmgr.h +++ b/libs/imgmgr/include/imgmgr/imgmgr.h @@ -29,7 +29,7 @@ #define IMGMGR_NMGR_OP_CORELIST 6 #define IMGMGR_NMGR_OP_CORELOAD 7 -#define IMGMGR_NMGR_MAX_MSG 120 +#define IMGMGR_NMGR_MAX_MSG 400 #define IMGMGR_NMGR_MAX_NAME 64 #define IMGMGR_NMGR_MAX_VER 25 /* 255.255.65535.4294967295\0 */ diff --git a/libs/imgmgr/src/imgmgr.c b/libs/imgmgr/src/imgmgr.c index 5e8539a1..ebdf1a81 100644 --- a/libs/imgmgr/src/imgmgr.c +++ b/libs/imgmgr/src/imgmgr.c @@ -28,6 +28,7 @@ #include <util/base64.h> #include <bootutil/image.h> +#include <bootutil/bootutil_misc.h> #include "imgmgr/imgmgr.h" #include "imgmgr_priv.h" @@ -494,5 +495,8 @@ imgmgr_module_init(void) rc = nmgr_group_register(&imgr_nmgr_group); assert(rc == 0); + + boot_vect_write_main(); + return rc; } diff --git a/libs/imgmgr/src/imgmgr_boot.c b/libs/imgmgr/src/imgmgr_boot.c index e678b74b..f636a5e1 100644 --- a/libs/imgmgr/src/imgmgr_boot.c +++ b/libs/imgmgr/src/imgmgr_boot.c @@ -63,6 +63,7 @@ imgr_boot_read(struct nmgr_jbuf *njb) { int rc; struct json_encoder *enc; + int slot; struct image_version ver; struct json_value jv; uint8_t hash[IMGMGR_HASH_LEN]; @@ -71,14 +72,20 @@ imgr_boot_read(struct nmgr_jbuf *njb) json_encode_object_start(enc); - rc = boot_vect_read_test(&ver); + rc = boot_vect_read_test(&slot); if (!rc) { - imgr_ver_jsonstr(enc, "test", &ver); + rc = imgr_read_info(slot, &ver, hash); + if (!rc) { + imgr_ver_jsonstr(enc, "test", &ver); + } } - rc = boot_vect_read_main(&ver); + rc = boot_vect_read_main(&slot); if (!rc) { - imgr_ver_jsonstr(enc, "main", &ver); + rc = imgr_read_info(slot, &ver, hash); + if (!rc) { + imgr_ver_jsonstr(enc, "main", &ver); + } } rc = imgr_read_info(bsp_imgr_current_slot(), &ver, hash); @@ -132,12 +139,11 @@ imgr_boot_write(struct nmgr_jbuf *njb) rc = NMGR_ERR_EINVAL; goto err; } - rc = boot_vect_write_test(&ver); + rc = boot_vect_write_test(rc); if (rc) { rc = NMGR_ERR_EINVAL; goto err; } - enc = &njb->njb_enc; json_encode_object_start(enc); @@ -162,22 +168,23 @@ imgr_boot2_read(struct nmgr_jbuf *njb) struct image_version ver; struct json_value jv; uint8_t hash[IMGMGR_HASH_LEN]; + int slot; enc = &njb->njb_enc; json_encode_object_start(enc); - rc = boot_vect_read_test(&ver); + rc = boot_vect_read_test(&slot); if (!rc) { - rc = imgr_find_by_ver(&ver, hash); + rc = imgr_read_info(slot, &ver, hash); if (rc >= 0) { imgr_hash_jsonstr(enc, "test", hash); } } - rc = boot_vect_read_main(&ver); + rc = boot_vect_read_main(&slot); if (!rc) { - rc = imgr_find_by_ver(&ver, hash); + rc = imgr_read_info(slot, &ver, hash); if (rc >= 0) { imgr_hash_jsonstr(enc, "main", hash); } @@ -226,11 +233,12 @@ imgr_boot2_write(struct nmgr_jbuf *njb) base64_decode(hash_str, hash); rc = imgr_find_by_hash(hash, &ver); if (rc >= 0) { - rc = boot_vect_write_test(&ver); + rc = boot_vect_write_test(rc); if (rc) { rc = NMGR_ERR_EUNKNOWN; goto err; } + rc = 0; } else { rc = NMGR_ERR_EINVAL; goto err; diff --git a/libs/inet_def_service/include/inet_def_service/inet_def_service.h b/libs/inet_def_service/include/inet_def_service/inet_def_service.h new file mode 100644 index 00000000..6b219fe4 --- /dev/null +++ b/libs/inet_def_service/include/inet_def_service/inet_def_service.h @@ -0,0 +1,25 @@ +/** + * 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. + */ + +#ifndef __INET_DEF_SERVICE_H__ +#define __INET_DEF_SERVICE_H__ + +int inet_def_service_init(uint8_t prio, os_stack_t *stack, uint16_t stack_sz); + +#endif /* __INET_DEF_SERVICE_H__ */ diff --git a/libs/inet_def_service/pkg.yml b/libs/inet_def_service/pkg.yml new file mode 100644 index 00000000..103b0d54 --- /dev/null +++ b/libs/inet_def_service/pkg.yml @@ -0,0 +1,38 @@ +# +# 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: libs/inet_def_service +pkg.description: INET services chargen, discard and echo +pkg.author: "Apache Mynewt <dev@mynewt.incubator.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - socket + - inet + - test + +pkg.deps: + - sys/mn_socket + - libs/os + - libs/util + - libs/testutil + +pkg.reqs: + - console + +pkg.features: diff --git a/libs/inet_def_service/src/inet_def_service.c b/libs/inet_def_service/src/inet_def_service.c new file mode 100644 index 00000000..fd756bf3 --- /dev/null +++ b/libs/inet_def_service/src/inet_def_service.c @@ -0,0 +1,333 @@ +/** + * 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 <assert.h> +#include <string.h> + +#include <os/os.h> +#include <os/endian.h> +#include <mn_socket/mn_socket.h> +#include <console/console.h> + +#include "inet_def_service/inet_def_service.h" + +#define ECHO_PORT 7 +#define DISCARD_PORT 9 +#define CHARGEN_PORT 19 + +enum inet_def_type { + INET_DEF_ECHO = 0, + INET_DEF_DISCARD, + INET_DEF_CHARGEN, + INET_DEF_MAXTYPE +}; + +#define INET_DEF_FROM_EVENT_TYPE(a) ((a) - OS_EVENT_T_PERUSER) +#define INET_EVENT_TYPE_FROM_DEF(a) ((a) + OS_EVENT_T_PERUSER) + +#define CHARGEN_WRITE_SZ 512 +static const char chargen_pattern[] = "1234567890"; +#define CHARGEN_PATTERN_SZ (sizeof(chargen_pattern) - 1) + +static struct os_task inet_def_task; +static struct os_eventq inet_def_evq; + +/* + * UDP service. + */ +struct inet_def_udp { + struct os_event ev; /* Datagram RX reported via event */ + struct mn_socket *socket; + uint32_t pkt_cnt; +}; + +/* + * Connected TCP socket. + */ +struct inet_def_tcp { + struct os_event ev; /* Data RX reported with this event */ + SLIST_ENTRY(inet_def_tcp) list; + struct mn_socket *socket; + int closed; +}; + +/* + * TCP service. + */ +struct inet_def_listen { + struct mn_socket *socket; + uint32_t conn_cnt; +}; + +static struct inet_def { + struct inet_def_listen tcp_service[INET_DEF_MAXTYPE]; + struct inet_def_udp udp_service[INET_DEF_MAXTYPE]; + SLIST_HEAD(, inet_def_tcp) tcp_conns; /* List of connected TCP sockets */ +} inet_def; + +/* + * UDP socket callbacks. Called in context of IP stack task. + */ +static void +inet_def_udp_readable(void *arg, int err) +{ + struct inet_def_udp *idu = (struct inet_def_udp *)arg; + + os_eventq_put(&inet_def_evq, &idu->ev); +} + +static const union mn_socket_cb inet_udp_cbs = { + .socket.readable = inet_def_udp_readable, +}; + +/* + * TCP socket callbacks. Called in context of IP stack task. + */ +static void +inet_def_tcp_readable(void *arg, int err) +{ + struct inet_def_tcp *idt = (struct inet_def_tcp *)arg; + + if (err) { + idt->closed = 1; + /* + * No locking here. Assuming new connection notifications, as well as + * close notifications arrive in context of a single task. + */ + SLIST_REMOVE(&inet_def.tcp_conns, idt, inet_def_tcp, list); + } + os_eventq_put(&inet_def_evq, &idt->ev); +} + +static const union mn_socket_cb inet_tcp_cbs = { + .socket.readable = inet_def_tcp_readable, + .socket.writable = inet_def_tcp_readable /* we want the same behavior */ +}; + +/* + * Callback for new connection for TCP listen socket. + */ +static int inet_def_newconn(void *arg, struct mn_socket *new) +{ + struct inet_def_listen *idl = (struct inet_def_listen *)arg; + struct inet_def_tcp *idt; + enum inet_def_type type; + + idt = (struct inet_def_tcp *)os_malloc(sizeof(*idt)); + if (!idt) { + return -1; + } + + memset(idt, 0, sizeof(*idt)); + idt->socket = new; + + /* + * Event type tells what type of service this connection belongs to. + * Ev_arg says whether it's TCP or UDP. + */ + type = idl - &inet_def.tcp_service[0]; + idt->ev.ev_type = INET_EVENT_TYPE_FROM_DEF(type); + idt->ev.ev_arg = (void *)MN_SOCK_STREAM; + mn_socket_set_cbs(new, idt, &inet_tcp_cbs); + SLIST_INSERT_HEAD(&inet_def.tcp_conns, idt, list); + + if (type == INET_DEF_CHARGEN) { + /* + * Start transmitting right away. + */ + os_eventq_put(&inet_def_evq, &idt->ev); + } + return 0; +} + +static const union mn_socket_cb inet_listen_cbs = { + .listen.newconn = inet_def_newconn, +}; + +static int +inet_def_create_srv(enum inet_def_type idx, uint16_t port) +{ + struct mn_socket *ms; + struct mn_sockaddr_in msin; + + memset(&msin, 0, sizeof(msin)); + msin.msin_len = sizeof(msin); + msin.msin_family = MN_AF_INET; + msin.msin_port = htons(port); + + /* + * Create TCP listen socket for service. + */ + if (mn_socket(&ms, MN_PF_INET, MN_SOCK_STREAM, 0)) { + goto err; + } + inet_def.tcp_service[idx].socket = ms; + mn_socket_set_cbs(ms, &inet_def.tcp_service[idx], &inet_listen_cbs); + + if (mn_bind(ms, (struct mn_sockaddr *)&msin)) { + goto err; + } + if (mn_listen(ms, 1)) { + goto err; + } + + /* + * Create UDP socket for service. + */ + if (mn_socket(&ms, MN_PF_INET, MN_SOCK_DGRAM, 0)) { + goto err; + } + inet_def.udp_service[idx].socket = ms; + mn_socket_set_cbs(ms, &inet_def.udp_service[idx], &inet_udp_cbs); + + if (mn_bind(ms, (struct mn_sockaddr *)&msin)) { + goto err; + } + + return 0; +err: + if (inet_def.tcp_service[idx].socket) { + mn_close(inet_def.tcp_service[idx].socket); + inet_def.tcp_service[idx].socket = NULL; + } + if (inet_def.udp_service[idx].socket) { + mn_close(inet_def.udp_service[idx].socket); + inet_def.udp_service[idx].socket = NULL; + } + return -1; +} + +static void +inet_def_srv(void *arg) +{ + struct inet_def_udp *idu; + struct inet_def_tcp *idt; + struct mn_socket *sock; + struct os_event *ev; + struct mn_sockaddr_in msin; + struct os_mbuf *m; + enum inet_def_type type; + int rc; + int off; + int loop_cnt; + + inet_def_create_srv(INET_DEF_ECHO, ECHO_PORT); + inet_def_create_srv(INET_DEF_DISCARD, DISCARD_PORT); + inet_def_create_srv(INET_DEF_CHARGEN, CHARGEN_PORT); + + while (1) { + /* + * Wait here. When event comes, check what type of socket got it, + * and then act on it. + */ + ev = os_eventq_get(&inet_def_evq); + type = INET_DEF_FROM_EVENT_TYPE(ev->ev_type); + idt = (struct inet_def_tcp *)ev; + idu = (struct inet_def_udp *)ev; + if ((int)ev->ev_arg == MN_SOCK_DGRAM) { + sock = idu->socket; + } else { + sock = idt->socket; + } + switch (type) { + case INET_DEF_ECHO: + while (mn_recvfrom(sock, &m, (struct mn_sockaddr *)&msin) == 0) { + console_printf("echo %d bytes\n", OS_MBUF_PKTLEN(m)); + rc = mn_sendto(sock, m, (struct mn_sockaddr *)&msin); + if (rc) { + console_printf(" failed: %d!!!!\n", rc); + os_mbuf_free_chain(m); + } + } + break; + case INET_DEF_DISCARD: + while (mn_recvfrom(sock, &m, NULL) == 0) { + console_printf("discard %d bytes\n", OS_MBUF_PKTLEN(m)); + os_mbuf_free_chain(m); + } + break; + case INET_DEF_CHARGEN: + while (mn_recvfrom(sock, &m, NULL) == 0) { + os_mbuf_free_chain(m); + } + if ((int)ev->ev_arg == MN_SOCK_STREAM && idt->closed) { + /* + * Don't try to send tons of data to a closed socket. + */ + break; + } + loop_cnt = 0; + do { + m = os_msys_get(CHARGEN_WRITE_SZ, 0); + if (m) { + for (off = 0; + OS_MBUF_TRAILINGSPACE(m) >= CHARGEN_PATTERN_SZ; + off += CHARGEN_PATTERN_SZ) { + os_mbuf_copyinto(m, off, chargen_pattern, + CHARGEN_PATTERN_SZ); + } + console_printf("chargen %d bytes\n", m->om_len); + rc = mn_sendto(sock, m, NULL); /* assumes TCP for now */ + if (rc) { + os_mbuf_free_chain(m); + if (rc != MN_ENOBUFS && rc != MN_EAGAIN) { + console_write(" sendto fail!!! %d\n", rc); + } + break; + } + } else { + /* + * Mbuf shortage. Wait for them to appear. + */ + os_time_delay(1); + } + loop_cnt++; + } while (loop_cnt < 32); + break; + default: + assert(0); + break; + } + if ((int)ev->ev_arg == MN_SOCK_STREAM && idt->closed) { + /* + * Remote end has closed the connection, as indicated in the + * callback. Close the socket and free the memory. + */ + mn_socket_set_cbs(idt->socket, NULL, NULL); + os_eventq_remove(&inet_def_evq, &idt->ev); + mn_close(idt->socket); + os_free(idt); + } + } +} + +int +inet_def_service_init(uint8_t prio, os_stack_t *stack, uint16_t stack_sz) +{ + int i; + + os_eventq_init(&inet_def_evq); + SLIST_INIT(&inet_def.tcp_conns); + for (i = 0; i < 3; i++) { + inet_def.udp_service[i].ev.ev_type = INET_EVENT_TYPE_FROM_DEF(i); + inet_def.udp_service[i].ev.ev_arg = (void *)MN_SOCK_DGRAM; + } + return os_task_init(&inet_def_task, "inet_def_task", + inet_def_srv, NULL, prio, OS_WAIT_FOREVER, stack, stack_sz); +} + diff --git a/libs/mbedtls/include/mbedtls/config_mynewt.h b/libs/mbedtls/include/mbedtls/config_mynewt.h index d200879c..a18ac6f7 100644 --- a/libs/mbedtls/include/mbedtls/config_mynewt.h +++ b/libs/mbedtls/include/mbedtls/config_mynewt.h @@ -40,6 +40,8 @@ #undef MBEDTLS_SELF_TEST #endif +#define MBEDTLS_SHA256_SMALLER /* comes with performance hit */ + /** * \name SECTION: Module configuration options * diff --git a/libs/newtmgr/pkg.yml b/libs/newtmgr/pkg.yml index 6b8435d9..a0e75a23 100644 --- a/libs/newtmgr/pkg.yml +++ b/libs/newtmgr/pkg.yml @@ -32,5 +32,8 @@ pkg.deps: - libs/shell - sys/reboot +pkg.deps.BLE_HOST: + - libs/newtmgr/transport/ble + pkg.features: - - NEWTMGR + - NEWTMGR diff --git a/libs/newtmgr/src/newtmgr.c b/libs/newtmgr/src/newtmgr.c index c3bc6141..d15b4b9a 100644 --- a/libs/newtmgr/src/newtmgr.c +++ b/libs/newtmgr/src/newtmgr.c @@ -472,7 +472,6 @@ nmgr_handle_req(struct nmgr_transport *nt, struct os_mbuf *req) rsp_hdr->nh_len = htons(rsp_hdr->nh_len); rsp_hdr->nh_group = htons(rsp_hdr->nh_group); - rsp_hdr->nh_id = htons(rsp_hdr->nh_id); off += sizeof(hdr) + OS_ALIGN(hdr.nh_len, 4); } diff --git a/libs/newtmgr/transport/ble/include/nmgrble/newtmgr_ble.h b/libs/newtmgr/transport/ble/include/nmgrble/newtmgr_ble.h new file mode 100644 index 00000000..a320eaab --- /dev/null +++ b/libs/newtmgr/transport/ble/include/nmgrble/newtmgr_ble.h @@ -0,0 +1,28 @@ +/** + * 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. + */ + +#ifndef _NEWTMGR_BLE_H_ +#define _NEWTMGR_BLE_H_ + +int +nmgr_ble_proc_mq_evt(struct os_event *ev); +int +nmgr_ble_gatt_svr_init(struct os_eventq *evq, struct ble_hs_cfg *cfg); + +#endif /* _NETMGR_H */ diff --git a/libs/newtmgr/transport/ble/pkg.yml b/libs/newtmgr/transport/ble/pkg.yml new file mode 100644 index 00000000..9ab9d4e5 --- /dev/null +++ b/libs/newtmgr/transport/ble/pkg.yml @@ -0,0 +1,30 @@ +# +# 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: libs/newtmgr/transport/ble +pkg.description: BLE transport newtmgr functionality. +pkg.author: "Apache Mynewt <dev@mynewt.incubator.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + +pkg.deps: + - libs/os + - net/nimble diff --git a/libs/newtmgr/transport/ble/src/newtmgr_ble.c b/libs/newtmgr/transport/ble/src/newtmgr_ble.c new file mode 100644 index 00000000..716a974e --- /dev/null +++ b/libs/newtmgr/transport/ble/src/newtmgr_ble.c @@ -0,0 +1,237 @@ +/** + * 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 <assert.h> +#include <stdio.h> +#include <string.h> +#include "host/ble_hs.h" +#include <newtmgr/newtmgr.h> +#include <os/endian.h> + +/* nmgr ble mqueue */ +struct os_mqueue ble_nmgr_mq; + +/* ble nmgr transport */ +struct nmgr_transport ble_nt; + +/* ble nmgr attr handle */ +uint16_t g_ble_nmgr_attr_handle; + +struct os_eventq *app_evq; +/** + * The vendor specific "newtmgr" service consists of one write no-rsp + * characteristic for newtmgr requests: a single-byte characteristic that can + * only accepts write-without-response commands. The contents of each write + * command contains an NMP request. NMP responses are sent back in the form of + * unsolicited notifications from the same characteristic. + */ + +/* {8D53DC1D-1DB7-4CD3-868B-8A527460AA84} */ +const uint8_t gatt_svr_svc_newtmgr[16] = { + 0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86, + 0xd3, 0x4c, 0xb7, 0x1d, 0x1d, 0xdc, 0x53, 0x8d +}; + +/* {DA2E7828-FBCE-4E01-AE9E-261174997C48} */ +const uint8_t gatt_svr_chr_newtmgr[16] = { + 0x48, 0x7c, 0x99, 0x74, 0x11, 0x26, 0x9e, 0xae, + 0x01, 0x4e, 0xce, 0xfb, 0x28, 0x78, 0x2e, 0xda +}; + +static int +gatt_svr_chr_access_newtmgr(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + /* Service: newtmgr */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid128 = (void *)gatt_svr_svc_newtmgr, + .characteristics = (struct ble_gatt_chr_def[]) { { + /* Characteristic: Write No Rsp */ + .uuid128 = (void *)gatt_svr_chr_newtmgr, + .access_cb = gatt_svr_chr_access_newtmgr, + .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, + .val_handle = &g_ble_nmgr_attr_handle, + }, { + 0, /* No more characteristics in this service */ + } }, + }, + + { + 0, /* No more services */ + }, +}; + +static int +gatt_svr_chr_access_newtmgr(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + int rc; + struct os_mbuf *m_req; + + switch (ctxt->op) { + case BLE_GATT_ACCESS_OP_WRITE_CHR: + /* Try to reuse the BLE packet mbuf as the newtmgr request. This + * requires a two-byte usrhdr to hold the BLE connection handle so + * that the newtmgr response can be sent to the correct peer. If + * it is not possible to reuse the mbuf, then allocate a new one + * and copy the request contents. + */ + if (OS_MBUF_USRHDR_LEN(ctxt->om) >= sizeof (conn_handle)) { + /* Sufficient usrhdr space already present. */ + m_req = ctxt->om; + ctxt->om = NULL; + } else if (OS_MBUF_LEADINGSPACE(ctxt->om) >= + sizeof (conn_handle)) { + + /* Usrhdr isn't present, but there is enough leading space to + * add one. + */ + m_req = ctxt->om; + ctxt->om = NULL; + + m_req->om_pkthdr_len += sizeof (conn_handle); + } else { + /* The mbuf can't be reused. Allocate a new one and perform a + * copy. Don't set ctxt->om to NULL; let the NimBLE host free + * it. + */ + m_req = os_msys_get_pkthdr(OS_MBUF_PKTLEN(ctxt->om), + sizeof (conn_handle)); + if (!m_req) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + rc = os_mbuf_appendfrom(m_req, ctxt->om, 0, + OS_MBUF_PKTLEN(ctxt->om)); + if (rc) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + } + + /* Write the connection handle to the newtmgr request usrhdr. This + * is necessary so that we later know who to send the newtmgr + * response to. + */ + memcpy(OS_MBUF_USRHDR(m_req), &conn_handle, sizeof(conn_handle)); + + rc = nmgr_rx_req(&ble_nt, m_req); + if (rc != 0) { + return BLE_ATT_ERR_UNLIKELY; + } + return 0; + + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } +} + +/** + * Nmgr ble process mqueue event + * Gets an event from the nmgr mqueue and does a notify with the response + * + * @param eventq + * @return 0 on success; non-zero on failure + */ + +int +nmgr_ble_proc_mq_evt(struct os_event *ev) +{ + struct os_mbuf *m_resp; + uint16_t conn_handle; + int rc; + + rc = 0; + switch (ev->ev_type) { + case OS_EVENT_T_MQUEUE_DATA: + if (ev->ev_arg != &ble_nmgr_mq) { + rc = -1; + goto done; + } + + while (1) { + m_resp = os_mqueue_get(&ble_nmgr_mq); + if (!m_resp) { + break; + } + assert(OS_MBUF_USRHDR_LEN(m_resp) >= sizeof (conn_handle)); + memcpy(&conn_handle, OS_MBUF_USRHDR(m_resp), + sizeof (conn_handle)); + ble_gattc_notify_custom(conn_handle, g_ble_nmgr_attr_handle, + m_resp); + } + break; + + default: + rc = -1; + goto done; + } + +done: + return rc; +} + +static int +nmgr_ble_out(struct nmgr_transport *nt, struct os_mbuf *m) +{ + int rc; + + rc = os_mqueue_put(&ble_nmgr_mq, app_evq, m); + if (rc != 0) { + goto err; + } + + return (0); +err: + return (rc); +} + +/** + * Nmgr ble GATT server initialization + * + * @param eventq + * @return 0 on success; non-zero on failure + */ +int +nmgr_ble_gatt_svr_init(struct os_eventq *evq, struct ble_hs_cfg *cfg) +{ + int rc; + + assert(evq != NULL); + + rc = ble_gatts_count_cfg(gatt_svr_svcs, cfg); + if (rc != 0) { + goto err; + } + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + if (rc != 0) { + return rc; + } + + app_evq = evq; + + os_mqueue_init(&ble_nmgr_mq, &ble_nmgr_mq); + + rc = nmgr_transport_init(&ble_nt, &nmgr_ble_out); + +err: + return rc; +} diff --git a/libs/os/include/os/arch/cortex_m0/os/os_arch.h b/libs/os/include/os/arch/cortex_m0/os/os_arch.h index e986702a..1a83ba9b 100755 --- a/libs/os/include/os/arch/cortex_m0/os/os_arch.h +++ b/libs/os/include/os/arch/cortex_m0/os/os_arch.h @@ -6,7 +6,7 @@ * 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, @@ -17,8 +17,8 @@ * under the License. */ -#ifndef _OS_ARCH_ARM_H -#define _OS_ARCH_ARM_H +#ifndef _OS_ARCH_ARM_H +#define _OS_ARCH_ARM_H #include <stdint.h> #include <mcu/cortex_m0.h> @@ -48,7 +48,7 @@ typedef uint32_t os_stack_t; (OS_ALIGN((__nmemb), OS_STACK_ALIGNMENT)) /* Enter a critical section, save processor state, and block interrupts */ -#define OS_ENTER_CRITICAL(__os_sr) (__os_sr = os_arch_save_sr()) +#define OS_ENTER_CRITICAL(__os_sr) (__os_sr = os_arch_save_sr()) /* Exit a critical section, restore processor state and unblock interrupts */ #define OS_EXIT_CRITICAL(__os_sr) (os_arch_restore_sr(__os_sr)) #define OS_ASSERT_CRITICAL() (assert(os_arch_in_critical())) @@ -63,7 +63,7 @@ void os_arch_init(void); uint32_t os_arch_start(void); os_error_t os_arch_os_init(void); os_error_t os_arch_os_start(void); -void os_set_env(void); +void os_set_env(os_stack_t *); void os_arch_init_task_stack(os_stack_t *sf); void os_default_irq_asm(void); @@ -71,4 +71,4 @@ void os_default_irq_asm(void); void os_bsp_systick_init(uint32_t os_ticks_per_sec, int prio); void os_bsp_ctx_sw(void); -#endif /* _OS_ARCH_X86_H */ +#endif /* _OS_ARCH_ARM_H */ diff --git a/libs/os/include/os/arch/cortex_m4/os/os_arch.h b/libs/os/include/os/arch/cortex_m4/os/os_arch.h index 8c67f09f..9e43a7a2 100755 --- a/libs/os/include/os/arch/cortex_m4/os/os_arch.h +++ b/libs/os/include/os/arch/cortex_m4/os/os_arch.h @@ -6,7 +6,7 @@ * 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, @@ -17,8 +17,8 @@ * under the License. */ -#ifndef _OS_ARCH_ARM_H -#define _OS_ARCH_ARM_H +#ifndef _OS_ARCH_ARM_H +#define _OS_ARCH_ARM_H #include <stdint.h> #include "mcu/cortex_m4.h" @@ -48,7 +48,7 @@ typedef uint32_t os_stack_t; (OS_ALIGN((__nmemb), OS_STACK_ALIGNMENT)) /* Enter a critical section, save processor state, and block interrupts */ -#define OS_ENTER_CRITICAL(__os_sr) (__os_sr = os_arch_save_sr()) +#define OS_ENTER_CRITICAL(__os_sr) (__os_sr = os_arch_save_sr()) /* Exit a critical section, restore processor state and unblock interrupts */ #define OS_EXIT_CRITICAL(__os_sr) (os_arch_restore_sr(__os_sr)) #define OS_ASSERT_CRITICAL() (assert(os_arch_in_critical())) @@ -63,7 +63,7 @@ void os_arch_init(void); uint32_t os_arch_start(void); os_error_t os_arch_os_init(void); os_error_t os_arch_os_start(void); -void os_set_env(void); +void os_set_env(os_stack_t *); void os_arch_init_task_stack(os_stack_t *sf); void os_default_irq_asm(void); @@ -72,4 +72,4 @@ void os_bsp_systick_init(uint32_t os_tick_per_sec, int prio); void os_bsp_idle(os_time_t ticks); void os_bsp_ctx_sw(void); -#endif /* _OS_ARCH_X86_H */ +#endif /* _OS_ARCH_ARM_H */ diff --git a/libs/os/include/os/os_mbuf.h b/libs/os/include/os/os_mbuf.h index 74b9548d..f3c48aeb 100644 --- a/libs/os/include/os/os_mbuf.h +++ b/libs/os/include/os/os_mbuf.h @@ -259,7 +259,8 @@ struct os_mbuf *os_mbuf_get_pkthdr(struct os_mbuf_pool *omp, /* Duplicate a mbuf from the pool */ struct os_mbuf *os_mbuf_dup(struct os_mbuf *m); -struct os_mbuf * os_mbuf_off(struct os_mbuf *om, int off, int *out_off); +struct os_mbuf *os_mbuf_off(const struct os_mbuf *om, int off, + uint16_t *out_off); /* Copy data from an mbuf to a flat buffer. */ int os_mbuf_copydata(const struct os_mbuf *m, int off, int len, void *dst); @@ -267,6 +268,9 @@ int os_mbuf_copydata(const struct os_mbuf *m, int off, int len, void *dst); /* Append data onto a mbuf */ int os_mbuf_append(struct os_mbuf *m, const void *, uint16_t); +int os_mbuf_appendfrom(struct os_mbuf *dst, const struct os_mbuf *src, + uint16_t src_off, uint16_t len); + /* Free a mbuf */ int os_mbuf_free(struct os_mbuf *mb); @@ -274,10 +278,14 @@ int os_mbuf_free(struct os_mbuf *mb); int os_mbuf_free_chain(struct os_mbuf *om); void os_mbuf_adj(struct os_mbuf *mp, int req_len); -int os_mbuf_memcmp(const struct os_mbuf *om, int off, const void *data, +int os_mbuf_cmpf(const struct os_mbuf *om, int off, const void *data, int len); +int os_mbuf_cmpm(const struct os_mbuf *om1, uint16_t offset1, + const struct os_mbuf *om2, uint16_t offset2, + uint16_t len); struct os_mbuf *os_mbuf_prepend(struct os_mbuf *om, int len); +struct os_mbuf *os_mbuf_prepend_pullup(struct os_mbuf *om, uint16_t len); int os_mbuf_copyinto(struct os_mbuf *om, int off, const void *src, int len); void os_mbuf_concat(struct os_mbuf *first, struct os_mbuf *second); void *os_mbuf_extend(struct os_mbuf *om, uint16_t len); diff --git a/libs/os/include/os/os_mempool.h b/libs/os/include/os/os_mempool.h index 6b0f24c1..a7316184 100644 --- a/libs/os/include/os/os_mempool.h +++ b/libs/os/include/os/os_mempool.h @@ -82,6 +82,9 @@ typedef uint64_t os_membuf_t; os_error_t os_mempool_init(struct os_mempool *mp, int blocks, int block_size, void *membuf, char *name); +/* Checks if a memory block was allocated from the specified mempool. */ +int os_memblock_from(struct os_mempool *mp, void *block_addr); + /* Get a memory block from the pool */ void *os_memblock_get(struct os_mempool *mp); diff --git a/libs/os/src/arch/cortex_m0/m0/HAL_CM0.s b/libs/os/src/arch/cortex_m0/m0/HAL_CM0.s index 03d79577..de7d46c7 100644 --- a/libs/os/src/arch/cortex_m0/m0/HAL_CM0.s +++ b/libs/os/src/arch/cortex_m0/m0/HAL_CM0.s @@ -52,7 +52,6 @@ os_set_env: .fnstart .cantunwind - MOV R0,SP /* Copy MSP to PSP */ MSR PSP,R0 LDR R0,=os_flags LDRB R0,[R0] diff --git a/libs/os/src/arch/cortex_m0/os_arch_arm.c b/libs/os/src/arch/cortex_m0/os_arch_arm.c index 40b92549..ef1be4d6 100755 --- a/libs/os/src/arch/cortex_m0/os_arch_arm.c +++ b/libs/os/src/arch/cortex_m0/os_arch_arm.c @@ -6,7 +6,7 @@ * 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, @@ -23,6 +23,8 @@ #include <hal/hal_os_tick.h> #include <bsp/cmsis_nvic.h> +#include "os_priv.h" + /* * From HAL_CM0.s */ @@ -223,13 +225,6 @@ os_arch_os_init(void) /* Set the SVC interrupt to priority 0 (highest configurable) */ NVIC_SetPriority(SVCall_IRQn, SVC_PRIO); - /* - * Set the os environment. This will set stack pointers and, based - * on the contents of os_flags, will determine if the tasks run in - * priviliged or un-privileged mode. - */ - os_set_env(); - /* Check if privileged or not */ if ((__get_CONTROL() & 1) == 0) { os_arch_init(); @@ -273,17 +268,26 @@ static inline void svc_os_arch_start(void) } /** - * Start the OS. First check to see if we are running with the correct stack - * pointer set (PSP) and privilege mode (PRIV). - * - * - * @return os_error_t + * Start the OS. First check to see if we are running with the correct stack + * pointer set (PSP) and privilege mode (PRIV). + * + * @return os_error_t */ os_error_t os_arch_os_start(void) { os_error_t err; + /* + * Set the os environment. This will set stack pointers and, based + * on the contents of os_flags, will determine if the tasks run in + * priviliged or un-privileged mode. + * + * We switch to using "empty" part of idle task's stack until + * the svc_os_arch_start() executes SVC, and we will never return. + */ + os_set_env(g_idle_task.t_stackptr - 1); + err = OS_ERR_IN_ISR; if (__get_IPSR() == 0) { /* @@ -304,7 +308,7 @@ os_arch_os_start(void) err = OS_ERR_PRIV; break; case 0x02: - /* + /* * We are running in Privileged Thread mode w/SP = PSP but we * are supposed to be un-privileged. */ @@ -313,7 +317,7 @@ os_arch_os_start(void) } break; case 0x03: - /* + /* * We are running in Unprivileged Thread mode w/SP = PSP but we * are supposed to be privileged. */ diff --git a/libs/os/src/arch/cortex_m4/m4/HAL_CM4.s b/libs/os/src/arch/cortex_m4/m4/HAL_CM4.s index c9ae1037..9c6ab521 100755 --- a/libs/os/src/arch/cortex_m4/m4/HAL_CM4.s +++ b/libs/os/src/arch/cortex_m4/m4/HAL_CM4.s @@ -52,7 +52,6 @@ os_set_env: .fnstart .cantunwind - MOV R0,SP /* Copy MSP to PSP */ MSR PSP,R0 LDR R0,=os_flags LDRB R0,[R0] @@ -63,6 +62,7 @@ os_set_env: .fnend .size os_set_env, .-os_set_env + /*--------------------------- os_set_env ------------------------------------*/ diff --git a/libs/os/src/arch/cortex_m4/os_arch_arm.c b/libs/os/src/arch/cortex_m4/os_arch_arm.c index 2bd084ab..48f69769 100755 --- a/libs/os/src/arch/cortex_m4/os_arch_arm.c +++ b/libs/os/src/arch/cortex_m4/os_arch_arm.c @@ -6,7 +6,7 @@ * 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, @@ -23,6 +23,8 @@ #include <hal/hal_os_tick.h> #include <bsp/cmsis_nvic.h> +#include "os_priv.h" + /* Initial program status register */ #define INITIAL_xPSR 0x01000000 @@ -217,13 +219,6 @@ os_arch_os_init(void) /* Set the SVC interrupt to priority 0 (highest configurable) */ NVIC_SetPriority(SVCall_IRQn, SVC_PRIO); - /* - * Set the os environment. This will set stack pointers and, based - * on the contents of os_flags, will determine if the tasks run in - * priviliged or un-privileged mode. - */ - os_set_env(); - /* Check if privileged or not */ if ((__get_CONTROL() & 1) == 0) { os_arch_init(); @@ -267,17 +262,27 @@ static inline void svc_os_arch_start(void) } /** - * Start the OS. First check to see if we are running with the correct stack - * pointer set (PSP) and privilege mode (PRIV). - * - * - * @return os_error_t + * Start the OS. First check to see if we are running with the correct stack + * pointer set (PSP) and privilege mode (PRIV). + * + * + * @return os_error_t */ os_error_t os_arch_os_start(void) { os_error_t err; + /* + * Set the os environment. This will set stack pointers and, based + * on the contents of os_flags, will determine if the tasks run in + * privileged or un-privileged mode. + * + * We switch to using "empty" part of idle task's stack until + * the svc_os_arch_start() executes SVC, and we will never return. + */ + os_set_env(g_idle_task.t_stackptr - 1); + err = OS_ERR_IN_ISR; if (__get_IPSR() == 0) { /* @@ -298,7 +303,7 @@ os_arch_os_start(void) err = OS_ERR_PRIV; break; case 0x02: - /* + /* * We are running in Privileged Thread mode w/SP = PSP but we * are supposed to be un-privileged. */ @@ -307,7 +312,7 @@ os_arch_os_start(void) } break; case 0x03: - /* + /* * We are running in Unprivileged Thread mode w/SP = PSP but we * are supposed to be privileged. */ diff --git a/libs/os/src/arch/sim/os_arch_sim.c b/libs/os/src/arch/sim/os_arch_sim.c index 287b4fc1..49705220 100644 --- a/libs/os/src/arch/sim/os_arch_sim.c +++ b/libs/os/src/arch/sim/os_arch_sim.c @@ -404,7 +404,7 @@ os_arch_os_init(void) mypid = getpid(); g_current_task = NULL; - TAILQ_INIT(&g_os_task_list); + STAILQ_INIT(&g_os_task_list); TAILQ_INIT(&g_os_run_list); TAILQ_INIT(&g_os_sleep_list); diff --git a/libs/os/src/os.c b/libs/os/src/os.c index 5ec71ec2..90f972ea 100644 --- a/libs/os/src/os.c +++ b/libs/os/src/os.c @@ -19,6 +19,7 @@ #include "os/os.h" #include "os/queue.h" +#include "os_priv.h" #include "hal/hal_os_tick.h" @@ -105,6 +106,9 @@ os_init(void) { os_error_t err; + TAILQ_INIT(&g_callout_list); + STAILQ_INIT(&g_os_task_list); + err = os_arch_os_init(); assert(err == OS_OK); } diff --git a/libs/os/src/os_callout.c b/libs/os/src/os_callout.c index 02462c0b..bc36a195 100644 --- a/libs/os/src/os_callout.c +++ b/libs/os/src/os_callout.c @@ -18,12 +18,12 @@ */ #include "os/os.h" +#include "os_priv.h" #include <assert.h> #include <string.h> -TAILQ_HEAD(, os_callout) g_callout_list = - TAILQ_HEAD_INITIALIZER(g_callout_list); +struct os_callout_list g_callout_list; static void _os_callout_init(struct os_callout *c, struct os_eventq *evq, void *ev_arg) diff --git a/libs/os/src/os_eventq.c b/libs/os/src/os_eventq.c index b3ff26f7..f9cc2837 100644 --- a/libs/os/src/os_eventq.c +++ b/libs/os/src/os_eventq.c @@ -117,6 +117,29 @@ pull_one: return (ev); } +static struct os_event * +os_eventq_poll_0timo(struct os_eventq **evq, int nevqs) +{ + struct os_event *ev; + os_sr_t sr; + int i; + + ev = NULL; + + OS_ENTER_CRITICAL(sr); + for (i = 0; i < nevqs; i++) { + ev = STAILQ_FIRST(&evq[i]->evq_list); + if (ev) { + STAILQ_REMOVE(&evq[i]->evq_list, ev, os_event, ev_next); + ev->ev_queued = 0; + break; + } + } + OS_EXIT_CRITICAL(sr); + + return ev; +} + /** * Poll the list of event queues specified by the evq parameter * (size nevqs), and return the "first" event available on any of @@ -137,6 +160,13 @@ os_eventq_poll(struct os_eventq **evq, int nevqs, os_time_t timo) int i, j; os_sr_t sr; + /* If the timeout is 0, don't involve the scheduler at all. Grab an event + * if one is available, else return immediately. + */ + if (timo == 0) { + return os_eventq_poll_0timo(evq, nevqs); + } + ev = NULL; OS_ENTER_CRITICAL(sr); @@ -147,8 +177,7 @@ os_eventq_poll(struct os_eventq **evq, int nevqs, os_time_t timo) if (ev) { STAILQ_REMOVE(&evq[i]->evq_list, ev, os_event, ev_next); ev->ev_queued = 0; - /* Reset the items that already have an evq task set - */ + /* Reset the items that already have an evq task set. */ for (j = 0; j < i; j++) { evq[j]->evq_task = NULL; } diff --git a/libs/os/src/os_mbuf.c b/libs/os/src/os_mbuf.c index 85232d88..a2302df7 100644 --- a/libs/os/src/os_mbuf.c +++ b/libs/os/src/os_mbuf.c @@ -37,6 +37,7 @@ #include <assert.h> #include <string.h> +#include <limits.h> STAILQ_HEAD(, os_mbuf_pool) g_msys_pool_list = STAILQ_HEAD_INITIALIZER(g_msys_pool_list); @@ -509,6 +510,49 @@ err: return (rc); } +/** + * Reads data from one mbuf and appends it to another. On error, the specified + * data range may be partially appended. Neither mbuf is required to contain + * an mbuf packet header. + * + * @param dst The mbuf to append to. + * @param src The mbuf to copy data from. + * @param src_off The absolute offset within the source mbuf + * chain to read from. + * @param len The number of bytes to append. + * + * @return 0 on success; + * OS_EINVAL if the specified range extends beyond + * the end of the source mbuf chain. + */ +int +os_mbuf_appendfrom(struct os_mbuf *dst, const struct os_mbuf *src, + uint16_t src_off, uint16_t len) +{ + const struct os_mbuf *src_cur_om; + uint16_t src_cur_off; + uint16_t chunk_sz; + int rc; + + src_cur_om = os_mbuf_off(src, src_off, &src_cur_off); + while (len > 0) { + if (src_cur_om == NULL) { + return OS_EINVAL; + } + + chunk_sz = min(len, src_cur_om->om_len - src_cur_off); + rc = os_mbuf_append(dst, src_cur_om->om_data + src_cur_off, chunk_sz); + if (rc != 0) { + return rc; + } + + len -= chunk_sz; + src_cur_om = SLIST_NEXT(src_cur_om, om_next); + src_cur_off = 0; + } + + return 0; +} /** * Duplicate a chain of mbufs. Return the start of the duplicated chain. @@ -576,26 +620,30 @@ err: * NULL if the specified offset is out of bounds. */ struct os_mbuf * -os_mbuf_off(struct os_mbuf *om, int off, int *out_off) +os_mbuf_off(const struct os_mbuf *om, int off, uint16_t *out_off) { struct os_mbuf *next; + struct os_mbuf *cur; + + /* Cast away const. */ + cur = (struct os_mbuf *)om; while (1) { - if (om == NULL) { + if (cur == NULL) { return NULL; } - next = SLIST_NEXT(om, om_next); + next = SLIST_NEXT(cur, om_next); - if (om->om_len > off || - (om->om_len == off && next == NULL)) { + if (cur->om_len > off || + (cur->om_len == off && next == NULL)) { *out_off = off; - return om; + return cur; } - off -= om->om_len; - om = next; + off -= cur->om_len; + cur = next; } } @@ -739,14 +787,14 @@ os_mbuf_adj(struct os_mbuf *mp, int req_len) * * @return 0 if both memory regions are identical; * A memcmp return code if there is a mismatch; - * -1 if the mbuf is too short. + * INT_MAX if the mbuf is too short. */ int -os_mbuf_memcmp(const struct os_mbuf *om, int off, const void *data, int len) +os_mbuf_cmpf(const struct os_mbuf *om, int off, const void *data, int len) { - int chunk_sz; - int data_off; - int om_off; + uint16_t chunk_sz; + uint16_t data_off; + uint16_t om_off; int rc; if (len <= 0) { @@ -754,10 +802,10 @@ os_mbuf_memcmp(const struct os_mbuf *om, int off, const void *data, int len) } data_off = 0; - om = os_mbuf_off((struct os_mbuf *)om, off, &om_off); + om = os_mbuf_off(om, off, &om_off); while (1) { if (om == NULL) { - return -1; + return INT_MAX; } chunk_sz = min(om->om_len - om_off, len - data_off); @@ -777,12 +825,84 @@ os_mbuf_memcmp(const struct os_mbuf *om, int off, const void *data, int len) om_off = 0; if (om == NULL) { - return -1; + return INT_MAX; } } } /** + * Compares the contents of two mbuf chains. The ranges of the two chains to + * be compared are specified via the two offset parameters and the len + * parameter. Neither mbuf chain is required to contain a packet header. + * + * @param om1 The first mbuf chain to compare. + * @param offset1 The absolute offset within om1 at which to + * start the comparison. + * @param om2 The second mbuf chain to compare. + * @param offset2 The absolute offset within om2 at which to + * start the comparison. + * @param len The number of bytes to compare. + * + * @return 0 if both mbuf segments are identical; + * A memcmp() return code if the segment contents + * differ; + * INT_MAX if a specified range extends beyond the + * end of its corresponding mbuf chain. + */ +int +os_mbuf_cmpm(const struct os_mbuf *om1, uint16_t offset1, + const struct os_mbuf *om2, uint16_t offset2, + uint16_t len) +{ + const struct os_mbuf *cur1; + const struct os_mbuf *cur2; + uint16_t bytes_remaining; + uint16_t chunk_sz; + uint16_t om1_left; + uint16_t om2_left; + uint16_t om1_off; + uint16_t om2_off; + int rc; + + cur1 = os_mbuf_off(om1, offset1, &om1_off); + cur2 = os_mbuf_off(om2, offset2, &om2_off); + + bytes_remaining = len; + while (1) { + if (bytes_remaining == 0) { + return 0; + } + + while (cur1 != NULL && om1_off >= cur1->om_len) { + cur1 = SLIST_NEXT(cur1, om_next); + om1_off = 0; + } + while (cur2 != NULL && om2_off >= cur2->om_len) { + cur2 = SLIST_NEXT(cur2, om_next); + om2_off = 0; + } + + if (cur1 == NULL || cur2 == NULL) { + return INT_MAX; + } + + om1_left = cur1->om_len - om1_off; + om2_left = cur2->om_len - om2_off; + chunk_sz = min(min(om1_left, om2_left), bytes_remaining); + + rc = memcmp(cur1->om_data + om1_off, cur2->om_data + om2_off, + chunk_sz); + if (rc != 0) { + return rc; + } + + om1_off += chunk_sz; + om2_off += chunk_sz; + bytes_remaining -= chunk_sz; + } +} + +/** * Increases the length of an mbuf chain by adding data to the front. If there * is insufficient room in the leading mbuf, additional mbufs are allocated and * prepended as necessary. If this function fails to allocate an mbuf, the @@ -851,6 +971,33 @@ os_mbuf_prepend(struct os_mbuf *om, int len) } /** + * Prepends a chunk of empty data to the specified mbuf chain and ensures the + * chunk is contiguous. If either operation fails, the specified mbuf chain is + * freed and NULL is returned. + * + * @param om The mbuf chain to prepend to. + * @param len The number of bytes to prepend and pullup. + * + * @return The modified mbuf on success; + * NULL on failure (and the mbuf chain is freed). + */ +struct os_mbuf * +os_mbuf_prepend_pullup(struct os_mbuf *om, uint16_t len) +{ + om = os_mbuf_prepend(om, len); + if (om == NULL) { + return NULL; + } + + om = os_mbuf_pullup(om, len); + if (om == NULL) { + return NULL; + } + + return om; +} + +/** * Copies the contents of a flat buffer into an mbuf chain, starting at the * specified destination offset. If the mbuf is too small for the source data, * it is extended as necessary. If the destination mbuf contains a packet @@ -870,8 +1017,8 @@ os_mbuf_copyinto(struct os_mbuf *om, int off, const void *src, int len) struct os_mbuf *next; struct os_mbuf *cur; const uint8_t *sptr; + uint16_t cur_off; int copylen; - int cur_off; int rc; /* Find the mbuf,offset pair for the start of the destination. */ diff --git a/libs/os/src/os_mempool.c b/libs/os/src/os_mempool.c index 1e7428ca..e940ba42 100644 --- a/libs/os/src/os_mempool.c +++ b/libs/os/src/os_mempool.c @@ -41,21 +41,29 @@ STAILQ_HEAD(, os_mempool) g_os_mempool_list = * @return os_error_t */ os_error_t -os_mempool_init(struct os_mempool *mp, int blocks, int block_size, void *membuf, - char *name) +os_mempool_init(struct os_mempool *mp, int blocks, int block_size, + void *membuf, char *name) { int true_block_size; uint8_t *block_addr; struct os_memblock *block_ptr; /* Check for valid parameters */ - if ((!mp) || (!membuf) || (blocks <= 0) || (block_size <= 0)) { + if ((!mp) || (blocks < 0) || (block_size <= 0)) { return OS_INVALID_PARM; } - /* Blocks need to be sized properly and memory buffer should be aligned */ - if (((uint32_t)membuf & (OS_ALIGNMENT - 1)) != 0) { - return OS_MEM_NOT_ALIGNED; + if ((!membuf) && (blocks != 0)) { + return OS_INVALID_PARM; + } + + if (membuf != NULL) { + /* Blocks need to be sized properly and memory buffer should be + * aligned + */ + if (((uint32_t)membuf & (OS_ALIGNMENT - 1)) != 0) { + return OS_MEM_NOT_ALIGNED; + } } true_block_size = OS_MEMPOOL_TRUE_BLOCK_SIZE(block_size); @@ -86,6 +94,42 @@ os_mempool_init(struct os_mempool *mp, int blocks, int block_size, void *membuf, } /** + * Checks if a memory block was allocated from the specified mempool. + * + * @param mp The mempool to check as parent. + * @param block_addr The memory block to check as child. + * + * @return 0 if the block does not belong to the mempool; + * 1 if the block does belong to the mempool. + */ +int +os_memblock_from(struct os_mempool *mp, void *block_addr) +{ + uint32_t true_block_size; + uint32_t baddr32; + uint32_t end; + + _Static_assert(sizeof block_addr == sizeof baddr32, + "Pointer to void must be 32-bits."); + + baddr32 = (uint32_t)block_addr; + true_block_size = OS_MEMPOOL_TRUE_BLOCK_SIZE(mp->mp_block_size); + end = mp->mp_membuf_addr + (mp->mp_num_blocks * true_block_size); + + /* Check that the block is in the memory buffer range. */ + if ((baddr32 < mp->mp_membuf_addr) || (baddr32 >= end)) { + return 0; + } + + /* All freed blocks should be on true block size boundaries! */ + if (((baddr32 - mp->mp_membuf_addr) % true_block_size) != 0) { + return 0; + } + + return 1; +} + +/** * os memblock get * * Get a memory block from a memory pool @@ -135,9 +179,6 @@ os_error_t os_memblock_put(struct os_mempool *mp, void *block_addr) { os_sr_t sr; - uint32_t end; - uint32_t true_block_size; - uint32_t baddr32; struct os_memblock *block; /* Make sure parameters are valid */ @@ -146,23 +187,10 @@ os_memblock_put(struct os_mempool *mp, void *block_addr) } /* Check that the block we are freeing is a valid block! */ - baddr32 = (uint32_t)block_addr; - true_block_size = OS_MEMPOOL_TRUE_BLOCK_SIZE(mp->mp_block_size); - end = mp->mp_membuf_addr + (mp->mp_num_blocks * true_block_size); - if ((baddr32 < mp->mp_membuf_addr) || (baddr32 >= end)) { - return OS_INVALID_PARM; - } - - /* All freed blocks should be on true block size boundaries! */ - if (((baddr32 - mp->mp_membuf_addr) % true_block_size) != 0) { + if (!os_memblock_from(mp, block_addr)) { return OS_INVALID_PARM; } - /* - * XXX: we should do boundary checks here! The block had better be within - * the pool. If it fails, do we return an error or assert()? Add this when - * we add the memory debug. - */ block = (struct os_memblock *)block_addr; OS_ENTER_CRITICAL(sr); diff --git a/libs/os/src/os_priv.h b/libs/os/src/os_priv.h index dfc9e384..b5c8f65e 100644 --- a/libs/os/src/os_priv.h +++ b/libs/os/src/os_priv.h @@ -6,7 +6,7 @@ * 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, @@ -20,11 +20,17 @@ #ifndef H_OS_PRIV_ #define H_OS_PRIV_ +#include "os/queue.h" + TAILQ_HEAD(os_task_list, os_task); +TAILQ_HEAD(os_callout_list, os_callout); +STAILQ_HEAD(os_task_stailq, os_task); +extern struct os_task g_idle_task; extern struct os_task_list g_os_run_list; extern struct os_task_list g_os_sleep_list; -extern struct os_task_list g_os_task_list; +extern struct os_task_stailq g_os_task_list; extern struct os_task *g_current_task; +extern struct os_callout_list g_callout_list; #endif diff --git a/libs/os/src/os_task.c b/libs/os/src/os_task.c index 55c1da24..e48fdf97 100644 --- a/libs/os/src/os_task.c +++ b/libs/os/src/os_task.c @@ -19,12 +19,13 @@ #include "os/os.h" +#include "os_priv.h" #include <string.h> uint8_t g_task_id; -STAILQ_HEAD(, os_task) g_os_task_list = STAILQ_HEAD_INITIALIZER(g_os_task_list); +struct os_task_stailq g_os_task_list; static void _clear_stack(os_stack_t *stack_bottom, int size) diff --git a/libs/os/src/os_time.c b/libs/os/src/os_time.c index 2ce379e8..4ef439f5 100644 --- a/libs/os/src/os_time.c +++ b/libs/os/src/os_time.c @@ -97,10 +97,14 @@ os_time_advance(int ticks) assert(ticks >= 0); if (ticks > 0) { - os_time_tick(ticks); - os_callout_tick(); - os_sched_os_timer_exp(); - os_sched(NULL); + if (!os_started()) { + g_os_time += ticks; + } else { + os_time_tick(ticks); + os_callout_tick(); + os_sched_os_timer_exp(); + os_sched(NULL); + } } } diff --git a/libs/os/src/test/callout_test.c b/libs/os/src/test/callout_test.c new file mode 100644 index 00000000..4e3811d7 --- /dev/null +++ b/libs/os/src/test/callout_test.c @@ -0,0 +1,330 @@ +/** + * 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 "testutil/testutil.h" +#include "os/os.h" +#include "os_test_priv.h" +#include "os/os_eventq.h" +#include "os/os_callout.h" +#include "os/os_time.h" + +/* Task 1 for sending */ +#define CALLOUT_STACK_SIZE (5120) +#define SEND_CALLOUT_TASK_PRIO (1) +struct os_task callout_task_struct_send; +os_stack_t callout_task_stack_send[CALLOUT_STACK_SIZE]; + +#define RECEIVE_CALLOUT_TASK_PRIO (2) +struct os_task callout_task_struct_receive; +os_stack_t callout_task_stack_receive[CALLOUT_STACK_SIZE]; + +/* Delearing variables for callout_func */ +struct os_callout_func callout_func_test; + +/* The event to be sent*/ +struct os_eventq callout_evq; +struct os_event callout_ev; + +/* The callout_stop task */ +#define SEND_STOP_CALLOUT_TASK_PRIO (3) +struct os_task callout_task_struct_stop_send; +os_stack_t callout_task_stack_stop_send[CALLOUT_STACK_SIZE]; + +#define RECEIVE_STOP_CALLOUT_TASK_PRIO (4) +struct os_task callout_task_struct_stop_receive; +os_stack_t callout_task_stack_stop_receive[CALLOUT_STACK_SIZE]; + +/* Delearing variables for callout_stop_func */ +#define MULTI_SIZE (2) +struct os_callout_func callout_func_stop_test[MULTI_SIZE]; + +/* The event to be sent*/ +struct os_eventq callout_stop_evq[MULTI_SIZE]; +struct os_event callout_stop_ev; + +/* Declearing varables for callout_speak */ +#define SPEAK_CALLOUT_TASK_PRIO (5) +struct os_task callout_task_struct_speak; +os_stack_t callout_task_stack_speak[CALLOUT_STACK_SIZE]; + +/* Declearing varaibles for listen */ +#define LISTEN_CALLOUT_TASK_PRIO (6) +struct os_task callout_task_struct_listen; +os_stack_t callout_task_stack_listen[CALLOUT_STACK_SIZE]; + +struct os_callout_func callout_func_speak; + +/* Global variables to be used by the callout functions */ +int p; +int q; +int t; +/* This is the function for callout_init*/ +void +my_callout_func(void *arg) +{ + p = 4; +} + +/* This is the function for callout_init of stop test_case*/ +void +my_callout_stop_func(void *arg) +{ + q = 1; +} +/* This is the function for callout_init for speak test_case*/ +void +my_callout_speak_func(void *arg) +{ + t = 2; +} + +/* This is a callout task to send data */ +void +callout_task_send(void *arg ) +{ + int i; + /* Should say whether callout is armed or not */ + i= os_callout_queued(&callout_func_test.cf_c); + TEST_ASSERT(i == 0); + + /* Arm the callout */ + i = os_callout_reset(&callout_func_test.cf_c, OS_TICKS_PER_SEC/ 50); + TEST_ASSERT_FATAL(i == 0); + + /* Should say whether callout is armed or not */ + i = os_callout_queued(&callout_func_test.cf_c); + TEST_ASSERT(i == 1); + + /* Send the callout */ + os_time_delay(OS_TICKS_PER_SEC ); +} + +/* This is the callout to receive data */ +void +callout_task_receive( void *arg) +{ + int i; + struct os_event *event; + struct os_callout_func *callout; + os_time_t now; + os_time_t tm; + os_sr_t sr; + /* Recieve using the os_eventq_poll */ + event = os_eventq_poll(&callout_func_test.cf_c.c_evq, 1, OS_WAIT_FOREVER); + TEST_ASSERT(event->ev_type == OS_EVENT_T_TIMER); + TEST_ASSERT(event->ev_arg == NULL); + callout = (struct os_callout_func *)event; + TEST_ASSERT(callout->cf_func == my_callout_func); + + /* Should say whether callout is armed or not */ + i = os_callout_queued(&callout_func_test.cf_c); + TEST_ASSERT(i == 0); + + OS_ENTER_CRITICAL(sr); + now = os_time_get(); + tm = os_callout_wakeup_ticks(now); + TEST_ASSERT(tm == OS_TIMEOUT_NEVER); + OS_EXIT_CRITICAL(sr); + + /* Finishes the test when OS has been started */ + os_test_restart(); + +} + +/* This is callout to send the stop_callout */ +void +callout_task_stop_send( void *arg) +{ + int k; + int j; + /* Should say whether callout is armed or not */ + for(k = 0; k<MULTI_SIZE; k++){ + j = os_callout_queued(&callout_func_stop_test[k].cf_c); + TEST_ASSERT(j == 0); + } + + + /* Show that callout is not armed after calling callout_stop */ + for(k = 0; k<MULTI_SIZE; k++){ + os_callout_stop(&callout_func_stop_test[k].cf_c); + j = os_callout_queued(&callout_func_stop_test[k].cf_c); + TEST_ASSERT(j == 0); + } + /* Arm the callout */ + for(k = 0; k<MULTI_SIZE; k++){ + j = os_callout_reset(&callout_func_stop_test[k].cf_c, OS_TICKS_PER_SEC/ 50); + TEST_ASSERT_FATAL(j == 0); + } + os_time_delay( OS_TICKS_PER_SEC ); +} + +/* This is the callout to receive stop_callout data */ +void +callout_task_stop_receive( void *arg ) +{ + int k; + struct os_event *event; + struct os_callout_func *callout; + /* Recieving using the os_eventq_poll */ + for(k=0; k<MULTI_SIZE; k++){ + event = os_eventq_poll(&callout_func_stop_test[k].cf_c.c_evq, 1, + OS_WAIT_FOREVER); + TEST_ASSERT(event->ev_type == OS_EVENT_T_TIMER); + TEST_ASSERT(event->ev_arg == NULL); + callout = (struct os_callout_func *)event; + TEST_ASSERT(callout->cf_func == my_callout_stop_func); + + + } + + /* Show that event is removed from the queued after calling callout_stop */ + for(k=0; k<MULTI_SIZE; k++){ + os_callout_stop(&callout_func_stop_test[k].cf_c); + /* Testing that the event has been removed from queue */ + TEST_ASSERT_FATAL(1); + } + /* Finishes the test when OS has been started */ + os_test_restart(); + +} + +/* This is a callout task to send data */ +void +callout_task_stop_speak( void *arg ) +{ + int i; + /* Arm the callout */ + i = os_callout_reset(&callout_func_speak.cf_c, OS_TICKS_PER_SEC/ 50); + TEST_ASSERT_FATAL(i == 0); + + /* should say whether callout is armed or not */ + i = os_callout_queued(&callout_func_speak.cf_c); + TEST_ASSERT(i == 1); + + os_callout_stop(&callout_func_speak.cf_c); + + /* Send the callout */ + os_time_delay(OS_TICKS_PER_SEC/ 100 ); + /* Finishes the test when OS has been started */ + os_test_restart(); +} + +void +callout_task_stop_listen( void *arg ) +{ + struct os_event *event; + struct os_callout_func *callout; + event = os_eventq_get(callout_func_speak.cf_c.c_evq); + TEST_ASSERT_FATAL(0); + callout = (struct os_callout_func *)event; + TEST_ASSERT(callout->cf_func == my_callout_speak_func); + +} + +/* Test case to test the basics of the callout */ +TEST_CASE(callout_test) +{ + + /* Initializing the OS */ + os_init(); + + /* Initialize the sending task */ + os_task_init(&callout_task_struct_send, "callout_task_send", + callout_task_send, NULL, SEND_CALLOUT_TASK_PRIO, OS_WAIT_FOREVER, + callout_task_stack_send, CALLOUT_STACK_SIZE); + + /* Initialize the receive task */ + os_task_init(&callout_task_struct_receive, "callout_task_receive", + callout_task_receive, NULL, RECEIVE_CALLOUT_TASK_PRIO, OS_WAIT_FOREVER, + callout_task_stack_receive, CALLOUT_STACK_SIZE); + + os_eventq_init(&callout_evq); + + /* Initialize the callout function */ + os_callout_func_init(&callout_func_test, &callout_evq, + my_callout_func, NULL); + + /* Does not return until OS_restart is called */ + os_start(); +} + +/* Test case of the callout_task_stop */ +TEST_CASE(callout_test_stop) +{ + int k; + /* Initializing the OS */ + os_init(); + + /* Initialize the sending task */ + os_task_init(&callout_task_struct_stop_send, "callout_task_stop_send", + callout_task_stop_send, NULL, SEND_STOP_CALLOUT_TASK_PRIO, OS_WAIT_FOREVER, + callout_task_stack_stop_send, CALLOUT_STACK_SIZE); + + /* Initialize the receiving task */ + os_task_init(&callout_task_struct_stop_receive, "callout_task_stop_receive", + callout_task_stop_receive, NULL, RECEIVE_STOP_CALLOUT_TASK_PRIO, + OS_WAIT_FOREVER, callout_task_stack_stop_receive, CALLOUT_STACK_SIZE); + + for(k = 0; k< MULTI_SIZE; k++){ + os_eventq_init(&callout_stop_evq[k]); + } + + /* Initialize the callout function */ + for(k = 0; k<MULTI_SIZE; k++){ + os_callout_func_init(&callout_func_stop_test[k], &callout_stop_evq[k], + my_callout_stop_func, NULL); + } + + /* Does not return until OS_restart is called */ + os_start(); + +} + +/* Test case to test case for speak and listen */ +TEST_CASE(callout_test_speak) +{ + /* Initializing the OS */ + os_init(); + + /* Initialize the sending task */ + os_task_init(&callout_task_struct_speak, "callout_task_speak", + callout_task_stop_speak, NULL, SPEAK_CALLOUT_TASK_PRIO, OS_WAIT_FOREVER, + callout_task_stack_speak, CALLOUT_STACK_SIZE); + + /* Initialize the receive task */ + os_task_init(&callout_task_struct_listen, "callout_task_listen", + callout_task_stop_listen, NULL, LISTEN_CALLOUT_TASK_PRIO, OS_WAIT_FOREVER, + callout_task_stack_listen, CALLOUT_STACK_SIZE); + + os_eventq_init(&callout_evq); + + /* Initialize the callout function */ + os_callout_func_init(&callout_func_speak, &callout_evq, + my_callout_speak_func, NULL); + /* Does not return until OS_restart is called */ + os_start(); + +} + +TEST_SUITE(os_callout_test_suite) +{ + callout_test(); + callout_test_stop(); + callout_test_speak(); +} diff --git a/libs/os/src/test/eventq_test.c b/libs/os/src/test/eventq_test.c index 29e5fe30..cb1ed94b 100644 --- a/libs/os/src/test/eventq_test.c +++ b/libs/os/src/test/eventq_test.c @@ -17,21 +17,21 @@ * under the License. */ - +#include <string.h> #include "testutil/testutil.h" #include "os/os.h" #include "os_test_priv.h" #include "os/os_eventq.h" #define MY_STACK_SIZE (5120) - -/*Task 1 sending task*/ +#define POLL_STACK_SIZE (4096) +/* Task 1 sending task */ /* Define task stack and task object */ #define SEND_TASK_PRIO (1) struct os_task eventq_task_s; os_stack_t eventq_task_stack_s[MY_STACK_SIZE]; -/*Task 2 receiving task*/ +/* Task 2 receiving task */ #define RECEIVE_TASK_PRIO (2) struct os_task eventq_task_r; os_stack_t eventq_task_stack_r[MY_STACK_SIZE]; @@ -48,6 +48,39 @@ struct os_event m_event[SIZE_MULTI_EVENT]; /* Setting the event to send and receive multiple data */ uint8_t my_event_type = 1; +/* Setting up data for the poll */ +/* Define the task stack for the eventq_task_poll_send */ +#define SEND_TASK_POLL_PRIO (3) +struct os_task eventq_task_poll_s; +os_stack_t eventq_task_stack_poll_s[POLL_STACK_SIZE]; + +/* Define the task stack for the eventq_task_poll_receive */ +#define RECEIVE_TASK_POLL_PRIO (4) +struct os_task eventq_task_poll_r; +os_stack_t eventq_task_stack_poll_r[POLL_STACK_SIZE ]; + +/* Setting the data for the poll timeout */ +/* Define the task stack for the eventq_task_poll_timeout_send */ +#define SEND_TASK_POLL_TIMEOUT_PRIO (5) +struct os_task eventq_task_poll_timeout_s; +os_stack_t eventq_task_stack_poll_timeout_s[POLL_STACK_SIZE]; + +/* Define the task stack for the eventq_task_poll_receive */ +#define RECEIVE_TASK_POLL_TIMEOUT_PRIO (6) +struct os_task eventq_task_poll_timeout_r; +os_stack_t eventq_task_stack_poll_timeout_r[POLL_STACK_SIZE]; + +/* Setting the data for the poll single */ +/* Define the task stack for the eventq_task_poll_single_send */ +#define SEND_TASK_POLL_SINGLE_PRIO (7) +struct os_task eventq_task_poll_single_s; +os_stack_t eventq_task_stack_poll_single_s[POLL_STACK_SIZE]; + +/* Define the task stack for the eventq_task_poll_single_receive */ +#define RECEIVE_TASK_POLL_SINGLE_PRIO (8) +struct os_task eventq_task_poll_single_r; +os_stack_t eventq_task_stack_poll_single_r[POLL_STACK_SIZE]; + /* This is the task function to send data */ void eventq_task_send(void *arg) @@ -95,6 +128,138 @@ eventq_task_receive(void *arg) os_test_restart(); } +void +eventq_task_poll_send(void *arg) +{ + struct os_eventq *eventqs[SIZE_MULTI_EVENT]; + int i; + + for (i = 0; i < SIZE_MULTI_EVENT; i++){ + eventqs[i] = &multi_eventq[i]; + } + + for (i = 0; i < SIZE_MULTI_EVENT; i++){ + m_event[i].ev_type = i + 10; + m_event[i].ev_arg = NULL; + + /* Put and send */ + os_eventq_put(eventqs[i], &m_event[i]); + os_time_delay(OS_TICKS_PER_SEC / 2); + } + + /* This task sleeps until the receive task completes the test. */ + os_time_delay(1000000); +} + +void +eventq_task_poll_receive(void *arg) +{ + struct os_eventq *eventqs[SIZE_MULTI_EVENT]; + struct os_event *event; + int i; + + for (i = 0; i < SIZE_MULTI_EVENT; i++){ + eventqs[i] = &multi_eventq[i]; + } + + /* Recieving using the os_eventq_poll*/ + for (i = 0; i < SIZE_MULTI_EVENT; i++) { + event = os_eventq_poll(eventqs, SIZE_MULTI_EVENT, OS_WAIT_FOREVER); + TEST_ASSERT(event->ev_type == i +10); + } + + /* Finishes the test when OS has been started */ + os_test_restart(); + +} + +/* Sending with a time failure */ +void +eventq_task_poll_timeout_send(void *arg) +{ + struct os_eventq *eventqs[SIZE_MULTI_EVENT]; + int i; + + for (i = 0; i < SIZE_MULTI_EVENT; i++){ + eventqs[i] = &multi_eventq[i]; + } + + for (i = 0; i < SIZE_MULTI_EVENT; i++){ + os_time_delay(1000); + + /* Put and send */ + os_eventq_put(eventqs[i], &m_event[i]); + os_time_delay(OS_TICKS_PER_SEC / 2); + } + + /* This task sleeps until the receive task completes the test. */ + os_time_delay(1000000); + +} + +/* Receiving multiple event queues with a time failure */ +void +eventq_task_poll_timeout_receive(void *arg) +{ + struct os_eventq *eventqs[SIZE_MULTI_EVENT]; + struct os_event *event; + int i; + + for (i = 0; i < SIZE_MULTI_EVENT; i++){ + eventqs[i] = &multi_eventq[i]; + } + + /* Recieving using the os_eventq_poll_timeout*/ + for (i = 0; i < SIZE_MULTI_EVENT; i++) { + event = os_eventq_poll(eventqs, SIZE_MULTI_EVENT, 200); + TEST_ASSERT(event == NULL); + } + + /* Finishes the test when OS has been started */ + os_test_restart(); + +} + +/* Sending a single event to poll */ +void +eventq_task_poll_single_send(void *arg) +{ + struct os_eventq *eventqs[SIZE_MULTI_EVENT]; + int i; + int position = 2; + + for (i = 0; i < SIZE_MULTI_EVENT; i++){ + eventqs[i] = &multi_eventq[i]; + } + + /* Put and send */ + os_eventq_put(eventqs[position], &m_event[position]); + os_time_delay(OS_TICKS_PER_SEC / 2); + + /* This task sleeps until the receive task completes the test. */ + os_time_delay(1000000); +} + +/* Recieving the single event */ +void +eventq_task_poll_single_receive(void *arg) +{ + struct os_eventq *eventqs[SIZE_MULTI_EVENT]; + struct os_event *event; + int i; + + for (i = 0; i < SIZE_MULTI_EVENT; i++){ + eventqs[i] = &multi_eventq[i]; + } + + /* Recieving using the os_eventq_poll*/ + event = os_eventq_poll(eventqs, SIZE_MULTI_EVENT, OS_WAIT_FOREVER); + TEST_ASSERT(event->ev_type == 20); + + /* Finishes the test when OS has been started */ + os_test_restart(); +} + TEST_CASE(event_test_sr) { int i; @@ -121,7 +286,131 @@ TEST_CASE(event_test_sr) } +/* To test for the basic function of os_eventq_poll() */ +TEST_CASE(event_test_poll_sr) +{ + int i; + + /* Initializing the OS */ + os_init(); + /* Initialize the task */ + os_task_init(&eventq_task_poll_s, "eventq_task_poll_s", eventq_task_poll_send, + NULL, SEND_TASK_POLL_PRIO, OS_WAIT_FOREVER, eventq_task_stack_poll_s, + POLL_STACK_SIZE); + + /* Receive events and check whether the eevnts are correctly received */ + os_task_init(&eventq_task_poll_r, "eventq_task_r", eventq_task_poll_receive, + NULL, RECEIVE_TASK_POLL_PRIO, OS_WAIT_FOREVER, eventq_task_stack_poll_r, + POLL_STACK_SIZE); + + /* Initializing the eventqs. */ + for (i = 0; i < SIZE_MULTI_EVENT; i++){ + os_eventq_init(&multi_eventq[i]); + } + + /* Does not return until OS_restart is called */ + os_start(); + +} + +/* Test case for poll timeout */ +TEST_CASE(event_test_poll_timeout_sr) +{ + int i; + + /* Initializing the OS */ + os_init(); + /* Initialize the task */ + os_task_init(&eventq_task_poll_timeout_s, "eventq_task_poll_timeout_s", + eventq_task_poll_timeout_send, NULL, SEND_TASK_POLL_TIMEOUT_PRIO, + OS_WAIT_FOREVER, eventq_task_stack_poll_timeout_s, POLL_STACK_SIZE); + + /* Receive events and check whether the eevnts are correctly received */ + os_task_init(&eventq_task_poll_timeout_r, "eventq_task_timeout_r", + eventq_task_poll_timeout_receive, NULL, RECEIVE_TASK_POLL_TIMEOUT_PRIO, + OS_WAIT_FOREVER, eventq_task_stack_poll_timeout_r, POLL_STACK_SIZE); + + /* Initializing the eventqs. */ + for (i = 0; i < SIZE_MULTI_EVENT; i++){ + os_eventq_init(&multi_eventq[i]); + + m_event[i].ev_type = i + 10; + m_event[i].ev_arg = NULL; + } + + /* Does not return until OS_restart is called */ + os_start(); + +} + +/* The case for poll single */ +/* Test case for poll timeout */ +TEST_CASE(event_test_poll_single_sr) +{ + int i; + + /* Initializing the OS */ + os_init(); + /* Initialize the task */ + os_task_init(&eventq_task_poll_single_s, "eventq_task_poll_single_s", + eventq_task_poll_single_send, NULL, SEND_TASK_POLL_SINGLE_PRIO, + OS_WAIT_FOREVER, eventq_task_stack_poll_single_s, POLL_STACK_SIZE); + + /* Receive events and check whether the eevnts are correctly received */ + os_task_init(&eventq_task_poll_single_r, "eventq_task_single_r", + eventq_task_poll_single_receive, NULL, RECEIVE_TASK_POLL_SINGLE_PRIO, + OS_WAIT_FOREVER, eventq_task_stack_poll_single_r, POLL_STACK_SIZE); + + for (i = 0; i < SIZE_MULTI_EVENT; i++){ + os_eventq_init(&multi_eventq[i]); + + m_event[i].ev_type = 10 * i; + m_event[i].ev_arg = NULL; + } + + /* Does not return until OS_restart is called */ + os_start(); + +} + +/** + * Tests eventq_poll() with a timeout of 0. This should not involve the + * scheduler at all, so it should work without starting the OS. + */ +TEST_CASE(event_test_poll_0timo) +{ + struct os_eventq *eventqs[SIZE_MULTI_EVENT]; + struct os_event *evp; + struct os_event ev; + int i; + + for (i = 0; i < SIZE_MULTI_EVENT; i++){ + os_eventq_init(&multi_eventq[i]); + eventqs[i] = &multi_eventq[i]; + } + + evp = os_eventq_poll(eventqs, SIZE_MULTI_EVENT, 0); + TEST_ASSERT(evp == NULL); + + /* Ensure no eventq thinks a task is waiting on it. */ + for (i = 0; i < SIZE_MULTI_EVENT; i++) { + TEST_ASSERT(eventqs[i]->evq_task == NULL); + } + + /* Put an event on one of the queues. */ + memset(&ev, 0, sizeof ev); + ev.ev_type = 1; + os_eventq_put(eventqs[3], &ev); + + evp = os_eventq_poll(eventqs, SIZE_MULTI_EVENT, 0); + TEST_ASSERT(evp == &ev); +} + TEST_SUITE(os_eventq_test_suite) { event_test_sr(); + event_test_poll_sr(); + event_test_poll_timeout_sr(); + event_test_poll_single_sr(); + event_test_poll_0timo(); } diff --git a/libs/os/src/test/os_test.c b/libs/os/src/test/os_test.c index 1b3af1ab..48f07206 100644 --- a/libs/os/src/test/os_test.c +++ b/libs/os/src/test/os_test.c @@ -31,7 +31,7 @@ os_test_all(void) os_sem_test_suite(); os_mbuf_test_suite(); os_eventq_test_suite(); - + os_callout_test_suite(); return tu_case_failed; } diff --git a/libs/os/src/test/os_test_priv.h b/libs/os/src/test/os_test_priv.h index 5193c33b..e923a6fa 100644 --- a/libs/os/src/test/os_test_priv.h +++ b/libs/os/src/test/os_test_priv.h @@ -27,6 +27,6 @@ int os_mbuf_test_suite(void); int os_mutex_test_suite(void); int os_sem_test_suite(void); int os_eventq_test_suite(void); - +int os_callout_test_suite(void); #endif diff --git a/libs/shell/src/shell.c b/libs/shell/src/shell.c index 95ddcb03..d38573c9 100644 --- a/libs/shell/src/shell.c +++ b/libs/shell/src/shell.c @@ -572,6 +572,8 @@ shell_task_init(uint8_t prio, os_stack_t *stack, uint16_t stack_size, os_eventq_init(&shell_evq); os_mqueue_init(&g_shell_nlip_mq, NULL); + console_init(shell_console_rx_cb); + rc = os_task_init(&shell_task, "shell", shell_task_func, NULL, prio, OS_WAIT_FOREVER, stack, stack_size); if (rc != 0) { diff --git a/libs/testutil/include/testutil/testutil.h b/libs/testutil/include/testutil/testutil.h index da3f618e..9c6192a2 100644 --- a/libs/testutil/include/testutil/testutil.h +++ b/libs/testutil/include/testutil/testutil.h @@ -57,6 +57,9 @@ extern const char *tu_suite_name; extern const char *tu_case_name; extern int tu_first_idx; +typedef void tu_post_test_fn_t(void *arg); + +void tu_suite_set_post_test_cb(tu_post_test_fn_t *cb, void *cb_arg); int tu_parse_args(int argc, char **argv); int tu_init(void); void tu_restart(void); @@ -65,6 +68,7 @@ void tu_restart(void); * Private declarations * *****************************************************************************/ +void tu_suite_complete(void); void tu_suite_init(const char *name); void tu_case_init(const char *name); @@ -74,7 +78,7 @@ void tu_case_fail_assert(int fatal, const char *file, int line, void tu_case_write_pass_auto(void); void tu_case_pass_manual(const char *file, int line, const char *format, ...); - +void tu_case_post_test(void); extern int tu_any_failed; extern int tu_suite_failed; @@ -91,6 +95,7 @@ extern jmp_buf tu_case_jb; { \ tu_suite_init(#suite_name); \ TEST_SUITE_##suite_name(); \ + tu_suite_complete(); \ \ return tu_suite_failed; \ } \ @@ -113,6 +118,7 @@ extern jmp_buf tu_case_jb; \ if (setjmp(tu_case_jb) == 0) { \ TEST_CASE_##case_name(); \ + tu_case_post_test(); \ tu_case_write_pass_auto(); \ } \ } \ diff --git a/libs/testutil/src/case.c b/libs/testutil/src/case.c index bea6efa3..614b1425 100644 --- a/libs/testutil/src/case.c +++ b/libs/testutil/src/case.c @@ -30,6 +30,8 @@ int tu_case_failed; int tu_case_idx; const char *tu_case_name; +tu_post_test_fn_t *tu_case_post_test_cb; +void *tu_case_post_test_cb_arg; #define TU_CASE_BUF_SZ 1024 @@ -101,6 +103,14 @@ tu_case_complete(void) tu_case_idx++; } +void +tu_case_post_test(void) +{ + if (tu_case_post_test_cb != NULL) { + tu_case_post_test_cb(tu_case_post_test_cb_arg); + } +} + static void tu_case_write_fail_buf(void) { diff --git a/libs/testutil/src/suite.c b/libs/testutil/src/suite.c index 5c2e5cbc..93ca639f 100644 --- a/libs/testutil/src/suite.c +++ b/libs/testutil/src/suite.c @@ -30,6 +30,30 @@ tu_suite_set_name(const char *name) tu_suite_name = name; } +/** + * Configures a callback that gets executed at the end of each test case in the + * current suite. This is useful when there are some checks that should be + * performed at the end of each test (e.g., verify no memory leaks). This + * callback is cleared when the current suite completes. + * + * @param cb The callback to execute at the end of each test + * case. + * @param cb_arg An optional argument that gets passed to the + * callback. + */ +void +tu_suite_set_post_test_cb(tu_post_test_fn_t *cb, void *cb_arg) +{ + tu_case_post_test_cb = cb; + tu_case_post_test_cb_arg = cb_arg; +} + +void +tu_suite_complete(void) +{ + tu_suite_set_post_test_cb(NULL, NULL); +} + void tu_suite_init(const char *name) { diff --git a/libs/testutil/src/testutil_priv.h b/libs/testutil/src/testutil_priv.h index 0047874e..3f8cb2dc 100644 --- a/libs/testutil/src/testutil_priv.h +++ b/libs/testutil/src/testutil_priv.h @@ -23,7 +23,12 @@ #include <stddef.h> #include <inttypes.h> +#include "testutil/testutil.h" + void tu_arch_restart(void); void tu_case_abort(void); +extern tu_post_test_fn_t *tu_case_post_test_cb; +extern void *tu_case_post_test_cb_arg; + #endif diff --git a/libs/util/include/util/mem.h b/libs/util/include/util/mem.h new file mode 100644 index 00000000..52ddce4d --- /dev/null +++ b/libs/util/include/util/mem.h @@ -0,0 +1,38 @@ +/** + * 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. + */ + +#ifndef H_UTIL_MEM_ +#define H_UTIL_MEM_ + +struct os_mempool; +struct os_mbuf_pool; + +int mem_malloc_mempool(struct os_mempool *mempool, int num_blocks, + int block_size, char *name, void **out_buf); + +int mem_malloc_mbuf_pool(struct os_mempool *mempool, + struct os_mbuf_pool *mbuf_pool, int num_blocks, + int block_size, char *name, + void **out_buf); +int mem_malloc_mbufpkt_pool(struct os_mempool *mempool, + struct os_mbuf_pool *mbuf_pool, int num_blocks, + int block_size, char *name, + void **out_buf); + +#endif diff --git a/libs/util/src/mem.c b/libs/util/src/mem.c new file mode 100644 index 00000000..faf82345 --- /dev/null +++ b/libs/util/src/mem.c @@ -0,0 +1,146 @@ +/** + * 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 "os/os.h" + +/** + * Mallocs a block of memory and initializes a mempool to use it. + * + * @param mempool The mempool to initialize. + * @param num_blocks The total number of memory blocks in the + * mempool. + * @param block_size The size of each mempool entry. + * @param name The name to give the mempool. + * @param out_buf On success, this points to the malloced memory. + * Pass NULL if you don't need this + * information. + * + * @return 0 on success; + * OS_ENOMEM on malloc failure; + * Other OS code on unexpected error. + */ +int +mem_malloc_mempool(struct os_mempool *mempool, int num_blocks, int block_size, + char *name, void **out_buf) +{ + void *buf; + int rc; + + block_size = OS_ALIGN(block_size, OS_ALIGNMENT); + + if (num_blocks > 0) { + buf = malloc(OS_MEMPOOL_BYTES(num_blocks, block_size)); + if (buf == NULL) { + return OS_ENOMEM; + } + } else { + buf = NULL; + } + + rc = os_mempool_init(mempool, num_blocks, block_size, buf, name); + if (rc != 0) { + free(buf); + return rc; + } + + if (out_buf != NULL) { + *out_buf = buf; + } + + return 0; +} + +/** + * Mallocs a block of memory and initializes an mbuf pool to use it. The + * specified block_size indicates the size of an mbuf acquired from the pool if + * it does not contain a pkthdr. + * + * @param mempool The mempool to initialize. + * @param mbuf_pool The mbuf pool to initialize. + * @param num_blocks The total number of mbufs in the pool. + * @param block_size The size of each mbuf. + * @param name The name to give the mempool. + * @param out_buf On success, this points to the malloced memory. + * Pass NULL if you don't need this + * information. + * + * @return 0 on success; + * OS_ENOMEM on malloc failure; + * Other OS code on unexpected error. + */ +int +mem_malloc_mbuf_pool(struct os_mempool *mempool, + struct os_mbuf_pool *mbuf_pool, int num_blocks, + int block_size, char *name, + void **out_buf) +{ + void *buf; + int rc; + + block_size = OS_ALIGN(block_size + sizeof (struct os_mbuf), OS_ALIGNMENT); + + rc = mem_malloc_mempool(mempool, num_blocks, block_size, name, &buf); + if (rc != 0) { + return rc; + } + + rc = os_mbuf_pool_init(mbuf_pool, mempool, block_size, num_blocks); + if (rc != 0) { + free(buf); + return rc; + } + + if (out_buf != NULL) { + *out_buf = buf; + } + + return 0; +} + +/** + * Mallocs a block of memory and initializes an mbuf pool to use it. The + * specified block_size indicates the size of an mbuf acquired from the pool if + * it contains a pkthdr. + * + * @param mempool The mempool to initialize. + * @param mbuf_pool The mbuf pool to initialize. + * @param num_blocks The total number of mbufs in the pool. + * @param block_size The size of each mbuf. + * @param name The name to give the mempool. + * @param out_buf On success, this points to the malloced memory. + * Pass NULL if you don't need this + * information. + * + * @return 0 on success; + * OS_ENOMEM on malloc failure; + * Other OS code on unexpected error. + */ +int +mem_malloc_mbufpkt_pool(struct os_mempool *mempool, + struct os_mbuf_pool *mbuf_pool, int num_blocks, + int block_size, char *name, + void **out_buf) +{ + int rc; + + rc = mem_malloc_mbuf_pool(mempool, mbuf_pool, num_blocks, + block_size + sizeof (struct os_mbuf_pkthdr), + name, out_buf); + return rc; +} diff --git a/libs/wifi_mgmt/include/wifi_mgmt/wifi_mgmt.h b/libs/wifi_mgmt/include/wifi_mgmt/wifi_mgmt.h new file mode 100644 index 00000000..411921a8 --- /dev/null +++ b/libs/wifi_mgmt/include/wifi_mgmt/wifi_mgmt.h @@ -0,0 +1,82 @@ +/** + * 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. + */ +#ifndef __WIFI_MGMT_H__ +#define __WIFI_MGMT_H__ + +/* + * Wi-Fi interface abstraction. + */ +#define WIFI_SSID_MAX 32 /* max SSID name length */ +#define WIFI_BSSID_LEN 6 /* MAC address len */ +#define WIFI_SCAN_CNT_MAX 20 +#define WIFI_KEY_MAX 64 /* max key length */ +#define WIFI_SSID_EMPTY(ssid) (ssid)[0] == '\0' + +/* + * Info about an access point. + */ +struct wifi_ap { + char wa_ssid[WIFI_SSID_MAX + 1]; + char wa_bssid[WIFI_BSSID_LEN + 1]; + int8_t wa_rssi; + uint8_t wa_key_type; + uint8_t wa_channel; +}; + +struct wifi_if_ops; + +/* + * Wifi interface + */ +struct wifi_if { + enum { + STOPPED = 0, + INIT, + CONNECTING, + DHCP_WAIT, + CONNECTED, + SCANNING + } wi_state, wi_tgt; + struct os_mutex wi_mtx; + struct os_event wi_event; + struct os_callout_func wi_timer; + const struct wifi_if_ops *wi_ops; + + uint8_t wi_scan_cnt; + struct wifi_ap wi_scan[WIFI_SCAN_CNT_MAX]; + char wi_ssid[WIFI_SSID_MAX + 1]; + char wi_key[WIFI_KEY_MAX + 1]; + uint8_t wi_myip[4]; +}; + +/* + * XXX. is wifi_if_lookup() needed? It is unlikely that there's going to be + * multiple wifi interfaces on a system. + */ +struct wifi_if *wifi_if_lookup(int port); +int wifi_if_register(struct wifi_if *, const struct wifi_if_ops *); + +int wifi_start(struct wifi_if *); +int wifi_connect(struct wifi_if *); +int wifi_stop(struct wifi_if *w); +int wifi_scan_start(struct wifi_if *w); + +int wifi_task_init(uint8_t prio, os_stack_t *stack, uint16_t stack_size); + +#endif /* __WIFI_MGMT_H__ */ diff --git a/libs/wifi_mgmt/include/wifi_mgmt/wifi_mgmt_if.h b/libs/wifi_mgmt/include/wifi_mgmt/wifi_mgmt_if.h new file mode 100644 index 00000000..c6e432da --- /dev/null +++ b/libs/wifi_mgmt/include/wifi_mgmt/wifi_mgmt_if.h @@ -0,0 +1,47 @@ +/** + * 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. + */ +#ifndef __WIFI_MGMT_IF_H__ +#define __WIFI_MGMT_IF_H__ + +/* + * Interface between Wi-fi management and the driver. + */ +struct wifi_if_ops { + int (*wio_init)(struct wifi_if *); + void (*wio_deinit)(struct wifi_if *); + int (*wio_scan_start)(struct wifi_if *); + int (*wio_connect)(struct wifi_if *, struct wifi_ap *); + void (*wio_disconnect)(struct wifi_if *); +}; + +/* + * Exported so driver can use this for it's timers. + */ +extern struct os_eventq wifi_evq; + +/* + * Called by the Wi-fi driver. + */ +void wifi_scan_result(struct wifi_if *, struct wifi_ap *); +void wifi_scan_done(struct wifi_if *, int status); +void wifi_connect_done(struct wifi_if *wi, int status); +void wifi_disconnected(struct wifi_if *wi, int status); +void wifi_dhcp_done(struct wifi_if *wi, uint8_t *ip); /* XXX more IP info */ + +#endif /* __WIFI_MGMT_IF_H__ */ diff --git a/libs/wifi_mgmt/pkg.yml b/libs/wifi_mgmt/pkg.yml new file mode 100644 index 00000000..ebe582c5 --- /dev/null +++ b/libs/wifi_mgmt/pkg.yml @@ -0,0 +1,35 @@ +# +# 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: libs/wifi_mgmt +pkg.description: Wifi state management +pkg.author: "Apache Mynewt <dev@mynewt.incubator.apache.org>" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: +pkg.deps: + - "@apache-mynewt-core/libs/os" + - "@apache-mynewt-core/libs/util" +pkg.reqs: + - console +pkg.cflags.SHELL: + - -DSHELL_PRESENT + +pkg.deps.TEST: + - libs/testutil + diff --git a/libs/wifi_mgmt/src/wifi.c b/libs/wifi_mgmt/src/wifi.c new file mode 100644 index 00000000..b5b6a72a --- /dev/null +++ b/libs/wifi_mgmt/src/wifi.c @@ -0,0 +1,348 @@ +/** + * 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 <stddef.h> +#include <string.h> +#include <assert.h> + +#include <os/os.h> +#include <bsp/bsp.h> +#include <hal/hal_gpio.h> +#include <shell/shell.h> +#include <console/console.h> + +#include "wifi_mgmt/wifi_mgmt.h" +#include "wifi_mgmt/wifi_mgmt_if.h" +#include "wifi_priv.h" + +#define WIFI_EV_STATE OS_EVENT_T_PERUSER + +static struct os_task wifi_os_task; +struct os_eventq wifi_evq; + +static struct wifi_ap *wifi_find_ap(struct wifi_if *wi, char *ssid); +static void wifi_events(void *arg); + +static struct wifi_if *wifi_if; + +/* + * Looks up interface based on port number. + */ +struct wifi_if * +wifi_if_lookup(int port) +{ + assert(port == 0); + return wifi_if; +} + +/* + * Called by wi-fi driver to register itself. + */ +int +wifi_if_register(struct wifi_if *wi, const struct wifi_if_ops *ops) +{ + if (wifi_if) { + return -1; + } + wifi_if = wi; + + wi->wi_ops = ops; + os_mutex_init(&wi->wi_mtx); + os_callout_func_init(&wi->wi_timer, &wifi_evq, wifi_events, wi); + wi->wi_event.ev_type = WIFI_EV_STATE; + wi->wi_event.ev_arg = wi; + + return 0; +} + +/* + * For Wi-fi mgmt state machine, set the target state, and queue an + * event to do the state transition in wifi task context. + */ +static void +wifi_tgt_state(struct wifi_if *wi, int state) +{ + wi->wi_tgt = state; + os_eventq_put(&wifi_evq, &wi->wi_event); +} + +/* + * Wi-fi driver reports a response to wifi scan request. + * Driver reports networks one at a time. + */ +void +wifi_scan_result(struct wifi_if *wi, struct wifi_ap *ap) +{ + if (wi->wi_scan_cnt == WIFI_SCAN_CNT_MAX) { + return; + } + wi->wi_scan[wi->wi_scan_cnt++] = *ap; +} + +/* + * Wifi driver reports that scan is finished. + */ +void +wifi_scan_done(struct wifi_if *wi, int status) +{ + struct wifi_ap *ap = NULL; + + console_printf("scan_results %d: %d\n", wi->wi_scan_cnt, status); + if (status) { + wifi_tgt_state(wi, STOPPED); + return; + } + + /* + * XXX decide what to do with scan results here. + */ + if (!WIFI_SSID_EMPTY(wi->wi_ssid)) { + ap = wifi_find_ap(wi, wi->wi_ssid); + } + if (ap) { + wifi_tgt_state(wi, CONNECTING); + } else { + wifi_tgt_state(wi, INIT); + } +} + +/* + * Wi-fi driver reports whether it was successful in establishing connection + * to an AP. XXX need status codes in wifi_mgmt.h + */ +void +wifi_connect_done(struct wifi_if *wi, int status) +{ + console_printf("connect_done : %d\n", status); + if (status) { + wifi_tgt_state(wi, INIT); + return; + } + wifi_tgt_state(wi, DHCP_WAIT); +} + +/* + * Wi-fi driver reports that there's an IP address. We can start using + * this interface. + */ +void +wifi_dhcp_done(struct wifi_if *wi, uint8_t *ip) +{ + console_printf("dhcp done %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); + wifi_tgt_state(wi, CONNECTED); +} + +/* + * Wi-fi driver reports that we are disconnected from an AP. + */ +void +wifi_disconnected(struct wifi_if *wi, int status) +{ + console_printf("disconnect : %d\n", status); + wifi_tgt_state(wi, INIT); +} + +static struct wifi_ap * +wifi_find_ap(struct wifi_if *wi, char *ssid) +{ + int i; + + for (i = 0; i < wi->wi_scan_cnt; i++) { + if (!strcmp(wi->wi_scan[i].wa_ssid, ssid)) { + return &wi->wi_scan[i]; + } + } + return NULL; +} + +static void +wifi_events(void *arg) +{ + /* + * Expire connection attempts. Periodic scanning if tgt AP not visible. + */ +} + +/* + * Called by user to start bringing up Wi-fi interface online. + */ +int +wifi_start(struct wifi_if *wi) +{ + if (wi->wi_state != STOPPED) { + return -1; + } + wifi_tgt_state(wi, INIT); + return 0; +} + +/* + * Called by user to stop Wi-fi interface. + */ +int +wifi_stop(struct wifi_if *wi) +{ + wifi_tgt_state(wi, STOPPED); + return 0; +} + +/* + * Called by user to connect Wi-fi interface to AP. Will fail if no + * SSID name is set. + */ +int +wifi_connect(struct wifi_if *wi) +{ + switch (wi->wi_state) { + case STOPPED: + return -1; + case INIT: + if (WIFI_SSID_EMPTY(wi->wi_ssid)) { + return -1; + } + wifi_tgt_state(wi, CONNECTING); + return 0; + default: + return -1; + } + return 0; +} + +/* + * From user to initiate Wi-Fi scan. + */ +int +wifi_scan_start(struct wifi_if *wi) +{ + if (wi->wi_state == INIT) { + wifi_tgt_state(wi, SCANNING); + return 0; + } + return -1; +} + +/* + * Wi-fi mgmt state machine. + */ +static void +wifi_step(struct wifi_if *wi) +{ + int rc; + struct wifi_ap *ap; + + switch (wi->wi_tgt) { + case STOPPED: + if (wi->wi_state != STOPPED) { + if (wi->wi_state >= CONNECTING) { + wi->wi_ops->wio_disconnect(wi); + } + wi->wi_ops->wio_deinit(wi); + wi->wi_state = STOPPED; + } + break; + case INIT: + if (wi->wi_state == STOPPED) { + rc = wi->wi_ops->wio_init(wi); + console_printf("wifi_init : %d\n", rc); + if (!rc) { + wi->wi_state = INIT; + } + } else if (wi->wi_state == SCANNING) { + wi->wi_state = wi->wi_tgt; + } else if (wi->wi_state == CONNECTING) { + wi->wi_state = wi->wi_tgt; + } + break; + case SCANNING: + if (wi->wi_state == INIT) { + memset(wi->wi_scan, 0, sizeof(wi->wi_scan)); + rc = wi->wi_ops->wio_scan_start(wi); + console_printf("wifi_request_scan : %d\n", rc); + if (rc != 0) { + break; + } + wi->wi_state = SCANNING; + } else { + wi->wi_tgt = wi->wi_state; + } + break; + case CONNECTING: + if (wi->wi_state == INIT || wi->wi_state == SCANNING) { + ap = wifi_find_ap(wi, wi->wi_ssid); + if (!ap) { + wifi_tgt_state(wi, SCANNING); + break; + } + rc = wi->wi_ops->wio_connect(wi, ap); + console_printf("wifi_connect : %d\n", rc); + if (rc == 0) { + wi->wi_state = CONNECTING; + } else { + wi->wi_tgt = STOPPED; + } + } + break; + case DHCP_WAIT: + wi->wi_state = wi->wi_tgt; + break; + case CONNECTED: + wi->wi_state = wi->wi_tgt; + break; + default: + console_printf("wifi_step() unknown tgt : %d\n", wi->wi_tgt); + wi->wi_state = wi->wi_tgt; + break; + } +} + +static void +wifi_task(void *arg) +{ + struct os_event *ev; + struct os_callout_func *cf; + struct wifi_if *wi; + + while ((ev = os_eventq_get(&wifi_evq))) { + switch (ev->ev_type) { + case OS_EVENT_T_TIMER: + cf = (struct os_callout_func *)ev; + cf->cf_func(CF_ARG(cf)); + break; + case WIFI_EV_STATE: + wi = (struct wifi_if *)ev->ev_arg; + while (wi->wi_state != wi->wi_tgt) { + wifi_step(wi); + } + break; + default: + break; + } + } +} + +int +wifi_task_init(uint8_t prio, os_stack_t *stack, uint16_t stack_size) +{ +#ifdef SHELL_PRESENT + shell_cmd_register(&wifi_cli_cmd); +#endif + os_eventq_init(&wifi_evq); + + return os_task_init(&wifi_os_task, "wifi", wifi_task, NULL, + prio, OS_WAIT_FOREVER, stack, stack_size); +} diff --git a/libs/wifi_mgmt/src/wifi_cli.c b/libs/wifi_mgmt/src/wifi_cli.c new file mode 100644 index 00000000..4cd95529 --- /dev/null +++ b/libs/wifi_mgmt/src/wifi_cli.c @@ -0,0 +1,89 @@ +/** + * 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. + */ +#ifdef SHELL_PRESENT +#include <stddef.h> +#include <string.h> +#include <assert.h> + +#include <shell/shell.h> +#include <console/console.h> + +#include "wifi_mgmt/wifi_mgmt.h" + +#include "wifi_priv.h" + +static int +wifi_cli(int argc, char **argv) +{ + struct wifi_if *wi; + int i; + + if (argc < 1) { + return 0; + } + wi = wifi_if_lookup(0); + + if (!strcmp(argv[1], "start")) { + wifi_start(wi); + } else if (!strcmp(argv[1], "stop")) { + wifi_stop(wi); + } else if (!strcmp(argv[1], "scan")) { + wifi_scan_start(wi); + } else if (!strcmp(argv[1], "aps")) { + int i; + struct wifi_ap *ap; + + console_printf(" %32s %4s %4s %s\n", "SSID", "RSSI", "chan", "sec"); + for (i = 0; i < wi->wi_scan_cnt; i++) { + ap = (struct wifi_ap *)&wi->wi_scan[i]; + console_printf("%2d:%32s %4d %4d %s\n", + i, ap->wa_ssid, ap->wa_rssi, ap->wa_channel, + ap->wa_key_type ? "X" : ""); + } + } else if (!strcmp(argv[1], "connect")) { + if (argc < 2) { + goto conn_usage; + } + i = strlen(argv[2]); + if (i >= sizeof(wi->wi_ssid)) { + goto conn_usage; + } + if (argc > 2) { + i = strlen(argv[2]); + if (i >= sizeof(wi->wi_key)) { + goto conn_usage; + } + strcpy(wi->wi_key, argv[3]); + } + strcpy(wi->wi_ssid, argv[2]); + if (wifi_connect(wi)) { +conn_usage: + console_printf("%s %s [<ssid> [<key>]]\n", + argv[0], argv[1]); + } + } + return 0; +} + +struct shell_cmd wifi_cli_cmd = { + .sc_cmd = "wifi", + .sc_cmd_func = wifi_cli +}; + +#endif diff --git a/libs/wifi_mgmt/src/wifi_priv.h b/libs/wifi_mgmt/src/wifi_priv.h new file mode 100644 index 00000000..dfba81da --- /dev/null +++ b/libs/wifi_mgmt/src/wifi_priv.h @@ -0,0 +1,27 @@ +/** + * 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. + */ + +#ifndef __WIFI_PRIV_H__ +#define __WIFI_PRIV_H__ + +#ifdef SHELL_PRESENT +extern struct shell_cmd wifi_cli_cmd; +#endif + +#endif /* __WIFI_PRIV_H__ */ |