File Coverage

blib/lib/USB/TMC.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             USB::TMC - Perl interface to USB Test & Measurement (USBTMC) backend.
5              
6             =head1 SYNOPSIS
7              
8             use USB::TMC;
9              
10             # Open usb connection to Agilent 34410A digital multimeter
11             my $usbtmc = USB::TMC->new(
12             vid => 0x0957,
13             pid => 0x0607,
14             serial => 'MY47000419', # only needed if vid/pid is ambiguous
15             );
16            
17             $usbtmc->write(data => "*CLS\n");
18             $usbtmc->write(data => "VOLT:NPLC 10\n");
19              
20             print $usbtmc->query(data => ":read?\n", length => 100);
21            
22             my $capabilities = $usbtmc->get_capabilities();
23             my $support_term_char = $capabilities->{support_term_char};
24              
25             =head1 DESCRIPTION
26              
27             This module provides a user-space L<USBTMC|http://www.usb.org/developers/docs/devclass_docs/USBTMC_1_006a.zip> driver.
28              
29             Internally this module is based on L<USB::LibUSB>.
30              
31             Does not yet support the additional features of USBTMC-USB488. But those could
32             easily be added if needed.
33              
34             =head1 METHODS
35              
36             Errors with USB transfers will result in a croak.
37              
38             Use L<default timeout|/new> if C<timeout> arg is not given.
39              
40             =cut
41              
42 1     1   45447 use strict;
  1         2  
  1         25  
43 1     1   4 use warnings;
  1         1  
  1         37  
44              
45             package USB::TMC;
46             $USB::TMC::VERSION = '0.004';
47 1     1   101 use USB::LibUSB;
  0            
  0            
