blob: c6d820708c13db050a55c76bd39b0b2e143da491 [file] [log] [blame]
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "qmi_rmtfs.h"
struct qmi_packet {
uint8_t flags;
uint16_t txn_id;
uint16_t msg_id;
uint16_t msg_len;
uint8_t data[];
} __attribute__((__packed__));
struct qmi_tlv {
void *allocated;
void *buf;
size_t size;
int error;
};
struct qmi_tlv_item {
uint8_t key;
uint16_t len;
uint8_t data[];
} __attribute__((__packed__));
struct qmi_tlv *qmi_tlv_init(unsigned txn, unsigned msg_id, unsigned msg_type)
{
struct qmi_packet *pkt;
struct qmi_tlv *tlv;
tlv = malloc(sizeof(struct qmi_tlv));
memset(tlv, 0, sizeof(struct qmi_tlv));
tlv->size = sizeof(struct qmi_packet);
tlv->allocated = malloc(tlv->size);
tlv->buf = tlv->allocated;
pkt = tlv->buf;
pkt->flags = msg_type;
pkt->txn_id = txn;
pkt->msg_id = msg_id;
pkt->msg_len = 0;
return tlv;
}
struct qmi_tlv *qmi_tlv_decode(void *buf, size_t len, unsigned *txn, unsigned msg_type)
{
struct qmi_packet *pkt = buf;
struct qmi_tlv *tlv;
if (pkt->flags != msg_type)
return NULL;
tlv = malloc(sizeof(struct qmi_tlv));
memset(tlv, 0, sizeof(struct qmi_tlv));
tlv->buf = buf;
tlv->size = len;
if (txn)
*txn = pkt->txn_id;
return tlv;
}
void *qmi_tlv_encode(struct qmi_tlv *tlv, size_t *len)
{
struct qmi_packet *pkt;
if (!tlv || tlv->error)
return NULL;
pkt = tlv->buf;
pkt->msg_len = tlv->size - sizeof(struct qmi_packet);
*len = tlv->size;
return tlv->buf;
}
void qmi_tlv_free(struct qmi_tlv *tlv)
{
free(tlv->allocated);
free(tlv);
}
static struct qmi_tlv_item *qmi_tlv_get_item(struct qmi_tlv *tlv, unsigned id)
{
struct qmi_tlv_item *item;
struct qmi_packet *pkt;
unsigned offset = 0;
void *pkt_data;
pkt = tlv->buf;
pkt_data = pkt->data;
while (offset < tlv->size) {
item = pkt_data + offset;
if (item->key == id)
return pkt_data + offset;
offset += sizeof(struct qmi_tlv_item) + item->len;
}
return NULL;
}
void *qmi_tlv_get(struct qmi_tlv *tlv, unsigned id, size_t *len)
{
struct qmi_tlv_item *item;
item = qmi_tlv_get_item(tlv, id);
if (!item)
return NULL;
*len = item->len;
return item->data;
}
void *qmi_tlv_get_array(struct qmi_tlv *tlv, unsigned id, unsigned len_size, size_t *len, size_t *size)
{
struct qmi_tlv_item *item;
unsigned count;
void *ptr;
item = qmi_tlv_get_item(tlv, id);
if (!item)
return NULL;
ptr = item->data;
switch (len_size) {
case 4:
count = *(uint32_t*)ptr++;
break;
case 2:
count = *(uint16_t*)ptr++;
break;
case 1:
count = *(uint8_t*)ptr++;
break;
}
*len = count;
*size = (item->len - len_size) / count;
return ptr;
}
static struct qmi_tlv_item *qmi_tlv_alloc_item(struct qmi_tlv *tlv, unsigned id, size_t len)
{
struct qmi_tlv_item *item;
size_t new_size;
bool migrate;
void *newp;
/* If using user provided buffer, migrate data */
migrate = !tlv->allocated;
new_size = tlv->size + sizeof(struct qmi_tlv_item) + len;
newp = realloc(tlv->allocated, new_size);
if (!newp)
return NULL;
if (migrate)
memcpy(newp, tlv->buf, tlv->size);
item = newp + tlv->size;
item->key = id;
item->len = len;
tlv->buf = tlv->allocated = newp;
tlv->size = new_size;
return item;
}
int qmi_tlv_set(struct qmi_tlv *tlv, unsigned id, void *buf, size_t len)
{
struct qmi_tlv_item *item;
if (!tlv)
return -EINVAL;
item = qmi_tlv_alloc_item(tlv, id, len);
if (!item) {
tlv->error = ENOMEM;
return -ENOMEM;
}
memcpy(item->data, buf, len);
return 0;
}
int qmi_tlv_set_array(struct qmi_tlv *tlv, unsigned id, unsigned len_size, void *buf, size_t len, size_t size)
{
struct qmi_tlv_item *item;
size_t array_size;
void *ptr;
if (!tlv)
return -EINVAL;
array_size = len * size;
item = qmi_tlv_alloc_item(tlv, id, len_size + array_size);
if (!item) {
tlv->error = ENOMEM;
return -ENOMEM;
}
ptr = item->data;
switch (len_size) {
case 4:
*(uint32_t*)ptr++ = len;
break;
case 2:
*(uint16_t*)ptr++ = len;
break;
case 1:
*(uint8_t*)ptr++ = len;
break;
}
memcpy(ptr, buf, array_size);
return 0;
}