File Coverage

blib/lib/USB/HID/Descriptor/Report.pm
Criterion Covered Total %
statement 9 91 9.8
branch 0 22 0.0
condition 0 12 0.0
subroutine 3 10 30.0
pod 7 7 100.0
total 19 142 13.3


line stmt bran cond sub pod time code
1             package USB::HID::Descriptor::Report;
2              
3 1     1   6 use strict;
  1         3  
  1         45  
4 1     1   7 use warnings;
  1         2  
  1         33  
5 1     1   6 use feature 'switch';
  1         2  
  1         3541  
6              
7             our $VERSION = '1';
8              
9             our %tags = (
10             # Main => 0
11             'input' => 0x80,
12             'output' => 0x90,
13             'feature' => 0xB0,
14             'collection'=> 0xA0,
15             'end' => 0xC0,
16             # Global => 1
17             'usage_page' => 0x04,
18             'logical_minimum' => 0x14,
19             'logical_maximum' => 0x24,
20             'physcial_minimum' => 0x34,
21             'physical_maximum' => 0x44,
22             'unit_exponent' => 0x54,
23             'unit' => 0x64,
24             'report_size' => 0x74,
25             'report_id' => 0x84,
26             'report_count' => 0x94,
27             'push' => 0xA4,
28             'pop' => 0xB4,
29             # Local => 2
30             'usage' => 0x08,
31             'usage_minimum' => 0x18,
32             'usage_maximum' => 0x28,
33             'designator_index' => 0x38,
34             'designator_minimum'=> 0x48,
35             'designator_maximum'=> 0x58,
36             'string_index' => 0x78,
37             'string_minimum' => 0x88,
38             'string_maximum' => 0x98,
39             'delimeter' => 0xA8,
40             );
41              
42             our %item_size = (0 => 0, 1 => 1, 2 => 2, 4 => 3);
43              
44             our %collection_type = (
45             'application' => 1,
46             'logical' => 2,
47             'named_array' => 4,
48             'physical' => 0,
49             'report' => 3,
50             'usage_switch' => 5,
51             'usage_modifier' => 7,
52             );
53              
54             our %usage_pages =
55             (
56             'GenericDesktop' => 0x01,
57             'SimulationControl' => 0x02,
58             'VRControl' => 0x03,
59             'SportControl' => 0x04,
60             'GameControl' => 0x05,
61             'GenericDevice' => 0x06,
62             'Keyboard' => 0x07, # Page 7 has 2 names
63             'Keypad' => 0x07,
64             'LED' => 0x08,
65             'Button' => 0x09,
66             'Ordinal' => 0x0A,
67             'Consumer' => 0x0C,
68             'Digitizers' => 0x0D,
69             'Unicode' => 0x10,
70             'AlphanumericDisplay' => 0x14,
71             'MedicalInstrument' => 0x40,
72             );
73              
74             =head1 NAME
75              
76             USB::HID::Descriptor::Report - USB Device Descriptor
77              
78             =head1 SYNOPSIS
79              
80             Methods for generating USB HID Report Descriptor items
81              
82             =head1 DESCRIPTION
83              
84             L provides a number of convenience methods for
85             generating the items that comprise a HID Report Descriptor.
86              
87             =head1 METHODS
88              
89             =over
90              
91             =item tag($tag, $size)
92              
93             Returns the first byte of an Item corresponding to the tag name C<$tag> and a data
94             size of C<$size>. The data bytes must be appended to the returned byte to create
95             a complete item.
96              
97             =item data_size(...)
98              
99             Determines the size of the data that will be appended to the byte returned by
100             C. If an array is passed, the data size will be determined by the length
101             of the array. If a single scalar is passed, the scalar's value is used to
102             determine the data size.
103              
104             =item item($tag, ...)
105              
106             Construct a report descriptor item given a tag name and associated data bytes.
107             Returns an array.
108              
109             =item item_type($tag)
110              
111             Returns the item type of the passed tag name ('main', 'global', 'local').
112              
113             =back
114              
115             =cut
116              
117             # Return a tag for the given tag name and data size
118             sub tag
119             {
120 0     0 1   my ($tag, $size) = @_;
121 0 0 0       $tags{$tag} | $item_size{$size} if exists $tags{$tag} && exists $item_size{$size};
122             }
123              
124             # Figure out the size of the data that's to be included with a tag
125             # If a single scalar is passed, use the value of the scalar
126             # If multiple scalars are passed, use the number of passed scalars
127             sub data_size
128             {
129 0 0   0 1   if( 1 == @_ )
130             {
131 0 0 0       if( not defined $_[0] ) { 0 }
  0 0 0        
    0          
132 0           elsif( ($_[0] >= -128) && ($_[0] <= 127) ) { 1 }
133 0           elsif( ($_[0] >= -32768) && ($_[0] <= 32767) ) { 2 }
134 0           else { 4 }
135             }
136             else
137             {
138 0           scalar(@_);
139             }
140             }
141              
142             # Construct a report descriptor item
143             # Expects the tag name followed by the data bytes
144             sub item
145             {
146 0     0 1   my $tag = shift;
147 0           given($tag)
148             {
149             # Handle Main items
150             when( 'collection' )
151 0           {
152 0           my $type = shift;
153 0 0         push @_, $collection_type{$type} if exists $collection_type{$type};
154             }
155             when(['input', 'output', 'feature'])
156 0           {
157             # Input items can't be volatile or non-volatile (page 28)
158 0 0         @_ = grep { $_ ne 'nonvolatile' and @_ ne 'volatile' } @_ if $tag eq 'input';
  0 0          
159 0           my $data = 0; # Main items default to 0
160 0           for( @_ )
161             {
162 0           when('data') { $data &= ~(1 << 0) }
  0            
163 0           when('constant') { $data |= (1 << 0) }
  0            
164 0           when('array') { $data &= ~(1 << 1) }
  0            
165 0           when('variable') { $data |= (1 << 1) }
  0            
166 0           when('absolute') { $data &= ~(1 << 2) }
  0            
167 0           when('relative') { $data |= (1 << 2) }
  0            
168 0           when('nowrap') { $data &= ~(1 << 3) }
  0            
169 0           when('wrap') { $data |= (1 << 3) }
  0            
170 0           when('linear') { $data &= ~(1 << 4) }
  0            
171 0           when('nonlinear') { $data |= (1 << 4) }
  0            
172 0           when('preferred') { $data &= ~(1 << 5) }
  0            
173 0           when('noprefered') { $data |= (1 << 5) }
  0            
174 0           when('nonull') { $data &= ~(1 << 6) }
  0            
175 0           when('null') { $data |= (1 << 6) }
  0            
176 0           when('nonvolatile') { $data &= ~(1 << 7) }
  0            
177 0           when('volatile') { $data |= (1 << 7) }
  0            
178 0           when('bitfield') { $data &= ~(1 << 8) }
  0            
179 0           when('buffered') { $data |= (1 << 8) }
  0            
180             }
181             # Input items are allowed to have a data size of zero, but feature
182             # and output items must have at leat one data byte. (page 29)
183 0           my $data_size = data_size($data);
184 0 0 0       $data_size = 1 if (0 == $data_size) && ($tag ne 'input');
185 0           return (tag($tag, $data_size), $data);
186             }
187             when( 'usage_page' )
188 0           {
189 0           my $page = shift;
190              
191             # Convert UsagePage names into integers
192 0 0         if( exists($usage_pages{$page}) ) # Parameter is a string?
193             {
194 0           unshift @_, $usage_pages{$page};
195             }
196             else # Nope
197             {
198             # Put it back and let it be handled normally
199 0           unshift @_, $page;
200             }
201             }
202             }
203              
204             # Split large data elements into individual bytes
205 0           my @b;
206 0           @_ = map {
207 0           @b = ();
208 0           do {
209 0           push @b, $_ & 0xFF;
210 0           $_ >>= 8;
211             } while( data_size($_) > 1 );
212 0           @b;
213             } @_;
214              
215 0           (tag($tag, data_size(@_)), @_);
216             }
217              
218             # Return an item's type given its tag
219             sub item_type
220             {
221 0     0 1   my $tag = shift;
222 0           given( $tags{$tag} & 0x0C )
223             {
224 0           when( 0x00 ) { return 'main' }
  0            
225 0           when( 0x04 ) { return 'global' }
  0            
226 0           when( 0x08 ) { return 'local' }
  0            
227             }
228             }
229              
230             =head1 WRAPPERS
231              
232             Wrap calls to C to make the calling code a bit prettier.
233              
234             =over
235              
236             =item Collection($type);
237              
238             Retuns a B item of the specified type ('application', 'logical' or
239             'physcial'). Returns an B item for 'end'.
240              
241             =item Usage($usage)
242              
243             Returns a B item constructed with the given usage number.
244              
245             =item UsagePage($usage)
246              
247             Returns a B item constructed with the given usage page number.
248              
249             =back
250              
251             =cut
252              
253             sub Collection
254             {
255 0 0   0 1   ($_[0] eq 'end') ? item('end') : item('collection', @_);
256             }
257              
258             sub Usage
259             {
260 0     0 1   item('usage', @_);
261             }
262              
263             sub UsagePage
264             {
265 0     0 1   item('usage_page', @_);
266             }
267              
268              
269             =head1 AUTHOR
270              
271             Brandon Fosdick, C<< >>
272              
273              
274             =head1 BUGS
275              
276             Please report any bugs or feature requests to C, or through
277             the web interface at L. I will be notified, and then you'll
278             automatically be notified of progress on your bug as I make changes.
279              
280              
281             =head1 SUPPORT
282              
283             You can find documentation for this module with the perldoc command.
284              
285             perldoc USB::HID::Descriptor::Report
286              
287              
288             You can also look for information at:
289              
290             =over 4
291              
292             =item * RT: CPAN's request tracker (report bugs here)
293              
294             L
295              
296             =item * AnnoCPAN: Annotated CPAN documentation
297              
298             L
299              
300             =item * CPAN Ratings
301              
302             L
303              
304             =item * Search CPAN
305              
306             L
307              
308             =back
309              
310              
311             =head1 ACKNOWLEDGEMENTS
312              
313              
314             =head1 LICENSE AND COPYRIGHT
315              
316             Copyright 2011 Brandon Fosdick.
317              
318             This program is released under the terms of the BSD License.
319              
320             =cut