mirror of
https://github.com/linux-msm/rmtfs.git
synced 2024-12-04 01:24:14 +08:00
d5e516a6c6
To support creating and parsing requests and indications as well qmic adds an extra parameter to the tlv alloc and decode functions, so that this can be used to generate and validate that the qmi packet header is of the correct type. Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
234 lines
4.0 KiB
C
234 lines
4.0 KiB
C
#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;
|
|
}
|
|
|
|
|