48             use Moose;
49             use MooseX::Params::Validate 'validated_list';
50             use Carp;
51             use Data::Dumper 'Dumper';
52              
53             use constant {
54             MSGID_DEV_DEP_MSG_OUT => 1,
55             MSGID_REQUEST_DEV_DEP_MSG_IN => 2,
56             MSGID_DEV_DEP_MSG_IN => 2,
57             MSGID_VENDOR_SPECIFIC_OUT => 126,
58             MSGID_REQUEST_VENDOR_SPECIFIC_IN => 127,
59             MSGID_VENDOR_SPECIFIC_IN => 127,
60              
61             MESSAGE_FINALIZES_TRANSFER => "\x{01}",
62             MESSAGE_DOES_NOT_FINALIZE_TRANSFER => "\x{00}",
63              
64             FEATURE_SELECTOR_ENDPOINT_HALT => 0,
65              
66             BULK_HEADER_LENGTH => 12,
67              
68             # bRequest values
69             INITIATE_ABORT_BULK_OUT => 1,
70             CHECK_ABORT_BULK_OUT_STATUS => 2,
71             INITIATE_ABORT_BULK_IN => 3,
72             CHECK_ABORT_BULK_IN_STATUS => 4,
73             INITIATE_CLEAR => 5,
74             CHECK_CLEAR_STATUS => 6,
75             GET_CAPABILITIES => 7,
76              
77             # status values
78             STATUS_SUCCESS => 0x01,
79             STATUS_PENDING => 0x02,
80             STATUS_FAILED => 0x80,
81              
82             };
83              
84             my $null_byte = "\x{00}";
85              
86             has 'vid' => (
87             is => 'ro',
88             isa => 'Int',
89             required => 1
90             );
91              
92             has 'pid' => (
93             is => 'ro',
94             isa => 'Int',
95             required => 1
96             );
97              
98             has 'serial' => (
99             is => 'ro',
100             isa => 'Str',
101             );
102              
103             has 'ctx' => (
104             is => 'ro',
105             isa => 'USB::LibUSB',
106             init_arg => undef,
107             writer => '_ctx',
108             );
109              
110             has 'device' => (
111             is => 'ro',
112             isa => 'USB::LibUSB::Device',
113             init_arg => undef,
114             writer => '_device',
115             );
116              
117             has 'handle' => (
118             is => 'ro',
119             isa => 'USB::LibUSB::Device::Handle',
120             init_arg => undef,
121             writer => '_handle',
122             );
123              
124             # Bulk endpoint addresses.
125             has 'bulk_out_endpoint' => (
126             is => 'ro',
127             isa => 'Int',
128             init_arg => undef,
129             writer => '_bulk_out_endpoint',
130             );
131              
132             has 'bulk_in_endpoint' => (
133             is => 'ro',
134             isa => 'Int',
135             init_arg => undef,
136             writer => '_bulk_in_endpoint',
137             );
138              
139             has 'btag' => (
140             is => 'ro',
141             isa => 'Int',
142             init_arg => undef,
143             writer => '_btag',
144             default => 0,
145             );
146              
147             has 'reset_device' => (
148             is => 'ro',
149             isa => 'Bool',
150             default => 0,
151             );
152              
153             has 'debug_mode' => (
154             is => 'ro',
155             isa => 'Bool',
156             default => 0
157             );
158              
159             has 'libusb_log_level' => (
160             is => 'ro',
161             isa => 'Int',
162             default => LIBUSB_LOG_LEVEL_WARNING,
163             );
164              
165             has 'term_char' => (
166             is => 'ro',
167             isa => 'Maybe[Str]',
168             default => undef,
169             );
170              
171             has 'interface_number' => (
172             is => 'ro',
173             isa => 'Int',
174             writer => '_interface_number',
175             init_arg => undef,
176              
177             );
178              
179             has 'timeout' => (
180             is => 'rw',
181             isa => 'Num',
182             default => 5,
183             );
184              
185             sub _get_timeout_arg {
186             my $self = shift;
187             my $timeout = shift;
188             if ( not defined $timeout ) {
189             $timeout = $self->timeout();
190             }
191              
192             $timeout = sprintf( "%.0f", $timeout * 1000 );
193             return $timeout;
194             }
195              
196             sub _timeout_arg {
197             return ( timeout => { isa => 'Maybe[Num]', optional => 1 } );
198             }
199              
200             sub _debug {
201             my $self = shift;
202             if ( $self->debug_mode() ) {
203             warn "USBTMC debug: ", @_, "\n";
204             }
205             }
206              
207             =head2 new
208              
209             my $usbtmc = USB::TMC->new(
210             vid => $vid,
211             pid => $pid,
212             serial => $serial, # optional
213             reset_device => 1, # default: do not reset device
214             debug_mode => 1, # print lots of debug messages
215             libusb_log_level => LIBUSB_LOG_LEVEL_DEBUG, # Import LIBUSB_LOG_LEVEL_* constant from USB::LibUSB
216             term_char => "\n", # Stop a read request if the term_char occurs in the
217             # byte stream. Default: do not use term char
218             timeout => 10, # timeout in seconds. default: 5
219             );
220              
221             =cut
222              
223             sub BUILD {
224             my $self = shift;
225              
226             # TermChar valid?
227             my $term_char = $self->term_char();
228             if ( defined $term_char ) {
229             if ( length $term_char != 1 || $term_char =~ /[^[:ascii:]]/ ) {
230             croak "invalid TermChar";
231             }
232             $self->_debug( "Using TermChar ", Dumper $term_char);
233             }
234             else {
235             $self->_debug("Not using TermChar");
236             }
237              
238             my $ctx = USB::LibUSB->init();
239             $ctx->set_debug( $self->libusb_log_level() );
240              
241             my $handle;
242             if ( $self->serial() ) {
243             $handle = $ctx->open_device_with_vid_pid_serial(
244             $self->vid(),
245             $self->pid(), $self->serial()
246             );
247             }
248             else {
249             # Croak if we have multiple devices with the same vid:pid.
250             $handle = $ctx->open_device_with_vid_pid_unique(
251             $self->vid(),
252             $self->pid()
253             );
254             }
255              
256             if ( $self->reset_device() ) {
257             $self->_debug("Doing device reset.");
258             $handle->reset_device();
259             }
260              
261             my $device = $handle->get_device();
262              
263             eval {
264             # This will throw on windows and darwin. Catch exception with eval.
265             $self->_debug("enable auto detach of kernel driver.");
266             $handle->set_auto_detach_kernel_driver(1);
267             };
268              
269             $self->_ctx($ctx);
270             $self->_device($device);
271             $self->_handle($handle);
272              
273             my $usbtmc_interface_number = $self->_find_usbtmc_interface();
274             $self->_interface_number($usbtmc_interface_number);
275              
276             $self->_debug("Claiming interface no. $usbtmc_interface_number");
277             $handle->claim_interface($usbtmc_interface_number);
278              
279             $self->_get_endpoint_addresses();
280              
281             # $self->_debug(
282             # "Request clear_feature endpoint_halt for both bulk endpoints.");
283              
284             # $self->clear();
285             # $self->clear_halt_out();
286             # $self->clear_halt_in();
287             # $self->clear_feature_endpoint_out();
288             # $self->clear_feature_endpoint_in();
289             }
290              
291             sub _find_usbtmc_interface {
292              
293             # Relevant if device has additional non-TMC interfaces.
294             my $self = shift;
295             my $config = $self->device()->get_active_config_descriptor();
296             my @interfaces = @{ $config->{interface} };
297             for my $interface (@interfaces) {
298             if ( $interface->{bInterfaceClass} == 0xFE
299             && $interface->{bInterfaceSubClass} == 3 ) {
300             my $number = $interface->{bInterfaceNumber};
301             $self->_debug("Found USBTMC interface at number $number");
302             return $number;
303             }
304             }
305             croak "Did not find a USBTMC interface. Interfaces: ",
306             Dumper \@interfaces;
307             }
308              
309             sub _get_endpoint_addresses {
310             my $self = shift;
311             my $interface_number = $self->interface_number();
312              
313             my $config = $self->device()->get_active_config_descriptor();
314             my $interface = $config->{interface}[$interface_number];
315             my @endpoints = @{ $interface->{endpoint} };
316              
317             if ( @endpoints != 2 && @endpoints != 3 ) {
318             croak "USBTMC interface needs either 2 or 3 endpoints.";
319             }
320              
321             my ( $bulk_out_address, $bulk_in_address );
322             for my $endpoint (@endpoints) {
323             my $address = $endpoint->{bEndpointAddress};
324             my $direction = $address & LIBUSB_ENDPOINT_DIR_MASK;
325             my $type = $endpoint->{bmAttributes} & LIBUSB_TRANSFER_TYPE_MASK;
326             if ( $type == LIBUSB_TRANSFER_TYPE_BULK ) {
327             if ( $direction == LIBUSB_ENDPOINT_OUT ) {
328             $self->_debug( "Found bulk-out endpoint with address "
329             . sprintf( "0x%x", $address ) );
330             $bulk_out_address = $address;
331             }
332             elsif ( $direction == LIBUSB_ENDPOINT_IN ) {
333             $self->_debug( "Found bulk-in endpoint with address "
334             . sprintf( "0x%x", $address ) );
335             $bulk_in_address = $address;
336             }
337             }
338             }
339              
340             if ( !$bulk_out_address || !$bulk_in_address ) {
341             croak "Did not find all required endpoints.";
342             }
343              
344             $self->_bulk_out_endpoint($bulk_out_address);
345             $self->_bulk_in_endpoint($bulk_in_address);
346             }
347              
348             =head2 write
349              
350             $usbtmc->write(data => $data, timeout => $timeout);
351              
352             Do DEV_DEP_MSG_OUT transfer. So far this only supports USBTMC messages
353             consisting of a single transfer.
354              
355             =cut
356              
357             sub write {
358             my $self = shift;
359             $self->dev_dep_msg_out(@_);
360             }
361              
362             =head2 read
363              
364             my $data = $usbtmc->read(length => $read_length, timeout => $timeout);
365              
366             Do REQUEST_DEV_DEP_MSG_IN and DEV_DEP_MSG_IN transfers.
367              
368             =cut
369              
370             sub read {
371             my $self = shift;
372             my ( $length, $timeout ) = validated_list(
373             \@_,
374             length => { isa => 'Int' },
375             _timeout_arg(),
376             );
377              
378             my $result = '';
379              
380             # Do read requests until EOM flag is set.
381             while ($length) {
382             $self->request_dev_dep_msg_in(
383             length => $length,
384             timeout => $timeout
385             );
386             my ( $data, $eom )
387             = $self->dev_dep_msg_in( length => $length, timeout => $timeout );
388             $result .= $data;
389             $length -= length($data);
390             if ($eom) {
391             last;
392             }
393             }
394             return $result;
395             }
396              
397             sub dev_dep_msg_out {
398             my $self = shift;
399             my ( $data, $timeout ) = validated_list(
400             \@_,
401             data => { isa => 'Str' },
402             _timeout_arg(),
403             );
404              
405             $self->_debug("dev_dep_msg_out with data $data");
406              
407             my $header = $self->_dev_dep_msg_out_header( length => length $data );
408             my $endpoint = $self->bulk_out_endpoint();
409              
410             # Ensure that total number of bytes is multiple of 4.
411             $data .= $null_byte x ( ( 4 - ( length $data ) % 4 ) % 4 );
412             $data = $header . $data;
413              
414             my $transferred = eval {
415             $self->handle()->bulk_transfer_write(
416             $endpoint, $data,
417             $self->_get_timeout_arg($timeout)
418             );
419             };
420             if ($@) {
421             $self->_debug(
422             "dev_dep_msg_out: write failed. Aborting bulk transfer.");
423             $self->abort_bulk_out( timeout => $timeout );
424             croak($@);
425             }
426              
427             my $data_length = length($data);
428             if ( $transferred != $data_length ) {
429             croak
430             "dev_dep_msg_out: data_length = $data_length, transferred = $transferred";
431             }
432             }
433              
434             sub dev_dep_msg_in {
435             my $self = shift;
436             my ( $length, $timeout ) = validated_list(
437             \@_,
438             length => { isa => 'Int' },
439             _timeout_arg(),
440             );
441              
442             $self->_debug("Doing dev_dep_msg_in with length $length");
443              
444             my $endpoint = $self->bulk_in_endpoint();
445             my $data = eval {
446             $self->handle()->bulk_transfer_read(
447             $endpoint, $length + BULK_HEADER_LENGTH
448             , $self->_get_timeout_arg($timeout)
449             );
450             };
451             if ($@) {
452             $self->_debug("dev_dep_msg_in: read failed. Aborting bulk transfer.");
453             $self->abort_bulk_in( timeout => $timeout );
454             croak($@);
455             }
456              
457             if ( length $data < BULK_HEADER_LENGTH ) {
458             croak "dev_dep_msg_in does not contain header";
459             }
460              
461             my $header = substr( $data, 0, BULK_HEADER_LENGTH );
462             my $msg_id = unpack( 'C', substr( $header, 0, 1 ) );
463             my $transfer_attributes = unpack( 'C', substr( $header, 8, 1 ) );
464              
465             if ( $msg_id != MSGID_DEV_DEP_MSG_IN ) {
466             croak "dev_dep_msg_in message with wrong message id '$msg_id'";
467             }
468              
469             my $transfer_size = unpack( 'V', substr( $header, 4, 4 ) );
470             if ( $transfer_size == 0 ) {
471             croak("dev_dep_msg_in: zero transfer size");
472             }
473              
474             # Data may contain trailing alignment bytes!
475             # strip them by returning only $transfer_size bytes.
476             $data = substr( $data, BULK_HEADER_LENGTH, $transfer_size );
477             my $eom = $transfer_attributes & 1;
478             return ( $data, $eom );
479             }
480              
481             sub request_dev_dep_msg_in {
482             my $self = shift;
483             my ( $length, $timeout ) = validated_list(
484             \@_,
485             length => { isa => 'Int', default => 1000 },
486             _timeout_arg(),
487             );
488              
489             $self->_debug("Doing request_dev_dep_msg_in with length $length");
490             my $header = $self->_request_dev_dep_msg_in_header( length => $length );
491             my $endpoint = $self->bulk_out_endpoint();
492              
493             # Length of $header is already multiple of 4.
494             my $transferred = eval {
495             $self->handle()->bulk_transfer_write(
496             $endpoint, $header,
497             $self->_get_timeout_arg($timeout)
498             );
499             };
500             if ($@) {
501             $self->_debug(
502             "request_dev_dep_msg_in: write failed. Aborting bulk transfer.");
503             $self->abort_bulk_out( timeout => $timeout );
504             croak($@);
505             }
506             }
507              
508             sub _dev_dep_msg_out_header {
509             my $self = shift;
510             my ($length) = validated_list( \@_, length => { isa => 'Int' } );
511              
512             my $header = $self->_bulk_out_header( MSGID => MSGID_DEV_DEP_MSG_OUT );
513             $header .= pack( 'V', $length );
514             $header .= MESSAGE_FINALIZES_TRANSFER;
515             $header .= $null_byte x 3; # Reserved bytes.
516             return $header;
517             }
518              
519             sub _request_dev_dep_msg_in_header {
520             my $self = shift;
521             my ($length) = validated_list( \@_, length => { isa => 'Int' } );
522             my $header
523             = $self->_bulk_out_header( MSGID => MSGID_REQUEST_DEV_DEP_MSG_IN );
524              
525             # Transfer length
526             $header .= pack( 'V', $length );
527              
528             my $term_char = $self->term_char();
529             if ( defined $term_char ) {
530             $header .= pack( 'C', 2 );
531             $header .= $term_char;
532             }
533             else {
534             $header .= pack( 'C', 0 );
535             $header .= $null_byte;
536             }
537             $header .= $null_byte x 2; # Reserved. Must be 0x00.
538              
539             return $header;
540             }
541              
542             sub _bulk_out_header {
543             my $self = shift;
544             my ($MSGID) = validated_list( \@_, MSGID => { isa => 'Int' } );
545             my $bulk_out_header = pack( 'C', $MSGID );
546             my ( $btag, $btag_inverse ) = $self->_btags();
547             $bulk_out_header .= $btag . $btag_inverse;
548              
549             $bulk_out_header .= $null_byte; # Reserved. Must be 0x00;
550              
551             return $bulk_out_header;
552             }
553              
554             sub _btags {
555             my $self = shift;
556             my $btag = $self->btag();
557             $btag++;
558             if ( $btag == 256 ) {
559             $btag = 1;
560             }
561             $self->_btag($btag);
562             my $btag_inverse = ( $btag ^ 0xff );
563             return ( pack( 'C', $btag ), pack( 'C', $btag_inverse ) );
564             }
565              
566             #
567             #
568             # USBTMC requests
569             #
570             #
571              
572             sub abort_bulk_out {
573             my $self = shift;
574             my ($timeout) = validated_list(
575             \@_,
576             _timeout_arg()
577             );
578             my $initiate_status
579             = $self->initiate_abort_bulk_out( timeout => $timeout );
580             $initiate_status = unpack( 'C', $initiate_status );
581             if ( $initiate_status != STATUS_SUCCESS ) {
582             carp "INITIATE_ABORT_BULK_OUT failed with status $initiate_status";
583             return;
584             }
585              
586             # Check status
587             while (1) {
588             my $clear_status
589             = $self->check_abort_bulk_out_status( timeout => $timeout );
590             my $status = unpack( 'C', substr( $clear_status, 0, 1 ) );
591             if ( $status == STATUS_PENDING ) {
592             next;
593             }
594             else {
595             if ( $status != STATUS_SUCCESS ) {
596             carp(
597             "CHECK_ABORT_BULK_OUT_STATUS failed with status $status");
598             }
599             $self->clear_feature_endpoint_out( timeout => $timeout );
600             return;
601             }
602             }
603             }
604              
605             sub abort_bulk_in {
606             my $self = shift;
607             my ($timeout) = validated_list(
608             \@_,
609             _timeout_arg()
610             );
611              
612             my $initiate_status
613             = $self->initiate_abort_bulk_in( timeout => $timeout );
614             $initiate_status = unpack( 'C', $initiate_status );
615             if ( $initiate_status != STATUS_SUCCESS ) {
616             carp "INITIATE_ABORT_BULK_IN failed with status $initiate_status";
617             return;
618             }
619              
620             # Check status
621             while (1) {
622             my $clear_status
623             = $self->check_abort_bulk_in_status( timeout => $timeout );
624             my $status = unpack( 'C', substr( $clear_status, 0, 1 ) );
625             my $bmAbortBulkIn = unpack( 'C', substr( $clear_status, 1, 1 ) );
626             if ( $status == STATUS_PENDING ) {
627             $self->_debug(
628             "check_abort_bulk_in_status bmAbortBulkIn = $bmAbortBulkIn\n"
629             );
630              
631             # If bmAbortBulkIn.D0 = 1, the Host should read from the
632             # Bulk-IN endpoint until a short packet is received.
633             if ( $bmAbortBulkIn & 1 ) {
634             my $endpoint = $self->bulk_in_endpoint();
635             my $data = $self->handle()->bulk_transfer_read(
636             $endpoint, 1000,
637             $self->_get_timeout_arg($timeout)
638             );
639             $self->_debug(
640             "check_abort_bulk_in_status read bytes: ",
641             length($data)
642             );
643             }
644             next;
645             }
646             else {
647             if ( $status != STATUS_SUCCESS ) {
648             carp("CHECK_ABORT_BULK_IN_STATUS failed with status $status");
649             }
650             return;
651             }
652             }
653             }
654              
655             sub initiate_abort_bulk_out {
656             my $self = shift;
657             my ($timeout) = validated_list(
658             \@_,
659             _timeout_arg()
660             );
661              
662             $self->_debug("initiate abort bulk out");
663             my $bmRequestType = 0xa2;
664             my $bRequest = INITIATE_ABORT_BULK_OUT;
665             my $wValue = $self->btag();
666             my $wIndex = $self->bulk_out_endpoint();
667             my $wLength = 2;
668             return $self->handle()->control_transfer_read(
669             $bmRequestType, $bRequest, $wValue, $wIndex,
670             $wLength, $self->_get_timeout_arg($timeout)
671             );
672             }
673              
674             sub initiate_abort_bulk_in {
675             my $self = shift;
676             my ($timeout) = validated_list(
677             \@_,
678             _timeout_arg()
679             );
680              
681             $self->_debug("initiate abort bulk in");
682             my $bmRequestType = 0xa2;
683             my $bRequest = INITIATE_ABORT_BULK_IN;
684             my $wValue = $self->btag();
685             my $wIndex = $self->bulk_in_endpoint();
686             my $wLength = 2;
687             return $self->handle()->control_transfer_read(
688             $bmRequestType, $bRequest, $wValue, $wIndex,
689             $wLength, $self->_get_timeout_arg($timeout)
690             );
691             }
692              
693             sub check_abort_bulk_out_status {
694             my $self = shift;
695             my ($timeout) = validated_list( \@_, _timeout_arg() );
696              
697             $self->_debug("check abort bulk out status");
698             my $bmRequestType = 0xa2;
699             my $bRequest = CHECK_ABORT_BULK_OUT_STATUS;
700             my $wValue = 0;
701             my $wIndex = $self->bulk_out_endpoint();
702             my $wLength = 8;
703             return $self->handle()->control_transfer_read(
704             $bmRequestType, $bRequest, $wValue, $wIndex,
705             $wLength, $self->_get_timeout_arg($timeout)
706             );
707             }
708              
709             sub check_abort_bulk_in_status {
710             my $self = shift;
711             my ($timeout) = validated_list( \@_, _timeout_arg() );
712              
713             $self->_debug("check abort bulk in status");
714             my $bmRequestType = 0xa2;
715             my $bRequest = CHECK_ABORT_BULK_IN_STATUS;
716             my $wValue = 0;
717             my $wIndex = $self->bulk_in_endpoint();
718             my $wLength = 8;
719             return $self->handle()->control_transfer_read(
720             $bmRequestType, $bRequest, $wValue, $wIndex,
721             $wLength, $self->_get_timeout_arg($timeout)
722             );
723             }
724              
725             =head2 clear
726              
727             $usbtmc->clear(timeout => $timeout);
728              
729             Do INITIATE_CLEAR / CHECK_CLEAR_STATUS split transaction. On success, send
730             CLEAR_FEATURE request to clear the Bulk-OUT Halt.
731              
732             =cut
733              
734             sub clear {
735             my $self = shift;
736             my ($timeout) = validated_list( \@_, _timeout_arg() );
737             my $initiate_status = $self->initiate_clear( timeout => $timeout );
738             $initiate_status = unpack( 'C', $initiate_status );
739             if ( $initiate_status != STATUS_SUCCESS ) {
740             carp "INITIATE_CLEAR failed with status $initiate_status";
741             return;
742             }
743              
744             # Check clear status
745             while (1) {
746             my $clear_status = $self->check_clear_status( timeout => $timeout );
747             my $status = unpack( 'C', substr( $clear_status, 0, 1 ) );
748             my $bmClear = unpack( 'C', substr( $clear_status, 1, 1 ) );
749             if ( $status == STATUS_SUCCESS ) {
750             last;
751             }
752             elsif ( $status == STATUS_PENDING ) {
753             $self->_debug(
754             "CHECK_CLEAR_STATUS: status pending, bmClear = $bmClear");
755              
756             # If bmClear.D0 = 1, the Host should read from the
757             # Bulk-IN endpoint until a short packet is received.
758             if ( $bmClear & 1 ) {
759             my $endpoint = $self->bulk_in_endpoint();
760             my $data = $self->handle()->bulk_transfer_read(
761             $endpoint, 1000,
762             $self->_get_timeout_arg($timeout)
763             );
764             $self->_debug( " read bytes: ", length($data) );
765             }
766             next;
767             }
768             else {
769             croak "CHECK_CLEAR_STATUS failed with status $status";
770             }
771             }
772             $self->clear_feature_endpoint_out( timeout => $timeout );
773             }
774              
775             sub initiate_clear {
776             my $self = shift;
777             my ($timeout) = validated_list( \@_, _timeout_arg() );
778              
779             $self->_debug("initiate clear");
780             my $bmRequestType = 0xa1;
781             my $bRequest = INITIATE_CLEAR;
782             my $wValue = 0;
783             my $wIndex = $self->interface_number();
784             my $wLength = 1;
785             return $self->handle()->control_transfer_read(
786             $bmRequestType, $bRequest, $wValue, $wIndex,
787             $wLength, $self->_get_timeout_arg($timeout)
788             );
789             }
790              
791             sub check_clear_status {
792             my $self = shift;
793             my ($timeout) = validated_list( \@_, _timeout_arg() );
794              
795             $self->_debug("check clear status");
796             my $bmRequestType = 0xa1;
797             my $bRequest = CHECK_CLEAR_STATUS;
798             my $wValue = 0;
799             my $wIndex = $self->interface_number();
800             my $wLength = 2;
801             return $self->handle()->control_transfer_read(
802             $bmRequestType, $bRequest, $wValue, $wIndex,
803             $wLength, $self->_get_timeout_arg($timeout)
804             );
805             }
806              
807             sub clear_feature_endpoint_out {
808             my $self = shift;
809             my ($timeout) = validated_list( \@_, _timeout_arg() );
810              
811             $self->_debug("clear feature endpoint out");
812             my $endpoint = $self->bulk_out_endpoint();
813             my $bmRequestType = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_STANDARD
814             | LIBUSB_RECIPIENT_ENDPOINT;
815             my $bRequest = LIBUSB_REQUEST_CLEAR_FEATURE;
816             my $wValue = FEATURE_SELECTOR_ENDPOINT_HALT;
817             my $wIndex = $endpoint;
818             $self->handle()->control_transfer_write(
819             $bmRequestType, $bRequest, $wValue,
820             $wIndex, "", $self->_get_timeout_arg($timeout)
821             );
822             }
823              
824             sub clear_feature_endpoint_in {
825             my $self = shift;
826             my ($timeout) = validated_list( \@_, _timeout_arg() );
827              
828             $self->_debug("clear feature endpoint in");
829             my $endpoint = $self->bulk_in_endpoint();
830             my $bmRequestType = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_STANDARD
831             | LIBUSB_RECIPIENT_ENDPOINT;
832             my $bRequest = LIBUSB_REQUEST_CLEAR_FEATURE;
833             my $wValue = FEATURE_SELECTOR_ENDPOINT_HALT;
834             my $wIndex = $endpoint;
835             $self->handle()->control_transfer_write(
836             $bmRequestType, $bRequest, $wValue,
837             $wIndex, "", $self->_get_timeout_arg($timeout)
838             );
839             }
840              
841             sub clear_halt_out {
842             my $self = shift;
843             my $endpoint = $self->bulk_out_endpoint();
844             $self->handle()->clear_halt($endpoint);
845             }
846              
847             sub clear_halt_in {
848             my $self = shift;
849             my $endpoint = $self->bulk_in_endpoint();
850             $self->handle()->clear_halt($endpoint);
851             }
852              
853             =head2 get_capabilities
854              
855             my $capabilites = $usbtmc->get_capabilities(timeout => $timeout);
856              
857             Do GET_CAPABILITIES request.
858              
859             The C<$capabilities> hash contains the following keys:
860              
861             =over
862              
863             =item bcdUSBTMC
864              
865             =item listen_only
866              
867             =item talk_only
868              
869             =item accept_indicator_pulse
870              
871             =item support_term_char
872              
873             =back
874              
875             =cut
876              
877             sub get_capabilities {
878             my $self = shift;
879             my ($timeout) = validated_list( \@_, _timeout_arg() );
880              
881             my $bmRequestType = 0xa1;
882             my $bRequest = 7;
883             my $wValue = 0;
884             my $wIndex = $self->interface_number();
885             my $wLength = 0x18;
886              
887             my $handle = $self->handle();
888             my $caps = $handle->control_transfer_read(
889             $bmRequestType, $bRequest, $wValue,
890             $wIndex, $wLength, $self->_get_timeout_arg($timeout)
891             );
892             if ( length $caps != $wLength ) {
893             croak "Incomplete response in get_capabilities.";
894             }
895              
896             my $status = unpack( 'C', substr( $caps, 0, 1 ) );
897              
898             if ( $status != 1 ) {
899             croak "GET_CAPABILITIES not successfull. status = $status";
900             }
901              
902             my $bcdUSBTMC = unpack( 'v', substr( $caps, 2, 2 ) );
903             my $interface_capabilities = unpack( 'C', substr( $caps, 4, 1 ) );
904             my $device_capabilites = unpack( 'C', substr( $caps, 5, 1 ) );
905              
906             return {
907             bcdUSBTMC => $bcdUSBTMC,
908             listen_only => $interface_capabilities & 1,
909             talk_only => ( $interface_capabilities >> 1 ) & 1,
910             accept_indicator_pulse => ( $interface_capabilities >> 2 ) & 1,
911             support_term_char => $device_capabilites & 1,
912             };
913             }
914              
915             __PACKAGE__->meta->make_immutable();
916              
917             1;
918