fix generated code for allocating arrays
[project/uqmi.git] / data / gen-code.pl
1 #!/usr/bin/env perl
2 use strict;
3
4 use FindBin '$Bin';
5 require "$Bin/gen-common.pm";
6
7 our %tlv_types;
8 our $ctl;
9
10 my $data = get_json();
11 my $varsize_field;
12
13 my %tlv_get = (
14 gint8 => "*(int8_t *) get_next(1)",
15 guint8 => "*(uint8_t *) get_next(1)",
16 gint16 => "le16_to_cpu(*(uint16_t *) get_next(2))",
17 guint16 => "le16_to_cpu(*(uint16_t *) get_next(2))",
18 gint32 => "le32_to_cpu(*(uint32_t *) get_next(4))",
19 guint32 => "le32_to_cpu(*(uint32_t *) get_next(4))",
20 gint64 => "le64_to_cpu(*(uint64_t *) get_next(8))",
21 guint64 => "le64_to_cpu(*(uint64_t *) get_next(8))",
22 );
23
24 my %tlv_get_be = (
25 gint16 => "be16_to_cpu(*(uint16_t *) get_next(2))",
26 guint16 => "be16_to_cpu(*(uint16_t *) get_next(2))",
27 gint32 => "be32_to_cpu(*(uint32_t *) get_next(4))",
28 guint32 => "be32_to_cpu(*(uint32_t *) get_next(4))",
29 gint64 => "be64_to_cpu(*(uint64_t *) get_next(8))",
30 guint64 => "be64_to_cpu(*(uint64_t *) get_next(8))",
31 );
32
33 sub gen_tlv_parse_field($$$$) {
34 my $var = shift;
35 my $elem = shift;
36 my $n_indent = shift;
37 my $iterator = shift;
38 my $data = "";
39
40 my $indent = "\t" x ($n_indent + 3);
41 my $use_iterator = 0;
42 my $field = 0;
43
44 my $type = $elem->{"format"};
45
46 $varsize_field and die "Cannot place fields after a variable-sized field (var: $var, field: $varsize_field)\n";
47
48 my $val;
49 if ($elem->{endian} eq 'network') {
50 $val = $tlv_get_be{$type};
51 } else {
52 $val = $tlv_get{$type};
53 }
54
55 if ($val) {
56 return $indent."$var = $val;\n";
57 } elsif ($type eq "array") {
58 my $size;
59 my $cur_varsize_field;
60 my $var_data;
61 my $var_iterator;
62
63 if ($elem->{"fixed-size"}) {
64 $size = $elem->{"fixed-size"};
65 $data .= $indent."for ($iterator = 0; $iterator < $size; $iterator\++) {\n";
66
67 ($var_data, $var_iterator) =
68 gen_tlv_parse_field($var."[$iterator]", $elem->{"array-element"}, $n_indent + 1, "i$iterator");
69
70 } else {
71 my $prefix = $elem->{"size-prefix-format"};
72 $prefix or $prefix = 'guint8';
73
74 $size = $tlv_get{$prefix};
75 die "Unknown size element type '$prefix'" if not defined $size;
76
77 ($var_data, $var_iterator) =
78 gen_tlv_parse_field($var."[$var\_n]", $elem->{"array-element"}, $n_indent + 1, "i$iterator");
79
80 $var_data .= $indent."\t$var\_n++;\n";
81 $data .= $indent."$iterator = $size;\n";
82 $data .= $indent."$var = __qmi_alloc_static($iterator * sizeof($var\[0]));\n";
83 $data .= $indent."while($iterator\-- > 0) {\n";
84 }
85
86 $var_iterator and $data .= $indent."\tint i$iterator;\n";
87 $data .= $var_data;
88 $data .= $indent."}\n";
89
90 $varsize_field = $cur_varsize_field;
91
92 return $data, 1;
93 } elsif ($type eq "struct" or $type eq "sequence") {
94 foreach my $field (@{$elem->{contents}}) {
95 my $field_cname = gen_cname($field->{name});
96 my ($var_data, $var_iterator) =
97 gen_tlv_parse_field("$var.$field_cname", $field, $n_indent, $iterator);
98
99 $data .= $var_data;
100 $var_iterator and $use_iterator = 1;
101 }
102 return $data, $use_iterator;
103 } elsif ($type eq "string") {
104 my $size = $elem->{"fixed-size"};
105 $size or do {
106 my $prefix = $elem->{"size-prefix-format"};
107 $prefix or do {
108 $elem->{type} eq 'TLV' or $prefix = 'guint8';
109 };
110
111 if ($prefix) {
112 $size = $tlv_get{$prefix};
113 } else {
114 $size = "cur_tlv_len - ofs";
115 $varsize_field = $var;
116 }
117 };
118
119 $data .= $indent."$iterator = $size;\n";
120 my $maxsize = $elem->{"max-size"};
121 $maxsize and do {
122 $data .= $indent."if ($iterator > $maxsize)\n";
123 $data .= $indent."\t$iterator = $maxsize;\n";
124 };
125 $data .= $indent.$var." = __qmi_copy_string(get_next($iterator), $iterator);\n";
126 return $data, 1;
127 } elsif ($type eq "guint-sized") {
128 my $size = $elem->{"guint-size"};
129 return $indent."$var = ({ uint64_t var; memcpy(&var, get_next($size), $size); le64_to_cpu(var); });\n";
130 } else {
131 die "Invalid type $type for variable $var";
132 }
133 }
134
135 sub gen_tlv_type($$) {
136 my $cname = shift;
137 my $elem = shift;
138
139 my $type = $elem->{"format"};
140 my $id = $elem->{"id"};
141 my $data = "";
142 undef $varsize_field;
143 my $indent = "\t\t\t";
144
145 $type or return undef;
146
147 print <<EOF;
148 case $id:
149 EOF
150
151 my $val = $tlv_get{$type};
152 if ($val) {
153 print $indent."qmi_set(res, $cname, $val);\n";
154 } elsif ($type eq "string") {
155 my ($var_data, $var_iterator) =
156 gen_tlv_parse_field("res->data.$cname", $elem, 0, "i");
157 print "$var_data";
158 } elsif ($type eq "array") {
159 $elem->{"fixed-size"} and $data = $indent."res->set.$cname = 1;\n";
160 my ($var_data, $var_iterator) =
161 gen_tlv_parse_field("res->data.$cname", $elem, 0, "i");
162 print "$data$var_data\n";
163 } elsif ($type eq "sequence" or $type eq "struct") {
164 my ($var_data, $var_iterator) =
165 gen_tlv_parse_field("res->data.$cname", $elem, 0, "i");
166
167 print $indent."res->set.$cname = 1;\n".$var_data;
168 }
169 print <<EOF;
170 break;
171
172 EOF
173 }
174
175 sub gen_parse_func($$)
176 {
177 my $name = shift;
178 my $data = shift;
179
180 my $type = "svc";
181 $ctl and $type = "ctl";
182
183 print gen_tlv_parse_func($name, $data)."\n";
184 print <<EOF;
185 {
186 void *tlv_buf = &msg->$type.tlv;
187 int tlv_len = le16_to_cpu(msg->$type.tlv_len);
188 EOF
189
190 if (gen_has_types($data)) {
191 print <<EOF;
192 struct tlv *tlv;
193 int i;
194
195 memset(res, 0, sizeof(*res));
196
197 __qmi_alloc_reset();
198 while ((tlv = tlv_get_next(&tlv_buf, &tlv_len)) != NULL) {
199 unsigned int cur_tlv_len = le16_to_cpu(tlv->len);
200 unsigned int ofs = 0;
201
202 switch(tlv->type) {
203 EOF
204 foreach my $field (@$data) {
205 my $cname = gen_cname($field->{name});
206 gen_tlv_type($cname, $field);
207 }
208
209 print <<EOF;
210 default:
211 break;
212 }
213 }
214
215 return 0;
216
217 error_len:
218 fprintf(stderr, "%s: Invalid TLV length in message, tlv=0x%02x, len=%d\\n",
219 __func__, tlv->type, le16_to_cpu(tlv->len));
220 return QMI_ERROR_INVALID_DATA;
221 EOF
222 } else {
223 print <<EOF;
224
225 return qmi_check_message_status(tlv_buf, tlv_len);
226 EOF
227 }
228
229 print <<EOF;
230 }
231
232 EOF
233 }
234
235 my %tlv_set = (
236 guint8 => sub { my $a = shift; my $b = shift; print "*(uint8_t *) $a = $b;\n" },
237 guint16 => sub { my $a = shift; my $b = shift; print "*(uint16_t *) $a = cpu_to_le16($b);\n" },
238 guint32 => sub { my $a = shift; my $b = shift; print "*(uint32_t *) $a = cpu_to_le32($b);\n" },
239 );
240
241 my %tlv_put = (
242 gint8 => sub { my $a = shift; "put_tlv_var(uint8_t, $a, 1);\n" },
243 guint8 => sub { my $a = shift; "put_tlv_var(uint8_t, $a, 1);\n" },
244 gint16 => sub { my $a = shift; "put_tlv_var(uint16_t, cpu_to_le16($a), 2);\n" },
245 guint16 => sub { my $a = shift; "put_tlv_var(uint16_t, cpu_to_le16($a), 2);\n" },
246 gint32 => sub { my $a = shift; "put_tlv_var(uint32_t, cpu_to_le32($a), 4);\n" },
247 guint32 => sub { my $a = shift; "put_tlv_var(uint32_t, cpu_to_le32($a), 4);\n" },
248 gint64 => sub { my $a = shift; "put_tlv_var(uint64_t, cpu_to_le64($a), 8);\n" },
249 guint64 => sub { my $a = shift; "put_tlv_var(uint64_t, cpu_to_le64($a), 8);\n" },
250 );
251
252 my %tlv_put_be = (
253 gint16 => sub { my $a = shift; "put_tlv_var(uint16_t, cpu_to_be16($a), 2);\n" },
254 guint16 => sub { my $a = shift; "put_tlv_var(uint16_t, cpu_to_be16($a), 2);\n" },
255 gint32 => sub { my $a = shift; "put_tlv_var(uint32_t, cpu_to_be32($a), 4);\n" },
256 guint32 => sub { my $a = shift; "put_tlv_var(uint32_t, cpu_to_be32($a), 4);\n" },
257 gint64 => sub { my $a = shift; "put_tlv_var(uint64_t, cpu_to_be64($a), 8);\n" },
258 guint64 => sub { my $a = shift; "put_tlv_var(uint64_t, cpu_to_be64($a), 8);\n" },
259 );
260
261 sub gen_tlv_val_set($$$$$)
262 {
263 my $cname = shift;
264 my $elem = shift;
265 my $indent = shift;
266 my $iterator = shift;
267 my $cond = shift;
268 my $prev_cond;
269
270 my $type = $elem->{format};
271 my $data = "";
272
273 my $put;
274 if ($elem->{endian} eq 'network') {
275 $put = $tlv_put_be{$type};
276 } else {
277 $put = $tlv_put{$type};
278 }
279 $put and return $indent.&$put($cname);
280
281 $type eq 'array' and do {
282 my $size = $elem->{"fixed-size"};
283
284 $size or do {
285 $cond and $$cond = $cname;
286 $size = $cname."_n";
287
288 my $prefix = $elem->{"size-prefix-format"};
289 $prefix or $prefix = 'gint8';
290
291 $put = $tlv_put{$prefix};
292 $put or die "Unknown size prefix type $prefix\n";
293
294 $data .= $indent.&$put($size);
295 };
296
297 $data .= $indent."for ($iterator = 0; $iterator < $size; $iterator++) {\n";
298 my ($var_data, $var_iterator) =
299 gen_tlv_val_set($cname."[$iterator]", $elem->{"array-element"}, "$indent\t", "i$iterator", undef);
300
301 $var_iterator and $data .= $indent."\tint i$iterator;\n";
302 $data .= $var_data;
303 $data .= $indent."}\n";
304
305 return $data, 1;
306 };
307
308 $type eq 'string' and do {
309 $cond and $$cond = $cname;
310
311 my $len = $elem->{"fixed-size"};
312 $len or $len = "strlen($cname)";
313
314 $data .= $indent."$iterator = $len;\n";
315
316 $len = $elem->{"max-size"};
317 $len and do {
318 $data .= $indent."if ($iterator > $len)\n";
319 $data .= $indent."\t$iterator = $len;\n";
320 };
321
322 my $prefix = $elem->{"size-prefix-format"};
323 $prefix or do {
324 $elem->{"type"} eq 'TLV' or $prefix = 'guint8';
325 };
326
327 $prefix and do {
328 my $put = $tlv_put{$prefix} or die "Unknown size prefix format $prefix";
329 $data .= $indent.&$put("$iterator");
330 };
331
332 $data .= $indent."strncpy(__qmi_alloc_static($iterator), $cname, $iterator);\n";
333
334 return $data, 1;
335 };
336
337 ($type eq 'sequence' or $type eq 'struct') and do {
338 my $use_iterator;
339
340 foreach my $field (@{$elem->{contents}}) {
341 my $field_cname = gen_cname($field->{name});
342 my ($var_data, $var_iterator) =
343 gen_tlv_val_set("$cname.$field_cname", $field, $indent, $iterator, undef);
344
345 $var_iterator and $use_iterator = 1;
346 $data .= $var_data;
347 }
348 return $data, $use_iterator;
349 };
350
351 die "Unknown type $type";
352 }
353
354 sub gen_tlv_attr_set($$)
355 {
356 my $cname = shift;
357 my $elem = shift;
358 my $indent = "\t";
359 my $data = "";
360 my $iterator = "";
361 my $size_var = "";
362 my $id = $elem->{id};
363
364 my $cond = "req->set.$cname";
365 my ($var_data, $use_iterator) =
366 gen_tlv_val_set("req->data.$cname", $elem, "\t\t", "i", \$cond);
367 $use_iterator and $iterator = "\t\tint i;\n";
368
369 $data = <<EOF;
370 if ($cond) {
371 void *buf;
372 int ofs;
373 $iterator$size_var
374 __qmi_alloc_reset();
375 $var_data
376 buf = __qmi_get_buf(&ofs);
377 tlv_new(msg, $id, ofs, buf);
378 }
379
380 EOF
381 print "$data";
382 }
383
384 sub gen_set_func($$)
385 {
386 my $name = shift;
387 my $fields = shift;
388 my $data = shift;
389
390 my $type = "svc";
391 my $service = $data->{service};
392 my $id = $data->{id};
393
394 $service eq 'CTL' and $type = 'ctl';
395
396 print gen_tlv_set_func($name, $fields)."\n";
397 print <<EOF;
398 {
399 qmi_init_request_message(msg, QMI_SERVICE_$service);
400 msg->$type.message = cpu_to_le16($id);
401
402 EOF
403 foreach my $field (@$fields) {
404 my $cname = gen_cname($field->{name});
405 gen_tlv_attr_set($cname, $field);
406 }
407
408 print <<EOF;
409 return 0;
410 }
411
412 EOF
413 }
414
415 print <<EOF;
416 /* generated by uqmi gen-code.pl */
417 #include <stdio.h>
418 #include <string.h>
419 #include "qmi-message.h"
420
421 #define get_next(_size) ({ void *_buf = &tlv->data[ofs]; ofs += _size; if (ofs > cur_tlv_len) goto error_len; _buf; })
422 #define copy_tlv(_val, _size) \\
423 do { \\
424 int __size = _size; \\
425 if (__size > 0) \\
426 memcpy(__qmi_alloc_static(__size), _val, __size); \\
427 } while (0);
428
429 #define put_tlv_var(_type, _val, _size) \\
430 do { \\
431 _type __var = _val; \\
432 copy_tlv(&__var, _size); \\
433 } while(0)
434
435 EOF
436
437 gen_foreach_message_type($data, \&gen_set_func, \&gen_parse_func);