File Coverage

blib/lib/Device/Chip/Adapter/UART.pm
Criterion Covered Total %
statement 44 93 47.3
branch 1 10 10.0
condition 1 9 11.1
subroutine 14 24 58.3
pod 0 13 0.0
total 60 149 40.2


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2018-2023 -- leonerd@leonerd.org.uk
5              
6 3     3   1436340 use v5.26;
  3         15  
7 3     3   23 use warnings;
  3         14  
  3         275  
8 3     3   2362 use Object::Pad 0.70;
  3         32734  
  3         231  
9              
10             package Device::Chip::Adapter::UART 0.03;
11             class Device::Chip::Adapter::UART;
12              
13             # Can't isa Device::Chip::Adapter because it doesn't have a 'new'
14 3     3   3680 use Device::Chip::Adapter;
  3         52831  
  3         290  
15             *make_protocol = \&Device::Chip::Adapter::make_protocol;
16              
17 3     3   30 use Carp;
  3         7  
  3         230  
18              
19 3     3   20 use Future;
  3         6  
  3         126  
20 3     3   1774 use Future::Buffer;
  3         11636  
  3         287  
21 3     3   810 use Future::IO 0.04; # ->syswrite_exactly
  3         46157  
  3         192  
22 3     3   2063 use IO::Termios;
  3         103255  
  3         26  
23              
24             =head1 NAME
25              
26             C - a C implementation for
27             serial devices
28              
29             =head1 DESCRIPTION
30              
31             This class implements the L interface around a regular
32             serial port, such as a USB UART adapter, allowing an instance of a
33             L driver to communicate with actual chip hardware using this
34             adapter.
35              
36             This adapter provides both the C and C protocols. The C
37             protocol wraps the modem control and handshaking lines. The C protocol
38             adds access to the transmit and receive lines by adding the L and
39             L methods.
40              
41             As the C interface is intended for hardware IO interfaces, it
42             does not support the concept that a serial stream might spontaneously become
43             disconnected. As such, an end-of-file condition on the stream filehandle will
44             be reported as a future failure.
45              
46             =cut
47              
48             =head1 CONSTRUCTOR
49              
50             =cut
51              
52             =head2 new
53              
54             $adapter = Device::Chip::Adapter::UART->new( %args )
55              
56             Returns a new instance of a C.
57              
58             Takes the following named arguments:
59              
60             =over 4
61              
62             =item dev => STRING
63              
64             Path to the device node representing the UART; usually something like
65             F or F.
66              
67             =back
68              
69             =cut
70              
71             field $_fh;
72             field %_config = (
73             bits => 8,
74             parity => "n",
75             stop => 1,
76             );
77              
78             ADJUST :params (
79             :$fh = undef,
80             :$dev = undef,
81             ) {
82             if( defined $fh ) {
83             $_fh = $fh;
84             # OK
85             }
86             else {
87             $_fh = IO::Termios->open( $dev ) or
88             die "Cannot open $dev - $!";
89              
90             $_fh->blocking( 0 );
91              
92             for( $_fh->getattr ) {
93             $_->cfmakeraw;
94             $_->setflag_clocal( 1 );
95              
96             $_fh->setattr( $_ );
97             }
98             }
99             }
100              
101             sub new_from_description
102             {
103 0     0 0 0 my $class = shift;
104 0         0 my %args = @_;
105 0         0 return $class->new( map { $_ => $args{$_} } qw( dev ) );
  0         0  
106             }
107              
108             =head1 PROTOCOLS
109              
110             The following C protocol types are supported
111              
112             =over 2
113              
114             =item *
115              
116             GPIO
117              
118             =back
119              
120             =cut
121              
122 0     0 0 0 sub make_protocol_GPIO { return Future->done( $_[0] ) }
123 0     0 0 0 sub make_protocol_UART { return Future->done( $_[0] ) }
124              
125             # Protocol implementation
126              
127             my %GPIOS_READ = (
128             DSR => 1,
129             CTS => 1,
130             CD => 1,
131             RI => 1,
132             );
133              
134 0     0 0 0 method configure ( %args )
  0         0  
  0         0  
  0         0  
135             {
136             exists $args{$_} and $_config{$_} = delete $args{$_}
137 0   0     0 for qw( baudrate bits parity stop );
138              
139 0 0       0 keys %args and
140             croak "Unrecognised configure options: " . join( ", ", keys %args );
141              
142             $_fh->set_mode( join ",",
143 0         0 @_config{qw( baudrate bits parity stop )}
144             );
145              
146 0         0 return Future->done;
147             }
148              
149 0     0 0 0 method power ( $ ) { return Future->done } # ignore
  0         0  
  0         0  
  0         0  
150              
151 0     0 0 0 method list_gpios ()
  0         0  
  0         0  
152             {
153 0         0 return qw( DTR DSR RTS CTS CD RI );
154             }
155              
156 0     0 0 0 method meta_gpios ()
  0         0  
  0         0  
157             {
158             return map {
159 0 0       0 $GPIOS_READ{$_} ?
  0         0  
160             Device::Chip::Adapter::GPIODefinition( $_, "r", 1 ) :
161             Device::Chip::Adapter::GPIODefinition( $_, "w", 1 );
162             } shift->list_gpios;
163             }
164              
165 0     0 0 0 method read_gpios ( $gpios )
  0         0  
  0         0  
  0         0  
166             {
167 0         0 my $values = $_fh->get_modem();
168              
169 0         0 my %ret;
170              
171 0         0 foreach my $gpio ( @$gpios ) {
172 0 0       0 $ret{$gpio} = $values->{lc $gpio} if $GPIOS_READ{$gpio};
173             }
174              
175 0         0 return Future->done( \%ret );
176             }
177              
178 0     0 0 0 method write_gpios ( $gpios )
  0         0  
  0         0  
  0         0  
179             {
180 0         0 my %set;
181             defined $gpios->{$_} and $set{lc $_} = $gpios->{$_}
182 0   0     0 for qw( DTR RTS );
183              
184 0 0       0 if( %set ) {
185 0         0 $_fh->set_modem( \%set );
186             }
187              
188 0         0 return Future->done;
189             }
190              
191 0     0 0 0 method tris_gpios ( $ )
  0         0  
  0         0  
192             {
193             # ignore
194 0         0 Future->done;
195             }
196              
197 1     1 0 1490 method write ( $bytes )
  1         5  
  1         3  
  1         2  
198             {
199 1         12 return Future::IO->syswrite_exactly( $_fh, $bytes );
200             }
201              
202             field $_readbuf;
203 1     1 0 2 method readbuffer ()
  1         3  
  1         2  
204             {
205 1   33     7 return $_readbuf //= do {
206 1         3 my $fh = $_fh;
207              
208             Future::Buffer->new(
209 1     1   53 fill => sub { Future::IO->sysread( $fh, 256 ) },
210 1         9 );
211             };
212             }
213              
214 1     1 0 2050 method read ( $len )
  1         5  
  1         2  
  1         2  
215             {
216             # This is a 'read_exactly'
217             return $self->readbuffer->read_exactly( $len )
218 1 50   1   4 ->then( sub { return @_ ? Future->done( @_ ) : Future->fail( "EOF" ) } );
  1         9913  
219             }
220              
221             =head1 AUTHOR
222              
223             Paul Evans
224              
225             =cut
226              
227             0x55AA;