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