remove empty lines after kernel annotation
[linux-gpio-pinout] / gpio.pl
1 #!/usr/bin/perl
2 use warnings;
3 use strict;
4 use autodie;
5 use Data::Dump qw(dump);
6 use Getopt::Long;
7
8 my $opt_svg = 0;
9 my $opt_alt = 0;
10 my $opt_invert = 0;
11 my $opt_vertical = 0;
12 my $opt_horizontal = 0;
13 my $opt_edge = 0;
14 my $opt_middle = 0;
15 my $opt_zebra = 0;
16 my $opt_lines = 0;
17 my $opt_read = '';
18 my $opt_pins = '';
19 my $opt_color = 0;
20 GetOptions(
21         'svg!' => \$opt_svg,
22         'alt!' => \$opt_alt,
23         'invert!' => \$opt_invert,
24         'vertical-flip!' => \$opt_vertical,
25         'horizontal-flip!' => \$opt_horizontal,
26         'edge-pins!' => \$opt_edge,
27         'middle-pins!' => \$opt_middle,
28         'zebra!' => \$opt_zebra,
29         'lines!' => \$opt_lines,
30         'read=s' => \$opt_read,
31         'pins=s' => \$opt_pins,
32         'color' => \$opt_color,
33 );
34
35 # svg font hints
36 my $font_w = 1.67; # < 2.54, font is not perfect square
37 my $font_b = 2.10; # font baseline position
38
39 $opt_read .= '/' unless $opt_read =~ m/\/$/;
40
41 sub slurp {
42         open(my $fh, '<', $opt_read . shift);
43         local $/ = undef;
44         <$fh>;
45 }
46
47 my $pins;
48
49 my $model = slurp('/proc/device-tree/model');
50 $model =~ s/\x00$//; # strip kernel NULL
51 warn "# model [$model]";
52
53 open(DATA, '<', $opt_pins) if $opt_pins;
54
55 my @lines;
56 my $line_i = 0;
57
58 my $include = 0;
59 while(<DATA>) {
60         chomp;
61         if ( m/^#\s(.+)/ ) {
62                 warn "MODEL [$1] == [$model] ?\n";
63                 if ( $model =~ m/$1/ ) {
64                         $include = 1;
65                 } else {
66                         $include = 0;
67                 }
68         } elsif ( $include || $opt_pins ) {
69                 push @{ $pins->{$1} }, $line_i while ( m/\t(\w+\d+)/g );
70
71                 push @lines, $_;
72
73                 $line_i++;
74         } else {
75                 warn "IGNORE: [$_]\n";
76         }
77 }
78
79 die "add pin definition for # $model" unless $pins;
80
81 #warn "# lines ",dump( \@lines );
82 warn "# pins ",dump($pins);
83
84 my $serial_tty;
85 foreach ( glob $opt_read . '/sys//devices/platform/soc*/*.serial/tty/tty*' ) {
86         my @v = split(/\//, $_);
87         $serial_tty->{ $v[-3] } = $v[-1];
88 }
89 warn "# serial_tty = ",dump($serial_tty);
90
91
92 my $pin_function;
93 my $device;
94
95 open(my $fh, '<', $opt_read . '/sys/kernel/debug/pinctrl/pinctrl-handles');
96 while(<$fh>) {
97         chomp;
98         if ( m/device: (\S+)/ ) {
99                 $device = $1;
100                 if ( my $replace = $serial_tty->{$device} ) {
101                         $device = $replace; # replace serial hex with kernel name
102                 } else {
103                         $device =~ s/^[0-9a-f]*\.//; # remove hex address
104                 }
105         } elsif ( m/group: (\w+\d+)\s.+function: (\S+)/ ) {
106                 my ($pin, $function) = ($1,$2);
107                 $pin_function->{$pin} = "$device $function";
108
109                 if ( $pins->{$pin} ) {
110                         foreach my $line ( @{$pins->{$pin}} ) {
111                                 my $t = $lines[$line];
112                                 if ( $opt_svg ) {
113                                         $t =~ s/$pin/[$device $function]/;
114                                 } else {
115                                         $t =~ s/$pin/$pin [$device $function]/ || warn "can't find $pin in [$t]";
116                                 }
117                                 $lines[$line] = $t;
118                                 warn "# $line: $lines[$line]\n";
119                         }
120                 } else {
121                         warn "IGNORED: pin $pin function $function\n";
122                 }
123         }
124 }
125
126 warn "# pin_function = ",dump($pin_function);
127
128 my @max_len = ( 0,0,0,0 );
129 my @line_parts;
130
131 shift(@lines) while ( ! $lines[0] );    # remove empty at beginning
132 pop(@lines) while ( ! $lines[-1] );     # remove empty at end
133
134 foreach my $line (@lines) {
135         if ( $line =~ m/^#/ ) {
136                 push @line_parts, [ $line ] unless $opt_svg && $line =~ m/^###+/; # SVG doesn't display 3rd level comments
137                 next;
138         }
139         $line =~ s/(\[(?:uart|serial|tty\w+))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
140         $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
141         $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
142         $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
143
144         # shorten duplicate kernel device/function
145         $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
146         $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
147
148         my @v = split(/\s*\t+\s*/,$line,4);
149         @v = ( $v[2], $v[3], $v[0], $v[1] ) if $opt_horizontal && $v[2];
150
151         push @line_parts, [ @v ];
152         foreach my $i ( 0 .. 3 ) {
153                 next unless exists $v[$i];
154                 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
155                 my $l = length($v[$i]);
156                 $max_len[$i] = $l if $l > $max_len[$i];
157         }
158 }
159
160 warn "# max_len = ",dump( \@max_len );
161 warn "# line_parts = ",dump( \@line_parts );
162
163 #print "$_\n" foreach @lines;
164
165 my $x = 20.00; # mm
166 my $y = 20.00; # mm
167
168 if ( $opt_svg ) {
169         print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
170 <svg
171    xmlns:dc="http://purl.org/dc/elements/1.1/"
172    xmlns:cc="http://creativecommons.org/ns#"
173    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
174    xmlns:svg="http://www.w3.org/2000/svg"
175    xmlns="http://www.w3.org/2000/svg"
176    xmlns:xlink="http://www.w3.org/1999/xlink"
177    id="svg8"
178    version="1.1"
179    viewBox="0 0 210 297"
180    height="297mm"
181    width="210mm">
182
183
184 <g id="layer1">
185
186         }; # svg, insert rest of rect
187
188         print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
189 }
190
191 my @later;
192
193 my $cols = {    # foreground background
194         txt  => [ '#000000', '#ffffff' ],
195         pins => [ '#ffffff', '#ff00ff' ],
196         vcc  => [ '#ff0000', '#ffff00' ],
197         gnd  => [ '#000000', '#00ffff' ],
198         i2c  => [ '#008800', '#ffcccc' ],
199         serial=>[ '#000088', '#ccffcc' ],
200         spi  => [ '#880000', '#ccccff' ],
201 };
202
203 sub swap_cols {
204         my $swap = shift;
205         die "$swap not found in ",dump($cols) unless $cols->{$swap};
206         my ( $c1, $c2 ) = @{ $cols->{$swap} };
207         $cols->{$swap} = [ $c2, $c1 ];
208 }
209
210 swap_cols 'txt' if $opt_invert;
211         
212
213 sub svg_style {
214         my ($name,$x,$y,$col) = @_;
215
216         return '' unless $opt_color;
217
218         $y -= $font_b; # shift box overlay to right vertical position based on font baseline
219
220         sub rect {
221                 my ($x,$y,$col,$fill) = @_;
222                 print qq{<rect x="$x" y="$y" height="2.54" width="}, $max_len[$col] * $font_w, qq{" style="fill:$fill;stroke:#ffffff;stroke-width:0.10" />\n};
223
224         }
225
226         if ( $name =~ m/^(\d+)$/ ) { # pins
227                 my $pin = $1;
228                 my ( $fg, $bg ) = @{ $cols->{pins} };
229                 if ( $pin == 1 ) {
230                         my $w  = $max_len[$col]*$font_w - 0.1;
231                         my $cx = $x + $w;
232                         my $cy = $y + 2.54;
233                         #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
234                         #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
235                         print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
236                         my ( $fg, $bg ) = @{ $cols->{txt} };
237                         print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
238                 } else {
239                         rect $x,$y,$col,$fg;
240                 }
241                 return qq{ style="fill:$bg"};
242         }
243
244         if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
245                 my ($fg,$bg) = @{ $cols->{vcc} };
246                 rect $x,$y,$col,$bg;
247                 return qq{ style="fill:$fg"};
248         } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
249                 my ($fg,$bg) = @{ $cols->{gnd} };
250                 rect $x,$y,$col,$bg;
251                 return qq{ style="fill:$fg"};
252         } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
253                 my $dev = $1;
254                 my ($fg,$bg) = @{ $cols->{txt} };
255                 $dev = 'serial' if $dev =~ m/^tty/;
256                 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
257                 rect $x,$y,$col,$bg;
258                 return qq{ style="fill:$fg"};
259         } else {
260                 my ( $fg, $bg ) = @{ $cols->{txt} };
261                 rect $x,$y,$col,$bg;
262                 #return qq{ style="fill:$fg"};
263                 return '';
264         }
265 }
266
267 my $alt_col = 0;
268
269 my @cols_order = ( 0,1,2,3 );
270 my @cols_align = ( '','-','','-' ); # sprintf prefix
271
272 my @cols_shuffle = @cols_order;
273
274 if ( $opt_edge ) {
275         # pins outside on the right
276         @cols_shuffle = ( 0,1,3,2 ) if $opt_edge;
277         @cols_align = ( '-','-','','' );
278 } elsif ( $opt_middle ) {
279         # pins in middle
280         @cols_shuffle = ( 1,0,2,3 );
281         @cols_align = ( '','','-','-' );
282 }
283
284 sub cols_shuffle {
285         my ( $what, $order ) = @_;
286         my $new = [];
287         foreach my $i ( 0 .. $#$what ) {
288                 $new->[$i] = $what->[ $order->[$i] ];
289         }
290         warn "# cols_shuffle what=",dump($what)," order=",dump($order)," new=",dump($new);
291         return @$new;
292 }
293
294 @cols_order = cols_shuffle( \@cols_order, \@cols_shuffle );
295 @max_len    = cols_shuffle( \@max_len,    \@cols_shuffle );
296
297 warn "# cols_order = ",dump( \@cols_order );
298 warn "# cols_align = ",dump( \@cols_align );
299
300 my $fmt = "%$cols_align[0]$max_len[0]s %$cols_align[1]$max_len[1]s %$cols_align[2]$max_len[2]s %$cols_align[3]$max_len[3]s\n";
301
302
303 # cut marks
304 my ($fg,$bg) = @{ $cols->{txt} };
305 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
306
307 my @cut_marks;
308 sub cut_mark {
309         my ($x,$y) = @_;
310         return unless $opt_svg;
311         push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b,   $x+5, $y-$font_b);
312         push @cut_marks, sprintf($line_fmt, $x,   $y-$font_b-5, $x,   $y-$font_b+5);
313 }
314 #cut_mark $x, $y;
315 my $max_x = $x;
316 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
317 #cut_mark $max_x, $y;
318
319 sub line {
320         my ($x,$y,$max_x) = @_;
321         push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
322 }
323
324
325 my $last_cut_mark = 0;
326
327 foreach my $i ( 0 .. $#line_parts ) {
328         $i = $#line_parts - $i if $opt_vertical;
329         my $line = $line_parts[$i];
330
331         if ( $opt_svg ) {
332
333                 # not a minimal two column pin description
334                 if ( ! exists $line->[1] ) {
335                         $last_cut_mark = 1 if $line->[0] =~ m/^##/; # skip comments
336
337                         # before first empty line
338                         if ( $last_cut_mark == 0 ) {
339                                 cut_mark $x, $y;
340                                 cut_mark $max_x, $y;
341                                 $last_cut_mark = 1;
342                                 line $x, $y, $max_x if $opt_lines;
343                                 $y += 15; # make spacing between pinouts
344                         }
345                 } elsif ( $last_cut_mark ) {
346                         # first full line
347                         cut_mark $x, $y;
348                         cut_mark $max_x, $y;
349                         $last_cut_mark = 0;
350                 } else {
351                         #warn "CUTMARK no magic";
352                 }
353
354                 line $x, $y, $max_x if $opt_lines && exists $line->[1];
355
356                 my ($fg,$bg) = @{ $cols->{txt} };
357                 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
358
359                 my $x_pos = $x;
360                 foreach my $i ( 0 .. $#cols_order ) {
361                         my $order = $cols_order[$i];
362                         next unless $line->[$order];
363
364                         my $text_anchor = 'middle';
365                         my $len = $max_len[$i];
366                         my $x2 = $x_pos + ( $len * $font_w ) / 2;
367                         # is this comment?
368                         if ( $#$line == 0 && $line->[$order] =~ s/^#+s*// ) {
369                                 # comment, center over whole width
370                                 $len = length($line->[$order]);
371                                 $x2 = $x + (($max_x-$x)/2); # middle
372                                 $tspan .= qq{\t<tspan x="$x2" text-anchor="$text_anchor"}.sprintf( '>%' . $cols_align[$i] . $len . 's</tspan>', $line->[0]);
373                         } else {
374                                 $tspan .= qq{\t<tspan x="$x2" text-anchor="$text_anchor"}.svg_style($line->[$order],$x_pos,$y,$i).sprintf( '>%' . $cols_align[$i] . $len . 's</tspan>', $line->[$order]);
375                         }
376                         $x_pos += $len * $font_w;
377                 }
378
379                 $tspan .= qq{\n</tspan>\n};
380                 push @later,sprintf $tspan, @$line;
381                 $y += 2.54;
382
383                 # swap pin colors for line stripe
384                 if ( $opt_zebra ) {
385                         swap_cols $_ foreach qw( pins txt );
386                 } else {
387                         swap_cols 'pins';
388                 }
389
390         } else {
391
392                 if ( $#$line == 0 ) {
393                         print $line->[0], "\n";
394                 } else {
395                         push @$line, '' while ($#$line < 3); # fill-in single row header
396                         printf $fmt, map { $line->[$_] } @cols_order;
397                 }
398
399         }
400 }
401
402 if ( $opt_svg ) {
403         cut_mark $x,$y;
404         cut_mark $max_x,$y;
405         line $x, $y, $max_x if $opt_lines;
406
407         print qq{
408     <text
409        id="text4506"
410        y="$x"
411        x="$y"
412        style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
413        xml:space="preserve">
414
415         }; #svg
416
417         print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
418
419 }
420
421 __DATA__
422 # Cubietech Cubieboard
423 ## U14 (Next to SATA connector)
424 ###     SPI0
425 48      PI13 (SPI0-MISO/UART6-RX/EINT25)        47      PI11 (SPI0-CLK/UART5-RX/EINT23)
426 46      PI12 (SPI0-MOSI/UART6-TX/EINT24)        45      PI10 (SPI0-CS/UART5-TX/EINT22)
427 ###     LCD
428 44      3.3V (nc in 2012-08-08)                 43      VCC-5V
429 42      Ground                                  41      SPDIF
430 40      PB10 (LCD0-SCK/LCD-PIO1)                39      PB11 (LCD0-SDA/LCD-PIO2)
431 38      Ground                                  37      PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
432 36      XN_TP (TP-X2)                           35      YN_TP (TP-Y2)
433 34      XP_TP (TP-X1)                           33      YP_TP (TP-Y1)
434 32      PD25 (LCDDE)                            31      PB2 (PWM0)
435 30      PD26 (LCDHSYNC/VGA-HSYNC)               29      PD24 (LCDCLK)
436 28      PD23 (LCDD23)                           27      PD27 (LCDVSYNC/VGA-VSYNC)
437 26      PD21 (LCDD21)                           25      PD22 (LCDD22)
438 24      PD19 (LCDD19/LVDS1N3)                   23      PD20 (LCDD20)
439 22      PD17 (LCDD17/LVDS1NC)                   21      PD18 (LCDD18/LVDS1P3)
440 20      Ground                                  19      PD16 (LCDD16/LVDS1PC)
441 18      PD14 (LCDD14/LVDS1P2)                   17      PD15 (LCDD15/LVDS1N2)
442 16      PD12 (LCDD12/LVDS1P1)                   15      PD13 (LCDD13/LVDS1N1)
443 14      PD10 (LCDD10/LVDS1P0)                   13      PD11 (LCDD11/LVDS1N0)
444 12      PD8 (LCDD8/LVDS0P3)                     11      PD9 (LCDD9/LVDS0N3)
445 10      PD7 (LCDD7/LVDS0NC)                     9       Ground
446 8       PD5 (LCDD5/LVDS0N2)                     7       PD6 (LCDD6/LVDS0PC)
447 6       PD3 (LCDD3/LVDS0N1)                     5       PD4 (LCDD4/LNVS0P2)
448 4       PD1 (LCDD1/LVDS0N0)                     3       PD2 (LCDD2/LVDS0P1)
449 2       Ground                                  1       PD0 (LCDD0/LVDSP0)
450
451 ## U15 (Between Ethernet port and USB ports)
452 ### CSI1/TS
453 1       VCC-5V                                  2       PH15 (CSI1-PWR/EINT15)
454 3       CSI1-IO-2V8                             4       PH14 (CSI1-RST#/EINT14)
455 5       PG0 (CSI1-PCLK/SDC1-CMD)                6       PB18 (TWI1-SCK)
456 7       PB19 (TWI1-SDA)                         8       PG3 (CSI1-VSYNC/SDC1-D1)
457 9       PG2 (CSI1-HSYNC/SDC1-D0)                10      PG1 (CSI1-MCLK/SDC1-CLK)
458 11      PG4 (CSI1-D0/SDC1-D2)                   12      PG5 (CSI1-D1/SDC1-D3)
459 13      PG6 (CSI1-D2/UART3-TX)                  14      PG7 (CSI1-D3/UART3-RX)
460 15      PG8 (CSI1-D4/UART3-RTS)                 16      PG9 (CSI1-D5/UART3-CTS)
461 17      PG10 (CSI1-D6/UART4-TX)                 18      PG11 (CSI1-D7/UART4-RX)
462 19      Ground                                  20      Ground
463 ###     Analog SDIO3
464 21      FMINL                                   22      PI4 (SDC3-CMD)
465 23      FMINR                                   24      PI5 (SDC3-CLK)
466 25      Ground                                  26      PI6 (SDC3-D0)
467 27      VGA-R                                   28      PI7 (SDC3-D1)
468 29      VGA-G                                   30      PI8 (SDC3-D2)
469 31      VGA-B                                   32      PI9 (SDC3-D3)
470 ###     CSI0/TS
471 33      LCD1-VSYNC                              34      PE4 (CSI0-D0)
472 35      LCD1-HSYNC                              36      PE5 (CSI0-D1)
473 37      Ground                                  38      PE6 (CSI0-D2)
474 39      AVCC                                    40      PE7 (CSI0-D3)
475 41      LRADC0                                  42      PE8 (CSI0-D4)
476 43      CVBS                                    44      PE9 (CSI0-D5)
477 45      HPL                                     46      PE10 (CSI0-D6)
478 47      HPR                                     48      PE11 (CSI0-D7)
479
480 ## DEBUG serial (middle of board)
481 4       PB22 (UART0-TX)
482 3       PB23 (UART0-RX)
483 2       VCC-3V3
484 1       GND
485
486
487 # Lamobo R1
488 ## CON3 rpi DIP26-254
489 1       3.3v                    2       5v     
490 3       PB20 SDA.1              4       5V     
491 5       PB21 SCL.1              6       0v     
492 7       PI3 PWM1                8       PH0 UART3_TX
493 9       0v                      10      PH1 UART3_RX
494 11      PI19 UART2_RX           12      PH2
495 13      PI18 UART2_TX           14      0v     
496 15      PI17 UART2_CTS          16      PH21 CAN_RX 
497 17      3.3v                    18      PH20 CAN_TX 
498 19      PI12 SPI0_MOSI          20      0v     
499 21      PI13 SPI0_MISO          22      PI16 UART2_RTS   
500 23      PI11 SPI0_SCLK          24      PI10 SPI0_CS0    
501 25      0v                      26      PI14 SPI0_CS1
502
503 ## J13 DIP2-254
504 2       PB22 UART0_TX
505 1       PB23 UART0_RX
506
507 ## J12 DIP8-254
508 8       GND                     7       GND
509 6       PI20 UART7_TX           5       PH3
510 4       PI21 UART7_RX           3       PH5
511 2       3V3                     1       SATA-5V
512
513 # Raspberry Pi
514 1       3.3v                    2       5v
515 3       gpio2 (SDA.1)           4       5v
516 5       gpio3 (SCL.1)           6       0v
517 7       gpio4 (WPi 7)           8       gpio14  (TxD)
518 9       0v                      10      gpio15  (RxD)
519 11      gpio17 (WPi 0)          12      gpio18  (WPi 1)
520 13      gpio27 (WPi 2)          14      0v
521 15      gpio22 (WPi 3)          16      gpio23  (WPi 4)
522 17      3.3v                    18      gpio24  (WPi 5)
523 19      gpio10 (MOSI)           20      0v
524 21      gpio9 (MISO)            22      gpio25  (WPi 6) 
525 23      gpio11 (SCLK)           24      gpio8   (CE0)
526 25      0v                      26      gpio7   (CE1)
527 # Raspberry Pi 3 Model B Rev 1.2
528 27      gpio0 (SDA.0)           28      gpio1   (SCL.0)
529 29      gpio5 (WPi 21)          30      0v
530 31      gpio6 (WPi 22)          32      gpio12  (WPi 26)
531 33      gpio13 (WPi 23)         34      0v
532 35      gpio19 (WPi 24)         36      gpio16  (WPi 27)
533 37      gpio26 (WPi 25)         38      gpio20  (WPi 28)
534 39      0v                      40      gpio21  (WPi 29)
535