blob: 58942725dea26b5528f652bc7afdd2a20bb53954 [file] [log] [blame]
#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, &notify_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, &notification_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, &notification_event, fota_request_id);
uloop_run();
ubus_unsubscribe(fota_ubus_ctx, &notification_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;
}