| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <pthread.h> |
| #include <libubox/blobmsg_json.h> |
| #include "libubus.h" |
| #include "ql/ql_fota.h" |
| |
| // #define DEBUG 1 |
| |
| #ifdef DEBUG |
| #define fota_log(...) printf(__VA_ARGS__) |
| #else |
| #define fota_log(...) |
| #endif |
| |
| #define UNUSEDPARAM(param) (void)param; |
| #define OTA_MAX_STRING_LEN 128 |
| |
| static struct ubus_context *fota_ubus_ctx = NULL; |
| static uint32_t fota_request_id; |
| static struct ubus_subscriber notification_event; |
| static pthread_t fota_status_pthread; |
| static struct blob_buf b; |
| static fota_callback fota_cb = NULL; |
| |
| enum { |
| ATTR_SMSGPS, |
| ATTR_SMSGPS_MAX, |
| }; |
| |
| static const struct blobmsg_policy ota_info_attr_policy[] = { |
| [ATTR_SMSGPS] ={.name = "notification", .type = BLOBMSG_TYPE_STRING,}, |
| }; |
| |
| /** |
| * \brief strstr_n |
| * |
| * find string return number |
| * |
| * \param param |
| * \return return type |
| */ |
| static int strstr_n(const char *s1, const char *s2) |
| { |
| int n; |
| int strlen = 0; |
| |
| if(*s2) { |
| while(*s1) { |
| for(n = 0; *(s1+n) == *(s2 + n); n++) { |
| if(!*(s2 + n + 1)) { |
| strlen++; |
| return strlen; |
| } |
| } |
| s1++; |
| strlen++; |
| } |
| return 0; |
| } |
| else |
| return 0; |
| } |
| |
| static int otad_notify(struct ubus_context* ctx, struct ubus_object* obj, |
| struct ubus_request_data* req, const char* method, |
| struct blob_attr* msg) |
| { |
| UNUSEDPARAM(ctx); |
| UNUSEDPARAM(obj); |
| UNUSEDPARAM(req); |
| UNUSEDPARAM(method); |
| // User can get downloading process information from here |
| int ret, len; |
| char progress[4] = {0}; |
| char *notify_str; |
| |
| struct blob_attr *tb[ATTR_SMSGPS_MAX]; |
| ret = blobmsg_parse(ota_info_attr_policy, ARRAY_SIZE(ota_info_attr_policy), tb, |
| blob_data(msg), blob_len(msg)); |
| |
| if (ret || !tb[ATTR_SMSGPS]) { |
| printf("invalid SMS\n"); |
| return -1; |
| } |
| |
| notify_str = blobmsg_get_string(tb[ATTR_SMSGPS]); |
| len = strlen(notify_str); |
| fota_log("%s : %s\r\n", __FUNCTION__, notify_str); |
| if (strstr_n(notify_str, "start")) { |
| fota_cb(0, 0); |
| } else if (strstr_n(notify_str, "end[1]")) { |
| fota_cb(0, 100); |
| printf("download firmware success!\r\n"); |
| } else if (strstr_n(notify_str, "end[0]")) { |
| fota_cb(1, 100); |
| } |
| ret = strstr_n(notify_str, "progress"); |
| if (ret) { |
| memcpy(progress, ¬ify_str[ret + 8], len - ret - 9); |
| fota_cb(0, atoi(progress)); |
| } |
| return 0; |
| } |
| |
| static void otad_subscriber_remove_cb(struct ubus_context* ctx, |
| struct ubus_subscriber* obj, uint32_t id) |
| { |
| UNUSEDPARAM(ctx); |
| UNUSEDPARAM(obj); |
| UNUSEDPARAM(id); |
| fota_log("%s,%d\n", __FUNCTION__, __LINE__); |
| } |
| |
| |
| /*******************************************************************************\ |
| * Function: main |
| \*******************************************************************************/ |
| void* fota_main(void* argc) |
| { |
| int ret, retries = 0; |
| UNUSEDPARAM(argc); |
| |
| //register for ril indication |
| ret = ubus_register_subscriber(fota_ubus_ctx, ¬ification_event); |
| if (ret) { |
| fota_log("%s,%d\n", __FUNCTION__, __LINE__); |
| pthread_exit(NULL); |
| } |
| notification_event.cb = otad_notify; |
| notification_event.remove_cb = otad_subscriber_remove_cb; |
| |
| ubus_subscribe(fota_ubus_ctx, ¬ification_event, fota_request_id); |
| uloop_run(); |
| ubus_unsubscribe(fota_ubus_ctx, ¬ification_event, fota_request_id); |
| pthread_exit(NULL); |
| |
| return NULL; |
| } |
| |
| int ql_fota_init(fota_callback cb) |
| { |
| int id; |
| int retries = 0; |
| |
| /*create ubus loop to listen to RIL event*/ |
| uloop_init(); |
| fota_ubus_ctx = ubus_connect(NULL); |
| if (!fota_ubus_ctx) { |
| fota_log("%s,%d\n", __FUNCTION__, __LINE__); |
| uloop_done(); |
| return 0; |
| } |
| |
| ubus_add_uloop(fota_ubus_ctx); |
| |
| do { |
| //register for ril request |
| retries = 0; |
| if (ubus_lookup_id(fota_ubus_ctx, "ota", &fota_request_id)) { |
| fota_log("%s,%d\n", __FUNCTION__, __LINE__); |
| sleep(1); |
| } else { |
| break; |
| } |
| } while (retries++ < 20); |
| if (retries >= 20) { |
| printf("%s,%d\n", __FUNCTION__, __LINE__); |
| goto fail1; |
| } |
| pthread_create(&fota_status_pthread, NULL, (void*)fota_main, NULL); |
| fota_cb = cb; |
| fail1: |
| return 0; |
| } |
| |
| static void sync_prog_cb(struct ubus_request *req, int type, struct blob_attr *msg) |
| { |
| char *str; |
| if (!msg) |
| return; |
| |
| /* |
| 在这里处理返回的消息。 |
| 本例子只是将返回的消息打印出来。 |
| */ |
| str = blobmsg_format_json_indent(msg, true, 0); |
| printf("%s\n", str); |
| |
| if (strstr_n(str, "end[1]")) { |
| fota_cb(0, 100); |
| printf("download firmware success!\r\n"); |
| } else if (strstr_n(str, "end[0]") || strstr_n(str, "failed")) { |
| fota_cb(1, 0); |
| } |
| free(str); |
| } |
| /******************************************************************************* |
| * @brief write firmware package, the firmware package is written in segments. |
| and The result of the write is output by calling the callback function. |
| the firmware package size must less than 32MB |
| @param |
| fname: firmware package file |
| segment_size: the length of once write, recommending 3*1024*1024 bytes |
| @return |
| if success return 0, else return -1 |
| *******************************************************************************/ |
| int ql_fota_fw_write(char* fname, int segment_size) |
| { |
| static struct ubus_request req; |
| int _segment_size; |
| blob_buf_init(&b, 0); |
| blobmsg_add_string(&b, "url", fname); |
| blobmsg_add_u32(&b, "type", 2); |
| blobmsg_add_u32(&b, "sync", 1); |
| _segment_size = segment_size; |
| if (_segment_size > 1024) { |
| blobmsg_add_u32(&b, "segment_size", _segment_size); |
| } |
| // blobmsg_add_u32(&b, "segment_size", segment_size); |
| /* 调用"ota"对象的"download"方法 */ |
| ubus_invoke(fota_ubus_ctx, fota_request_id, |
| "download", b.head, sync_prog_cb, NULL, 30 * 1000); |
| |
| return 0; |
| } |
| |
| |
| /******************************************************************************* |
| * @brief download firmware by url, and write firmware package, the firmware |
| package is written in segments. The result of the write is output by |
| calling the callback function. the firmware package size must less than |
| 32MB |
| @param |
| url: [IN] the address of download firmware package file, the url |
| support http or https protocol. |
| segment_size: [IN] the length of once write, recommending 3*1024*1024 bytes |
| conn_timeout: [IN] timeout to connect to the server, if set 0 that means |
| switch to the default build-in connection timeout(300s) |
| download_timeout: [IN] timeout for download the firmware file. if set 0 that means |
| it never time out |
| @return |
| if success return 0, else return -1 |
| *******************************************************************************/ |
| int ql_fota_fw_write_by_url(char* url, int segment_size, |
| int conn_timeout, int download_timeout) |
| { |
| static struct ubus_request req; |
| int _segment_size; |
| blob_buf_init(&b, 0); |
| blobmsg_add_string(&b, "url", url); |
| blobmsg_add_string(&b, "username", "user name"); |
| blobmsg_add_u32(&b, "type", 0); |
| blobmsg_add_u32(&b, "sync", 1); |
| _segment_size = segment_size; |
| if (_segment_size > 1024) { |
| blobmsg_add_u32(&b, "segment_size", _segment_size); |
| } |
| blobmsg_add_u32(&b, "sync", 1); |
| ubus_invoke_async(fota_ubus_ctx, fota_request_id, "download", b.head, &req); |
| ubus_complete_request_async(fota_ubus_ctx, &req); |
| return 0; |
| } |
| /******************************************************************************* |
| * @brief reboot system and clear env |
| @param |
| is_reboot: if set 1, after fota success, reboot system |
| @return |
| if success return 0, else return -1 |
| *******************************************************************************/ |
| int ql_fota_done(int is_reboot) |
| { |
| int ret; |
| |
| ret = pthread_cancel(fota_status_pthread); |
| fota_log("kill pthread : %d \n", ret); |
| pthread_join(fota_status_pthread, NULL); |
| do { |
| ret = pthread_kill(fota_status_pthread, 0); |
| fota_log("kill pthread: %d \n", ret); |
| if (ret == ESRCH) { |
| fota_log("The specified thread does not exist or has terminated\n"); |
| } else if (ret == EINVAL) { |
| fota_log("Useless signal\n"); |
| } else { |
| fota_log("The thread exists\n"); |
| } |
| usleep(100000); |
| } while (0 == ret); |
| |
| |
| ubus_free(fota_ubus_ctx); |
| uloop_done(); |
| |
| fota_cb = NULL; |
| |
| if (is_reboot) { |
| system("reboot"); |
| } |
| return 0; |
| } |