diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2016-11-30 12:05:35 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2017-01-16 11:24:13 +0100 |
commit | bcb36f89ba64feb94912f3d26a62ced54787333e (patch) | |
tree | d3693831dd32479fe4b0c6ceacac95933d2c5d86 | |
parent | 04f2e44d42aa3d17b97e9d670eb546533b42766b (diff) | |
download | external_libqmi-bcb36f89ba64feb94912f3d26a62ced54787333e.zip external_libqmi-bcb36f89ba64feb94912f3d26a62ced54787333e.tar.gz external_libqmi-bcb36f89ba64feb94912f3d26a62ced54787333e.tar.bz2 |
qmi-firmware-update: setup QfuQdlMessage builder/parsers
-rw-r--r-- | src/qmi-firmware-update/Makefile.am | 4 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-qdl-message.c | 548 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-qdl-message.h | 102 |
3 files changed, 653 insertions, 1 deletions
diff --git a/src/qmi-firmware-update/Makefile.am b/src/qmi-firmware-update/Makefile.am index 7ee8047..1c194ae 100644 --- a/src/qmi-firmware-update/Makefile.am +++ b/src/qmi-firmware-update/Makefile.am @@ -13,6 +13,7 @@ qmi_firmware_update_CPPFLAGS = \ ENUMS = \ qfu-image.h \ + qfu-qdl-message.h \ $(NULL) ENUMS_GENERATED = \ @@ -21,7 +22,7 @@ ENUMS_GENERATED = \ qfu-enum-types.h: Makefile.am $(ENUMS) $(top_srcdir)/build-aux/templates/qmi-enum-types-template.h $(AM_V_GEN) $(GLIB_MKENUMS) \ - --fhead "#ifndef QFU_ENUM_TYPES_H\n#define QFU_ENUM_TYPES_H\n#include \"qfu-image.h\"\n" \ + --fhead "#ifndef QFU_ENUM_TYPES_H\n#define QFU_ENUM_TYPES_H\n#include \"qfu-image.h\"\n#include \"qfu-qdl-message.h\"\n" \ --template $(top_srcdir)/build-aux/templates/qmi-enum-types-template.h \ --ftail "#endif /* __QFUENUM_TYPES_H__ */\n" \ $(ENUMS) > $@ @@ -47,6 +48,7 @@ qmi_firmware_update_SOURCES = \ qfu-image.h qfu-image.c \ qfu-image-cwe.h qfu-image-cwe.c \ qfu-image-factory.h qfu-image-factory.c \ + qfu-qdl-message.h qfu-qdl-message.c \ qfu-utils.h qfu-utils.c \ $(NULL) diff --git a/src/qmi-firmware-update/qfu-qdl-message.c b/src/qmi-firmware-update/qfu-qdl-message.c new file mode 100644 index 0000000..9650ddb --- /dev/null +++ b/src/qmi-firmware-update/qfu-qdl-message.c @@ -0,0 +1,548 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * qmi-firmware-update -- Command line tool to update firmware in QMI devices + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright (C) 2016 Bjørn Mork <bjorn@mork.no> + * Copyright (C) 2016 Zodiac Inflight Innovations + * Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es> + */ + +#include <string.h> + +#include <glib-object.h> +#include <gio/gio.h> + +#include "qfu-qdl-message.h" +#include "qfu-utils.h" +#include "qfu-enum-types.h" + +/******************************************************************************/ +/* QDL generic */ + +/* Generic message for operations that just require the command id */ +typedef struct _QdlMsg QdlMsg; +struct _QdlMsg { + guint8 cmd; +} __attribute__ ((packed)); + +G_STATIC_ASSERT (sizeof (QdlMsg) <= QFU_QDL_MESSAGE_MAX_HEADER_SIZE); + +static gsize +qdl_message_generic_build (guint8 *buffer, + gsize buffer_len, + QfuQdlCmd cmd) +{ + QdlMsg *req; + + g_assert (buffer_len >= sizeof (QdlMsg)); + + /* Create request */ + req = (QdlMsg *) buffer; + req->cmd = (guint8) cmd; + + g_debug ("[qfu,qdl-message] sent %s:", qfu_qdl_cmd_get_string ((QfuQdlCmd) req->cmd)); + + return (sizeof (QdlMsg)); +} + +/******************************************************************************/ +/* QDL Hello */ + +/* feature bits */ +#define QDL_FEATURE_GENERIC_UNFRAMED 0x10 +#define QDL_FEATURE_QDL_UNFRAMED 0x20 +#define QDL_FEATURE_BAR_MODE 0x40 + +typedef struct _QdlHelloReq QdlHelloReq; +struct _QdlHelloReq { + guint8 cmd; /* 0x01 */ + gchar magic[32]; + guint8 maxver; + guint8 minver; + guint8 features; +} __attribute__ ((packed)); + +G_STATIC_ASSERT (sizeof (QdlHelloReq) <= QFU_QDL_MESSAGE_MAX_HEADER_SIZE); + +gsize +qfu_qdl_request_hello_build (guint8 *buffer, + gsize buffer_len, + guint8 minver, + guint8 maxver) +{ + static const QdlHelloReq common_hello_req = { + .cmd = QFU_QDL_CMD_HELLO_REQ, + .magic = { "QCOM high speed protocol hst" }, + .maxver = 0, + .minver = 0, + .features = QDL_FEATURE_QDL_UNFRAMED | QDL_FEATURE_GENERIC_UNFRAMED, + }; + QdlHelloReq *req; + + g_assert (buffer_len >= sizeof (QdlHelloReq)); + + /* Create request */ + req = (QdlHelloReq *) buffer; + memcpy (req, &common_hello_req, sizeof (QdlHelloReq)); + req->minver = minver; + req->maxver = maxver; + + g_debug ("[qfu,qdl-message] sent %s:", qfu_qdl_cmd_get_string ((QfuQdlCmd) req->cmd)); + g_debug ("[qfu,qdl-message] magic: %.*s", req->maxver <= 5 ? 24 : 32, req->magic); + g_debug ("[qfu,qdl-message] maximum version: %u", req->maxver); + g_debug ("[qfu,qdl-message] minimum version: %u", req->minver); + g_debug ("[qfu,qdl-message] features: 0x%02x", req->features); + + return (sizeof (QdlHelloReq)); +} + +typedef struct _QdlHelloRsp QdlHelloRsp; +struct _QdlHelloRsp { + guint8 cmd; /* 0x02 */ + gchar magic[32]; + guint8 maxver; + guint8 minver; + guint32 reserved1; + guint32 reserved2; + guint8 reserved3; + guint16 reserved4; + guint16 reserved5; + guint8 features; +} __attribute__ ((packed)); + +G_STATIC_ASSERT (sizeof (QdlHelloRsp) <= QFU_QDL_MESSAGE_MAX_HEADER_SIZE); + +gboolean +qfu_qdl_response_hello_parse (const guint8 *buffer, + gsize buffer_len, + GError **error) +{ + QdlHelloRsp *rsp; + + if (buffer_len != sizeof (QdlHelloRsp)) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "message size mismatch: %" G_GSIZE_FORMAT " != %" G_GSIZE_FORMAT, + buffer_len, sizeof (QdlHelloRsp)); + return FALSE; + } + + rsp = (QdlHelloRsp *) buffer; + g_assert (rsp->cmd == QFU_QDL_CMD_HELLO_RSP); + + g_debug ("[qfu,qdl-message] received %s:", qfu_qdl_cmd_get_string ((QfuQdlCmd) rsp->cmd)); + g_debug ("[qfu,qdl-message] magic: %.*s", rsp->maxver <= 5 ? 24 : 32, rsp->magic); + g_debug ("[qfu,qdl-message] maximum version: %u", rsp->maxver); + g_debug ("[qfu,qdl-message] minimum version: %u", rsp->minver); + g_debug ("[qfu,qdl-message] features: 0x%02x", rsp->features); + + /* For now, ignore fields */ + + return TRUE; +} + +/******************************************************************************/ +/* QDL Error */ + +typedef enum { + QDL_ERROR_NONE = 0x00, + QDL_ERROR_01_RESERVED = 0x01, + QDL_ERROR_BAD_ADDR = 0x02, + QDL_ERROR_BAD_LEN = 0x03, + QDL_ERROR_BAD_PACKET = 0x04, + QDL_ERROR_BAD_CMD = 0x05, + QDL_ERROR_06 = 0x06, + QDL_ERROR_OP_FAILED = 0x07, + QDL_ERROR_BAD_FLASH_ID = 0x08, + QDL_ERROR_BAD_VOLTAGE = 0x09, + QDL_ERROR_WRITE_FAILED = 0x0a, + QDL_ERROR_11_RESERVED = 0x0b, + QDL_ERROR_BAD_SPC = 0x0c, + QDL_ERROR_POWERDOWN = 0x0d, + QDL_ERROR_UNSUPPORTED = 0x0e, + QDL_ERROR_CMD_SEQ = 0x0f, + QDL_ERROR_CLOSE = 0x10, + QDL_ERROR_BAD_FEATURES = 0x11, + QDL_ERROR_SPACE = 0x12, + QDL_ERROR_BAD_SECURITY = 0x13, + QDL_ERROR_MULTI_UNSUPPORTED = 0x14, + QDL_ERROR_POWEROFF = 0x15, + QDL_ERROR_CMD_UNSUPPORTED = 0x16, + QDL_ERROR_BAD_CRC = 0x17, + QDL_ERROR_STATE = 0x18, + QDL_ERROR_TIMEOUT = 0x19, + QDL_ERROR_IMAGE_AUTH = 0x1a, + QDL_ERROR_LAST +} QdlError; + +static const gchar *qdl_error_str[] = { + [QDL_ERROR_NONE ] = "None", + [QDL_ERROR_01_RESERVED ] = "Reserved", + [QDL_ERROR_BAD_ADDR ] = "Invalid destination address", + [QDL_ERROR_BAD_LEN ] = "Invalid length", + [QDL_ERROR_BAD_PACKET ] = "Unexpected end of packet", + [QDL_ERROR_BAD_CMD ] = "Invalid command", + [QDL_ERROR_06 ] = "Reserved", + [QDL_ERROR_OP_FAILED ] = "Operation failed", + [QDL_ERROR_BAD_FLASH_ID ] = "Invalid flash intelligent ID", + [QDL_ERROR_BAD_VOLTAGE ] = "Invalid programming voltage", + [QDL_ERROR_WRITE_FAILED ] = "Write verify failed", + [QDL_ERROR_11_RESERVED ] = "Reserved", + [QDL_ERROR_BAD_SPC ] = "Invalid security code", + [QDL_ERROR_POWERDOWN ] = "Power-down failed", + [QDL_ERROR_UNSUPPORTED ] = "NAND flash programming not supported", + [QDL_ERROR_CMD_SEQ ] = "Command out of sequence", + [QDL_ERROR_CLOSE ] = "Close failed", + [QDL_ERROR_BAD_FEATURES ] = "Invalid feature bits", + [QDL_ERROR_SPACE ] = "Out of space", + [QDL_ERROR_BAD_SECURITY ] = "Invalid security mode", + [QDL_ERROR_MULTI_UNSUPPORTED ] = "Multi-image NAND not supported", + [QDL_ERROR_POWEROFF ] = "Power-off command not supported", + [QDL_ERROR_CMD_UNSUPPORTED ] = "Command not supported", + [QDL_ERROR_BAD_CRC ] = "Invalid CRC", + [QDL_ERROR_STATE ] = "Command received in invalid state", + [QDL_ERROR_TIMEOUT ] = "Receive timeout", + [QDL_ERROR_IMAGE_AUTH ] = "Image authentication error", +}; + +G_STATIC_ASSERT (G_N_ELEMENTS (qdl_error_str) == QDL_ERROR_LAST); + +static GIOErrorEnum +qdl_error_to_gio_error_enum (QdlError err) +{ + switch (err) { + case QDL_ERROR_CMD_UNSUPPORTED: + return G_IO_ERROR_NOT_SUPPORTED; + default: + return G_IO_ERROR_FAILED; + } +} + +static const gchar * +qdl_error_to_string (QdlError err) +{ + return (err < QDL_ERROR_LAST ? qdl_error_str[err] : "Unknown"); +} + +typedef struct _QdlErrRsp QdlErrRsp; +struct _QdlErrRsp { + guint8 cmd; /* 0x0d */ + guint32 error; + guint8 errortxt; +} __attribute__ ((packed)); + +G_STATIC_ASSERT (sizeof (QdlErrRsp) <= QFU_QDL_MESSAGE_MAX_HEADER_SIZE); + +gboolean +qfu_qdl_response_error_parse (const guint8 *buffer, + gsize buffer_len, + GError **error) +{ + QdlErrRsp *rsp; + + if (buffer_len != sizeof (QdlErrRsp)) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "message size mismatch: %" G_GSIZE_FORMAT " != %" G_GSIZE_FORMAT, + buffer_len, sizeof (QdlErrRsp)); + return FALSE; + } + + rsp = (QdlErrRsp *) buffer; + g_assert (rsp->cmd == QFU_QDL_CMD_ERROR); + + g_debug ("[qfu,qdl-message] received %s", qfu_qdl_cmd_get_string ((QfuQdlCmd) rsp->cmd)); + g_debug ("[qfu,qdl-message] error: %" G_GUINT32_FORMAT, GUINT32_FROM_LE (rsp->error)); + g_debug ("[qfu,qdl-message] errortxt: %u", rsp->errortxt); + + /* Always return an error in this case */ + g_set_error (error, G_IO_ERROR, qdl_error_to_gio_error_enum (GUINT32_FROM_LE (rsp->error)), + "%s", qdl_error_to_string ((QdlError) GUINT32_FROM_LE (rsp->error))); + return FALSE; +} + +/******************************************************************************/ +/* QDL Ufopen */ + +typedef struct _QdlUfopenReq QdlUfopenReq; +struct _QdlUfopenReq { + guint8 cmd; /* 0x25 */ + guint8 type; + guint32 length; + guint8 windowsize; + guint32 chunksize; + guint16 reserved; +} __attribute__ ((packed)); + +G_STATIC_ASSERT (sizeof (QdlUfopenReq) <= QFU_QDL_MESSAGE_MAX_HEADER_SIZE); + +gssize +qfu_qdl_request_ufopen_build (guint8 *buffer, + gsize buffer_len, + QfuImage *image, + GCancellable *cancellable, + GError **error) +{ + QdlUfopenReq *req; + + g_assert (buffer_len >= sizeof (QdlUfopenReq)); + + /* Create request */ + req = (QdlUfopenReq *) buffer; + memset (req, 0, sizeof (QdlUfopenReq)); + req->cmd = QFU_QDL_CMD_OPEN_UNFRAMED_REQ; + req->type = (guint8) qfu_image_get_image_type (image); + req->windowsize = 1; /* snooped */ + req->length = GUINT32_TO_LE (qfu_image_get_header_size (image) + qfu_image_get_data_size (image)); + req->chunksize = GUINT32_TO_LE (qfu_image_get_data_size (image)); + + /* Append header */ + if (qfu_image_read_header (image, buffer + sizeof (QdlUfopenReq), buffer_len - sizeof (QdlUfopenReq), cancellable, error) < 0) { + g_prefix_error (error, "couldn't read image header: "); + return -1; + } + + g_debug ("[qfu,qdl-message] sent %s:", qfu_qdl_cmd_get_string ((QfuQdlCmd) req->cmd)); + g_debug ("[qfu,qdl-message] type: %u", req->type); + g_debug ("[qfu,qdl-message] length: %" G_GUINT32_FORMAT, GUINT32_FROM_LE (req->length)); + g_debug ("[qfu,qdl-message] window size: %u", req->windowsize); + g_debug ("[qfu,qdl-message] chunk size: %" G_GUINT32_FORMAT, GUINT32_FROM_LE (req->chunksize)); + + return (sizeof (QdlUfopenReq) + qfu_image_get_header_size (image)); +} + +typedef struct _QdlUfopenRsp QdlUfopenRsp; +struct _QdlUfopenRsp { + guint8 cmd; /* 0x26 */ + guint16 status; + guint8 windowsize; + guint32 chunksize; +} __attribute__ ((packed)); + +G_STATIC_ASSERT (sizeof (QdlUfopenRsp) <= QFU_QDL_MESSAGE_MAX_HEADER_SIZE); + +gboolean +qfu_qdl_response_ufopen_parse (const guint8 *buffer, + gsize buffer_len, + GError **error) +{ + QdlUfopenRsp *rsp; + + if (buffer_len != sizeof (QdlUfopenRsp)) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "message size mismatch: %" G_GSIZE_FORMAT " != %" G_GSIZE_FORMAT, + buffer_len, sizeof (QdlUfopenRsp)); + return FALSE; + } + + rsp = (QdlUfopenRsp *) buffer; + g_assert (rsp->cmd == QFU_QDL_CMD_OPEN_UNFRAMED_RSP); + + g_debug ("[qfu,qdl-message] received %s", qfu_qdl_cmd_get_string ((QfuQdlCmd) rsp->cmd)); + g_debug ("[qfu,qdl-message] status: %" G_GUINT16_FORMAT, GUINT16_FROM_LE (rsp->status)); + g_debug ("[qfu,qdl-message] window size: %u", rsp->windowsize); + g_debug ("[qfu,qdl-message] chunk size: %" G_GUINT32_FORMAT, GUINT32_FROM_LE (rsp->chunksize)); + + /* For now, ignore all fields but build a GError based on status */ + + /* Return error if status != 0 */ + if (rsp->status != 0) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "operation returned an error status: %" G_GUINT16_FORMAT, + GUINT16_FROM_LE (rsp->status)); + return FALSE; + } + + return TRUE; +} + +/******************************************************************************/ +/* QDL Ufwrite */ + +/* This request is not HDLC framed, so this "header" includes the crc */ +typedef struct _QdlUfwriteReq QdlUfwriteReq; +struct _QdlUfwriteReq { + guint8 cmd; /* 0x27 */ + guint16 sequence; + guint32 reserved; + guint32 chunksize; + guint16 crc; +} __attribute__ ((packed)); + +G_STATIC_ASSERT (sizeof (QdlUfwriteReq) <= QFU_QDL_MESSAGE_MAX_HEADER_SIZE); + +gssize +qfu_qdl_request_ufwrite_build (guint8 *buffer, + gsize buffer_len, + QfuImage *image, + guint16 sequence, + GCancellable *cancellable, + GError **error) +{ + QdlUfwriteReq *req; + gssize n_read; + + g_assert (buffer_len >= sizeof (QdlUfwriteReq)); + + /* Append chunk */ + n_read = qfu_image_read_data_chunk (image, sequence, buffer + sizeof (QdlUfwriteReq), buffer_len - sizeof (QdlUfwriteReq), cancellable, error); + if (n_read < 0) { + g_prefix_error (error, "couldn't read image chunk #%u: ", sequence); + return -1; + } + + /* Create request after appending, so that we have correct chunksize */ + req = (QdlUfwriteReq *) buffer; + memset (req, 0, sizeof (QdlUfwriteReq)); + req->cmd = QFU_QDL_CMD_WRITE_UNFRAMED_REQ; + req->sequence = GUINT16_TO_LE (sequence); + req->reserved = 0; + req->chunksize = GUINT32_TO_LE ((guint32) n_read); + req->crc = GUINT16_TO_LE (qfu_utils_crc16 (buffer, sizeof (QdlUfwriteReq) - 2)); + + g_debug ("[qfu,qdl-message] sent %s:", qfu_qdl_cmd_get_string ((QfuQdlCmd) req->cmd)); + g_debug ("[qfu,qdl-message] sequence: %" G_GUINT16_FORMAT, GUINT16_FROM_LE (req->sequence)); + g_debug ("[qfu,qdl-message] chunk size: %" G_GUINT32_FORMAT, GUINT32_FROM_LE (req->chunksize)); + + return (sizeof (QdlUfopenReq) + n_read); +} + +/* The response is HDLC framed, so the crc is part of the framing */ +typedef struct _QdlUfwriteRsp QdlUfwriteRsp; +struct _QdlUfwriteRsp { + guint8 cmd; /* 0x28 */ + guint16 sequence; + guint32 reserved; + guint16 status; +} __attribute__ ((packed)); + +G_STATIC_ASSERT (sizeof (QdlUfwriteRsp) <= QFU_QDL_MESSAGE_MAX_HEADER_SIZE); + +gboolean +qfu_qdl_response_ufwrite_parse (const guint8 *buffer, + gsize buffer_len, + guint16 *sequence, + GError **error) +{ + QdlUfwriteRsp *rsp; + + if (buffer_len != sizeof (QdlUfwriteRsp)) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "message size mismatch: %" G_GSIZE_FORMAT " != %" G_GSIZE_FORMAT, + buffer_len, sizeof (QdlUfwriteRsp)); + return FALSE; + } + + rsp = (QdlUfwriteRsp *) buffer; + g_assert (rsp->cmd == QFU_QDL_CMD_WRITE_UNFRAMED_RSP); + + g_debug ("[qfu,qdl-message] received %s", qfu_qdl_cmd_get_string ((QfuQdlCmd) rsp->cmd)); + g_debug ("[qfu,qdl-message] status: %" G_GUINT16_FORMAT, GUINT16_FROM_LE (rsp->status)); + g_debug ("[qfu,qdl-message] sequence: %" G_GUINT16_FORMAT, GUINT16_FROM_LE (rsp->sequence)); + + /* Only return sequence and return GError based on status */ + + /* Return error if status != 0 */ + if (rsp->status != 0) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "operation returned an error status: %" G_GUINT16_FORMAT, + GUINT16_FROM_LE (rsp->status)); + return FALSE; + } + + if (sequence) + *sequence = GUINT16_FROM_LE (rsp->sequence); + + return TRUE; +} + +/******************************************************************************/ +/* QDL Ufclose */ + +gsize +qfu_qdl_request_ufclose_build (guint8 *buffer, + gsize buffer_len) +{ + return qdl_message_generic_build (buffer, buffer_len, QFU_QDL_CMD_CLOSE_UNFRAMED_REQ); +} + +typedef struct _QdlUfcloseRsp QdlUfcloseRsp; +struct _QdlUfcloseRsp { + guint8 cmd; /* 0x2a */ + guint16 status; + guint8 type; + guint8 errortxt; +} __attribute__ ((packed)); + +gboolean +qfu_qdl_response_ufclose_parse (const guint8 *buffer, + gsize buffer_len, + GError **error) +{ + QdlUfcloseRsp *rsp; + + if (buffer_len != sizeof (QdlUfcloseRsp)) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "message size mismatch: %" G_GSIZE_FORMAT " != %" G_GSIZE_FORMAT, + buffer_len, sizeof (QdlUfcloseRsp)); + return FALSE; + } + + rsp = (QdlUfcloseRsp *) buffer; + g_assert (rsp->cmd == QFU_QDL_CMD_CLOSE_UNFRAMED_RSP); + + g_debug ("[qfu,qdl-message] received %s", qfu_qdl_cmd_get_string ((QfuQdlCmd) rsp->cmd)); + g_debug ("[qfu,qdl-message] status: %" G_GUINT16_FORMAT, GUINT16_FROM_LE (rsp->status)); + g_debug ("[qfu,qdl-message] type: %u", rsp->type); + g_debug ("[qfu,qdl-message] errortxt: %u", rsp->errortxt); + + /* For now, ignore all fields but build a GError based on status */ + + /* Return error if status != 0 */ + if (rsp->status != 0) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "operation returned an error status: %" G_GUINT16_FORMAT, + GUINT16_FROM_LE (rsp->status)); + return FALSE; + } + + return TRUE; +} + +/******************************************************************************/ +/* QDL session close */ + +gsize +qfu_qdl_request_reset_build (guint8 *buffer, + gsize buffer_len) +{ + return qdl_message_generic_build (buffer, buffer_len, QFU_QDL_CMD_RESET_REQ); +} + +/******************************************************************************/ +/* Other unused messages */ + +/* 0x29 - cmd only */ +/* 0x2d - cmd only */ +/* 0x2e - cmd only */ + +typedef struct _QdlImageprefRsp QdlImageprefRsp; +struct _QdlImageprefRsp { + guint8 cmd; /* 0x2f */ + guint8 entries; + struct { + guint8 type; + gchar id[16]; + } image[]; +} __attribute__ ((packed)); diff --git a/src/qmi-firmware-update/qfu-qdl-message.h b/src/qmi-firmware-update/qfu-qdl-message.h new file mode 100644 index 0000000..a6c8210 --- /dev/null +++ b/src/qmi-firmware-update/qfu-qdl-message.h @@ -0,0 +1,102 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * qmi-firmware-update -- Command line tool to update firmware in QMI devices + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright (C) 2016 Bjørn Mork <bjorn@mork.no> + * Copyright (C) 2016 Zodiac Inflight Innovations + * Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es> + */ + +#ifndef QFU_QDL_MESSAGE_H +#define QFU_QDL_MESSAGE_H + +#include <glib-object.h> +#include <gio/gio.h> + +#include "qfu-image.h" + +G_BEGIN_DECLS + +/* Maximum QDL header size (i.e. without payload) */ +#define QFU_QDL_MESSAGE_MAX_HEADER_SIZE 50 + +/* Maximum QDL message size (header and payload) */ +#define QFU_QDL_MESSAGE_MAX_SIZE (QFU_QDL_MESSAGE_MAX_HEADER_SIZE + QFU_IMAGE_CHUNK_SIZE) + +/* from GobiAPI_1.0.40/Core/QDLEnum.h and + * GobiAPI_1.0.40/Core/QDLBuffers.h with additional details from USB + * snooping + */ +typedef enum { + QFU_QDL_CMD_HELLO_REQ = 0x01, + QFU_QDL_CMD_HELLO_RSP = 0x02, + QFU_QDL_CMD_ERROR = 0x0d, + QFU_QDL_CMD_OPEN_UNFRAMED_REQ = 0x25, + QFU_QDL_CMD_OPEN_UNFRAMED_RSP = 0x26, + QFU_QDL_CMD_WRITE_UNFRAMED_REQ = 0x27, + QFU_QDL_CMD_WRITE_UNFRAMED_RSP = 0x28, + QFU_QDL_CMD_CLOSE_UNFRAMED_REQ = 0x29, + QFU_QDL_CMD_CLOSE_UNFRAMED_RSP = 0x2a, + QFU_QDL_CMD_DOWNLOAD_REQ = 0x2b, + QFU_QDL_CMD_RESET_REQ = 0x2d, + QFU_QDL_CMD_GET_IMAGE_PREF_REQ = 0x2e, + QFU_QDL_CMD_GET_IMAGE_PREF_RSP = 0x2f, +} QfuQdlCmd; + +/* Request builders */ + +gsize qfu_qdl_request_hello_build (guint8 *buffer, + gsize buffer_len, + guint8 minver, + guint8 maxver); +gssize qfu_qdl_request_ufopen_build (guint8 *buffer, + gsize buffer_len, + QfuImage *image, + GCancellable *cancellable, + GError **error); +gssize qfu_qdl_request_ufwrite_build (guint8 *buffer, + gsize buffer_len, + QfuImage *image, + guint16 sequence, + GCancellable *cancellable, + GError **error); +gsize qfu_qdl_request_ufclose_build (guint8 *buffer, + gsize buffer_len); +gsize qfu_qdl_request_reset_build (guint8 *buffer, + gsize buffer_len); + +/* Response parsers */ + +gboolean qfu_qdl_response_hello_parse (const guint8 *buffer, + gsize buffer_len, + GError **error); +gboolean qfu_qdl_response_error_parse (const guint8 *buffer, + gsize buffer_len, + GError **error); +gboolean qfu_qdl_response_ufopen_parse (const guint8 *buffer, + gsize buffer_len, + GError **error); +gboolean qfu_qdl_response_ufwrite_parse (const guint8 *buffer, + gsize buffer_len, + guint16 *sequence, + GError **error); +gboolean qfu_qdl_response_ufclose_parse (const guint8 *buffer, + gsize buffer_len, + GError **error); + +G_END_DECLS + +#endif /* QFU_QDL_MESSAGE_H */ |