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