forked from calebccff/qmic
-
Notifications
You must be signed in to change notification settings - Fork 0
/
qmi_tlv.c
142 lines (105 loc) · 2.48 KB
/
qmi_tlv.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
struct qmi_tlv *qmi_tlv_init(void)
{
struct qmi_tlv *tlv;
tlv = malloc(sizeof(struct qmi_tlv));
memset(tlv, 0, sizeof(struct qmi_tlv));
return tlv;
}
struct qmi_tlv *qmi_tlv_decode(void *buf, size_t len)
{
struct qmi_tlv *tlv;
tlv = malloc(sizeof(struct qmi_tlv));
memset(tlv, 0, sizeof(struct qmi_tlv));
tlv->buf = buf;
tlv->size = len;
return tlv;
}
void *qmi_tlv_encode(struct qmi_tlv *tlv, size_t *len)
{
*len = tlv->size;
return tlv->buf;
}
void qmi_tlv_free(struct qmi_tlv *tlv)
{
free(tlv->allocated);
free(tlv);
}
static struct qmi_tlv_header *qmi_tlv_get_item(struct qmi_tlv *tlv, unsigned id)
{
struct qmi_tlv_header *hdr;
unsigned offset = 0;
while (offset < tlv->size) {
hdr = tlv->buf + offset;
if (hdr->key == id)
return tlv->buf + offset;
offset += sizeof(struct qmi_tlv_header) + hdr->len;
}
return NULL;
}
void *qmi_tlv_get(struct qmi_tlv *tlv, unsigned id, size_t *len)
{
struct qmi_tlv_header *hdr;
hdr = qmi_tlv_get_item(tlv, id);
if (!hdr)
return NULL;
*len = hdr->len;
return hdr->data;
}
void *qmi_tlv_get_array(struct qmi_tlv *tlv, unsigned id, size_t *len, size_t *size)
{
struct qmi_tlv_header *hdr;
uint8_t count;
void *ptr;
hdr = qmi_tlv_get_item(tlv, id);
if (!hdr)
return NULL;
ptr = hdr->data;
count = *(uint8_t*)ptr++;
*len = count;
*size = (hdr->len - 1) / count;
return ptr;
}
static struct qmi_tlv_header *qmi_tlv_alloc_item(struct qmi_tlv *tlv, unsigned id, size_t len)
{
struct qmi_tlv_header *hdr;
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_header) + len;
newp = realloc(tlv->allocated, new_size);
if (!newp)
return NULL;
if (migrate)
memcpy(newp, tlv->buf, tlv->size);
hdr = newp + tlv->size;
hdr->key = id;
hdr->len = len;
tlv->buf = tlv->allocated = newp;
tlv->size = new_size;
return hdr;
}
int qmi_tlv_set(struct qmi_tlv *tlv, unsigned id, void *buf, size_t len)
{
struct qmi_tlv_header *hdr;
hdr = qmi_tlv_alloc_item(tlv, id, len);
if (!hdr)
return -ENOMEM;
memcpy(hdr->data, buf, len);
return 0;
}
int qmi_tlv_set_array(struct qmi_tlv *tlv, unsigned id, void *buf, size_t len, size_t size)
{
struct qmi_tlv_header *hdr;
size_t array_size;
void *ptr;
array_size = len * size;
hdr = qmi_tlv_alloc_item(tlv, id, sizeof(uint8_t) + array_size);
if (!hdr)
return -ENOMEM;
ptr = hdr->data;
*(uint8_t*)ptr++ = len;
memcpy(ptr, buf, array_size);
return 0;
}