#
# Note that the package line is different from the file name. We only need to add
# functionality to the Device::USB::Device package, and this is a convenient way.
#
package Device::USB::Win32Async;
use warnings;
use strict;
our $VERSION = 0.36;
require 5.006;
use Carp;
use Inline (
C => "DATA",
($ENV{LIBUSB_LIBDIR}
? ( LIBS => "-L\"$ENV{LIBUSB_LIBDIR}\" " .
($^O eq 'MSWin32' ? ' -llibusb -L\"$ENV{WINDDK}\\lib\\crt\\i386\" -lmsvcrt ' : '-lusb') )
: ( LIBS => '-lusb', )
),
($ENV{LIBUSB_INCDIR} ? ( INC => "-I\"$ENV{LIBUSB_INCDIR}\"" ) : () ),
NAME => 'Device::USB::Win32Async',
VERSION => '0.36',
);
Inline->init();
=head1 NAME
Device::USB::Win32Async - Add async functions to Device::USB
=head1 VERSION
Version 0.36
=head1 SYNOPSIS
Device::USB provides a Perl wrapper around the libusb library.
Device::USB::Win32Async adds the async functions from libusb-win32 to Device::USB.
This is only available for Win32 systems.
use Device::USB;
use Device::USB::Win32Async;
my $usb = Device::USB->new();
my $dev = $usb->find_device( $VENDOR, $PRODUCT );
...
my $Context; # Context variable for asynch I/O
my $Buffer; # Buffer for results (input) or to transmit (output)
my $NumBytes = 5000; # of bytes to transfer
$dev->bulk_setup_async($Context,$Endpoint);
$Status = $dev->submit_async($Context,$Buffer,$NumBytes); # Start the transfer
while( 1 ) {
$Response = $dev->reap_async_nocancel($Context,50); # 50 mS wait time
last
if $Response != Device::USB::Device::ETIMEDOUT;
#
# Do other tasks while waiting, such as update the GUI
#
# For example, a TK program might call $MainWindow->update();
#
<Other Task code>
}
See the libusb-win32 manual for more information about the methods. The
functionality is the same as the libusb function whose name is
the method name prepended with "usb_".
Generally, define a $Context variable which the library will use to keep track of
the asynchronous call. Activate the transfer (read or write, depending on the
endpoint) using submit_async() as shown, then loop calling reap_async_nocancel()
while checking the return code.
You can have any number of async operations pending on different endpoints - just
define multiple context variables as needed (ie - $Context1, $Context2, &c).
=cut
##########################################################################################
#
# Version 0.34 - Added support for asynchronous I/O
#
# The caller supplies a scalar $Context which we use to keep opaque (from the caller)
# information about the I/O operation.
#
# The libusb driver will malloc some data and pass back a pointer to use as the context,
# but we need to keep track of the buffer as well as this malloc'd context.
#
# So in the caller's $Context we store a ref to an anonymous array with two elements:
# [0] => the libusb context, and [1] => ref to the user's buffer.
#
# We do this using $_[1], which is an alias to the user's var.
#
=over
=item isochronous_setup_async($Context,$Endpoint,$Packetsize)
Setup a Context for use in subsequent asynchronous operations
=over 4
=item Context
A scalar to store opaque information about the operation
=item Endpoint
The endpoint the asynchronous operation will use
=item Packetsize
The size of the isochronous packets
=back
Returns 0 on success, < 0 on error (consult errno.h for explanation)
=cut
sub isochronous_setup_async {
my $self = shift;
$self->_assert_open();
#
# ($Context,$Endpoint,$Packetsize) = @_;
#
$_[0] = [ 0, "" ]; # User's var is now anonymous array
return libusb_isochronous_setup_async($self->{handle},$_[0][0],$_[1],$_[2]);
}
=item bulk_setup_async($Context,$Endpoint)
Setup a Context for use in subsequent asynchronous operations
=over 4
=item Context
A scalar to store opaque information about the operation
=item Endpoint
The endpoint the asynchronous operation will use
=back
Returns 0 on success, < 0 on error (consult errno.h for explanation)
=cut
sub bulk_setup_async {
my $self = shift;
$self->_assert_open();
#
# ($Context,$Endpoint) = @_;
#
$_[0] = [ 0, "" ]; # User's var is now anonymous array
return libusb_bulk_setup_async($self->{handle},$_[0][0],$_[1]);
}
=item interrupt_setup_async($Context,$Endpoint)
Setup a Context for use in subsequent asynchronous operations
=over 4
=item Context
A scalar to store opaque information about the operation
=item Endpoint
The endpoint the asynchronous operation will use
=back
Returns 0 on success, < 0 on error (consult errno.h for explanation)
=cut
sub interrupt_setup_async {
my $self = shift;
$self->_assert_open();
#
# ($Context,$Endpoint) = @_;
#
$_[0] = [ 0, "" ]; # User's var is now anonymous array
return libusb_interrupt_setup_async($self->{handle},$_[0][0],$_[1]);
}
=item submit_async($Context,$Buffer,$Size)
Start an asynchronous I/O operation
=over 4
=item Context
A previously prepared context generated by one of the xxx_setup_async functions above
=item Buffer
A string buffer to receive the resulting data
=item Size
The number of bytes to pre-allocate to hold the incoming data.
=back
Returns 0 on success, < 0 on error (consult errno.h for explanation)
=cut
sub submit_async {
my $self = shift;
$self->_assert_open();
#
# ($Context,$Buffer,$Size) = @_;
#
$_[0][1] = \$_[1]; # Save user's buffer for reap (below)
return libusb_submit_async($_[0][0],$_[1],$_[2]);
}
=item reap_async($Context,$Timeout)
Get the results of an asynchronous operation and cancel if not complete.
=over 4
=item Context
A previously prepared context generated by one of the xxx_setup_async functions above
=item Timeout
Number of milliseconds to wait before timeout
=back
Returns 0 on success, < 0 on error (consult errno.h for explanation)
=cut
sub reap_async {
my $self = shift;
$self->_assert_open();
#
# ($Context,$Timeout) = @_;
#
my $Status = libusb_reap_async($_[0][0],$_[1]);
#
# We kept the buffer so that we could set the actual number of bytes read.
#
${$_[0][1]} = substr(${$_[0][1]},0,$Status)
if( $Status >= 0 );
return $Status;
}
=item reap_async_nocancel($Context,$Timeout)
Get the results of an asynchronous operation, but continue request (return
Device::USB::Device::ETIMEDOUT => -116) if not complete yet.
=over 4
=item Context
A previously prepared context generated by one of the xxx_setup_async functions above
=item Timeout
Number of milliseconds to wait before timeout
=back
Returns 0 on success, < 0 on error (consult errno.h for explanation)
=cut
use constant ETIMEDOUT => -116;
use Data::Dumper;
sub reap_async_nocancel {
my $self = shift;
$self->_assert_open();
#
# ($Context,$Timeout) = @_;
#
my $Status = libusb_reap_async_nocancel($_[0][0],$_[1]);
#
# We kept the buffer so that we could set the actual number of bytes read.
#
${$_[0][1]} = substr(${$_[0][1]},0,$Status)
if( $Status >= 0 );
return $Status;
}
=item cancel_async($Context)
Cancel an asynchronous operation in progress
=over 4
=item Context
A previously prepared context generated by one of the xxx_setup_async functions above
=back
Returns 0 on success, < 0 on error (consult errno.h for explanation)
=cut
sub cancel_async {
my $self = shift;
$self->_assert_open();
#
# ($Context) = @_;
#
return libusb_cancel_async($_[0][0]);
}
=item free_async($Context)
Free up resources allocated for the asynchrounous context
=over 4
=item Context
A previously prepared context generated by one of the xxx_setup_async functions above
=back
Returns 0 on success, < 0 on error (consult errno.h for explanation)
=cut
sub free_async {
my $self = shift;
$self->_assert_open();
#
# ($Context) = @_;
#
return libusb_free_async($_[0][0]);
}
# Patch the new methods into the Device::USB::Device class.
{
package Device::USB::Device;
*free_async = \&Device::USB::Win32Async::free_async;
*cancel_async = \&Device::USB::Win32Async::cancel_async;
*reap_async_nocancel = \&Device::USB::Win32Async::reap_async_nocancel;
*reap_async = \&Device::USB::Win32Async::reap_async;
*submit_async = \&Device::USB::Win32Async::submit_async;
*interrupt_setup_async = \&Device::USB::Win32Async::interrupt_setup_async;
*bulk_setup_async = \&Device::USB::Win32Async::bulk_setup_async;
*isochronous_setup_async = \&Device::USB::Win32Async::isochronous_setup_async;
*ETIMEDOUT = \&Device::USB::Win32Async::ETIMEDOUT;
}
=item ETIMEDOUT
Constant representing a return from an asynchronous routine due to timeout.
=back
=head1 DEPENDENCIES
L<Carp>, L<Inline::C>, and L<Device::USB>.
Also depends on the libusb-win32 library.
=head1 AUTHOR
Rajstennaj Barrabas wrote the code.
The module is maintained by G. Wade Johnson (wade at anomaly dot org).
=head1 BUGS
Please report any bugs or feature requests to
C<bug-device-usb-win32async@rt.cpan.org>, or through the web interface at
L<http://rt.cpan.org/NoAuth/ReportBug.html?Device::USB::Win32Async>.
I will be notified, and then you'll automatically be notified of progress on
your bug as I make changes.
=head1 LIMITATIONS
This module depends on extensions to the libusb library added in the
LibUsb-Win32 library. As such, the module is only expected to work on a
Win32-based system.
=head1 COPYRIGHT & LICENSE
Copyright 2009 Rajstennaj Barrabas
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl 5.10.0.
=cut
1;
__DATA__
__C__
#include <usb.h>
//unsigned DeviceUSBDebugLevel(); // Defined in Device::USB::USB.pm
int libusb_isochronous_setup_async(void *dev, SV *Context, unsigned char ep, int pktsize) {
void *TempContext; // Place to received returned ptr
// if( DeviceUSBDebugLevel() > 0 )
// printf("libusb_isochronous_setup_async( %p, %u, %u //)\n",SvIVX(Context),ep,pktsize);
int Status = usb_isochronous_setup_async((usb_dev_handle *)dev,&TempContext,
ep, pktsize);
SvIV_set(Context,(int) TempContext); // Put ptr into perl var as int
return Status;
}
int libusb_bulk_setup_async(void *dev, SV *Context, unsigned char ep) {
void *TempContext; // Place to received returned ptr
// if( DeviceUSBDebugLevel() > 0 )
// printf("libusb_bulk_setup_async( %p, %u )\n",SvIVX(Context),ep);
int Status = usb_bulk_setup_async((usb_dev_handle *)dev,&TempContext, ep);
SvIV_set(Context,(int) TempContext); // Put ptr into perl var as int
return Status;
}
int libusb_interrupt_setup_async(void *dev, SV *Context, unsigned char ep) {
void *TempContext; // Place to received returned ptr
// if( DeviceUSBDebugLevel() > 0 )
// printf("libusb_interrupt_setup_async( %p, %u )\n",SvIVX(Context),ep);
int Status = usb_interrupt_setup_async((usb_dev_handle *)dev,&TempContext, ep);
SvIV_set(Context,(int) TempContext); // Put ptr into perl var as int
return Status;
}
int libusb_submit_async(void *context, SV *bytes, int size) {
SvPOK_on(bytes); // Force perl var type to string
char *Buffer = SvGROW(bytes,size); // Grow to <size> bytes
SvCUR_set(bytes,size); // Set length of string to <size>
return usb_submit_async(context,SvPV_nolen(bytes),size);
}
int libusb_reap_async(void *context, int timeout) {
return usb_reap_async(context,timeout);
}
int libusb_reap_async_nocancel(void *context, int timeout) {
return usb_reap_async_nocancel(context,timeout);
}
int libusb_cancel_async(void *context) {
return usb_cancel_async(context);
}
int libusb_free_async(void *context) {
return usb_free_async(&context);
}