File Coverage

blib/lib/USB/Descriptor/Endpoint.pm
Criterion Covered Total %
statement 15 119 12.6
branch 0 66 0.0
condition 0 15 0.0
subroutine 5 18 27.7
pod 11 11 100.0
total 31 229 13.5


line stmt bran cond sub pod time code
1             package USB::Descriptor::Endpoint;
2              
3 1     1   6 use strict;
  1         2  
  1         35  
4 1     1   5 use warnings;
  1         2  
  1         27  
5 1     1   30 use 5.010; # Require perl 5.10 to enable the switch feature
  1         4  
  1         92  
6              
7             our $VERSION = '2'; # Bump this when the interface changes
8              
9 1     1   2263 use overload '@{}' => \&bytes;
  1         1413  
  1         19  
10              
11 1         6906 use constant fields => qw(
12             bLength bDescriptorType bEndpointAddress bmAttributes wMaxPacketSize
13             bInterval
14 1     1   75 );
  1         2  
15              
16             =head1 NAME
17              
18             USB::Descriptor::Endpoint - USB Endpoint Descriptor
19              
20             =head1 SYNOPSIS
21              
22             An object representation of a USB endpoint descriptor.
23              
24             use USB::Descriptor::Endpoint;
25              
26             my $endpoint = USB::Descriptor::Endpoint->new( address => 1 );
27             $endpoint->type('interrupt');
28             $endpoint->direction('in');
29             ...
30              
31             =head1 DESCRIPTION
32              
33             L represents a USB interface descriptor. When added
34             to the descriptor tree of a L object it can be used to
35             generate the data structures needed to compile the firmware for a USB device.
36              
37             =head1 CONSTRUCTOR
38              
39             =over
40              
41             =item $endpoint = USB::Descriptor::Endpoint->new(interval=>$interval, ...);
42              
43             Constructs and returns a new L object using the
44             passed options. Each option key is the name of an accessor method.
45              
46             When constructing endpoint objects, the endpoint direction and endpoint
47             number can be specified with a single key/value pair. For example, you can use
48             C<< new('in' => 3) >> instead of C<< new('direction' => 'in', 'number' => 3) >>.
49              
50             =back
51              
52             =cut
53              
54             sub new
55             {
56 0     0 1   my ($this, %options) = @_;
57 0   0       my $class = ref($this) || $this;
58 0           my $self =
59             {
60             'address' => 0, # Endpoint 0 OUT is invalid
61             'attributes' => 0, # Control
62             'bMaxPacketSize'=> 0,
63             'bInterval' => 10, # 10ms at low/full speed, 125ms at high speed
64             };
65 0           bless $self, $class;
66              
67 0           while( my ($key, $value) = each %options )
68             {
69             # Handle the 'direction => number' shortcut for specifying endpoints
70 0 0 0       if( ($key eq 'in') or ($key eq 'out') )
71             {
72 0           $self->direction($key);
73 0           $self->number($value);
74             }
75             else
76             {
77 0           $self->$key($value);
78             }
79             }
80              
81 0           return $self;
82             }
83              
84             =head1 Arrayification
85              
86             =over
87              
88             =item $endpoint->bytes (or @{$interface} )
89              
90             Returns an array of bytes containing all of the fields in the endpoint
91             descriptor.
92              
93             =back
94              
95             =cut
96              
97             sub bytes
98             {
99 0     0 1   my $s = shift;
100              
101 0           my @bytes;
102              
103 0           push @bytes, 7; # Endpoint descriptors are 7 bytes long
104 0           push @bytes, 0x05; # bDescriptorType
105 0           push @bytes, $s->address; # bEndpointAddress
106 0           push @bytes, $s->attributes; # bmAttributes
107 0           push @bytes, $s->max_packet_size & 0xFF; # wMaxPacketSize low
108 0           push @bytes, ($s->max_packet_size >> 8) & 0xFF; # wMaxPacketSize high
109              
110             # Force interval=1 for isochronous endpoints
111 0 0         $s->interval(1) if( 1 == ($s->{'attributes'} & 0x03) );
112              
113 0           push @bytes, $s->interval; # bInterval
114              
115 0 0         warn "Endpoint descriptor length is wrong" unless $bytes[0] == scalar @bytes;
116              
117 0           return \@bytes;
118             }
119              
120             =head1 ATTRIBUTES
121              
122             =over
123              
124             =item $interface->address
125              
126             Direct access to the bEndpointAddress value. Don't use this unless you know what
127             you're doing.
128              
129             =item $interface->attributes
130              
131             Direct access to the bmAttributes value. Don't use this unless you know what
132             you're doing.
133              
134             =item $interface->direction
135              
136             Get/Set the endpoint's direction (bEndpointAddress). Pass 'in' for an IN
137             endpoint or 'out' for an OUT endpoint.
138              
139             =item $interface->interval
140              
141             Get/Set the endpoint's polling interval in frame counts (bInterval). Forced to
142             1 for isochronous endpoints as required by the USB specification.
143              
144             =item $interface->max_packet_size
145              
146             Get/Set the endpoint's maximum packet size (wMaxPacketSize).
147              
148             =item $interface->number
149              
150             Get/Set the endpoint number (bEndpointAddress).
151              
152             =item $interface->synchronization_type
153              
154             Get/Set the endpoint's synchronization type (bmAttributes). Only used by
155             isochronous endpoints.
156              
157             =item $interface->type
158              
159             Get/Set the endpoint's type (bmAttributes). Valid values are 'control',
160             'isochronous', 'bulk', and 'interrupt'.
161              
162             =item $interface->usage_type
163              
164             Get/Set the endpoint's usage type (bmAttributes). Only used by isochronous
165             endpoints.
166              
167             =back
168              
169             =cut
170              
171             sub address
172             {
173 0     0 1   my $s = shift;
174 0 0         $s->{'address'} = int(shift) & 0xFF if scalar @_;
175 0           $s->{'address'};
176             }
177              
178             sub attributes
179             {
180 0     0 1   my $s = shift;
181 0 0         $s->{'attributes'} = int(shift) & 0xFF if scalar @_;
182 0           $s->{'attributes'};
183             }
184              
185             sub direction
186             {
187 0     0 1   my $s = shift;
188 0 0         if( scalar @_ )
189             {
190 0           my $d = shift;
191 0           given($d)
192             {
193 0           when('in') { $s->{'address'} |= 0x80; } # Set the direction bit for IN
  0            
194 0           when('out') { $s->{'address'} &= ~0x80; } # Clear the direction bit for OUT
  0            
195             }
196             }
197 0 0         ($s->{'address'} & 0x80) ? 'in' : 'out';
198             }
199              
200             sub interval
201             {
202 0     0 1   my $s = shift;
203 0 0         $s->{'bInterval'} = int(shift) & 0xFF if scalar @_;
204 0           $s->{'bInterval'};
205             }
206              
207             sub max_packet_size
208             {
209 0     0 1   my $s = shift;
210 0 0         $s->{'bMaxPacketSize'} = int(shift) & 0xFF if scalar @_;
211 0           $s->{'bMaxPacketSize'};
212             }
213              
214             sub number
215             {
216 0     0 1   my $s = shift;
217 0 0         $s->{'address'} = ($s->{'address'} & ~0x0F) | (int(shift) & 0x0F) if scalar @_;
218 0           $s->{'address'} & 0x0F;
219             }
220              
221             sub synchronization_type
222             {
223 0     0 1   my $s = shift;
224 0 0         if( scalar @_ )
225             {
226 0           my $a = shift;
227 0           my $masked = $s->{'attributes'} & ~0x0C;
228 0           given($a)
229             {
230 0           when('none') { $s->{'attributes'} = $masked; }
  0            
231 0           when('asynchronous'){ $s->{'attributes'} = $masked | (0x01 << 2); }
  0            
232 0           when('adaptive' ) { $s->{'attributes'} = $masked | (0x02 << 2); }
  0            
233 0           when('synchronous') { $s->{'attributes'} = $masked | (0x03 << 2); }
  0            
234             }
235             }
236              
237 0           my $t = ($s->{'attributes'} & 0x0C) >> 2;
238              
239 0 0         if( 0 == $t ) { 'none'; }
  0 0          
    0          
    0          
240 0           elsif( 1 == $t) { 'asynchronous'; }
241 0           elsif( 2 == $t) { 'adaptive'; }
242 0           elsif( 3 == $t) { 'synchronous'; }
243 0           else { undef; }
244             }
245              
246             sub type
247             {
248 0     0 1   my $s = shift;
249 0 0         if( scalar @_ )
250             {
251 0           my $d = shift;
252 0           my $masked = $s->{'attributes'} & ~0x03;
253 0 0         if( $d eq 'control' )
    0          
    0          
    0          
254             {
255 0           $s->{'attributes'} = $masked;
256             }
257             elsif( $d eq 'isochronous' )
258             {
259 0           $s->{'attributes'} = $masked | 0x01;
260 0           $s->interval(1); # Isochronous endpoints must have interval == 1
261             }
262             elsif( $d eq 'bulk' )
263             {
264 0           $s->{'attributes'} = $masked | 0x02;
265             }
266             elsif( $d eq 'interrupt' )
267             {
268 0           $s->{'attributes'} = $masked | 0x03;
269             }
270             }
271              
272 0           my $t = $s->{'attributes'} & 0x03;
273              
274 0 0         if( 0 == $t ) { 'control'; }
  0 0          
    0          
    0          
275 0           elsif( 1 == $t) { 'isochronous'; }
276 0           elsif( 2 == $t) { 'bulk'; }
277 0           elsif( 3 == $t) { 'interrupt'; }
278 0           else { undef; }
279             }
280              
281             sub usage_type
282             {
283 0     0 1   my $s = shift;
284 0 0         if( scalar @_ )
285             {
286 0           my $a = shift;
287 0           my $masked = $s->{'attributes'} & ~0x0C;
288 0 0         if( $a eq 'data' )
    0          
    0          
289             {
290 0           $s->{'attributes'} = $masked;
291             }
292             elsif( $a eq 'feedback' )
293             {
294 0           $s->{'attributes'} = $masked | (0x01 << 2);
295             }
296             elsif( $a eq 'explicit' )
297             {
298 0           $s->{'attributes'} = $masked | (0x02 << 2);
299             }
300             }
301              
302 0           my $t = ($s->{'attributes'} & 0x0C) >> 2;
303              
304 0 0         if( 0 == $t ) { 'data'; }
  0 0          
    0          
305 0           elsif( 1 == $t) { 'feedback'; }
306 0           elsif( 2 == $t) { 'explicit'; }
307 0           else { undef; }
308             }
309              
310              
311             # --- String Descriptor support ---
312              
313             sub _index_for_string
314             {
315 0     0     my ($s, $string) = @_;
316 0 0 0       if( defined($string) and length($string) and defined($s->_parent) )
      0        
317             {
318 0           return $s->_parent->_index_for_string($string);
319             }
320 0           return 0;
321             }
322              
323             sub _parent
324             {
325 0     0     my $s = shift;
326 0 0 0       $s->{'parent'} = shift if scalar(@_) && $_[0]->can('_index_for_string');
327 0           $s->{'parent'};
328             }
329              
330             1;
331              
332             =head1 AUTHOR
333              
334             Brandon Fosdick, C<< >>
335              
336              
337             =head1 BUGS
338              
339             Please report any bugs or feature requests to C, or through
340             the web interface at L. I will be notified, and then you'll
341             automatically be notified of progress on your bug as I make changes.
342              
343              
344             =head1 SUPPORT
345              
346             You can find documentation for this module with the perldoc command.
347              
348             perldoc USB::Descriptor::Endpoint
349              
350              
351             You can also look for information at:
352              
353             =over 4
354              
355             =item * RT: CPAN's request tracker (report bugs here)
356              
357             L
358              
359             =item * AnnoCPAN: Annotated CPAN documentation
360              
361             L
362              
363             =item * CPAN Ratings
364              
365             L
366              
367             =item * Search CPAN
368              
369             L
370              
371             =back
372              
373              
374             =head1 ACKNOWLEDGEMENTS
375              
376              
377             =head1 LICENSE AND COPYRIGHT
378              
379             Copyright 2011 Brandon Fosdick.
380              
381             This program is released under the terms of the BSD License.
382              
383             =cut