support Quanta and Blackberry modes
[project/usbmode.git] / convert-modeswitch.pl
1 #!/usr/bin/perl
2 use strict;
3
4 my $msg_ctr = 0;
5 my %messages;
6 my %devices;
7
8 sub add_message {
9         my $msg = shift;
10         my $val = $messages{$msg};
11
12         defined($val) or do {
13                 $val = $msg_ctr++;
14                 $messages{$msg} = $val;
15         };
16
17         return $val;
18 }
19
20 sub add_device($) {
21         my $id = shift;
22         my $dev = {};
23         my $match;
24
25         $id =~ /^(\w{4}:\w{4})(:.*)?/ or do {
26                 warn "Invalid device ID string $id\n";
27                 return $dev;
28         };
29
30         $id = $1;
31         $match = $2 or $match = "*";
32
33         $devices{$id} or $devices{$id} = {};
34         $devices{$id}->{$match} = $dev;
35
36         return $dev;
37 }
38
39 sub add_hex {
40         $_[0] =~ s/^0x//;
41         return hex($_[0]);
42 }
43
44 sub add_mode {
45         $_[1] =~ s/^(\w+)Mode$/$1/;
46         return $_[1];
47 }
48
49 my $hex_option = [ undef, \&add_hex ];
50 my $msg_option = [ undef, \&add_message ];
51 my $mode_option = [ "Mode", \&add_mode ];
52 my %options = (
53         TargetVendor => $hex_option,
54         TargetProductList => [ "TargetProduct", sub { return [ map(hex,split(/,/, $_[0])) ]; } ],
55         TargetProduct => [ "TargetProduct", sub { return [ hex($_[0]) ]; } ],
56         TargetClass => $hex_option,
57         MessageContent => $msg_option,
58         MessageContent2 => $msg_option,
59         MessageContent3 => $msg_option,
60         WaitBefore => [ ],
61         DetachStorageOnly => [ ],
62         MBIM => $mode_option,
63         HuaweiMode => $mode_option,
64         HuaweiNewMode => $mode_option,
65         QuantaMode => $mode_option,
66         BlackberryMode => $mode_option,
67         OptionMode => $mode_option,
68         SierraMode => $mode_option,
69         SonyMode => $mode_option,
70         QisdaMode => $mode_option,
71         GCTMode => $mode_option,
72         KobilMode => $mode_option,
73         SequansMode => $mode_option,
74         MobileActionMode => $mode_option,
75         CiscoMode => $mode_option,
76         StandardEject => $mode_option,
77         NoDriverLoading => [],
78         MessageEndpoint => $hex_option,
79         ReleaseDelay => [],
80         NeedResponse => [],
81         ResponseEndpoint => $hex_option,
82         ResetUSB => [],
83         InquireDevice => [],
84         CheckSuccess => $hex_option,
85         Interface => $hex_option,
86         Configuration => $hex_option,
87         AltSetting => $hex_option,
88 );
89
90 sub parse_file($) {
91         my $file = shift;
92         my $id;
93
94         $id = $file;
95         $file =~ /\/?([^\/]+)$/ and $id = $1;
96
97         my $dev = add_device($id);
98
99         open FILE, "<$file" or die "Cannot open file '$file'\n";
100         while (<FILE>) {
101                 chomp;
102                 s/^\s*(.+?)\s*$/$1/;
103                 s/#.+$//;
104                 next unless /\w/;
105                 /(\w+)\s*=\s*(.+)\s*/ or do {
106                         warn "Invalid Line: $_";
107                         next;
108                 };
109
110                 my ($var, $val) = ($1, $2);
111                 $val =~ s/^"(.+)"$/$1/;
112
113                 my $opt = $options{$var};
114                 $opt or do {
115                         warn "Unrecognized option $var in file $file\n";
116                         next;
117                 };
118
119                 $opt->[1] and $val = &{$opt->[1]}($val, $var);
120                 $opt->[0] and $var = $opt->[0];
121                 $dev->{$var} = $val;
122         }
123 }
124
125 foreach my $file (@ARGV) {
126         parse_file $file;
127 }
128
129 sub json_chr {
130         my $chr = shift;
131
132         $chr eq "\b" and return "\\b";
133         $chr eq "\n" and return "\\n";
134         $chr eq "\r" and return "\\r";
135         $chr eq "\t" and return "\\t";
136         $chr eq "\\" and return "\\\\";
137         $chr eq "\"" and return "\\\"";
138         $chr eq '/' and return "\\/";
139         return sprintf("\\u%04x", ord($chr));
140 };
141
142 sub json_str {
143         $_[0] =~ s/([\x00- \/"\\])/json_chr($1)/eg;
144         return $_[0];
145 }
146
147 sub json_val($$) {
148         my ($val, $type) = (shift, shift);
149         $type eq 'bool' and $val = $val > 0 ? "true" : "false";
150         $type eq 'string' and $val = "\"$val\"";
151         return $val;
152 }
153
154 sub dev_opt {
155         my ($val, $name, $type, $sep) = (shift, shift, shift, shift);
156         return unless defined($val);
157         if ($type =~ /array:(.+)/) {
158                 $type = $1;
159                 my @val = @$val;
160                 undef $val;
161                 foreach my $elem (@val) {
162                         my $json = json_val($elem, $type);
163                         next unless defined $json;
164                         if (defined $val) {
165                                 $val = "$val, $json"
166                         } else {
167                                 $val = $json;
168                         }
169                 }
170                 $val = "[ $val ]";
171         } else {
172                 $val = json_val($val, $type);
173         }
174         print "$$sep\t\t\t\t\"".json_str($name)."\": $val";
175         $$sep = ",\n";
176 }
177
178 print <<EOF;
179 {
180         "messages" : [
181 EOF
182 my $suffix = "";
183 foreach my $msg (sort { $messages{$a} <=> $messages{$b} } keys %messages) {
184         print "$suffix\t\t\"".json_str($msg)."\"";
185         $suffix = ",\n";
186 }
187 print <<EOF;
188
189         ],
190
191         "devices" : {
192 EOF
193 my $dev_sep = "";
194 foreach my $devid (sort keys %devices) {
195         my $dev = $devices{$devid};
196
197         print "$dev_sep\t\t\"".json_str($devid)."\": {\n";
198         $dev_sep = ",\n";
199
200         my $match_sep = "";
201         foreach my $match (sort keys %$dev) {
202                 my $cur = $dev->{$match};
203                 my $sep = "";
204
205                 print "$match_sep\t\t\t\"".json_str($match)."\": {\n";
206                 $match_sep = ",\n";
207
208                 dev_opt($cur->{TargetVendor}, "t_vendor", "int", \$sep);
209                 dev_opt($cur->{TargetProduct}, "t_product", "array:int", \$sep);
210                 dev_opt($cur->{TargetClass}, "t_class", "int", \$sep);
211                 dev_opt($cur->{DetachStorageOnly}, "detach_storage", "bool", \$sep);
212                 dev_opt($cur->{Mode}, "mode", "string", \$sep);
213                 dev_opt($cur->{NoDriverLoading}, "no_driver", "bool", \$sep);
214                 dev_opt($cur->{MessageEndpoint}, "msg_endpoint", "int", \$sep);
215                 my $msg = [
216                         $cur->{MessageContent},
217                         $cur->{MessageContent2},
218                         $cur->{MessageContent3}
219                 ];
220                 dev_opt($msg, "msg", "array:int", \$sep);
221                 dev_opt($cur->{WaitBefore}, "wait", "int", \$sep);
222                 dev_opt($cur->{ReleaseDelay}, "release_delay", "int", \$sep);
223                 dev_opt($cur->{NeedResponse}, "response", "bool", \$sep);
224                 dev_opt($cur->{ResponseEndpoint}, "response_endpoint", "int", \$sep);
225                 dev_opt($cur->{ResetUSB}, "reset", "bool", \$sep);
226                 dev_opt($cur->{InquireDevice}, "inquire", "int", \$sep);
227                 dev_opt($cur->{CheckSuccess}, "check", "bool", \$sep);
228                 dev_opt($cur->{Interface}, "interface", "int", \$sep);
229                 dev_opt($cur->{Configuration}, "config", "int", \$sep);
230                 dev_opt($cur->{AltSetting}, "alt", "int", \$sep);
231                 print "\n\t\t\t}";
232         }
233         print "\n\t\t}"
234 }
235 print <<EOF;
236
237         }
238 }
239 EOF