63871a01eb7da50625da50c2c5c0e3f3c598a6f9
[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."\tunsigned int 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         my $idx = shift;
139         my $idx_word = "found[".int($idx / 32)."]";
140         my $idx_bit = "(1 << ".($idx % 32).")";
141
142         my $type = $elem->{"format"};
143         my $id = $elem->{"id"};
144         my $data = "";
145         undef $varsize_field;
146         my $indent = "\t\t\t";
147
148         $type or return undef;
149
150         print <<EOF;
151                 case $id:
152                         if ($idx_word & $idx_bit)
153                                 break;
154
155                         $idx_word |= $idx_bit;
156 EOF
157
158         my $val = $tlv_get{$type};
159         if ($val) {
160                 print $indent."qmi_set(res, $cname, $val);\n";
161         } elsif ($type eq "string") {
162                 my ($var_data, $var_iterator) =
163                         gen_tlv_parse_field("res->data.$cname", $elem, 0, "i");
164                 print "$var_data";
165         } elsif ($type eq "array") {
166                 $elem->{"fixed-size"} and $data = $indent."res->set.$cname = 1;\n";
167                 my ($var_data, $var_iterator) =
168                         gen_tlv_parse_field("res->data.$cname", $elem, 0, "i");
169                 print "$data$var_data\n";
170         } elsif ($type eq "sequence" or $type eq "struct") {
171                 my ($var_data, $var_iterator) =
172                         gen_tlv_parse_field("res->data.$cname", $elem, 0, "i");
173
174                 print $indent."res->set.$cname = 1;\n".$var_data;
175         }
176         print <<EOF;
177                         break;
178
179 EOF
180 }
181
182 sub gen_parse_func($$)
183 {
184         my $name = shift;
185         my $data = shift;
186
187         my $type = "svc";
188         $ctl and $type = "ctl";
189
190         print gen_tlv_parse_func($name, $data)."\n";
191         print <<EOF;
192 {
193         void *tlv_buf = &msg->$type.tlv;
194         unsigned int tlv_len = le16_to_cpu(msg->$type.tlv_len);
195 EOF
196
197         if (gen_has_types($data)) {
198                 my $n_bits = scalar @$data;
199                 my $n_words = int(($n_bits + 31) / 32);
200                 my $i = 0;
201
202                 print <<EOF;
203         struct tlv *tlv;
204         int i;
205         uint32_t found[$n_words] = {};
206
207         memset(res, 0, sizeof(*res));
208
209         __qmi_alloc_reset();
210         while ((tlv = tlv_get_next(&tlv_buf, &tlv_len)) != NULL) {
211                 unsigned int cur_tlv_len = le16_to_cpu(tlv->len);
212                 unsigned int ofs = 0;
213
214                 switch(tlv->type) {
215 EOF
216                 foreach my $field (@$data) {
217                         my $cname = gen_cname($field->{name});
218                         gen_tlv_type($cname, $field, $i++);
219                 }
220
221                 print <<EOF;
222                 default:
223                         break;
224                 }
225         }
226
227         return 0;
228
229 error_len:
230         fprintf(stderr, "%s: Invalid TLV length in message, tlv=0x%02x, len=%d\\n",
231                 __func__, tlv->type, le16_to_cpu(tlv->len));
232         return QMI_ERROR_INVALID_DATA;
233 EOF
234         } else {
235                 print <<EOF;
236
237         return qmi_check_message_status(tlv_buf, tlv_len);
238 EOF
239         }
240
241         print <<EOF;
242 }
243
244 EOF
245 }
246
247 my %tlv_set = (
248         guint8 => sub { my $a = shift; my $b = shift; print "*(uint8_t *) $a = $b;\n" },
249         guint16 => sub { my $a = shift; my $b = shift; print "*(uint16_t *) $a = cpu_to_le16($b);\n" },
250         guint32 => sub { my $a = shift; my $b = shift; print "*(uint32_t *) $a = cpu_to_le32($b);\n" },
251 );
252
253 my %tlv_put = (
254         gint8 => sub { my $a = shift; "put_tlv_var(uint8_t, $a, 1);\n" },
255         guint8 => sub { my $a = shift; "put_tlv_var(uint8_t, $a, 1);\n" },
256         gint16 => sub { my $a = shift; "put_tlv_var(uint16_t, cpu_to_le16($a), 2);\n" },
257         guint16 => sub { my $a = shift; "put_tlv_var(uint16_t, cpu_to_le16($a), 2);\n" },
258         gint32 => sub { my $a = shift; "put_tlv_var(uint32_t, cpu_to_le32($a), 4);\n" },
259         guint32 => sub { my $a = shift; "put_tlv_var(uint32_t, cpu_to_le32($a), 4);\n" },
260         gint64 => sub { my $a = shift; "put_tlv_var(uint64_t, cpu_to_le64($a), 8);\n" },
261         guint64 => sub { my $a = shift; "put_tlv_var(uint64_t, cpu_to_le64($a), 8);\n" },
262 );
263
264 my %tlv_put_be = (
265         gint16 => sub { my $a = shift; "put_tlv_var(uint16_t, cpu_to_be16($a), 2);\n" },
266         guint16 => sub { my $a = shift; "put_tlv_var(uint16_t, cpu_to_be16($a), 2);\n" },
267         gint32 => sub { my $a = shift; "put_tlv_var(uint32_t, cpu_to_be32($a), 4);\n" },
268         guint32 => sub { my $a = shift; "put_tlv_var(uint32_t, cpu_to_be32($a), 4);\n" },
269         gint64 => sub { my $a = shift; "put_tlv_var(uint64_t, cpu_to_be64($a), 8);\n" },
270         guint64 => sub { my $a = shift; "put_tlv_var(uint64_t, cpu_to_be64($a), 8);\n" },
271 );
272
273 sub gen_tlv_val_set($$$$$)
274 {
275         my $cname = shift;
276         my $elem = shift;
277         my $indent = shift;
278         my $iterator = shift;
279         my $cond = shift;
280         my $prev_cond;
281
282         my $type = $elem->{format};
283         my $data = "";
284
285         my $put;
286         if ($elem->{endian} eq 'network') {
287                 $put = $tlv_put_be{$type};
288         } else {
289                 $put = $tlv_put{$type};
290         }
291         $put and return $indent.&$put($cname);
292
293         $type eq 'array' and do {
294                 my $size = $elem->{"fixed-size"};
295
296                 $size or do {
297                         $cond and $$cond = $cname;
298                         $size = $cname."_n";
299
300                         my $prefix = $elem->{"size-prefix-format"};
301                         $prefix or $prefix = 'gint8';
302
303                         $put = $tlv_put{$prefix};
304                         $put or die "Unknown size prefix type $prefix\n";
305
306                         $data .= $indent.&$put($size);
307                 };
308
309                 $data .= $indent."for ($iterator = 0; $iterator < $size; $iterator++) {\n";
310                 my ($var_data, $var_iterator) =
311                         gen_tlv_val_set($cname."[$iterator]", $elem->{"array-element"}, "$indent\t", "i$iterator", undef);
312
313                 $var_iterator and $data .= $indent."\tunsigned int i$iterator;\n";
314                 $data .= $var_data;
315                 $data .= $indent."}\n";
316
317                 return $data, 1;
318         };
319
320         $type eq 'string' and do {
321                 $cond and $$cond = $cname;
322
323                 my $len = $elem->{"fixed-size"};
324                 $len or $len = "strlen($cname)";
325
326                 $data .= $indent."$iterator = $len;\n";
327
328                 $len = $elem->{"max-size"};
329                 $len and do {
330                         $data .= $indent."if ($iterator > $len)\n";
331                         $data .= $indent."\t$iterator = $len;\n";
332                 };
333
334                 my $prefix = $elem->{"size-prefix-format"};
335                 $prefix or do {
336                         $elem->{"type"} eq 'TLV' or $prefix = 'guint8';
337                 };
338
339                 $prefix and do {
340                         my $put = $tlv_put{$prefix} or die "Unknown size prefix format $prefix";
341                         $data .= $indent.&$put("$iterator");
342                 };
343
344                 $data .= $indent."strncpy(__qmi_alloc_static($iterator), $cname, $iterator);\n";
345
346                 return $data, 1;
347         };
348
349         ($type eq 'sequence' or $type eq 'struct') and do {
350                 my $use_iterator;
351
352                 foreach my $field (@{$elem->{contents}}) {
353                 my $field_cname = gen_cname($field->{name});
354                         my ($var_data, $var_iterator) =
355                                 gen_tlv_val_set("$cname.$field_cname", $field, $indent, $iterator, undef);
356
357                         $var_iterator and $use_iterator = 1;
358                         $data .= $var_data;
359                 }
360                 return $data, $use_iterator;
361         };
362
363         die "Unknown type $type";
364 }
365
366 sub gen_tlv_attr_set($$)
367 {
368         my $cname = shift;
369         my $elem = shift;
370         my $indent = "\t";
371         my $data = "";
372         my $iterator = "";
373         my $size_var = "";
374         my $id = $elem->{id};
375
376         my $cond = "req->set.$cname";
377         my ($var_data, $use_iterator) =
378                 gen_tlv_val_set("req->data.$cname", $elem, "\t\t", "i", \$cond);
379         $use_iterator and $iterator = "\t\tunsigned int i;\n";
380
381         $data = <<EOF;
382         if ($cond) {
383                 void *buf;
384                 unsigned int ofs;
385 $iterator$size_var
386                 __qmi_alloc_reset();
387 $var_data
388                 buf = __qmi_get_buf(&ofs);
389                 tlv_new(msg, $id, ofs, buf);
390         }
391
392 EOF
393         print "$data";
394 }
395
396 sub gen_set_func($$)
397 {
398         my $name = shift;
399         my $fields = shift;
400         my $data = shift;
401
402         my $type = "svc";
403         my $service = $data->{service};
404         my $id = $data->{id};
405
406         $service eq 'CTL' and $type = 'ctl';
407
408         print gen_tlv_set_func($name, $fields)."\n";
409         print <<EOF;
410 {
411         qmi_init_request_message(msg, QMI_SERVICE_$service);
412         msg->$type.message = cpu_to_le16($id);
413
414 EOF
415         foreach my $field (@$fields) {
416                 my $cname = gen_cname($field->{name});
417                 gen_tlv_attr_set($cname, $field);
418         }
419
420         print <<EOF;
421         return 0;
422 }
423
424 EOF
425 }
426
427 print <<EOF;
428 /* generated by uqmi gen-code.pl */
429 #include <stdio.h>
430 #include <string.h>
431 #include "qmi-message.h"
432
433 #define get_next(_size) ({ void *_buf = &tlv->data[ofs]; ofs += _size; if (ofs > cur_tlv_len) goto error_len; _buf; })
434 #define copy_tlv(_val, _size) \\
435         do { \\
436                 unsigned int __size = _size; \\
437                 if (__size > 0) \\
438                         memcpy(__qmi_alloc_static(__size), _val, __size); \\
439         } while (0);
440
441 #define put_tlv_var(_type, _val, _size) \\
442         do { \\
443                 _type __var = _val; \\
444                 copy_tlv(&__var, _size); \\
445         } while(0)
446
447 EOF
448
449 gen_foreach_message_type($data, \&gen_set_func, \&gen_parse_func);