mirror of https://github.com/ethereum/go-ethereum
parent
833e4d1319
commit
52bd4e29ff
@ -0,0 +1 @@ |
||||
*.sw[op] |
@ -0,0 +1,12 @@ |
||||
language: go |
||||
|
||||
matrix: |
||||
include: |
||||
- os: linux |
||||
dist: trusty |
||||
go: 1.7.4 |
||||
- os: osx |
||||
go: 1.7.4 |
||||
|
||||
script: |
||||
- go test -v -test.run='BCD|Parse' ./... |
@ -0,0 +1,202 @@ |
||||
|
||||
Apache License |
||||
Version 2.0, January 2004 |
||||
http://www.apache.org/licenses/ |
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
||||
1. Definitions. |
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, |
||||
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by |
||||
the copyright owner that is granting the License. |
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all |
||||
other entities that control, are controlled by, or are under common |
||||
control with that entity. For the purposes of this definition, |
||||
"control" means (i) the power, direct or indirect, to cause the |
||||
direction or management of such entity, whether by contract or |
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity |
||||
exercising permissions granted by this License. |
||||
|
||||
"Source" form shall mean the preferred form for making modifications, |
||||
including but not limited to software source code, documentation |
||||
source, and configuration files. |
||||
|
||||
"Object" form shall mean any form resulting from mechanical |
||||
transformation or translation of a Source form, including but |
||||
not limited to compiled object code, generated documentation, |
||||
and conversions to other media types. |
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or |
||||
Object form, made available under the License, as indicated by a |
||||
copyright notice that is included in or attached to the work |
||||
(an example is provided in the Appendix below). |
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object |
||||
form, that is based on (or derived from) the Work and for which the |
||||
editorial revisions, annotations, elaborations, or other modifications |
||||
represent, as a whole, an original work of authorship. For the purposes |
||||
of this License, Derivative Works shall not include works that remain |
||||
separable from, or merely link (or bind by name) to the interfaces of, |
||||
the Work and Derivative Works thereof. |
||||
|
||||
"Contribution" shall mean any work of authorship, including |
||||
the original version of the Work and any modifications or additions |
||||
to that Work or Derivative Works thereof, that is intentionally |
||||
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
or by an individual or Legal Entity authorized to submit on behalf of |
||||
the copyright owner. For the purposes of this definition, "submitted" |
||||
means any form of electronic, verbal, or written communication sent |
||||
to the Licensor or its representatives, including but not limited to |
||||
communication on electronic mailing lists, source code control systems, |
||||
and issue tracking systems that are managed by, or on behalf of, the |
||||
Licensor for the purpose of discussing and improving the Work, but |
||||
excluding communication that is conspicuously marked or otherwise |
||||
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
on behalf of whom a Contribution has been received by Licensor and |
||||
subsequently incorporated within the Work. |
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
copyright license to reproduce, prepare Derivative Works of, |
||||
publicly display, publicly perform, sublicense, and distribute the |
||||
Work and such Derivative Works in Source or Object form. |
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
(except as stated in this section) patent license to make, have made, |
||||
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
where such license applies only to those patent claims licensable |
||||
by such Contributor that are necessarily infringed by their |
||||
Contribution(s) alone or by combination of their Contribution(s) |
||||
with the Work to which such Contribution(s) was submitted. If You |
||||
institute patent litigation against any entity (including a |
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
or a Contribution incorporated within the Work constitutes direct |
||||
or contributory patent infringement, then any patent licenses |
||||
granted to You under this License for that Work shall terminate |
||||
as of the date such litigation is filed. |
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the |
||||
Work or Derivative Works thereof in any medium, with or without |
||||
modifications, and in Source or Object form, provided that You |
||||
meet the following conditions: |
||||
|
||||
(a) You must give any other recipients of the Work or |
||||
Derivative Works a copy of this License; and |
||||
|
||||
(b) You must cause any modified files to carry prominent notices |
||||
stating that You changed the files; and |
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works |
||||
that You distribute, all copyright, patent, trademark, and |
||||
attribution notices from the Source form of the Work, |
||||
excluding those notices that do not pertain to any part of |
||||
the Derivative Works; and |
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its |
||||
distribution, then any Derivative Works that You distribute must |
||||
include a readable copy of the attribution notices contained |
||||
within such NOTICE file, excluding those notices that do not |
||||
pertain to any part of the Derivative Works, in at least one |
||||
of the following places: within a NOTICE text file distributed |
||||
as part of the Derivative Works; within the Source form or |
||||
documentation, if provided along with the Derivative Works; or, |
||||
within a display generated by the Derivative Works, if and |
||||
wherever such third-party notices normally appear. The contents |
||||
of the NOTICE file are for informational purposes only and |
||||
do not modify the License. You may add Your own attribution |
||||
notices within Derivative Works that You distribute, alongside |
||||
or as an addendum to the NOTICE text from the Work, provided |
||||
that such additional attribution notices cannot be construed |
||||
as modifying the License. |
||||
|
||||
You may add Your own copyright statement to Your modifications and |
||||
may provide additional or different license terms and conditions |
||||
for use, reproduction, or distribution of Your modifications, or |
||||
for any such Derivative Works as a whole, provided Your use, |
||||
reproduction, and distribution of the Work otherwise complies with |
||||
the conditions stated in this License. |
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
any Contribution intentionally submitted for inclusion in the Work |
||||
by You to the Licensor shall be under the terms and conditions of |
||||
this License, without any additional terms or conditions. |
||||
Notwithstanding the above, nothing herein shall supersede or modify |
||||
the terms of any separate license agreement you may have executed |
||||
with Licensor regarding such Contributions. |
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade |
||||
names, trademarks, service marks, or product names of the Licensor, |
||||
except as required for reasonable and customary use in describing the |
||||
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
agreed to in writing, Licensor provides the Work (and each |
||||
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
implied, including, without limitation, any warranties or conditions |
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
appropriateness of using or redistributing the Work and assume any |
||||
risks associated with Your exercise of permissions under this License. |
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, |
||||
whether in tort (including negligence), contract, or otherwise, |
||||
unless required by applicable law (such as deliberate and grossly |
||||
negligent acts) or agreed to in writing, shall any Contributor be |
||||
liable to You for damages, including any direct, indirect, special, |
||||
incidental, or consequential damages of any character arising as a |
||||
result of this License or out of the use or inability to use the |
||||
Work (including but not limited to damages for loss of goodwill, |
||||
work stoppage, computer failure or malfunction, or any and all |
||||
other commercial damages or losses), even if such Contributor |
||||
has been advised of the possibility of such damages. |
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing |
||||
the Work or Derivative Works thereof, You may choose to offer, |
||||
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
or other liability obligations and/or rights consistent with this |
||||
License. However, in accepting such obligations, You may act only |
||||
on Your own behalf and on Your sole responsibility, not on behalf |
||||
of any other Contributor, and only if You agree to indemnify, |
||||
defend, and hold each Contributor harmless for any liability |
||||
incurred by, or claims asserted against, such Contributor by reason |
||||
of your accepting any such warranty or additional liability. |
||||
|
||||
END OF TERMS AND CONDITIONS |
||||
|
||||
APPENDIX: How to apply the Apache License to your work. |
||||
|
||||
To apply the Apache License to your work, attach the following |
||||
boilerplate notice, with the fields enclosed by brackets "[]" |
||||
replaced with your own identifying information. (Don't include |
||||
the brackets!) The text should be enclosed in the appropriate |
||||
comment syntax for the file format. We also recommend that a |
||||
file or class name and description of purpose be included on the |
||||
same "printed page" as the copyright notice for easier |
||||
identification within third-party archives. |
||||
|
||||
Copyright [yyyy] [name of copyright owner] |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
@ -0,0 +1,47 @@ |
||||
Introduction |
||||
============ |
||||
|
||||
[![Travis Build Status][travisimg]][travis] |
||||
[![AppVeyor Build Status][appveyorimg]][appveyor] |
||||
[![GoDoc][docimg]][doc] |
||||
|
||||
The gousb package is an attempt at wrapping the `libusb` library into a Go-like binding in a fully self-contained, go-gettable package. Supported platforms include Linux, macOS and Windows as well as the mobile platforms Android and iOS. |
||||
|
||||
This package is a fork of [`github.com/kylelemons/gousb`](https://github.com/kylelemons/gousb), which at the moment seems to be unmaintained. The current fork is different from the upstream package as it contains code to embed `libusb` directly into the Go package (thus becoming fully self-cotnained and go-gettable), as well as it features a few contributions and bugfixes that never really got addressed in the upstream package, but which address important issues nonetheless. |
||||
|
||||
*Note, if @kylelemons decides to pick development of the upstream project up again, consider all commits made by me to this repo as ready contributions. I cannot vouch for other commits as the upstream repo needs a signed CLA for Google.* |
||||
|
||||
[travisimg]: https://travis-ci.org/karalabe/gousb.svg?branch=master |
||||
[travis]: https://travis-ci.org/karalabe/gousb |
||||
[appveyorimg]: https://ci.appveyor.com/api/projects/status/84k9xse10rl72gn2/branch/master?svg=true |
||||
[appveyor]: https://ci.appveyor.com/project/karalabe/gousb |
||||
[docimg]: https://godoc.org/github.com/karalabe/gousb?status.svg |
||||
[doc]: https://godoc.org/github.com/karalabe/gousb |
||||
|
||||
Installation |
||||
============ |
||||
|
||||
Example: lsusb |
||||
-------------- |
||||
The gousb project provides a simple but useful example: lsusb. This binary will list the USB devices connected to your system and various interesting tidbits about them, their configurations, endpoints, etc. To install it, run the following command: |
||||
|
||||
go get -v github.com/karalabe/gousb/lsusb |
||||
|
||||
gousb |
||||
----- |
||||
If you installed the lsusb example, both libraries below are already installed. |
||||
|
||||
Installing the primary gousb package is really easy: |
||||
|
||||
go get -v github.com/karalabe/gousb/usb |
||||
|
||||
There is also a `usbid` package that will not be installed by default by this command, but which provides useful information including the human-readable vendor and product codes for detected hardware. It's not installed by default and not linked into the `usb` package by default because it adds ~400kb to the resulting binary. If you want both, they can be installed thus: |
||||
|
||||
go get -v github.com/karalabe/gousb/usb{,id} |
||||
|
||||
Documentation |
||||
============= |
||||
The documentation can be viewed via local godoc or via the excellent [godoc.org](http://godoc.org/): |
||||
|
||||
- [usb](http://godoc.org/github.com/karalabe/gousb/usb) |
||||
- [usbid](http://godoc.org/pkg/github.com/karalabe/gousb/usbid) |
@ -0,0 +1,34 @@ |
||||
os: Visual Studio 2015 |
||||
|
||||
# Clone directly into GOPATH. |
||||
clone_folder: C:\gopath\src\github.com\karalabe\gousb |
||||
clone_depth: 5 |
||||
version: "{branch}.{build}" |
||||
environment: |
||||
global: |
||||
GOPATH: C:\gopath |
||||
CC: gcc.exe |
||||
matrix: |
||||
- GOARCH: amd64 |
||||
MSYS2_ARCH: x86_64 |
||||
MSYS2_BITS: 64 |
||||
MSYSTEM: MINGW64 |
||||
PATH: C:\msys64\mingw64\bin\;%PATH% |
||||
- GOARCH: 386 |
||||
MSYS2_ARCH: i686 |
||||
MSYS2_BITS: 32 |
||||
MSYSTEM: MINGW32 |
||||
PATH: C:\msys64\mingw32\bin\;%PATH% |
||||
|
||||
install: |
||||
- rmdir C:\go /s /q |
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.7.4.windows-%GOARCH%.zip |
||||
- 7z x go1.7.4.windows-%GOARCH%.zip -y -oC:\ > NUL |
||||
- go version |
||||
- gcc --version |
||||
|
||||
build_script: |
||||
- go install ./... |
||||
|
||||
test_script: |
||||
- go test -v -test.run="BCD|Parse" ./... |
@ -0,0 +1,89 @@ |
||||
Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com> |
||||
Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org> |
||||
Copyright © 2010-2012 Peter Stuge <peter@stuge.se> |
||||
Copyright © 2008-2016 Nathan Hjelm <hjelmn@users.sourceforge.net> |
||||
Copyright © 2009-2013 Pete Batard <pete@akeo.ie> |
||||
Copyright © 2009-2013 Ludovic Rousseau <ludovic.rousseau@gmail.com> |
||||
Copyright © 2010-2012 Michael Plante <michael.plante@gmail.com> |
||||
Copyright © 2011-2013 Hans de Goede <hdegoede@redhat.com> |
||||
Copyright © 2012-2013 Martin Pieuchot <mpi@openbsd.org> |
||||
Copyright © 2012-2013 Toby Gray <toby.gray@realvnc.com> |
||||
Copyright © 2013-2015 Chris Dickens <christopher.a.dickens@gmail.com> |
||||
|
||||
Other contributors: |
||||
Akshay Jaggi |
||||
Alan Ott |
||||
Alan Stern |
||||
Alex Vatchenko |
||||
Andrew Fernandes |
||||
Anthony Clay |
||||
Antonio Ospite |
||||
Artem Egorkine |
||||
Aurelien Jarno |
||||
Bastien Nocera |
||||
Bei Zhang |
||||
Benjamin Dobell |
||||
Carl Karsten |
||||
Colin Walters |
||||
Dave Camarillo |
||||
David Engraf |
||||
David Moore |
||||
Davidlohr Bueso |
||||
Federico Manzan |
||||
Felipe Balbi |
||||
Florian Albrechtskirchinger |
||||
Francesco Montorsi |
||||
Francisco Facioni |
||||
Gaurav Gupta |
||||
Graeme Gill |
||||
Gustavo Zacarias |
||||
Hans Ulrich Niedermann |
||||
Hector Martin |
||||
Hoi-Ho Chan |
||||
Ilya Konstantinov |
||||
James Hanko |
||||
John Sheu |
||||
Joshua Blake |
||||
Justin Bischoff |
||||
Karsten Koenig |
||||
Konrad Rzepecki |
||||
Kuangye Guo |
||||
Lars Kanis |
||||
Lars Wirzenius |
||||
Luca Longinotti |
||||
Marcus Meissner |
||||
Markus Heidelberg |
||||
Martin Ettl |
||||
Martin Koegler |
||||
Matthias Bolte |
||||
Mike Frysinger |
||||
Mikhail Gusarov |
||||
Moritz Fischer |
||||
Ларионов Даниил |
||||
Nicholas Corgan |
||||
Omri Iluz |
||||
Orin Eman |
||||
Paul Fertser |
||||
Pekka Nikander |
||||
Rob Walker |
||||
Sean McBride |
||||
Sebastian Pipping |
||||
Simon Haggett |
||||
Simon Newton |
||||
Thomas Röfer |
||||
Tim Hutt |
||||
Tim Roberts |
||||
Tobias Klauser |
||||
Toby Peterson |
||||
Tormod Volden |
||||
Trygve Laugstøl |
||||
Uri Lublin |
||||
Vasily Khoruzhick |
||||
Vegard Storheil Eriksen |
||||
Venkatesh Shukla |
||||
Vitali Lovich |
||||
Xiaofan Chen |
||||
Zoltán Kovács |
||||
Роман Донченко |
||||
parafin |
||||
xantares |
@ -0,0 +1,3 @@ |
||||
#ifndef CONFIG_H |
||||
#define CONFIG_H |
||||
#endif |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,350 @@ |
||||
/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ |
||||
/*
|
||||
* Hotplug functions for libusb |
||||
* Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.com> |
||||
* Copyright © 2012-2013 Peter Stuge <peter@stuge.se> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#include <errno.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#ifdef HAVE_SYS_TYPES_H |
||||
#include <sys/types.h> |
||||
#endif |
||||
#include <assert.h> |
||||
|
||||
#include "libusbi.h" |
||||
#include "hotplug.h" |
||||
|
||||
/**
|
||||
* @defgroup libusb_hotplug Device hotplug event notification |
||||
* This page details how to use the libusb hotplug interface, where available. |
||||
* |
||||
* Be mindful that not all platforms currently implement hotplug notification and |
||||
* that you should first call on \ref libusb_has_capability() with parameter |
||||
* \ref LIBUSB_CAP_HAS_HOTPLUG to confirm that hotplug support is available. |
||||
* |
||||
* \page libusb_hotplug Device hotplug event notification |
||||
* |
||||
* \section hotplug_intro Introduction |
||||
* |
||||
* Version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, has added support |
||||
* for hotplug events on <b>some</b> platforms (you should test if your platform |
||||
* supports hotplug notification by calling \ref libusb_has_capability() with |
||||
* parameter \ref LIBUSB_CAP_HAS_HOTPLUG).
|
||||
* |
||||
* This interface allows you to request notification for the arrival and departure |
||||
* of matching USB devices. |
||||
* |
||||
* To receive hotplug notification you register a callback by calling |
||||
* \ref libusb_hotplug_register_callback(). This function will optionally return |
||||
* a callback handle that can be passed to \ref libusb_hotplug_deregister_callback(). |
||||
* |
||||
* A callback function must return an int (0 or 1) indicating whether the callback is |
||||
* expecting additional events. Returning 0 will rearm the callback and 1 will cause |
||||
* the callback to be deregistered. Note that when callbacks are called from |
||||
* libusb_hotplug_register_callback() because of the \ref LIBUSB_HOTPLUG_ENUMERATE |
||||
* flag, the callback return value is ignored, iow you cannot cause a callback |
||||
* to be deregistered by returning 1 when it is called from |
||||
* libusb_hotplug_register_callback(). |
||||
* |
||||
* Callbacks for a particular context are automatically deregistered by libusb_exit(). |
||||
* |
||||
* As of 1.0.16 there are two supported hotplug events: |
||||
* - LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: A device has arrived and is ready to use |
||||
* - LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: A device has left and is no longer available |
||||
* |
||||
* A hotplug event can listen for either or both of these events. |
||||
* |
||||
* Note: If you receive notification that a device has left and you have any |
||||
* a libusb_device_handles for the device it is up to you to call libusb_close() |
||||
* on each device handle to free up any remaining resources associated with the device. |
||||
* Once a device has left any libusb_device_handle associated with the device |
||||
* are invalid and will remain so even if the device comes back. |
||||
* |
||||
* When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED event it is considered |
||||
* safe to call any libusb function that takes a libusb_device. It also safe to |
||||
* open a device and submit asynchronous transfers. However, most other functions |
||||
* that take a libusb_device_handle are <b>not</b> safe to call. Examples of such |
||||
* functions are any of the \ref libusb_syncio "synchronous API" functions or the blocking |
||||
* functions that retrieve various \ref libusb_desc "USB descriptors". These functions must |
||||
* be used outside of the context of the hotplug callback. |
||||
* |
||||
* When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT event the only safe function |
||||
* is libusb_get_device_descriptor(). |
||||
* |
||||
* The following code provides an example of the usage of the hotplug interface: |
||||
\code |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <time.h> |
||||
#include <libusb.h> |
||||
|
||||
static int count = 0; |
||||
|
||||
int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev, |
||||
libusb_hotplug_event event, void *user_data) { |
||||
static libusb_device_handle *dev_handle = NULL; |
||||
struct libusb_device_descriptor desc; |
||||
int rc; |
||||
|
||||
(void)libusb_get_device_descriptor(dev, &desc); |
||||
|
||||
if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) { |
||||
rc = libusb_open(dev, &dev_handle); |
||||
if (LIBUSB_SUCCESS != rc) { |
||||
printf("Could not open USB device\n"); |
||||
} |
||||
} else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) { |
||||
if (dev_handle) { |
||||
libusb_close(dev_handle); |
||||
dev_handle = NULL; |
||||
} |
||||
} else { |
||||
printf("Unhandled event %d\n", event); |
||||
} |
||||
count++; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int main (void) { |
||||
libusb_hotplug_callback_handle callback_handle; |
||||
int rc; |
||||
|
||||
libusb_init(NULL); |
||||
|
||||
rc = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | |
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, 0x045a, 0x5005, |
||||
LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, NULL, |
||||
&callback_handle); |
||||
if (LIBUSB_SUCCESS != rc) { |
||||
printf("Error creating a hotplug callback\n"); |
||||
libusb_exit(NULL); |
||||
return EXIT_FAILURE; |
||||
} |
||||
|
||||
while (count < 2) { |
||||
libusb_handle_events_completed(NULL, NULL); |
||||
nanosleep(&(struct timespec){0, 10000000UL}, NULL); |
||||
} |
||||
|
||||
libusb_hotplug_deregister_callback(NULL, callback_handle); |
||||
libusb_exit(NULL); |
||||
|
||||
return 0; |
||||
} |
||||
\endcode |
||||
*/ |
||||
|
||||
static int usbi_hotplug_match_cb (struct libusb_context *ctx, |
||||
struct libusb_device *dev, libusb_hotplug_event event, |
||||
struct libusb_hotplug_callback *hotplug_cb) |
||||
{ |
||||
/* Handle lazy deregistration of callback */ |
||||
if (hotplug_cb->needs_free) { |
||||
/* Free callback */ |
||||
return 1; |
||||
} |
||||
|
||||
if (!(hotplug_cb->events & event)) { |
||||
return 0; |
||||
} |
||||
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->vendor_id && |
||||
hotplug_cb->vendor_id != dev->device_descriptor.idVendor) { |
||||
return 0; |
||||
} |
||||
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->product_id && |
||||
hotplug_cb->product_id != dev->device_descriptor.idProduct) { |
||||
return 0; |
||||
} |
||||
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->dev_class && |
||||
hotplug_cb->dev_class != dev->device_descriptor.bDeviceClass) { |
||||
return 0; |
||||
} |
||||
|
||||
return hotplug_cb->cb (ctx, dev, event, hotplug_cb->user_data); |
||||
} |
||||
|
||||
void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, |
||||
libusb_hotplug_event event) |
||||
{ |
||||
struct libusb_hotplug_callback *hotplug_cb, *next; |
||||
int ret; |
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock); |
||||
|
||||
list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) { |
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock); |
||||
ret = usbi_hotplug_match_cb (ctx, dev, event, hotplug_cb); |
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock); |
||||
|
||||
if (ret) { |
||||
list_del(&hotplug_cb->list); |
||||
free(hotplug_cb); |
||||
} |
||||
} |
||||
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock); |
||||
|
||||
/* the backend is expected to call the callback for each active transfer */ |
||||
} |
||||
|
||||
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev, |
||||
libusb_hotplug_event event) |
||||
{ |
||||
int pending_events; |
||||
libusb_hotplug_message *message = calloc(1, sizeof(*message)); |
||||
|
||||
if (!message) { |
||||
usbi_err(ctx, "error allocating hotplug message"); |
||||
return; |
||||
} |
||||
|
||||
message->event = event; |
||||
message->device = dev; |
||||
|
||||
/* Take the event data lock and add this message to the list.
|
||||
* Only signal an event if there are no prior pending events. */ |
||||
usbi_mutex_lock(&ctx->event_data_lock); |
||||
pending_events = usbi_pending_events(ctx); |
||||
list_add_tail(&message->list, &ctx->hotplug_msgs); |
||||
if (!pending_events) |
||||
usbi_signal_event(ctx); |
||||
usbi_mutex_unlock(&ctx->event_data_lock); |
||||
} |
||||
|
||||
int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, |
||||
libusb_hotplug_event events, libusb_hotplug_flag flags, |
||||
int vendor_id, int product_id, int dev_class, |
||||
libusb_hotplug_callback_fn cb_fn, void *user_data, |
||||
libusb_hotplug_callback_handle *callback_handle) |
||||
{ |
||||
libusb_hotplug_callback *new_callback; |
||||
static int handle_id = 1; |
||||
|
||||
/* check for hotplug support */ |
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { |
||||
return LIBUSB_ERROR_NOT_SUPPORTED; |
||||
} |
||||
|
||||
/* check for sane values */ |
||||
if ((LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) || |
||||
(LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) || |
||||
(LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) || |
||||
!cb_fn) { |
||||
return LIBUSB_ERROR_INVALID_PARAM; |
||||
} |
||||
|
||||
USBI_GET_CONTEXT(ctx); |
||||
|
||||
new_callback = (libusb_hotplug_callback *)calloc(1, sizeof (*new_callback)); |
||||
if (!new_callback) { |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
} |
||||
|
||||
new_callback->ctx = ctx; |
||||
new_callback->vendor_id = vendor_id; |
||||
new_callback->product_id = product_id; |
||||
new_callback->dev_class = dev_class; |
||||
new_callback->flags = flags; |
||||
new_callback->events = events; |
||||
new_callback->cb = cb_fn; |
||||
new_callback->user_data = user_data; |
||||
new_callback->needs_free = 0; |
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock); |
||||
|
||||
/* protect the handle by the context hotplug lock. it doesn't matter if the same handle
|
||||
* is used for different contexts only that the handle is unique for this context */ |
||||
new_callback->handle = handle_id++; |
||||
|
||||
list_add(&new_callback->list, &ctx->hotplug_cbs); |
||||
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock); |
||||
|
||||
|
||||
if (flags & LIBUSB_HOTPLUG_ENUMERATE) { |
||||
int i, len; |
||||
struct libusb_device **devs; |
||||
|
||||
len = (int) libusb_get_device_list(ctx, &devs); |
||||
if (len < 0) { |
||||
libusb_hotplug_deregister_callback(ctx, |
||||
new_callback->handle); |
||||
return len; |
||||
} |
||||
|
||||
for (i = 0; i < len; i++) { |
||||
usbi_hotplug_match_cb(ctx, devs[i], |
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, |
||||
new_callback); |
||||
} |
||||
|
||||
libusb_free_device_list(devs, 1); |
||||
} |
||||
|
||||
|
||||
if (callback_handle) |
||||
*callback_handle = new_callback->handle; |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
void API_EXPORTED libusb_hotplug_deregister_callback (struct libusb_context *ctx, |
||||
libusb_hotplug_callback_handle callback_handle) |
||||
{ |
||||
struct libusb_hotplug_callback *hotplug_cb; |
||||
|
||||
/* check for hotplug support */ |
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { |
||||
return; |
||||
} |
||||
|
||||
USBI_GET_CONTEXT(ctx); |
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock); |
||||
list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list, |
||||
struct libusb_hotplug_callback) { |
||||
if (callback_handle == hotplug_cb->handle) { |
||||
/* Mark this callback for deregistration */ |
||||
hotplug_cb->needs_free = 1; |
||||
} |
||||
} |
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock); |
||||
|
||||
usbi_hotplug_notification(ctx, NULL, 0); |
||||
} |
||||
|
||||
void usbi_hotplug_deregister_all(struct libusb_context *ctx) { |
||||
struct libusb_hotplug_callback *hotplug_cb, *next; |
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock); |
||||
list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, |
||||
struct libusb_hotplug_callback) { |
||||
list_del(&hotplug_cb->list); |
||||
free(hotplug_cb); |
||||
} |
||||
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock); |
||||
} |
@ -0,0 +1,90 @@ |
||||
/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ |
||||
/*
|
||||
* Hotplug support for libusb |
||||
* Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.com> |
||||
* Copyright © 2012-2013 Peter Stuge <peter@stuge.se> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#if !defined(USBI_HOTPLUG_H) |
||||
#define USBI_HOTPLUG_H |
||||
|
||||
#ifndef LIBUSBI_H |
||||
#include "libusbi.h" |
||||
#endif |
||||
|
||||
/** \ingroup hotplug
|
||||
* The hotplug callback structure. The user populates this structure with |
||||
* libusb_hotplug_prepare_callback() and then calls libusb_hotplug_register_callback() |
||||
* to receive notification of hotplug events. |
||||
*/ |
||||
struct libusb_hotplug_callback { |
||||
/** Context this callback is associated with */ |
||||
struct libusb_context *ctx; |
||||
|
||||
/** Vendor ID to match or LIBUSB_HOTPLUG_MATCH_ANY */ |
||||
int vendor_id; |
||||
|
||||
/** Product ID to match or LIBUSB_HOTPLUG_MATCH_ANY */ |
||||
int product_id; |
||||
|
||||
/** Device class to match or LIBUSB_HOTPLUG_MATCH_ANY */ |
||||
int dev_class; |
||||
|
||||
/** Hotplug callback flags */ |
||||
libusb_hotplug_flag flags; |
||||
|
||||
/** Event(s) that will trigger this callback */ |
||||
libusb_hotplug_event events; |
||||
|
||||
/** Callback function to invoke for matching event/device */ |
||||
libusb_hotplug_callback_fn cb; |
||||
|
||||
/** Handle for this callback (used to match on deregister) */ |
||||
libusb_hotplug_callback_handle handle; |
||||
|
||||
/** User data that will be passed to the callback function */ |
||||
void *user_data; |
||||
|
||||
/** Callback is marked for deletion */ |
||||
int needs_free; |
||||
|
||||
/** List this callback is registered in (ctx->hotplug_cbs) */ |
||||
struct list_head list; |
||||
}; |
||||
|
||||
typedef struct libusb_hotplug_callback libusb_hotplug_callback; |
||||
|
||||
struct libusb_hotplug_message { |
||||
/** The hotplug event that occurred */ |
||||
libusb_hotplug_event event; |
||||
|
||||
/** The device for which this hotplug event occurred */ |
||||
struct libusb_device *device; |
||||
|
||||
/** List this message is contained in (ctx->hotplug_msgs) */ |
||||
struct list_head list; |
||||
}; |
||||
|
||||
typedef struct libusb_hotplug_message libusb_hotplug_message; |
||||
|
||||
void usbi_hotplug_deregister_all(struct libusb_context *ctx); |
||||
void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, |
||||
libusb_hotplug_event event); |
||||
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev, |
||||
libusb_hotplug_event event); |
||||
|
||||
#endif |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,164 @@ |
||||
/*
|
||||
* darwin backend for libusb 1.0 |
||||
* Copyright © 2008-2015 Nathan Hjelm <hjelmn@users.sourceforge.net> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#if !defined(LIBUSB_DARWIN_H) |
||||
#define LIBUSB_DARWIN_H |
||||
|
||||
#include "libusbi.h" |
||||
|
||||
#include <IOKit/IOTypes.h> |
||||
#include <IOKit/IOCFBundle.h> |
||||
#include <IOKit/usb/IOUSBLib.h> |
||||
#include <IOKit/IOCFPlugIn.h> |
||||
|
||||
/* IOUSBInterfaceInferface */ |
||||
#if defined (kIOUSBInterfaceInterfaceID700) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 |
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface700 |
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID700 |
||||
#define InterfaceVersion 700 |
||||
|
||||
#elif defined (kIOUSBInterfaceInterfaceID550) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 |
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface550 |
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550 |
||||
#define InterfaceVersion 550 |
||||
|
||||
#elif defined (kIOUSBInterfaceInterfaceID500) |
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface500 |
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500 |
||||
#define InterfaceVersion 500 |
||||
|
||||
#elif defined (kIOUSBInterfaceInterfaceID300) |
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface300 |
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300 |
||||
#define InterfaceVersion 300 |
||||
|
||||
#elif defined (kIOUSBInterfaceInterfaceID245) |
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface245 |
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245 |
||||
#define InterfaceVersion 245 |
||||
|
||||
#elif defined (kIOUSBInterfaceInterfaceID220) |
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface220 |
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 |
||||
#define InterfaceVersion 220 |
||||
|
||||
#else |
||||
|
||||
#error "IOUSBFamily is too old. Please upgrade your OS" |
||||
|
||||
#endif |
||||
|
||||
/* IOUSBDeviceInterface */ |
||||
#if defined (kIOUSBDeviceInterfaceID500) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 |
||||
|
||||
#define usb_device_t IOUSBDeviceInterface500 |
||||
#define DeviceInterfaceID kIOUSBDeviceInterfaceID500 |
||||
#define DeviceVersion 500 |
||||
|
||||
#elif defined (kIOUSBDeviceInterfaceID320) |
||||
|
||||
#define usb_device_t IOUSBDeviceInterface320 |
||||
#define DeviceInterfaceID kIOUSBDeviceInterfaceID320 |
||||
#define DeviceVersion 320 |
||||
|
||||
#elif defined (kIOUSBDeviceInterfaceID300) |
||||
|
||||
#define usb_device_t IOUSBDeviceInterface300 |
||||
#define DeviceInterfaceID kIOUSBDeviceInterfaceID300 |
||||
#define DeviceVersion 300 |
||||
|
||||
#elif defined (kIOUSBDeviceInterfaceID245) |
||||
|
||||
#define usb_device_t IOUSBDeviceInterface245 |
||||
#define DeviceInterfaceID kIOUSBDeviceInterfaceID245 |
||||
#define DeviceVersion 245 |
||||
|
||||
#elif defined (kIOUSBDeviceInterfaceID220) |
||||
#define usb_device_t IOUSBDeviceInterface197 |
||||
#define DeviceInterfaceID kIOUSBDeviceInterfaceID197 |
||||
#define DeviceVersion 197 |
||||
|
||||
#else |
||||
|
||||
#error "IOUSBFamily is too old. Please upgrade your OS" |
||||
|
||||
#endif |
||||
|
||||
#if !defined(IO_OBJECT_NULL) |
||||
#define IO_OBJECT_NULL ((io_object_t) 0) |
||||
#endif |
||||
|
||||
typedef IOCFPlugInInterface *io_cf_plugin_ref_t; |
||||
typedef IONotificationPortRef io_notification_port_t; |
||||
|
||||
/* private structures */ |
||||
struct darwin_cached_device { |
||||
struct list_head list; |
||||
IOUSBDeviceDescriptor dev_descriptor; |
||||
UInt32 location; |
||||
UInt64 parent_session; |
||||
UInt64 session; |
||||
UInt16 address; |
||||
char sys_path[21]; |
||||
usb_device_t **device; |
||||
int open_count; |
||||
UInt8 first_config, active_config, port;
|
||||
int can_enumerate; |
||||
int refcount; |
||||
}; |
||||
|
||||
struct darwin_device_priv { |
||||
struct darwin_cached_device *dev; |
||||
}; |
||||
|
||||
struct darwin_device_handle_priv { |
||||
int is_open; |
||||
CFRunLoopSourceRef cfSource; |
||||
|
||||
struct darwin_interface { |
||||
usb_interface_t **interface; |
||||
uint8_t num_endpoints; |
||||
CFRunLoopSourceRef cfSource; |
||||
uint64_t frames[256]; |
||||
uint8_t endpoint_addrs[USB_MAXENDPOINTS]; |
||||
} interfaces[USB_MAXINTERFACES]; |
||||
}; |
||||
|
||||
struct darwin_transfer_priv { |
||||
/* Isoc */ |
||||
IOUSBIsocFrame *isoc_framelist; |
||||
int num_iso_packets; |
||||
|
||||
/* Control */ |
||||
IOUSBDevRequestTO req; |
||||
|
||||
/* Bulk */ |
||||
|
||||
/* Completion status */ |
||||
IOReturn result; |
||||
UInt32 size; |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,367 @@ |
||||
/*
|
||||
* Copyright 2007-2008, Haiku Inc. All rights reserved. |
||||
* Distributed under the terms of the MIT License. |
||||
* |
||||
* Authors: |
||||
* Michael Lotz <mmlr@mlotz.ch> |
||||
*/ |
||||
|
||||
#include "haiku_usb.h" |
||||
#include <cstdio> |
||||
#include <Directory.h> |
||||
#include <Entry.h> |
||||
#include <Looper.h> |
||||
#include <Messenger.h> |
||||
#include <Node.h> |
||||
#include <NodeMonitor.h> |
||||
#include <Path.h> |
||||
#include <cstring> |
||||
|
||||
class WatchedEntry { |
||||
public: |
||||
WatchedEntry(BMessenger *, entry_ref *); |
||||
~WatchedEntry(); |
||||
bool EntryCreated(entry_ref *ref); |
||||
bool EntryRemoved(ino_t node); |
||||
bool InitCheck(); |
||||
|
||||
private: |
||||
BMessenger* fMessenger; |
||||
node_ref fNode; |
||||
bool fIsDirectory; |
||||
USBDevice* fDevice; |
||||
WatchedEntry* fEntries; |
||||
WatchedEntry* fLink; |
||||
bool fInitCheck; |
||||
}; |
||||
|
||||
|
||||
class RosterLooper : public BLooper { |
||||
public: |
||||
RosterLooper(USBRoster *); |
||||
void Stop(); |
||||
virtual void MessageReceived(BMessage *); |
||||
bool InitCheck(); |
||||
|
||||
private: |
||||
USBRoster* fRoster; |
||||
WatchedEntry* fRoot; |
||||
BMessenger* fMessenger; |
||||
bool fInitCheck; |
||||
}; |
||||
|
||||
|
||||
WatchedEntry::WatchedEntry(BMessenger *messenger, entry_ref *ref) |
||||
: fMessenger(messenger), |
||||
fIsDirectory(false), |
||||
fDevice(NULL), |
||||
fEntries(NULL), |
||||
fLink(NULL), |
||||
fInitCheck(false) |
||||
{ |
||||
BEntry entry(ref); |
||||
entry.GetNodeRef(&fNode); |
||||
|
||||
BDirectory directory; |
||||
if (entry.IsDirectory() && directory.SetTo(ref) >= B_OK) { |
||||
fIsDirectory = true; |
||||
|
||||
while (directory.GetNextEntry(&entry) >= B_OK) { |
||||
if (entry.GetRef(ref) < B_OK) |
||||
continue; |
||||
|
||||
WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref); |
||||
if (child == NULL) |
||||
continue; |
||||
if (child->InitCheck() == false) { |
||||
delete child; |
||||
continue; |
||||
} |
||||
|
||||
child->fLink = fEntries; |
||||
fEntries = child; |
||||
} |
||||
|
||||
watch_node(&fNode, B_WATCH_DIRECTORY, *fMessenger); |
||||
} |
||||
else { |
||||
if (strncmp(ref->name, "raw", 3) == 0) |
||||
return; |
||||
|
||||
BPath path, parent_path; |
||||
entry.GetPath(&path); |
||||
fDevice = new(std::nothrow) USBDevice(path.Path()); |
||||
if (fDevice != NULL && fDevice->InitCheck() == true) { |
||||
// Add this new device to each active context's device list
|
||||
struct libusb_context *ctx; |
||||
unsigned long session_id = (unsigned long)&fDevice; |
||||
|
||||
usbi_mutex_lock(&active_contexts_lock); |
||||
list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { |
||||
struct libusb_device *dev = usbi_get_device_by_session_id(ctx, session_id); |
||||
if (dev) { |
||||
usbi_dbg("using previously allocated device with location %lu", session_id); |
||||
libusb_unref_device(dev); |
||||
continue; |
||||
} |
||||
usbi_dbg("allocating new device with location %lu", session_id); |
||||
dev = usbi_alloc_device(ctx, session_id); |
||||
if (!dev) { |
||||
usbi_dbg("device allocation failed"); |
||||
continue; |
||||
} |
||||
*((USBDevice **)dev->os_priv) = fDevice; |
||||
|
||||
// Calculate pseudo-device-address
|
||||
int addr, tmp; |
||||
if (strcmp(path.Leaf(), "hub") == 0) |
||||
tmp = 100; //Random Number
|
||||
else |
||||
sscanf(path.Leaf(), "%d", &tmp); |
||||
addr = tmp + 1; |
||||
path.GetParent(&parent_path); |
||||
while (strcmp(parent_path.Leaf(), "usb") != 0) { |
||||
sscanf(parent_path.Leaf(), "%d", &tmp); |
||||
addr += tmp + 1; |
||||
parent_path.GetParent(&parent_path); |
||||
} |
||||
sscanf(path.Path(), "/dev/bus/usb/%d", &dev->bus_number); |
||||
dev->device_address = addr - (dev->bus_number + 1); |
||||
|
||||
if (usbi_sanitize_device(dev) < 0) { |
||||
usbi_dbg("device sanitization failed"); |
||||
libusb_unref_device(dev); |
||||
continue; |
||||
} |
||||
usbi_connect_device(dev); |
||||
} |
||||
usbi_mutex_unlock(&active_contexts_lock); |
||||
} |
||||
else if (fDevice) { |
||||
delete fDevice; |
||||
fDevice = NULL; |
||||
return; |
||||
} |
||||
} |
||||
fInitCheck = true; |
||||
} |
||||
|
||||
|
||||
WatchedEntry::~WatchedEntry() |
||||
{ |
||||
if (fIsDirectory) { |
||||
watch_node(&fNode, B_STOP_WATCHING, *fMessenger); |
||||
|
||||
WatchedEntry *child = fEntries; |
||||
while (child) { |
||||
WatchedEntry *next = child->fLink; |
||||
delete child; |
||||
child = next; |
||||
} |
||||
} |
||||
|
||||
if (fDevice) { |
||||
// Remove this device from each active context's device list
|
||||
struct libusb_context *ctx; |
||||
struct libusb_device *dev; |
||||
unsigned long session_id = (unsigned long)&fDevice; |
||||
|
||||
usbi_mutex_lock(&active_contexts_lock); |
||||
list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { |
||||
dev = usbi_get_device_by_session_id(ctx, session_id); |
||||
if (dev != NULL) { |
||||
usbi_disconnect_device(dev); |
||||
libusb_unref_device(dev); |
||||
} else { |
||||
usbi_dbg("device with location %lu not found", session_id); |
||||
} |
||||
} |
||||
usbi_mutex_static_unlock(&active_contexts_lock); |
||||
delete fDevice; |
||||
} |
||||
} |
||||
|
||||
|
||||
bool |
||||
WatchedEntry::EntryCreated(entry_ref *ref) |
||||
{ |
||||
if (!fIsDirectory) |
||||
return false; |
||||
|
||||
if (ref->directory != fNode.node) { |
||||
WatchedEntry *child = fEntries; |
||||
while (child) { |
||||
if (child->EntryCreated(ref)) |
||||
return true; |
||||
child = child->fLink; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
WatchedEntry *child = new(std::nothrow) WatchedEntry(fMessenger, ref); |
||||
if (child == NULL) |
||||
return false; |
||||
child->fLink = fEntries; |
||||
fEntries = child; |
||||
return true; |
||||
} |
||||
|
||||
|
||||
bool |
||||
WatchedEntry::EntryRemoved(ino_t node) |
||||
{ |
||||
if (!fIsDirectory) |
||||
return false; |
||||
|
||||
WatchedEntry *child = fEntries; |
||||
WatchedEntry *lastChild = NULL; |
||||
while (child) { |
||||
if (child->fNode.node == node) { |
||||
if (lastChild) |
||||
lastChild->fLink = child->fLink; |
||||
else |
||||
fEntries = child->fLink; |
||||
delete child; |
||||
return true; |
||||
} |
||||
|
||||
if (child->EntryRemoved(node)) |
||||
return true; |
||||
|
||||
lastChild = child; |
||||
child = child->fLink; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
|
||||
bool |
||||
WatchedEntry::InitCheck() |
||||
{ |
||||
return fInitCheck; |
||||
} |
||||
|
||||
|
||||
RosterLooper::RosterLooper(USBRoster *roster) |
||||
: BLooper("LibusbRoster Looper"), |
||||
fRoster(roster), |
||||
fRoot(NULL), |
||||
fMessenger(NULL), |
||||
fInitCheck(false) |
||||
{ |
||||
BEntry entry("/dev/bus/usb"); |
||||
if (!entry.Exists()) { |
||||
usbi_err(NULL, "usb_raw not published"); |
||||
return; |
||||
} |
||||
|
||||
Run(); |
||||
fMessenger = new(std::nothrow) BMessenger(this); |
||||
if (fMessenger == NULL) { |
||||
usbi_err(NULL, "error creating BMessenger object"); |
||||
return; |
||||
} |
||||
|
||||
if (Lock()) { |
||||
entry_ref ref; |
||||
entry.GetRef(&ref); |
||||
fRoot = new(std::nothrow) WatchedEntry(fMessenger, &ref); |
||||
Unlock(); |
||||
if (fRoot == NULL) |
||||
return; |
||||
if (fRoot->InitCheck() == false) { |
||||
delete fRoot; |
||||
fRoot = NULL; |
||||
return; |
||||
} |
||||
} |
||||
fInitCheck = true; |
||||
} |
||||
|
||||
|
||||
void |
||||
RosterLooper::Stop() |
||||
{ |
||||
Lock(); |
||||
delete fRoot; |
||||
delete fMessenger; |
||||
Quit(); |
||||
} |
||||
|
||||
|
||||
void |
||||
RosterLooper::MessageReceived(BMessage *message) |
||||
{ |
||||
int32 opcode; |
||||
if (message->FindInt32("opcode", &opcode) < B_OK) |
||||
return; |
||||
|
||||
switch (opcode) { |
||||
case B_ENTRY_CREATED: |
||||
{ |
||||
dev_t device; |
||||
ino_t directory; |
||||
const char *name; |
||||
if (message->FindInt32("device", &device) < B_OK || |
||||
message->FindInt64("directory", &directory) < B_OK || |
||||
message->FindString("name", &name) < B_OK) |
||||
break; |
||||
|
||||
entry_ref ref(device, directory, name); |
||||
fRoot->EntryCreated(&ref); |
||||
break; |
||||
} |
||||
case B_ENTRY_REMOVED: |
||||
{ |
||||
ino_t node; |
||||
if (message->FindInt64("node", &node) < B_OK) |
||||
break; |
||||
fRoot->EntryRemoved(node); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
bool |
||||
RosterLooper::InitCheck() |
||||
{ |
||||
return fInitCheck; |
||||
} |
||||
|
||||
|
||||
USBRoster::USBRoster() |
||||
: fLooper(NULL) |
||||
{ |
||||
} |
||||
|
||||
|
||||
USBRoster::~USBRoster() |
||||
{ |
||||
Stop(); |
||||
} |
||||
|
||||
|
||||
int |
||||
USBRoster::Start() |
||||
{ |
||||
if (fLooper == NULL) { |
||||
fLooper = new(std::nothrow) RosterLooper(this); |
||||
if (fLooper == NULL || ((RosterLooper *)fLooper)->InitCheck() == false) { |
||||
if (fLooper) |
||||
fLooper = NULL; |
||||
return LIBUSB_ERROR_OTHER; |
||||
} |
||||
} |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
|
||||
void |
||||
USBRoster::Stop() |
||||
{ |
||||
if (fLooper) { |
||||
((RosterLooper *)fLooper)->Stop(); |
||||
fLooper = NULL; |
||||
} |
||||
} |
@ -0,0 +1,112 @@ |
||||
/*
|
||||
* Haiku Backend for libusb |
||||
* Copyright © 2014 Akshay Jaggi <akshay1994.leo@gmail.com> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <List.h> |
||||
#include <Locker.h> |
||||
#include <Autolock.h> |
||||
#include <USBKit.h> |
||||
#include <map> |
||||
#include "libusbi.h" |
||||
#include "haiku_usb_raw.h" |
||||
|
||||
using namespace std; |
||||
|
||||
class USBDevice; |
||||
class USBDeviceHandle; |
||||
class USBTransfer; |
||||
|
||||
class USBDevice { |
||||
public: |
||||
USBDevice(const char *); |
||||
virtual ~USBDevice(); |
||||
const char* Location() const; |
||||
uint8 CountConfigurations() const; |
||||
const usb_device_descriptor* Descriptor() const; |
||||
const usb_configuration_descriptor* ConfigurationDescriptor(uint32) const; |
||||
const usb_configuration_descriptor* ActiveConfiguration() const; |
||||
uint8 EndpointToIndex(uint8) const; |
||||
uint8 EndpointToInterface(uint8) const; |
||||
int ClaimInterface(int); |
||||
int ReleaseInterface(int); |
||||
int CheckInterfacesFree(int); |
||||
int SetActiveConfiguration(int); |
||||
int ActiveConfigurationIndex() const; |
||||
bool InitCheck(); |
||||
private: |
||||
int Initialise(); |
||||
unsigned int fClaimedInterfaces; // Max Interfaces can be 32. Using a bitmask
|
||||
usb_device_descriptor fDeviceDescriptor; |
||||
unsigned char** fConfigurationDescriptors; |
||||
int fActiveConfiguration; |
||||
char* fPath; |
||||
map<uint8,uint8> fConfigToIndex; |
||||
map<uint8,uint8>* fEndpointToIndex; |
||||
map<uint8,uint8>* fEndpointToInterface; |
||||
bool fInitCheck; |
||||
}; |
||||
|
||||
class USBDeviceHandle { |
||||
public: |
||||
USBDeviceHandle(USBDevice *dev); |
||||
virtual ~USBDeviceHandle(); |
||||
int ClaimInterface(int); |
||||
int ReleaseInterface(int); |
||||
int SetConfiguration(int); |
||||
int SetAltSetting(int, int); |
||||
status_t SubmitTransfer(struct usbi_transfer *); |
||||
status_t CancelTransfer(USBTransfer *); |
||||
bool InitCheck(); |
||||
private: |
||||
int fRawFD; |
||||
static status_t TransfersThread(void *); |
||||
void TransfersWorker(); |
||||
USBDevice* fUSBDevice; |
||||
unsigned int fClaimedInterfaces; |
||||
BList fTransfers; |
||||
BLocker fTransfersLock; |
||||
sem_id fTransfersSem; |
||||
thread_id fTransfersThread; |
||||
bool fInitCheck; |
||||
}; |
||||
|
||||
class USBTransfer { |
||||
public: |
||||
USBTransfer(struct usbi_transfer *, USBDevice *); |
||||
virtual ~USBTransfer(); |
||||
void Do(int); |
||||
struct usbi_transfer* UsbiTransfer(); |
||||
void SetCancelled(); |
||||
bool IsCancelled(); |
||||
private: |
||||
struct usbi_transfer* fUsbiTransfer; |
||||
struct libusb_transfer* fLibusbTransfer; |
||||
USBDevice* fUSBDevice; |
||||
BLocker fStatusLock; |
||||
bool fCancelled; |
||||
}; |
||||
|
||||
class USBRoster { |
||||
public: |
||||
USBRoster(); |
||||
virtual ~USBRoster(); |
||||
int Start(); |
||||
void Stop(); |
||||
private: |
||||
void* fLooper; |
||||
}; |
517
vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_backend.cpp
generated
vendored
517
vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/haiku_usb_backend.cpp
generated
vendored
@ -0,0 +1,517 @@ |
||||
/*
|
||||
* Haiku Backend for libusb |
||||
* Copyright © 2014 Akshay Jaggi <akshay1994.leo@gmail.com> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
|
||||
#include <unistd.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <new> |
||||
#include <vector> |
||||
|
||||
#include "haiku_usb.h" |
||||
|
||||
int _errno_to_libusb(int status) |
||||
{ |
||||
return status; |
||||
} |
||||
|
||||
USBTransfer::USBTransfer(struct usbi_transfer *itransfer, USBDevice *device) |
||||
{ |
||||
fUsbiTransfer = itransfer; |
||||
fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
fUSBDevice = device; |
||||
fCancelled = false; |
||||
} |
||||
|
||||
USBTransfer::~USBTransfer() |
||||
{ |
||||
} |
||||
|
||||
struct usbi_transfer * |
||||
USBTransfer::UsbiTransfer() |
||||
{ |
||||
return fUsbiTransfer; |
||||
} |
||||
|
||||
void |
||||
USBTransfer::SetCancelled() |
||||
{ |
||||
fCancelled = true; |
||||
} |
||||
|
||||
bool |
||||
USBTransfer::IsCancelled() |
||||
{ |
||||
return fCancelled; |
||||
} |
||||
|
||||
void |
||||
USBTransfer::Do(int fRawFD) |
||||
{ |
||||
switch (fLibusbTransfer->type) { |
||||
case LIBUSB_TRANSFER_TYPE_CONTROL: |
||||
{ |
||||
struct libusb_control_setup *setup = (struct libusb_control_setup *)fLibusbTransfer->buffer; |
||||
usb_raw_command command; |
||||
command.control.request_type = setup->bmRequestType; |
||||
command.control.request = setup->bRequest; |
||||
command.control.value = setup->wValue; |
||||
command.control.index = setup->wIndex; |
||||
command.control.length = setup->wLength; |
||||
command.control.data = fLibusbTransfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; |
||||
if (fCancelled) |
||||
break; |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) || |
||||
command.control.status != B_USB_RAW_STATUS_SUCCESS) { |
||||
fUsbiTransfer->transferred = -1; |
||||
usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed control transfer"); |
||||
break; |
||||
} |
||||
fUsbiTransfer->transferred = command.control.length; |
||||
} |
||||
break; |
||||
case LIBUSB_TRANSFER_TYPE_BULK: |
||||
case LIBUSB_TRANSFER_TYPE_INTERRUPT: |
||||
{ |
||||
usb_raw_command command; |
||||
command.transfer.interface = fUSBDevice->EndpointToInterface(fLibusbTransfer->endpoint); |
||||
command.transfer.endpoint = fUSBDevice->EndpointToIndex(fLibusbTransfer->endpoint); |
||||
command.transfer.data = fLibusbTransfer->buffer; |
||||
command.transfer.length = fLibusbTransfer->length; |
||||
if (fCancelled) |
||||
break; |
||||
if (fLibusbTransfer->type == LIBUSB_TRANSFER_TYPE_BULK) { |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_BULK_TRANSFER, &command, sizeof(command)) || |
||||
command.transfer.status != B_USB_RAW_STATUS_SUCCESS) { |
||||
fUsbiTransfer->transferred = -1; |
||||
usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed bulk transfer"); |
||||
break; |
||||
} |
||||
} |
||||
else { |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_INTERRUPT_TRANSFER, &command, sizeof(command)) || |
||||
command.transfer.status != B_USB_RAW_STATUS_SUCCESS) { |
||||
fUsbiTransfer->transferred = -1; |
||||
usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed interrupt transfer"); |
||||
break; |
||||
} |
||||
} |
||||
fUsbiTransfer->transferred = command.transfer.length; |
||||
} |
||||
break; |
||||
// IsochronousTransfers not tested
|
||||
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: |
||||
{ |
||||
usb_raw_command command; |
||||
command.isochronous.interface = fUSBDevice->EndpointToInterface(fLibusbTransfer->endpoint); |
||||
command.isochronous.endpoint = fUSBDevice->EndpointToIndex(fLibusbTransfer->endpoint); |
||||
command.isochronous.data = fLibusbTransfer->buffer; |
||||
command.isochronous.length = fLibusbTransfer->length; |
||||
command.isochronous.packet_count = fLibusbTransfer->num_iso_packets; |
||||
int i; |
||||
usb_iso_packet_descriptor *packetDescriptors = new usb_iso_packet_descriptor[fLibusbTransfer->num_iso_packets]; |
||||
for (i = 0; i < fLibusbTransfer->num_iso_packets; i++) { |
||||
if ((int16)(fLibusbTransfer->iso_packet_desc[i]).length != (fLibusbTransfer->iso_packet_desc[i]).length) { |
||||
fUsbiTransfer->transferred = -1; |
||||
usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed isochronous transfer"); |
||||
break; |
||||
} |
||||
packetDescriptors[i].request_length = (int16)(fLibusbTransfer->iso_packet_desc[i]).length; |
||||
} |
||||
if (i < fLibusbTransfer->num_iso_packets) |
||||
break; // TODO Handle this error
|
||||
command.isochronous.packet_descriptors = packetDescriptors; |
||||
if (fCancelled) |
||||
break; |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER, &command, sizeof(command)) || |
||||
command.isochronous.status != B_USB_RAW_STATUS_SUCCESS) { |
||||
fUsbiTransfer->transferred = -1; |
||||
usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed isochronous transfer"); |
||||
break; |
||||
} |
||||
for (i = 0; i < fLibusbTransfer->num_iso_packets; i++) { |
||||
(fLibusbTransfer->iso_packet_desc[i]).actual_length = packetDescriptors[i].actual_length; |
||||
switch (packetDescriptors[i].status) { |
||||
case B_OK: |
||||
(fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_COMPLETED; |
||||
break; |
||||
default: |
||||
(fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_ERROR; |
||||
break; |
||||
} |
||||
} |
||||
delete[] packetDescriptors; |
||||
// Do we put the length of transfer here, for isochronous transfers?
|
||||
fUsbiTransfer->transferred = command.transfer.length; |
||||
} |
||||
break; |
||||
default: |
||||
usbi_err(TRANSFER_CTX(fLibusbTransfer), "Unknown type of transfer"); |
||||
} |
||||
} |
||||
|
||||
bool |
||||
USBDeviceHandle::InitCheck() |
||||
{ |
||||
return fInitCheck; |
||||
} |
||||
|
||||
status_t |
||||
USBDeviceHandle::TransfersThread(void *self) |
||||
{ |
||||
USBDeviceHandle *handle = (USBDeviceHandle *)self; |
||||
handle->TransfersWorker(); |
||||
return B_OK; |
||||
} |
||||
|
||||
void |
||||
USBDeviceHandle::TransfersWorker() |
||||
{ |
||||
while (true) { |
||||
status_t status = acquire_sem(fTransfersSem); |
||||
if (status == B_BAD_SEM_ID) |
||||
break; |
||||
if (status == B_INTERRUPTED) |
||||
continue; |
||||
fTransfersLock.Lock(); |
||||
USBTransfer *fPendingTransfer = (USBTransfer *) fTransfers.RemoveItem((int32)0); |
||||
fTransfersLock.Unlock(); |
||||
fPendingTransfer->Do(fRawFD); |
||||
usbi_signal_transfer_completion(fPendingTransfer->UsbiTransfer()); |
||||
} |
||||
} |
||||
|
||||
status_t |
||||
USBDeviceHandle::SubmitTransfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
USBTransfer *transfer = new USBTransfer(itransfer, fUSBDevice); |
||||
*((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = transfer; |
||||
BAutolock locker(fTransfersLock); |
||||
fTransfers.AddItem(transfer); |
||||
release_sem(fTransfersSem); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
status_t |
||||
USBDeviceHandle::CancelTransfer(USBTransfer *transfer) |
||||
{ |
||||
transfer->SetCancelled(); |
||||
fTransfersLock.Lock(); |
||||
bool removed = fTransfers.RemoveItem(transfer); |
||||
fTransfersLock.Unlock(); |
||||
if(removed) |
||||
usbi_signal_transfer_completion(transfer->UsbiTransfer()); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
USBDeviceHandle::USBDeviceHandle(USBDevice *dev) |
||||
: |
||||
fTransfersThread(-1), |
||||
fUSBDevice(dev), |
||||
fClaimedInterfaces(0), |
||||
fInitCheck(false) |
||||
{ |
||||
fRawFD = open(dev->Location(), O_RDWR | O_CLOEXEC); |
||||
if (fRawFD < 0) { |
||||
usbi_err(NULL,"failed to open device"); |
||||
return; |
||||
} |
||||
fTransfersSem = create_sem(0, "Transfers Queue Sem"); |
||||
fTransfersThread = spawn_thread(TransfersThread, "Transfer Worker", B_NORMAL_PRIORITY, this); |
||||
resume_thread(fTransfersThread); |
||||
fInitCheck = true; |
||||
} |
||||
|
||||
USBDeviceHandle::~USBDeviceHandle() |
||||
{ |
||||
if (fRawFD > 0) |
||||
close(fRawFD); |
||||
for(int i = 0; i < 32; i++) { |
||||
if (fClaimedInterfaces & (1 << i)) |
||||
ReleaseInterface(i); |
||||
} |
||||
delete_sem(fTransfersSem); |
||||
if (fTransfersThread > 0) |
||||
wait_for_thread(fTransfersThread, NULL); |
||||
} |
||||
|
||||
int |
||||
USBDeviceHandle::ClaimInterface(int inumber) |
||||
{ |
||||
int status = fUSBDevice->ClaimInterface(inumber); |
||||
if (status == LIBUSB_SUCCESS) |
||||
fClaimedInterfaces |= (1 << inumber); |
||||
return status; |
||||
} |
||||
|
||||
int |
||||
USBDeviceHandle::ReleaseInterface(int inumber) |
||||
{ |
||||
fUSBDevice->ReleaseInterface(inumber); |
||||
fClaimedInterfaces &= ~(1 << inumber); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
int |
||||
USBDeviceHandle::SetConfiguration(int config) |
||||
{ |
||||
int config_index = fUSBDevice->CheckInterfacesFree(config); |
||||
if(config_index == LIBUSB_ERROR_BUSY || config_index == LIBUSB_ERROR_NOT_FOUND) |
||||
return config_index; |
||||
usb_raw_command command; |
||||
command.config.config_index = config_index; |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_SET_CONFIGURATION, &command, sizeof(command)) || |
||||
command.config.status != B_USB_RAW_STATUS_SUCCESS) { |
||||
return _errno_to_libusb(command.config.status); |
||||
} |
||||
fUSBDevice->SetActiveConfiguration(config_index); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
int |
||||
USBDeviceHandle::SetAltSetting(int inumber, int alt) |
||||
{ |
||||
usb_raw_command command; |
||||
command.alternate.config_index = fUSBDevice->ActiveConfigurationIndex(); |
||||
command.alternate.interface_index = inumber; |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ACTIVE_ALT_INTERFACE_INDEX, &command, sizeof(command)) || |
||||
command.alternate.status != B_USB_RAW_STATUS_SUCCESS) { |
||||
usbi_err(NULL, "Error retrieving active alternate interface"); |
||||
return _errno_to_libusb(command.alternate.status); |
||||
} |
||||
if (command.alternate.alternate_info == alt) { |
||||
usbi_dbg("Setting alternate interface successful"); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
command.alternate.alternate_info = alt; |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_SET_ALT_INTERFACE, &command, sizeof(command)) || |
||||
command.alternate.status != B_USB_RAW_STATUS_SUCCESS) { //IF IOCTL FAILS DEVICE DISONNECTED PROBABLY
|
||||
usbi_err(NULL, "Error setting alternate interface"); |
||||
return _errno_to_libusb(command.alternate.status); |
||||
} |
||||
usbi_dbg("Setting alternate interface successful"); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
|
||||
USBDevice::USBDevice(const char *path) |
||||
: |
||||
fPath(NULL), |
||||
fActiveConfiguration(0), //0?
|
||||
fConfigurationDescriptors(NULL), |
||||
fClaimedInterfaces(0), |
||||
fEndpointToIndex(NULL), |
||||
fEndpointToInterface(NULL), |
||||
fInitCheck(false) |
||||
{ |
||||
fPath=strdup(path); |
||||
Initialise(); |
||||
} |
||||
|
||||
USBDevice::~USBDevice() |
||||
{ |
||||
free(fPath); |
||||
if (fConfigurationDescriptors) { |
||||
for(int i = 0; i < fDeviceDescriptor.num_configurations; i++) { |
||||
if (fConfigurationDescriptors[i]) |
||||
delete fConfigurationDescriptors[i]; |
||||
} |
||||
delete[] fConfigurationDescriptors; |
||||
} |
||||
if (fEndpointToIndex) |
||||
delete[] fEndpointToIndex; |
||||
if (fEndpointToInterface) |
||||
delete[] fEndpointToInterface; |
||||
} |
||||
|
||||
bool |
||||
USBDevice::InitCheck() |
||||
{ |
||||
return fInitCheck; |
||||
} |
||||
|
||||
const char * |
||||
USBDevice::Location() const |
||||
{ |
||||
return fPath; |
||||
} |
||||
|
||||
uint8 |
||||
USBDevice::CountConfigurations() const |
||||
{ |
||||
return fDeviceDescriptor.num_configurations; |
||||
} |
||||
|
||||
const usb_device_descriptor * |
||||
USBDevice::Descriptor() const |
||||
{ |
||||
return &fDeviceDescriptor; |
||||
} |
||||
|
||||
const usb_configuration_descriptor * |
||||
USBDevice::ConfigurationDescriptor(uint32 index) const |
||||
{ |
||||
if (index > CountConfigurations()) |
||||
return NULL; |
||||
return (usb_configuration_descriptor *) fConfigurationDescriptors[index]; |
||||
} |
||||
|
||||
const usb_configuration_descriptor * |
||||
USBDevice::ActiveConfiguration() const |
||||
{ |
||||
return (usb_configuration_descriptor *) fConfigurationDescriptors[fActiveConfiguration]; |
||||
} |
||||
|
||||
int |
||||
USBDevice::ActiveConfigurationIndex() const |
||||
{ |
||||
return fActiveConfiguration; |
||||
} |
||||
|
||||
int USBDevice::ClaimInterface(int interface) |
||||
{ |
||||
if (interface > ActiveConfiguration()->number_interfaces) |
||||
return LIBUSB_ERROR_NOT_FOUND; |
||||
if (fClaimedInterfaces & (1 << interface)) |
||||
return LIBUSB_ERROR_BUSY; |
||||
fClaimedInterfaces |= (1 << interface); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
int USBDevice::ReleaseInterface(int interface) |
||||
{ |
||||
fClaimedInterfaces &= ~(1 << interface); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
int |
||||
USBDevice::CheckInterfacesFree(int config) |
||||
{ |
||||
if (fConfigToIndex.count(config) == 0) |
||||
return LIBUSB_ERROR_NOT_FOUND; |
||||
if (fClaimedInterfaces == 0) |
||||
return fConfigToIndex[(uint8)config]; |
||||
return LIBUSB_ERROR_BUSY; |
||||
} |
||||
|
||||
int |
||||
USBDevice::SetActiveConfiguration(int config_index) |
||||
{ |
||||
fActiveConfiguration = config_index; |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
uint8 |
||||
USBDevice::EndpointToIndex(uint8 address) const |
||||
{ |
||||
return fEndpointToIndex[fActiveConfiguration][address]; |
||||
} |
||||
|
||||
uint8 |
||||
USBDevice::EndpointToInterface(uint8 address) const |
||||
{ |
||||
return fEndpointToInterface[fActiveConfiguration][address]; |
||||
} |
||||
|
||||
int |
||||
USBDevice::Initialise() //Do we need more error checking, etc? How to report?
|
||||
{ |
||||
int fRawFD = open(fPath, O_RDWR | O_CLOEXEC); |
||||
if (fRawFD < 0) |
||||
return B_ERROR; |
||||
usb_raw_command command; |
||||
command.device.descriptor = &fDeviceDescriptor; |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_DEVICE_DESCRIPTOR, &command, sizeof(command)) || |
||||
command.device.status != B_USB_RAW_STATUS_SUCCESS) { |
||||
close(fRawFD); |
||||
return B_ERROR; |
||||
} |
||||
|
||||
fConfigurationDescriptors = new(std::nothrow) unsigned char *[fDeviceDescriptor.num_configurations]; |
||||
fEndpointToIndex = new(std::nothrow) map<uint8,uint8> [fDeviceDescriptor.num_configurations]; |
||||
fEndpointToInterface = new(std::nothrow) map<uint8,uint8> [fDeviceDescriptor.num_configurations]; |
||||
for (int i = 0; i < fDeviceDescriptor.num_configurations; i++) { |
||||
usb_configuration_descriptor tmp_config; |
||||
command.config.descriptor = &tmp_config; |
||||
command.config.config_index = i; |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR, &command, sizeof(command)) || |
||||
command.config.status != B_USB_RAW_STATUS_SUCCESS) { |
||||
usbi_err(NULL, "failed retrieving configuration descriptor"); |
||||
close(fRawFD); |
||||
return B_ERROR; |
||||
} |
||||
fConfigToIndex[tmp_config.configuration_value] = i; |
||||
fConfigurationDescriptors[i] = new(std::nothrow) unsigned char[tmp_config.total_length]; |
||||
command.control.request_type = 128; |
||||
command.control.request = 6; |
||||
command.control.value = (2 << 8) | i; |
||||
command.control.index = 0; |
||||
command.control.length = tmp_config.total_length; |
||||
command.control.data = fConfigurationDescriptors[i]; |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) || |
||||
command.control.status!=B_USB_RAW_STATUS_SUCCESS) { |
||||
usbi_err(NULL, "failed retrieving full configuration descriptor"); |
||||
close(fRawFD); |
||||
return B_ERROR; |
||||
} |
||||
for (int j = 0; j < tmp_config.number_interfaces; j++) { |
||||
command.alternate.config_index = i; |
||||
command.alternate.interface_index = j; |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ALT_INTERFACE_COUNT, &command, sizeof(command)) || |
||||
command.config.status != B_USB_RAW_STATUS_SUCCESS) { |
||||
usbi_err(NULL, "failed retrieving number of alternate interfaces"); |
||||
close(fRawFD); |
||||
return B_ERROR; |
||||
} |
||||
int num_alternate = command.alternate.alternate_info; |
||||
for (int k = 0; k < num_alternate; k++) { |
||||
usb_interface_descriptor tmp_interface; |
||||
command.interface_etc.config_index = i; |
||||
command.interface_etc.interface_index = j; |
||||
command.interface_etc.alternate_index = k; |
||||
command.interface_etc.descriptor = &tmp_interface; |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR_ETC, &command, sizeof(command)) || |
||||
command.config.status != B_USB_RAW_STATUS_SUCCESS) { |
||||
usbi_err(NULL, "failed retrieving interface descriptor"); |
||||
close(fRawFD); |
||||
return B_ERROR; |
||||
} |
||||
for (int l = 0; l < tmp_interface.num_endpoints; l++) { |
||||
usb_endpoint_descriptor tmp_endpoint; |
||||
command.endpoint_etc.config_index = i; |
||||
command.endpoint_etc.interface_index = j; |
||||
command.endpoint_etc.alternate_index = k; |
||||
command.endpoint_etc.endpoint_index = l; |
||||
command.endpoint_etc.descriptor = &tmp_endpoint; |
||||
if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR_ETC, &command, sizeof(command)) || |
||||
command.config.status != B_USB_RAW_STATUS_SUCCESS) { |
||||
usbi_err(NULL, "failed retrieving endpoint descriptor"); |
||||
close(fRawFD); |
||||
return B_ERROR; |
||||
} |
||||
fEndpointToIndex[i][tmp_endpoint.endpoint_address] = l; |
||||
fEndpointToInterface[i][tmp_endpoint.endpoint_address] = j; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
close(fRawFD); |
||||
fInitCheck = true; |
||||
return B_OK; |
||||
} |
@ -0,0 +1,250 @@ |
||||
/*
|
||||
* Haiku Backend for libusb |
||||
* Copyright © 2014 Akshay Jaggi <akshay1994.leo@gmail.com> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
|
||||
#include <unistd.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <new> |
||||
#include <vector> |
||||
|
||||
#include "haiku_usb.h" |
||||
|
||||
USBRoster gUsbRoster; |
||||
int32 gInitCount = 0; |
||||
|
||||
static int |
||||
haiku_init(struct libusb_context *ctx) |
||||
{ |
||||
if (atomic_add(&gInitCount, 1) == 0) |
||||
return gUsbRoster.Start(); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static void |
||||
haiku_exit(void) |
||||
{ |
||||
if (atomic_add(&gInitCount, -1) == 1) |
||||
gUsbRoster.Stop(); |
||||
} |
||||
|
||||
static int |
||||
haiku_open(struct libusb_device_handle *dev_handle) |
||||
{ |
||||
USBDevice *dev = *((USBDevice **)dev_handle->dev->os_priv); |
||||
USBDeviceHandle *handle = new(std::nothrow) USBDeviceHandle(dev); |
||||
if (handle == NULL) |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
if (handle->InitCheck() == false) { |
||||
delete handle; |
||||
return LIBUSB_ERROR_NO_DEVICE; |
||||
} |
||||
*((USBDeviceHandle **)dev_handle->os_priv) = handle; |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static void |
||||
haiku_close(struct libusb_device_handle *dev_handle) |
||||
{ |
||||
USBDeviceHandle *handle = *((USBDeviceHandle **)dev_handle->os_priv); |
||||
if (handle == NULL) |
||||
return; |
||||
delete handle; |
||||
*((USBDeviceHandle **)dev_handle->os_priv) = NULL; |
||||
} |
||||
|
||||
static int |
||||
haiku_get_device_descriptor(struct libusb_device *device, unsigned char *buffer, int *host_endian) |
||||
{ |
||||
USBDevice *dev = *((USBDevice **)device->os_priv); |
||||
memcpy(buffer, dev->Descriptor(), DEVICE_DESC_LENGTH); |
||||
*host_endian = 0; |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int |
||||
haiku_get_active_config_descriptor(struct libusb_device *device, unsigned char *buffer, size_t len, int *host_endian) |
||||
{ |
||||
USBDevice *dev = *((USBDevice **)device->os_priv); |
||||
const usb_configuration_descriptor *act_config = dev->ActiveConfiguration(); |
||||
if (len > act_config->total_length) |
||||
return LIBUSB_ERROR_OVERFLOW; |
||||
memcpy(buffer, act_config, len); |
||||
*host_endian = 0; |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int |
||||
haiku_get_config_descriptor(struct libusb_device *device, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) |
||||
{ |
||||
USBDevice *dev = *((USBDevice **)device->os_priv); |
||||
const usb_configuration_descriptor *config = dev->ConfigurationDescriptor(config_index); |
||||
if (config == NULL) { |
||||
usbi_err(DEVICE_CTX(device), "failed getting configuration descriptor"); |
||||
return LIBUSB_ERROR_INVALID_PARAM; |
||||
} |
||||
if (len > config->total_length) |
||||
len = config->total_length; |
||||
memcpy(buffer, config, len); |
||||
*host_endian = 0; |
||||
return len; |
||||
} |
||||
|
||||
static int |
||||
haiku_set_configuration(struct libusb_device_handle *dev_handle, int config) |
||||
{ |
||||
USBDeviceHandle *handle= *((USBDeviceHandle **)dev_handle->os_priv); |
||||
return handle->SetConfiguration(config); |
||||
} |
||||
|
||||
static int |
||||
haiku_claim_interface(struct libusb_device_handle *dev_handle, int interface_number) |
||||
{ |
||||
USBDeviceHandle *handle = *((USBDeviceHandle **)dev_handle->os_priv); |
||||
return handle->ClaimInterface(interface_number); |
||||
} |
||||
|
||||
static int |
||||
haiku_set_altsetting(struct libusb_device_handle *dev_handle, int interface_number, int altsetting) |
||||
{ |
||||
USBDeviceHandle *handle = *((USBDeviceHandle **)dev_handle->os_priv); |
||||
return handle->SetAltSetting(interface_number, altsetting); |
||||
} |
||||
|
||||
static int |
||||
haiku_release_interface(struct libusb_device_handle *dev_handle, int interface_number) |
||||
{ |
||||
USBDeviceHandle *handle = *((USBDeviceHandle **)dev_handle->os_priv); |
||||
haiku_set_altsetting(dev_handle,interface_number, 0); |
||||
return handle->ReleaseInterface(interface_number); |
||||
} |
||||
|
||||
static int |
||||
haiku_submit_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
USBDeviceHandle *fDeviceHandle = *((USBDeviceHandle **)fLibusbTransfer->dev_handle->os_priv); |
||||
return fDeviceHandle->SubmitTransfer(itransfer); |
||||
} |
||||
|
||||
static int |
||||
haiku_cancel_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
USBDeviceHandle *fDeviceHandle = *((USBDeviceHandle **)fLibusbTransfer->dev_handle->os_priv); |
||||
return fDeviceHandle->CancelTransfer(*((USBTransfer **)usbi_transfer_get_os_priv(itransfer))); |
||||
} |
||||
|
||||
static void |
||||
haiku_clear_transfer_priv(struct usbi_transfer *itransfer) |
||||
{ |
||||
USBTransfer *transfer = *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)); |
||||
delete transfer; |
||||
*((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = NULL; |
||||
} |
||||
|
||||
static int |
||||
haiku_handle_transfer_completion(struct usbi_transfer *itransfer) |
||||
{ |
||||
USBTransfer *transfer = *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)); |
||||
|
||||
usbi_mutex_lock(&itransfer->lock); |
||||
if (transfer->IsCancelled()) { |
||||
delete transfer; |
||||
*((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = NULL; |
||||
usbi_mutex_unlock(&itransfer->lock); |
||||
if (itransfer->transferred < 0) |
||||
itransfer->transferred = 0; |
||||
return usbi_handle_transfer_cancellation(itransfer); |
||||
} |
||||
libusb_transfer_status status = LIBUSB_TRANSFER_COMPLETED; |
||||
if (itransfer->transferred < 0) { |
||||
usbi_err(ITRANSFER_CTX(itransfer), "error in transfer"); |
||||
status = LIBUSB_TRANSFER_ERROR; |
||||
itransfer->transferred = 0; |
||||
} |
||||
delete transfer; |
||||
*((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = NULL; |
||||
usbi_mutex_unlock(&itransfer->lock); |
||||
return usbi_handle_transfer_completion(itransfer, status); |
||||
} |
||||
|
||||
static int |
||||
haiku_clock_gettime(int clkid, struct timespec *tp) |
||||
{ |
||||
if (clkid == USBI_CLOCK_REALTIME) |
||||
return clock_gettime(CLOCK_REALTIME, tp); |
||||
if (clkid == USBI_CLOCK_MONOTONIC) |
||||
return clock_gettime(CLOCK_MONOTONIC, tp); |
||||
return LIBUSB_ERROR_INVALID_PARAM; |
||||
} |
||||
|
||||
const struct usbi_os_backend haiku_usb_raw_backend = { |
||||
/*.name =*/ "Haiku usbfs", |
||||
/*.caps =*/ 0, |
||||
/*.init =*/ haiku_init, |
||||
/*.exit =*/ haiku_exit, |
||||
/*.get_device_list =*/ NULL, |
||||
/*.hotplug_poll =*/ NULL, |
||||
/*.open =*/ haiku_open, |
||||
/*.close =*/ haiku_close, |
||||
/*.get_device_descriptor =*/ haiku_get_device_descriptor, |
||||
/*.get_active_config_descriptor =*/ haiku_get_active_config_descriptor, |
||||
/*.get_config_descriptor =*/ haiku_get_config_descriptor, |
||||
/*.get_config_descriptor_by_value =*/ NULL, |
||||
|
||||
|
||||
/*.get_configuration =*/ NULL, |
||||
/*.set_configuration =*/ haiku_set_configuration, |
||||
/*.claim_interface =*/ haiku_claim_interface, |
||||
/*.release_interface =*/ haiku_release_interface, |
||||
|
||||
/*.set_interface_altsetting =*/ haiku_set_altsetting, |
||||
/*.clear_halt =*/ NULL, |
||||
/*.reset_device =*/ NULL, |
||||
|
||||
/*.alloc_streams =*/ NULL, |
||||
/*.free_streams =*/ NULL, |
||||
|
||||
/*.dev_mem_alloc =*/ NULL, |
||||
/*.dev_mem_free =*/ NULL, |
||||
|
||||
/*.kernel_driver_active =*/ NULL, |
||||
/*.detach_kernel_driver =*/ NULL, |
||||
/*.attach_kernel_driver =*/ NULL, |
||||
|
||||
/*.destroy_device =*/ NULL, |
||||
|
||||
/*.submit_transfer =*/ haiku_submit_transfer, |
||||
/*.cancel_transfer =*/ haiku_cancel_transfer, |
||||
/*.clear_transfer_priv =*/ haiku_clear_transfer_priv, |
||||
|
||||
/*.handle_events =*/ NULL, |
||||
/*.handle_transfer_completion =*/ haiku_handle_transfer_completion, |
||||
|
||||
/*.clock_gettime =*/ haiku_clock_gettime, |
||||
|
||||
#ifdef USBI_TIMERFD_AVAILABLE |
||||
/*.get_timerfd_clockid =*/ NULL, |
||||
#endif |
||||
|
||||
/*.device_priv_size =*/ sizeof(USBDevice *), |
||||
/*.device_handle_priv_size =*/ sizeof(USBDeviceHandle *), |
||||
/*.transfer_priv_size =*/ sizeof(USBTransfer *), |
||||
}; |
@ -0,0 +1,180 @@ |
||||
/*
|
||||
* Copyright 2006-2008, Haiku Inc. All rights reserved. |
||||
* Distributed under the terms of the MIT License. |
||||
*/ |
||||
|
||||
#ifndef _USB_RAW_H_ |
||||
#define _USB_RAW_H_ |
||||
|
||||
#include <USB3.h> |
||||
|
||||
#define B_USB_RAW_PROTOCOL_VERSION 0x0015 |
||||
#define B_USB_RAW_ACTIVE_ALTERNATE 0xffffffff |
||||
|
||||
typedef enum { |
||||
B_USB_RAW_COMMAND_GET_VERSION = 0x1000, |
||||
|
||||
B_USB_RAW_COMMAND_GET_DEVICE_DESCRIPTOR = 0x2000, |
||||
B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR, |
||||
B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR, |
||||
B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR, |
||||
B_USB_RAW_COMMAND_GET_STRING_DESCRIPTOR, |
||||
B_USB_RAW_COMMAND_GET_GENERIC_DESCRIPTOR, |
||||
B_USB_RAW_COMMAND_GET_ALT_INTERFACE_COUNT, |
||||
B_USB_RAW_COMMAND_GET_ACTIVE_ALT_INTERFACE_INDEX, |
||||
B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR_ETC, |
||||
B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR_ETC, |
||||
B_USB_RAW_COMMAND_GET_GENERIC_DESCRIPTOR_ETC, |
||||
|
||||
B_USB_RAW_COMMAND_SET_CONFIGURATION = 0x3000, |
||||
B_USB_RAW_COMMAND_SET_FEATURE, |
||||
B_USB_RAW_COMMAND_CLEAR_FEATURE, |
||||
B_USB_RAW_COMMAND_GET_STATUS, |
||||
B_USB_RAW_COMMAND_GET_DESCRIPTOR, |
||||
B_USB_RAW_COMMAND_SET_ALT_INTERFACE, |
||||
|
||||
B_USB_RAW_COMMAND_CONTROL_TRANSFER = 0x4000, |
||||
B_USB_RAW_COMMAND_INTERRUPT_TRANSFER, |
||||
B_USB_RAW_COMMAND_BULK_TRANSFER, |
||||
B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER |
||||
} usb_raw_command_id; |
||||
|
||||
|
||||
typedef enum { |
||||
B_USB_RAW_STATUS_SUCCESS = 0, |
||||
|
||||
B_USB_RAW_STATUS_FAILED, |
||||
B_USB_RAW_STATUS_ABORTED, |
||||
B_USB_RAW_STATUS_STALLED, |
||||
B_USB_RAW_STATUS_CRC_ERROR, |
||||
B_USB_RAW_STATUS_TIMEOUT, |
||||
|
||||
B_USB_RAW_STATUS_INVALID_CONFIGURATION, |
||||
B_USB_RAW_STATUS_INVALID_INTERFACE, |
||||
B_USB_RAW_STATUS_INVALID_ENDPOINT, |
||||
B_USB_RAW_STATUS_INVALID_STRING, |
||||
|
||||
B_USB_RAW_STATUS_NO_MEMORY |
||||
} usb_raw_command_status; |
||||
|
||||
|
||||
typedef union { |
||||
struct { |
||||
status_t status; |
||||
} version; |
||||
|
||||
struct { |
||||
status_t status; |
||||
usb_device_descriptor *descriptor; |
||||
} device; |
||||
|
||||
struct { |
||||
status_t status; |
||||
usb_configuration_descriptor *descriptor; |
||||
uint32 config_index; |
||||
} config; |
||||
|
||||
struct { |
||||
status_t status; |
||||
uint32 alternate_info; |
||||
uint32 config_index; |
||||
uint32 interface_index; |
||||
} alternate; |
||||
|
||||
struct { |
||||
status_t status; |
||||
usb_interface_descriptor *descriptor; |
||||
uint32 config_index; |
||||
uint32 interface_index; |
||||
} interface; |
||||
|
||||
struct { |
||||
status_t status; |
||||
usb_interface_descriptor *descriptor; |
||||
uint32 config_index; |
||||
uint32 interface_index; |
||||
uint32 alternate_index; |
||||
} interface_etc; |
||||
|
||||
struct { |
||||
status_t status; |
||||
usb_endpoint_descriptor *descriptor; |
||||
uint32 config_index; |
||||
uint32 interface_index; |
||||
uint32 endpoint_index; |
||||
} endpoint; |
||||
|
||||
struct { |
||||
status_t status; |
||||
usb_endpoint_descriptor *descriptor; |
||||
uint32 config_index; |
||||
uint32 interface_index; |
||||
uint32 alternate_index; |
||||
uint32 endpoint_index; |
||||
} endpoint_etc; |
||||
|
||||
struct { |
||||
status_t status; |
||||
usb_descriptor *descriptor; |
||||
uint32 config_index; |
||||
uint32 interface_index; |
||||
uint32 generic_index; |
||||
size_t length; |
||||
} generic; |
||||
|
||||
struct { |
||||
status_t status; |
||||
usb_descriptor *descriptor; |
||||
uint32 config_index; |
||||
uint32 interface_index; |
||||
uint32 alternate_index; |
||||
uint32 generic_index; |
||||
size_t length; |
||||
} generic_etc; |
||||
|
||||
struct { |
||||
status_t status; |
||||
usb_string_descriptor *descriptor; |
||||
uint32 string_index; |
||||
size_t length; |
||||
} string; |
||||
|
||||
struct { |
||||
status_t status; |
||||
uint8 type; |
||||
uint8 index; |
||||
uint16 language_id; |
||||
void *data; |
||||
size_t length; |
||||
} descriptor; |
||||
|
||||
struct { |
||||
status_t status; |
||||
uint8 request_type; |
||||
uint8 request; |
||||
uint16 value; |
||||
uint16 index; |
||||
uint16 length; |
||||
void *data; |
||||
} control; |
||||
|
||||
struct { |
||||
status_t status; |
||||
uint32 interface; |
||||
uint32 endpoint; |
||||
void *data; |
||||
size_t length; |
||||
} transfer; |
||||
|
||||
struct { |
||||
status_t status; |
||||
uint32 interface; |
||||
uint32 endpoint; |
||||
void *data; |
||||
size_t length; |
||||
usb_iso_packet_descriptor *packet_descriptors; |
||||
uint32 packet_count; |
||||
} isochronous; |
||||
} usb_raw_command; |
||||
|
||||
#endif // _USB_RAW_H_
|
@ -0,0 +1,400 @@ |
||||
/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */ |
||||
/*
|
||||
* Linux usbfs backend for libusb |
||||
* Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org> |
||||
* Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> |
||||
* Copyright (c) 2013 Nathan Hjelm <hjelmn@mac.com> |
||||
* Copyright (c) 2016 Chris Dickens <christopher.a.dickens@gmail.com> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#include <assert.h> |
||||
#include <errno.h> |
||||
#include <fcntl.h> |
||||
#include <poll.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
#include <sys/types.h> |
||||
|
||||
#ifdef HAVE_ASM_TYPES_H |
||||
#include <asm/types.h> |
||||
#endif |
||||
|
||||
#include <sys/socket.h> |
||||
#include <linux/netlink.h> |
||||
|
||||
#include "libusbi.h" |
||||
#include "linux_usbfs.h" |
||||
|
||||
#define NL_GROUP_KERNEL 1 |
||||
|
||||
static int linux_netlink_socket = -1; |
||||
static int netlink_control_pipe[2] = { -1, -1 }; |
||||
static pthread_t libusb_linux_event_thread; |
||||
|
||||
static void *linux_netlink_event_thread_main(void *arg); |
||||
|
||||
static int set_fd_cloexec_nb(int fd) |
||||
{ |
||||
int flags; |
||||
|
||||
#if defined(FD_CLOEXEC) |
||||
flags = fcntl(fd, F_GETFD); |
||||
if (flags == -1) { |
||||
usbi_err(NULL, "failed to get netlink fd flags (%d)", errno); |
||||
return -1; |
||||
} |
||||
|
||||
if (!(flags & FD_CLOEXEC)) { |
||||
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { |
||||
usbi_err(NULL, "failed to set netlink fd flags (%d)", errno); |
||||
return -1; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
flags = fcntl(fd, F_GETFL); |
||||
if (flags == -1) { |
||||
usbi_err(NULL, "failed to get netlink fd status flags (%d)", errno); |
||||
return -1; |
||||
} |
||||
|
||||
if (!(flags & O_NONBLOCK)) { |
||||
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { |
||||
usbi_err(NULL, "failed to set netlink fd status flags (%d)", errno); |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int linux_netlink_start_event_monitor(void) |
||||
{ |
||||
struct sockaddr_nl sa_nl = { .nl_family = AF_NETLINK, .nl_groups = NL_GROUP_KERNEL }; |
||||
int socktype = SOCK_RAW; |
||||
int opt = 1; |
||||
int ret; |
||||
|
||||
#if defined(SOCK_CLOEXEC) |
||||
socktype |= SOCK_CLOEXEC; |
||||
#endif |
||||
#if defined(SOCK_NONBLOCK) |
||||
socktype |= SOCK_NONBLOCK; |
||||
#endif |
||||
|
||||
linux_netlink_socket = socket(PF_NETLINK, socktype, NETLINK_KOBJECT_UEVENT); |
||||
if (linux_netlink_socket == -1 && errno == EINVAL) { |
||||
usbi_dbg("failed to create netlink socket of type %d, attempting SOCK_RAW", socktype); |
||||
linux_netlink_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT); |
||||
} |
||||
|
||||
if (linux_netlink_socket == -1) { |
||||
usbi_err(NULL, "failed to create netlink socket (%d)", errno); |
||||
goto err; |
||||
} |
||||
|
||||
ret = set_fd_cloexec_nb(linux_netlink_socket); |
||||
if (ret == -1) |
||||
goto err_close_socket; |
||||
|
||||
ret = bind(linux_netlink_socket, (struct sockaddr *)&sa_nl, sizeof(sa_nl)); |
||||
if (ret == -1) { |
||||
usbi_err(NULL, "failed to bind netlink socket (%d)", errno); |
||||
goto err_close_socket; |
||||
} |
||||
|
||||
ret = setsockopt(linux_netlink_socket, SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)); |
||||
if (ret == -1) { |
||||
usbi_err(NULL, "failed to set netlink socket SO_PASSCRED option (%d)", errno); |
||||
goto err_close_socket; |
||||
} |
||||
|
||||
ret = usbi_pipe(netlink_control_pipe); |
||||
if (ret) { |
||||
usbi_err(NULL, "failed to create netlink control pipe"); |
||||
goto err_close_socket; |
||||
} |
||||
|
||||
ret = pthread_create(&libusb_linux_event_thread, NULL, linux_netlink_event_thread_main, NULL); |
||||
if (ret != 0) { |
||||
usbi_err(NULL, "failed to create netlink event thread (%d)", ret); |
||||
goto err_close_pipe; |
||||
} |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
|
||||
err_close_pipe: |
||||
close(netlink_control_pipe[0]); |
||||
close(netlink_control_pipe[1]); |
||||
netlink_control_pipe[0] = -1; |
||||
netlink_control_pipe[1] = -1; |
||||
err_close_socket: |
||||
close(linux_netlink_socket); |
||||
linux_netlink_socket = -1; |
||||
err: |
||||
return LIBUSB_ERROR_OTHER; |
||||
} |
||||
|
||||
int linux_netlink_stop_event_monitor(void) |
||||
{ |
||||
char dummy = 1; |
||||
ssize_t r; |
||||
|
||||
assert(linux_netlink_socket != -1); |
||||
|
||||
/* Write some dummy data to the control pipe and
|
||||
* wait for the thread to exit */ |
||||
r = usbi_write(netlink_control_pipe[1], &dummy, sizeof(dummy)); |
||||
if (r <= 0) |
||||
usbi_warn(NULL, "netlink control pipe signal failed"); |
||||
|
||||
pthread_join(libusb_linux_event_thread, NULL); |
||||
|
||||
close(linux_netlink_socket); |
||||
linux_netlink_socket = -1; |
||||
|
||||
/* close and reset control pipe */ |
||||
close(netlink_control_pipe[0]); |
||||
close(netlink_control_pipe[1]); |
||||
netlink_control_pipe[0] = -1; |
||||
netlink_control_pipe[1] = -1; |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static const char *netlink_message_parse(const char *buffer, size_t len, const char *key) |
||||
{ |
||||
const char *end = buffer + len; |
||||
size_t keylen = strlen(key); |
||||
|
||||
while (buffer < end && *buffer) { |
||||
if (strncmp(buffer, key, keylen) == 0 && buffer[keylen] == '=') |
||||
return buffer + keylen + 1; |
||||
buffer += strlen(buffer) + 1; |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
/* parse parts of netlink message common to both libudev and the kernel */ |
||||
static int linux_netlink_parse(const char *buffer, size_t len, int *detached, |
||||
const char **sys_name, uint8_t *busnum, uint8_t *devaddr) |
||||
{ |
||||
const char *tmp, *slash; |
||||
|
||||
errno = 0; |
||||
|
||||
*sys_name = NULL; |
||||
*detached = 0; |
||||
*busnum = 0; |
||||
*devaddr = 0; |
||||
|
||||
tmp = netlink_message_parse(buffer, len, "ACTION"); |
||||
if (!tmp) { |
||||
return -1; |
||||
} else if (strcmp(tmp, "remove") == 0) { |
||||
*detached = 1; |
||||
} else if (strcmp(tmp, "add") != 0) { |
||||
usbi_dbg("unknown device action %s", tmp); |
||||
return -1; |
||||
} |
||||
|
||||
/* check that this is a usb message */ |
||||
tmp = netlink_message_parse(buffer, len, "SUBSYSTEM"); |
||||
if (!tmp || strcmp(tmp, "usb") != 0) { |
||||
/* not usb. ignore */ |
||||
return -1; |
||||
} |
||||
|
||||
/* check that this is an actual usb device */ |
||||
tmp = netlink_message_parse(buffer, len, "DEVTYPE"); |
||||
if (!tmp || strcmp(tmp, "usb_device") != 0) { |
||||
/* not usb. ignore */ |
||||
return -1; |
||||
} |
||||
|
||||
tmp = netlink_message_parse(buffer, len, "BUSNUM"); |
||||
if (tmp) { |
||||
*busnum = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff); |
||||
if (errno) { |
||||
errno = 0; |
||||
return -1; |
||||
} |
||||
|
||||
tmp = netlink_message_parse(buffer, len, "DEVNUM"); |
||||
if (NULL == tmp) |
||||
return -1; |
||||
|
||||
*devaddr = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff); |
||||
if (errno) { |
||||
errno = 0; |
||||
return -1; |
||||
} |
||||
} else { |
||||
/* no bus number. try "DEVICE" */ |
||||
tmp = netlink_message_parse(buffer, len, "DEVICE"); |
||||
if (!tmp) { |
||||
/* not usb. ignore */ |
||||
return -1; |
||||
} |
||||
|
||||
/* Parse a device path such as /dev/bus/usb/003/004 */ |
||||
slash = strrchr(tmp, '/'); |
||||
if (!slash) |
||||
return -1; |
||||
|
||||
*busnum = (uint8_t)(strtoul(slash - 3, NULL, 10) & 0xff); |
||||
if (errno) { |
||||
errno = 0; |
||||
return -1; |
||||
} |
||||
|
||||
*devaddr = (uint8_t)(strtoul(slash + 1, NULL, 10) & 0xff); |
||||
if (errno) { |
||||
errno = 0; |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
tmp = netlink_message_parse(buffer, len, "DEVPATH"); |
||||
if (!tmp) |
||||
return -1; |
||||
|
||||
slash = strrchr(tmp, '/'); |
||||
if (slash) |
||||
*sys_name = slash + 1; |
||||
|
||||
/* found a usb device */ |
||||
return 0; |
||||
} |
||||
|
||||
static int linux_netlink_read_message(void) |
||||
{ |
||||
char cred_buffer[CMSG_SPACE(sizeof(struct ucred))]; |
||||
char msg_buffer[2048]; |
||||
const char *sys_name = NULL; |
||||
uint8_t busnum, devaddr; |
||||
int detached, r; |
||||
ssize_t len; |
||||
struct cmsghdr *cmsg; |
||||
struct ucred *cred; |
||||
struct sockaddr_nl sa_nl; |
||||
struct iovec iov = { .iov_base = msg_buffer, .iov_len = sizeof(msg_buffer) }; |
||||
struct msghdr msg = { |
||||
.msg_iov = &iov, .msg_iovlen = 1, |
||||
.msg_control = cred_buffer, .msg_controllen = sizeof(cred_buffer), |
||||
.msg_name = &sa_nl, .msg_namelen = sizeof(sa_nl) |
||||
}; |
||||
|
||||
/* read netlink message */ |
||||
len = recvmsg(linux_netlink_socket, &msg, 0); |
||||
if (len == -1) { |
||||
if (errno != EAGAIN && errno != EINTR) |
||||
usbi_err(NULL, "error receiving message from netlink (%d)", errno); |
||||
return -1; |
||||
} |
||||
|
||||
if (len < 32 || (msg.msg_flags & MSG_TRUNC)) { |
||||
usbi_err(NULL, "invalid netlink message length"); |
||||
return -1; |
||||
} |
||||
|
||||
if (sa_nl.nl_groups != NL_GROUP_KERNEL || sa_nl.nl_pid != 0) { |
||||
usbi_dbg("ignoring netlink message from unknown group/PID (%u/%u)", |
||||
(unsigned int)sa_nl.nl_groups, (unsigned int)sa_nl.nl_pid); |
||||
return -1; |
||||
} |
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg); |
||||
if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) { |
||||
usbi_dbg("ignoring netlink message with no sender credentials"); |
||||
return -1; |
||||
} |
||||
|
||||
cred = (struct ucred *)CMSG_DATA(cmsg); |
||||
if (cred->uid != 0) { |
||||
usbi_dbg("ignoring netlink message with non-zero sender UID %u", (unsigned int)cred->uid); |
||||
return -1; |
||||
} |
||||
|
||||
r = linux_netlink_parse(msg_buffer, (size_t)len, &detached, &sys_name, &busnum, &devaddr); |
||||
if (r) |
||||
return r; |
||||
|
||||
usbi_dbg("netlink hotplug found device busnum: %hhu, devaddr: %hhu, sys_name: %s, removed: %s", |
||||
busnum, devaddr, sys_name, detached ? "yes" : "no"); |
||||
|
||||
/* signal device is available (or not) to all contexts */ |
||||
if (detached) |
||||
linux_device_disconnected(busnum, devaddr); |
||||
else |
||||
linux_hotplug_enumerate(busnum, devaddr, sys_name); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void *linux_netlink_event_thread_main(void *arg) |
||||
{ |
||||
char dummy; |
||||
ssize_t r; |
||||
struct pollfd fds[] = { |
||||
{ .fd = netlink_control_pipe[0], |
||||
.events = POLLIN }, |
||||
{ .fd = linux_netlink_socket, |
||||
.events = POLLIN }, |
||||
}; |
||||
|
||||
UNUSED(arg); |
||||
|
||||
usbi_dbg("netlink event thread entering"); |
||||
|
||||
while (poll(fds, 2, -1) >= 0) { |
||||
if (fds[0].revents & POLLIN) { |
||||
/* activity on control pipe, read the byte and exit */ |
||||
r = usbi_read(netlink_control_pipe[0], &dummy, sizeof(dummy)); |
||||
if (r <= 0) |
||||
usbi_warn(NULL, "netlink control pipe read failed"); |
||||
break; |
||||
} |
||||
if (fds[1].revents & POLLIN) { |
||||
usbi_mutex_static_lock(&linux_hotplug_lock); |
||||
linux_netlink_read_message(); |
||||
usbi_mutex_static_unlock(&linux_hotplug_lock); |
||||
} |
||||
} |
||||
|
||||
usbi_dbg("netlink event thread exiting"); |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
void linux_netlink_hotplug_poll(void) |
||||
{ |
||||
int r; |
||||
|
||||
usbi_mutex_static_lock(&linux_hotplug_lock); |
||||
do { |
||||
r = linux_netlink_read_message(); |
||||
} while (r == 0); |
||||
usbi_mutex_static_unlock(&linux_hotplug_lock); |
||||
} |
@ -0,0 +1,311 @@ |
||||
/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */ |
||||
/*
|
||||
* Linux usbfs backend for libusb |
||||
* Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org> |
||||
* Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> |
||||
* Copyright (c) 2012-2013 Nathan Hjelm <hjelmn@mac.com> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#include <assert.h> |
||||
#include <ctype.h> |
||||
#include <dirent.h> |
||||
#include <errno.h> |
||||
#include <fcntl.h> |
||||
#include <poll.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <sys/ioctl.h> |
||||
#include <sys/stat.h> |
||||
#include <sys/types.h> |
||||
#include <sys/utsname.h> |
||||
#include <sys/socket.h> |
||||
#include <unistd.h> |
||||
#include <libudev.h> |
||||
|
||||
#include "libusbi.h" |
||||
#include "linux_usbfs.h" |
||||
|
||||
/* udev context */ |
||||
static struct udev *udev_ctx = NULL; |
||||
static int udev_monitor_fd = -1; |
||||
static int udev_control_pipe[2] = {-1, -1}; |
||||
static struct udev_monitor *udev_monitor = NULL; |
||||
static pthread_t linux_event_thread; |
||||
|
||||
static void udev_hotplug_event(struct udev_device* udev_dev); |
||||
static void *linux_udev_event_thread_main(void *arg); |
||||
|
||||
int linux_udev_start_event_monitor(void) |
||||
{ |
||||
int r; |
||||
|
||||
assert(udev_ctx == NULL); |
||||
udev_ctx = udev_new(); |
||||
if (!udev_ctx) { |
||||
usbi_err(NULL, "could not create udev context"); |
||||
goto err; |
||||
} |
||||
|
||||
udev_monitor = udev_monitor_new_from_netlink(udev_ctx, "udev"); |
||||
if (!udev_monitor) { |
||||
usbi_err(NULL, "could not initialize udev monitor"); |
||||
goto err_free_ctx; |
||||
} |
||||
|
||||
r = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", "usb_device"); |
||||
if (r) { |
||||
usbi_err(NULL, "could not initialize udev monitor filter for \"usb\" subsystem"); |
||||
goto err_free_monitor; |
||||
} |
||||
|
||||
if (udev_monitor_enable_receiving(udev_monitor)) { |
||||
usbi_err(NULL, "failed to enable the udev monitor"); |
||||
goto err_free_monitor; |
||||
} |
||||
|
||||
udev_monitor_fd = udev_monitor_get_fd(udev_monitor); |
||||
|
||||
/* Some older versions of udev are not non-blocking by default,
|
||||
* so make sure this is set */ |
||||
r = fcntl(udev_monitor_fd, F_GETFL); |
||||
if (r == -1) { |
||||
usbi_err(NULL, "getting udev monitor fd flags (%d)", errno); |
||||
goto err_free_monitor; |
||||
} |
||||
r = fcntl(udev_monitor_fd, F_SETFL, r | O_NONBLOCK); |
||||
if (r) { |
||||
usbi_err(NULL, "setting udev monitor fd flags (%d)", errno); |
||||
goto err_free_monitor; |
||||
} |
||||
|
||||
r = usbi_pipe(udev_control_pipe); |
||||
if (r) { |
||||
usbi_err(NULL, "could not create udev control pipe"); |
||||
goto err_free_monitor; |
||||
} |
||||
|
||||
r = pthread_create(&linux_event_thread, NULL, linux_udev_event_thread_main, NULL); |
||||
if (r) { |
||||
usbi_err(NULL, "creating hotplug event thread (%d)", r); |
||||
goto err_close_pipe; |
||||
} |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
|
||||
err_close_pipe: |
||||
close(udev_control_pipe[0]); |
||||
close(udev_control_pipe[1]); |
||||
err_free_monitor: |
||||
udev_monitor_unref(udev_monitor); |
||||
udev_monitor = NULL; |
||||
udev_monitor_fd = -1; |
||||
err_free_ctx: |
||||
udev_unref(udev_ctx); |
||||
err: |
||||
udev_ctx = NULL; |
||||
return LIBUSB_ERROR_OTHER; |
||||
} |
||||
|
||||
int linux_udev_stop_event_monitor(void) |
||||
{ |
||||
char dummy = 1; |
||||
int r; |
||||
|
||||
assert(udev_ctx != NULL); |
||||
assert(udev_monitor != NULL); |
||||
assert(udev_monitor_fd != -1); |
||||
|
||||
/* Write some dummy data to the control pipe and
|
||||
* wait for the thread to exit */ |
||||
r = usbi_write(udev_control_pipe[1], &dummy, sizeof(dummy)); |
||||
if (r <= 0) { |
||||
usbi_warn(NULL, "udev control pipe signal failed"); |
||||
} |
||||
pthread_join(linux_event_thread, NULL); |
||||
|
||||
/* Release the udev monitor */ |
||||
udev_monitor_unref(udev_monitor); |
||||
udev_monitor = NULL; |
||||
udev_monitor_fd = -1; |
||||
|
||||
/* Clean up the udev context */ |
||||
udev_unref(udev_ctx); |
||||
udev_ctx = NULL; |
||||
|
||||
/* close and reset control pipe */ |
||||
close(udev_control_pipe[0]); |
||||
close(udev_control_pipe[1]); |
||||
udev_control_pipe[0] = -1; |
||||
udev_control_pipe[1] = -1; |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static void *linux_udev_event_thread_main(void *arg) |
||||
{ |
||||
char dummy; |
||||
int r; |
||||
struct udev_device* udev_dev; |
||||
struct pollfd fds[] = { |
||||
{.fd = udev_control_pipe[0], |
||||
.events = POLLIN}, |
||||
{.fd = udev_monitor_fd, |
||||
.events = POLLIN}, |
||||
}; |
||||
|
||||
usbi_dbg("udev event thread entering."); |
||||
|
||||
while ((r = poll(fds, 2, -1)) >= 0 || errno == EINTR) { |
||||
if (r < 0) { |
||||
/* temporary failure */ |
||||
continue; |
||||
} |
||||
if (fds[0].revents & POLLIN) { |
||||
/* activity on control pipe, read the byte and exit */ |
||||
r = usbi_read(udev_control_pipe[0], &dummy, sizeof(dummy)); |
||||
if (r <= 0) { |
||||
usbi_warn(NULL, "udev control pipe read failed"); |
||||
} |
||||
break; |
||||
} |
||||
if (fds[1].revents & POLLIN) { |
||||
usbi_mutex_static_lock(&linux_hotplug_lock); |
||||
udev_dev = udev_monitor_receive_device(udev_monitor); |
||||
if (udev_dev) |
||||
udev_hotplug_event(udev_dev); |
||||
usbi_mutex_static_unlock(&linux_hotplug_lock); |
||||
} |
||||
} |
||||
|
||||
usbi_dbg("udev event thread exiting"); |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
static int udev_device_info(struct libusb_context *ctx, int detached, |
||||
struct udev_device *udev_dev, uint8_t *busnum, |
||||
uint8_t *devaddr, const char **sys_name) { |
||||
const char *dev_node; |
||||
|
||||
dev_node = udev_device_get_devnode(udev_dev); |
||||
if (!dev_node) { |
||||
return LIBUSB_ERROR_OTHER; |
||||
} |
||||
|
||||
*sys_name = udev_device_get_sysname(udev_dev); |
||||
if (!*sys_name) { |
||||
return LIBUSB_ERROR_OTHER; |
||||
} |
||||
|
||||
return linux_get_device_address(ctx, detached, busnum, devaddr, |
||||
dev_node, *sys_name); |
||||
} |
||||
|
||||
static void udev_hotplug_event(struct udev_device* udev_dev) |
||||
{ |
||||
const char* udev_action; |
||||
const char* sys_name = NULL; |
||||
uint8_t busnum = 0, devaddr = 0; |
||||
int detached; |
||||
int r; |
||||
|
||||
do { |
||||
udev_action = udev_device_get_action(udev_dev); |
||||
if (!udev_action) { |
||||
break; |
||||
} |
||||
|
||||
detached = !strncmp(udev_action, "remove", 6); |
||||
|
||||
r = udev_device_info(NULL, detached, udev_dev, &busnum, &devaddr, &sys_name); |
||||
if (LIBUSB_SUCCESS != r) { |
||||
break; |
||||
} |
||||
|
||||
usbi_dbg("udev hotplug event. action: %s.", udev_action); |
||||
|
||||
if (strncmp(udev_action, "add", 3) == 0) { |
||||
linux_hotplug_enumerate(busnum, devaddr, sys_name); |
||||
} else if (detached) { |
||||
linux_device_disconnected(busnum, devaddr); |
||||
} else { |
||||
usbi_err(NULL, "ignoring udev action %s", udev_action); |
||||
} |
||||
} while (0); |
||||
|
||||
udev_device_unref(udev_dev); |
||||
} |
||||
|
||||
int linux_udev_scan_devices(struct libusb_context *ctx) |
||||
{ |
||||
struct udev_enumerate *enumerator; |
||||
struct udev_list_entry *devices, *entry; |
||||
struct udev_device *udev_dev; |
||||
const char *sys_name; |
||||
int r; |
||||
|
||||
assert(udev_ctx != NULL); |
||||
|
||||
enumerator = udev_enumerate_new(udev_ctx); |
||||
if (NULL == enumerator) { |
||||
usbi_err(ctx, "error creating udev enumerator"); |
||||
return LIBUSB_ERROR_OTHER; |
||||
} |
||||
|
||||
udev_enumerate_add_match_subsystem(enumerator, "usb"); |
||||
udev_enumerate_add_match_property(enumerator, "DEVTYPE", "usb_device"); |
||||
udev_enumerate_scan_devices(enumerator); |
||||
devices = udev_enumerate_get_list_entry(enumerator); |
||||
|
||||
udev_list_entry_foreach(entry, devices) { |
||||
const char *path = udev_list_entry_get_name(entry); |
||||
uint8_t busnum = 0, devaddr = 0; |
||||
|
||||
udev_dev = udev_device_new_from_syspath(udev_ctx, path); |
||||
|
||||
r = udev_device_info(ctx, 0, udev_dev, &busnum, &devaddr, &sys_name); |
||||
if (r) { |
||||
udev_device_unref(udev_dev); |
||||
continue; |
||||
} |
||||
|
||||
linux_enumerate_device(ctx, busnum, devaddr, sys_name); |
||||
udev_device_unref(udev_dev); |
||||
} |
||||
|
||||
udev_enumerate_unref(enumerator); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
void linux_udev_hotplug_poll(void) |
||||
{ |
||||
struct udev_device* udev_dev; |
||||
|
||||
usbi_mutex_static_lock(&linux_hotplug_lock); |
||||
do { |
||||
udev_dev = udev_monitor_receive_device(udev_monitor); |
||||
if (udev_dev) { |
||||
usbi_dbg("Handling hotplug event from hotplug_poll"); |
||||
udev_hotplug_event(udev_dev); |
||||
} |
||||
} while (udev_dev); |
||||
usbi_mutex_static_unlock(&linux_hotplug_lock); |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,193 @@ |
||||
/*
|
||||
* usbfs header structures |
||||
* Copyright © 2007 Daniel Drake <dsd@gentoo.org> |
||||
* Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#ifndef LIBUSB_USBFS_H |
||||
#define LIBUSB_USBFS_H |
||||
|
||||
#include <linux/types.h> |
||||
|
||||
#define SYSFS_DEVICE_PATH "/sys/bus/usb/devices" |
||||
|
||||
struct usbfs_ctrltransfer { |
||||
/* keep in sync with usbdevice_fs.h:usbdevfs_ctrltransfer */ |
||||
uint8_t bmRequestType; |
||||
uint8_t bRequest; |
||||
uint16_t wValue; |
||||
uint16_t wIndex; |
||||
uint16_t wLength; |
||||
|
||||
uint32_t timeout; /* in milliseconds */ |
||||
|
||||
/* pointer to data */ |
||||
void *data; |
||||
}; |
||||
|
||||
struct usbfs_bulktransfer { |
||||
/* keep in sync with usbdevice_fs.h:usbdevfs_bulktransfer */ |
||||
unsigned int ep; |
||||
unsigned int len; |
||||
unsigned int timeout; /* in milliseconds */ |
||||
|
||||
/* pointer to data */ |
||||
void *data; |
||||
}; |
||||
|
||||
struct usbfs_setinterface { |
||||
/* keep in sync with usbdevice_fs.h:usbdevfs_setinterface */ |
||||
unsigned int interface; |
||||
unsigned int altsetting; |
||||
}; |
||||
|
||||
#define USBFS_MAXDRIVERNAME 255 |
||||
|
||||
struct usbfs_getdriver { |
||||
unsigned int interface; |
||||
char driver[USBFS_MAXDRIVERNAME + 1]; |
||||
}; |
||||
|
||||
#define USBFS_URB_SHORT_NOT_OK 0x01 |
||||
#define USBFS_URB_ISO_ASAP 0x02 |
||||
#define USBFS_URB_BULK_CONTINUATION 0x04 |
||||
#define USBFS_URB_QUEUE_BULK 0x10 |
||||
#define USBFS_URB_ZERO_PACKET 0x40 |
||||
|
||||
enum usbfs_urb_type { |
||||
USBFS_URB_TYPE_ISO = 0, |
||||
USBFS_URB_TYPE_INTERRUPT = 1, |
||||
USBFS_URB_TYPE_CONTROL = 2, |
||||
USBFS_URB_TYPE_BULK = 3, |
||||
}; |
||||
|
||||
struct usbfs_iso_packet_desc { |
||||
unsigned int length; |
||||
unsigned int actual_length; |
||||
unsigned int status; |
||||
}; |
||||
|
||||
#define MAX_ISO_BUFFER_LENGTH 49152 * 128 |
||||
#define MAX_BULK_BUFFER_LENGTH 16384 |
||||
#define MAX_CTRL_BUFFER_LENGTH 4096 |
||||
|
||||
struct usbfs_urb { |
||||
unsigned char type; |
||||
unsigned char endpoint; |
||||
int status; |
||||
unsigned int flags; |
||||
void *buffer; |
||||
int buffer_length; |
||||
int actual_length; |
||||
int start_frame; |
||||
union { |
||||
int number_of_packets; /* Only used for isoc urbs */ |
||||
unsigned int stream_id; /* Only used with bulk streams */ |
||||
}; |
||||
int error_count; |
||||
unsigned int signr; |
||||
void *usercontext; |
||||
struct usbfs_iso_packet_desc iso_frame_desc[0]; |
||||
}; |
||||
|
||||
struct usbfs_connectinfo { |
||||
unsigned int devnum; |
||||
unsigned char slow; |
||||
}; |
||||
|
||||
struct usbfs_ioctl { |
||||
int ifno; /* interface 0..N ; negative numbers reserved */ |
||||
int ioctl_code; /* MUST encode size + direction of data so the
|
||||
* macros in <asm/ioctl.h> give correct values */ |
||||
void *data; /* param buffer (in, or out) */ |
||||
}; |
||||
|
||||
struct usbfs_hub_portinfo { |
||||
unsigned char numports; |
||||
unsigned char port[127]; /* port to device num mapping */ |
||||
}; |
||||
|
||||
#define USBFS_CAP_ZERO_PACKET 0x01 |
||||
#define USBFS_CAP_BULK_CONTINUATION 0x02 |
||||
#define USBFS_CAP_NO_PACKET_SIZE_LIM 0x04 |
||||
#define USBFS_CAP_BULK_SCATTER_GATHER 0x08 |
||||
#define USBFS_CAP_REAP_AFTER_DISCONNECT 0x10 |
||||
|
||||
#define USBFS_DISCONNECT_CLAIM_IF_DRIVER 0x01 |
||||
#define USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER 0x02 |
||||
|
||||
struct usbfs_disconnect_claim { |
||||
unsigned int interface; |
||||
unsigned int flags; |
||||
char driver[USBFS_MAXDRIVERNAME + 1]; |
||||
}; |
||||
|
||||
struct usbfs_streams { |
||||
unsigned int num_streams; /* Not used by USBDEVFS_FREE_STREAMS */ |
||||
unsigned int num_eps; |
||||
unsigned char eps[0]; |
||||
}; |
||||
|
||||
#define IOCTL_USBFS_CONTROL _IOWR('U', 0, struct usbfs_ctrltransfer) |
||||
#define IOCTL_USBFS_BULK _IOWR('U', 2, struct usbfs_bulktransfer) |
||||
#define IOCTL_USBFS_RESETEP _IOR('U', 3, unsigned int) |
||||
#define IOCTL_USBFS_SETINTF _IOR('U', 4, struct usbfs_setinterface) |
||||
#define IOCTL_USBFS_SETCONFIG _IOR('U', 5, unsigned int) |
||||
#define IOCTL_USBFS_GETDRIVER _IOW('U', 8, struct usbfs_getdriver) |
||||
#define IOCTL_USBFS_SUBMITURB _IOR('U', 10, struct usbfs_urb) |
||||
#define IOCTL_USBFS_DISCARDURB _IO('U', 11) |
||||
#define IOCTL_USBFS_REAPURB _IOW('U', 12, void *) |
||||
#define IOCTL_USBFS_REAPURBNDELAY _IOW('U', 13, void *) |
||||
#define IOCTL_USBFS_CLAIMINTF _IOR('U', 15, unsigned int) |
||||
#define IOCTL_USBFS_RELEASEINTF _IOR('U', 16, unsigned int) |
||||
#define IOCTL_USBFS_CONNECTINFO _IOW('U', 17, struct usbfs_connectinfo) |
||||
#define IOCTL_USBFS_IOCTL _IOWR('U', 18, struct usbfs_ioctl) |
||||
#define IOCTL_USBFS_HUB_PORTINFO _IOR('U', 19, struct usbfs_hub_portinfo) |
||||
#define IOCTL_USBFS_RESET _IO('U', 20) |
||||
#define IOCTL_USBFS_CLEAR_HALT _IOR('U', 21, unsigned int) |
||||
#define IOCTL_USBFS_DISCONNECT _IO('U', 22) |
||||
#define IOCTL_USBFS_CONNECT _IO('U', 23) |
||||
#define IOCTL_USBFS_CLAIM_PORT _IOR('U', 24, unsigned int) |
||||
#define IOCTL_USBFS_RELEASE_PORT _IOR('U', 25, unsigned int) |
||||
#define IOCTL_USBFS_GET_CAPABILITIES _IOR('U', 26, __u32) |
||||
#define IOCTL_USBFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbfs_disconnect_claim) |
||||
#define IOCTL_USBFS_ALLOC_STREAMS _IOR('U', 28, struct usbfs_streams) |
||||
#define IOCTL_USBFS_FREE_STREAMS _IOR('U', 29, struct usbfs_streams) |
||||
|
||||
extern usbi_mutex_static_t linux_hotplug_lock; |
||||
|
||||
#if defined(HAVE_LIBUDEV) |
||||
int linux_udev_start_event_monitor(void); |
||||
int linux_udev_stop_event_monitor(void); |
||||
int linux_udev_scan_devices(struct libusb_context *ctx); |
||||
void linux_udev_hotplug_poll(void); |
||||
#else |
||||
int linux_netlink_start_event_monitor(void); |
||||
int linux_netlink_stop_event_monitor(void); |
||||
void linux_netlink_hotplug_poll(void); |
||||
#endif |
||||
|
||||
void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_name); |
||||
void linux_device_disconnected(uint8_t busnum, uint8_t devaddr); |
||||
|
||||
int linux_get_device_address (struct libusb_context *ctx, int detached, |
||||
uint8_t *busnum, uint8_t *devaddr, const char *dev_node, |
||||
const char *sys_name); |
||||
int linux_enumerate_device(struct libusb_context *ctx, |
||||
uint8_t busnum, uint8_t devaddr, const char *sysfs_dir); |
||||
|
||||
#endif |
@ -0,0 +1,677 @@ |
||||
/*
|
||||
* Copyright © 2011 Martin Pieuchot <mpi@openbsd.org> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#include <sys/time.h> |
||||
#include <sys/types.h> |
||||
|
||||
#include <errno.h> |
||||
#include <fcntl.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
|
||||
#include <dev/usb/usb.h> |
||||
|
||||
#include "libusbi.h" |
||||
|
||||
struct device_priv { |
||||
char devnode[16]; |
||||
int fd; |
||||
|
||||
unsigned char *cdesc; /* active config descriptor */ |
||||
usb_device_descriptor_t ddesc; /* usb device descriptor */ |
||||
}; |
||||
|
||||
struct handle_priv { |
||||
int endpoints[USB_MAX_ENDPOINTS]; |
||||
}; |
||||
|
||||
/*
|
||||
* Backend functions |
||||
*/ |
||||
static int netbsd_get_device_list(struct libusb_context *, |
||||
struct discovered_devs **); |
||||
static int netbsd_open(struct libusb_device_handle *); |
||||
static void netbsd_close(struct libusb_device_handle *); |
||||
|
||||
static int netbsd_get_device_descriptor(struct libusb_device *, unsigned char *, |
||||
int *); |
||||
static int netbsd_get_active_config_descriptor(struct libusb_device *, |
||||
unsigned char *, size_t, int *); |
||||
static int netbsd_get_config_descriptor(struct libusb_device *, uint8_t, |
||||
unsigned char *, size_t, int *); |
||||
|
||||
static int netbsd_get_configuration(struct libusb_device_handle *, int *); |
||||
static int netbsd_set_configuration(struct libusb_device_handle *, int); |
||||
|
||||
static int netbsd_claim_interface(struct libusb_device_handle *, int); |
||||
static int netbsd_release_interface(struct libusb_device_handle *, int); |
||||
|
||||
static int netbsd_set_interface_altsetting(struct libusb_device_handle *, int, |
||||
int); |
||||
static int netbsd_clear_halt(struct libusb_device_handle *, unsigned char); |
||||
static int netbsd_reset_device(struct libusb_device_handle *); |
||||
static void netbsd_destroy_device(struct libusb_device *); |
||||
|
||||
static int netbsd_submit_transfer(struct usbi_transfer *); |
||||
static int netbsd_cancel_transfer(struct usbi_transfer *); |
||||
static void netbsd_clear_transfer_priv(struct usbi_transfer *); |
||||
static int netbsd_handle_transfer_completion(struct usbi_transfer *); |
||||
static int netbsd_clock_gettime(int, struct timespec *); |
||||
|
||||
/*
|
||||
* Private functions |
||||
*/ |
||||
static int _errno_to_libusb(int); |
||||
static int _cache_active_config_descriptor(struct libusb_device *, int); |
||||
static int _sync_control_transfer(struct usbi_transfer *); |
||||
static int _sync_gen_transfer(struct usbi_transfer *); |
||||
static int _access_endpoint(struct libusb_transfer *); |
||||
|
||||
const struct usbi_os_backend netbsd_backend = { |
||||
"Synchronous NetBSD backend", |
||||
0, |
||||
NULL, /* init() */ |
||||
NULL, /* exit() */ |
||||
netbsd_get_device_list, |
||||
NULL, /* hotplug_poll */ |
||||
netbsd_open, |
||||
netbsd_close, |
||||
|
||||
netbsd_get_device_descriptor, |
||||
netbsd_get_active_config_descriptor, |
||||
netbsd_get_config_descriptor, |
||||
NULL, /* get_config_descriptor_by_value() */ |
||||
|
||||
netbsd_get_configuration, |
||||
netbsd_set_configuration, |
||||
|
||||
netbsd_claim_interface, |
||||
netbsd_release_interface, |
||||
|
||||
netbsd_set_interface_altsetting, |
||||
netbsd_clear_halt, |
||||
netbsd_reset_device, |
||||
|
||||
NULL, /* alloc_streams */ |
||||
NULL, /* free_streams */ |
||||
|
||||
NULL, /* dev_mem_alloc() */ |
||||
NULL, /* dev_mem_free() */ |
||||
|
||||
NULL, /* kernel_driver_active() */ |
||||
NULL, /* detach_kernel_driver() */ |
||||
NULL, /* attach_kernel_driver() */ |
||||
|
||||
netbsd_destroy_device, |
||||
|
||||
netbsd_submit_transfer, |
||||
netbsd_cancel_transfer, |
||||
netbsd_clear_transfer_priv, |
||||
|
||||
NULL, /* handle_events() */ |
||||
netbsd_handle_transfer_completion, |
||||
|
||||
netbsd_clock_gettime, |
||||
sizeof(struct device_priv), |
||||
sizeof(struct handle_priv), |
||||
0, /* transfer_priv_size */ |
||||
}; |
||||
|
||||
int |
||||
netbsd_get_device_list(struct libusb_context * ctx, |
||||
struct discovered_devs **discdevs) |
||||
{ |
||||
struct libusb_device *dev; |
||||
struct device_priv *dpriv; |
||||
struct usb_device_info di; |
||||
unsigned long session_id; |
||||
char devnode[16]; |
||||
int fd, err, i; |
||||
|
||||
usbi_dbg(""); |
||||
|
||||
/* Only ugen(4) is supported */ |
||||
for (i = 0; i < USB_MAX_DEVICES; i++) { |
||||
/* Control endpoint is always .00 */ |
||||
snprintf(devnode, sizeof(devnode), "/dev/ugen%d.00", i); |
||||
|
||||
if ((fd = open(devnode, O_RDONLY)) < 0) { |
||||
if (errno != ENOENT && errno != ENXIO) |
||||
usbi_err(ctx, "could not open %s", devnode); |
||||
continue; |
||||
} |
||||
|
||||
if (ioctl(fd, USB_GET_DEVICEINFO, &di) < 0) |
||||
continue; |
||||
|
||||
session_id = (di.udi_bus << 8 | di.udi_addr); |
||||
dev = usbi_get_device_by_session_id(ctx, session_id); |
||||
|
||||
if (dev == NULL) { |
||||
dev = usbi_alloc_device(ctx, session_id); |
||||
if (dev == NULL) |
||||
return (LIBUSB_ERROR_NO_MEM); |
||||
|
||||
dev->bus_number = di.udi_bus; |
||||
dev->device_address = di.udi_addr; |
||||
dev->speed = di.udi_speed; |
||||
|
||||
dpriv = (struct device_priv *)dev->os_priv; |
||||
strlcpy(dpriv->devnode, devnode, sizeof(devnode)); |
||||
dpriv->fd = -1; |
||||
|
||||
if (ioctl(fd, USB_GET_DEVICE_DESC, &dpriv->ddesc) < 0) { |
||||
err = errno; |
||||
goto error; |
||||
} |
||||
|
||||
dpriv->cdesc = NULL; |
||||
if (_cache_active_config_descriptor(dev, fd)) { |
||||
err = errno; |
||||
goto error; |
||||
} |
||||
|
||||
if ((err = usbi_sanitize_device(dev))) |
||||
goto error; |
||||
} |
||||
close(fd); |
||||
|
||||
if (discovered_devs_append(*discdevs, dev) == NULL) |
||||
return (LIBUSB_ERROR_NO_MEM); |
||||
|
||||
libusb_unref_device(dev); |
||||
} |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
|
||||
error: |
||||
close(fd); |
||||
libusb_unref_device(dev); |
||||
return _errno_to_libusb(err); |
||||
} |
||||
|
||||
int |
||||
netbsd_open(struct libusb_device_handle *handle) |
||||
{ |
||||
struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; |
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; |
||||
|
||||
dpriv->fd = open(dpriv->devnode, O_RDWR); |
||||
if (dpriv->fd < 0) { |
||||
dpriv->fd = open(dpriv->devnode, O_RDONLY); |
||||
if (dpriv->fd < 0) |
||||
return _errno_to_libusb(errno); |
||||
} |
||||
|
||||
usbi_dbg("open %s: fd %d", dpriv->devnode, dpriv->fd); |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
void |
||||
netbsd_close(struct libusb_device_handle *handle) |
||||
{ |
||||
struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; |
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; |
||||
|
||||
usbi_dbg("close: fd %d", dpriv->fd); |
||||
|
||||
close(dpriv->fd); |
||||
dpriv->fd = -1; |
||||
} |
||||
|
||||
int |
||||
netbsd_get_device_descriptor(struct libusb_device *dev, unsigned char *buf, |
||||
int *host_endian) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)dev->os_priv; |
||||
|
||||
usbi_dbg(""); |
||||
|
||||
memcpy(buf, &dpriv->ddesc, DEVICE_DESC_LENGTH); |
||||
|
||||
*host_endian = 0; |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
netbsd_get_active_config_descriptor(struct libusb_device *dev, |
||||
unsigned char *buf, size_t len, int *host_endian) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)dev->os_priv; |
||||
usb_config_descriptor_t *ucd; |
||||
|
||||
ucd = (usb_config_descriptor_t *) dpriv->cdesc; |
||||
len = MIN(len, UGETW(ucd->wTotalLength)); |
||||
|
||||
usbi_dbg("len %d", len); |
||||
|
||||
memcpy(buf, dpriv->cdesc, len); |
||||
|
||||
*host_endian = 0; |
||||
|
||||
return len; |
||||
} |
||||
|
||||
int |
||||
netbsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx, |
||||
unsigned char *buf, size_t len, int *host_endian) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)dev->os_priv; |
||||
struct usb_full_desc ufd; |
||||
int fd, err; |
||||
|
||||
usbi_dbg("index %d, len %d", idx, len); |
||||
|
||||
/* A config descriptor may be requested before opening the device */ |
||||
if (dpriv->fd >= 0) { |
||||
fd = dpriv->fd; |
||||
} else { |
||||
fd = open(dpriv->devnode, O_RDONLY); |
||||
if (fd < 0) |
||||
return _errno_to_libusb(errno); |
||||
} |
||||
|
||||
ufd.ufd_config_index = idx; |
||||
ufd.ufd_size = len; |
||||
ufd.ufd_data = buf; |
||||
|
||||
if ((ioctl(fd, USB_GET_FULL_DESC, &ufd)) < 0) { |
||||
err = errno; |
||||
if (dpriv->fd < 0) |
||||
close(fd); |
||||
return _errno_to_libusb(err); |
||||
} |
||||
|
||||
if (dpriv->fd < 0) |
||||
close(fd); |
||||
|
||||
*host_endian = 0; |
||||
|
||||
return len; |
||||
} |
||||
|
||||
int |
||||
netbsd_get_configuration(struct libusb_device_handle *handle, int *config) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; |
||||
|
||||
usbi_dbg(""); |
||||
|
||||
if (ioctl(dpriv->fd, USB_GET_CONFIG, config) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
usbi_dbg("configuration %d", *config); |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
netbsd_set_configuration(struct libusb_device_handle *handle, int config) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; |
||||
|
||||
usbi_dbg("configuration %d", config); |
||||
|
||||
if (ioctl(dpriv->fd, USB_SET_CONFIG, &config) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
return _cache_active_config_descriptor(handle->dev, dpriv->fd); |
||||
} |
||||
|
||||
int |
||||
netbsd_claim_interface(struct libusb_device_handle *handle, int iface) |
||||
{ |
||||
struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; |
||||
int i; |
||||
|
||||
for (i = 0; i < USB_MAX_ENDPOINTS; i++) |
||||
hpriv->endpoints[i] = -1; |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
netbsd_release_interface(struct libusb_device_handle *handle, int iface) |
||||
{ |
||||
struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; |
||||
int i; |
||||
|
||||
for (i = 0; i < USB_MAX_ENDPOINTS; i++) |
||||
if (hpriv->endpoints[i] >= 0) |
||||
close(hpriv->endpoints[i]); |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
netbsd_set_interface_altsetting(struct libusb_device_handle *handle, int iface, |
||||
int altsetting) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; |
||||
struct usb_alt_interface intf; |
||||
|
||||
usbi_dbg("iface %d, setting %d", iface, altsetting); |
||||
|
||||
memset(&intf, 0, sizeof(intf)); |
||||
|
||||
intf.uai_interface_index = iface; |
||||
intf.uai_alt_no = altsetting; |
||||
|
||||
if (ioctl(dpriv->fd, USB_SET_ALTINTERFACE, &intf) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
netbsd_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; |
||||
struct usb_ctl_request req; |
||||
|
||||
usbi_dbg(""); |
||||
|
||||
req.ucr_request.bmRequestType = UT_WRITE_ENDPOINT; |
||||
req.ucr_request.bRequest = UR_CLEAR_FEATURE; |
||||
USETW(req.ucr_request.wValue, UF_ENDPOINT_HALT); |
||||
USETW(req.ucr_request.wIndex, endpoint); |
||||
USETW(req.ucr_request.wLength, 0); |
||||
|
||||
if (ioctl(dpriv->fd, USB_DO_REQUEST, &req) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
netbsd_reset_device(struct libusb_device_handle *handle) |
||||
{ |
||||
usbi_dbg(""); |
||||
|
||||
return (LIBUSB_ERROR_NOT_SUPPORTED); |
||||
} |
||||
|
||||
void |
||||
netbsd_destroy_device(struct libusb_device *dev) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)dev->os_priv; |
||||
|
||||
usbi_dbg(""); |
||||
|
||||
free(dpriv->cdesc); |
||||
} |
||||
|
||||
int |
||||
netbsd_submit_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer; |
||||
struct handle_priv *hpriv; |
||||
int err = 0; |
||||
|
||||
usbi_dbg(""); |
||||
|
||||
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
hpriv = (struct handle_priv *)transfer->dev_handle->os_priv; |
||||
|
||||
switch (transfer->type) { |
||||
case LIBUSB_TRANSFER_TYPE_CONTROL: |
||||
err = _sync_control_transfer(itransfer); |
||||
break; |
||||
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: |
||||
if (IS_XFEROUT(transfer)) { |
||||
/* Isochronous write is not supported */ |
||||
err = LIBUSB_ERROR_NOT_SUPPORTED; |
||||
break; |
||||
} |
||||
err = _sync_gen_transfer(itransfer); |
||||
break; |
||||
case LIBUSB_TRANSFER_TYPE_BULK: |
||||
case LIBUSB_TRANSFER_TYPE_INTERRUPT: |
||||
if (IS_XFEROUT(transfer) && |
||||
transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) { |
||||
err = LIBUSB_ERROR_NOT_SUPPORTED; |
||||
break; |
||||
} |
||||
err = _sync_gen_transfer(itransfer); |
||||
break; |
||||
case LIBUSB_TRANSFER_TYPE_BULK_STREAM: |
||||
err = LIBUSB_ERROR_NOT_SUPPORTED; |
||||
break; |
||||
} |
||||
|
||||
if (err) |
||||
return (err); |
||||
|
||||
usbi_signal_transfer_completion(itransfer); |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
netbsd_cancel_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
usbi_dbg(""); |
||||
|
||||
return (LIBUSB_ERROR_NOT_SUPPORTED); |
||||
} |
||||
|
||||
void |
||||
netbsd_clear_transfer_priv(struct usbi_transfer *itransfer) |
||||
{ |
||||
usbi_dbg(""); |
||||
|
||||
/* Nothing to do */ |
||||
} |
||||
|
||||
int |
||||
netbsd_handle_transfer_completion(struct usbi_transfer *itransfer) |
||||
{ |
||||
return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); |
||||
} |
||||
|
||||
int |
||||
netbsd_clock_gettime(int clkid, struct timespec *tp) |
||||
{ |
||||
usbi_dbg("clock %d", clkid); |
||||
|
||||
if (clkid == USBI_CLOCK_REALTIME) |
||||
return clock_gettime(CLOCK_REALTIME, tp); |
||||
|
||||
if (clkid == USBI_CLOCK_MONOTONIC) |
||||
return clock_gettime(CLOCK_MONOTONIC, tp); |
||||
|
||||
return (LIBUSB_ERROR_INVALID_PARAM); |
||||
} |
||||
|
||||
int |
||||
_errno_to_libusb(int err) |
||||
{ |
||||
switch (err) { |
||||
case EIO: |
||||
return (LIBUSB_ERROR_IO); |
||||
case EACCES: |
||||
return (LIBUSB_ERROR_ACCESS); |
||||
case ENOENT: |
||||
return (LIBUSB_ERROR_NO_DEVICE); |
||||
case ENOMEM: |
||||
return (LIBUSB_ERROR_NO_MEM); |
||||
} |
||||
|
||||
usbi_dbg("error: %s", strerror(err)); |
||||
|
||||
return (LIBUSB_ERROR_OTHER); |
||||
} |
||||
|
||||
int |
||||
_cache_active_config_descriptor(struct libusb_device *dev, int fd) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)dev->os_priv; |
||||
struct usb_config_desc ucd; |
||||
struct usb_full_desc ufd; |
||||
unsigned char* buf; |
||||
int len; |
||||
|
||||
usbi_dbg("fd %d", fd); |
||||
|
||||
ucd.ucd_config_index = USB_CURRENT_CONFIG_INDEX; |
||||
|
||||
if ((ioctl(fd, USB_GET_CONFIG_DESC, &ucd)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
usbi_dbg("active bLength %d", ucd.ucd_desc.bLength); |
||||
|
||||
len = UGETW(ucd.ucd_desc.wTotalLength); |
||||
buf = malloc(len); |
||||
if (buf == NULL) |
||||
return (LIBUSB_ERROR_NO_MEM); |
||||
|
||||
ufd.ufd_config_index = ucd.ucd_config_index; |
||||
ufd.ufd_size = len; |
||||
ufd.ufd_data = buf; |
||||
|
||||
usbi_dbg("index %d, len %d", ufd.ufd_config_index, len); |
||||
|
||||
if ((ioctl(fd, USB_GET_FULL_DESC, &ufd)) < 0) { |
||||
free(buf); |
||||
return _errno_to_libusb(errno); |
||||
} |
||||
|
||||
if (dpriv->cdesc) |
||||
free(dpriv->cdesc); |
||||
dpriv->cdesc = buf; |
||||
|
||||
return (0); |
||||
} |
||||
|
||||
int |
||||
_sync_control_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer; |
||||
struct libusb_control_setup *setup; |
||||
struct device_priv *dpriv; |
||||
struct usb_ctl_request req; |
||||
|
||||
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv; |
||||
setup = (struct libusb_control_setup *)transfer->buffer; |
||||
|
||||
usbi_dbg("type %d request %d value %d index %d length %d timeout %d", |
||||
setup->bmRequestType, setup->bRequest, |
||||
libusb_le16_to_cpu(setup->wValue), |
||||
libusb_le16_to_cpu(setup->wIndex), |
||||
libusb_le16_to_cpu(setup->wLength), transfer->timeout); |
||||
|
||||
req.ucr_request.bmRequestType = setup->bmRequestType; |
||||
req.ucr_request.bRequest = setup->bRequest; |
||||
/* Don't use USETW, libusb already deals with the endianness */ |
||||
(*(uint16_t *)req.ucr_request.wValue) = setup->wValue; |
||||
(*(uint16_t *)req.ucr_request.wIndex) = setup->wIndex; |
||||
(*(uint16_t *)req.ucr_request.wLength) = setup->wLength; |
||||
req.ucr_data = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; |
||||
|
||||
if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) |
||||
req.ucr_flags = USBD_SHORT_XFER_OK; |
||||
|
||||
if ((ioctl(dpriv->fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
if ((ioctl(dpriv->fd, USB_DO_REQUEST, &req)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
itransfer->transferred = req.ucr_actlen; |
||||
|
||||
usbi_dbg("transferred %d", itransfer->transferred); |
||||
|
||||
return (0); |
||||
} |
||||
|
||||
int |
||||
_access_endpoint(struct libusb_transfer *transfer) |
||||
{ |
||||
struct handle_priv *hpriv; |
||||
struct device_priv *dpriv; |
||||
char *s, devnode[16]; |
||||
int fd, endpt; |
||||
mode_t mode; |
||||
|
||||
hpriv = (struct handle_priv *)transfer->dev_handle->os_priv; |
||||
dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv; |
||||
|
||||
endpt = UE_GET_ADDR(transfer->endpoint); |
||||
mode = IS_XFERIN(transfer) ? O_RDONLY : O_WRONLY; |
||||
|
||||
usbi_dbg("endpoint %d mode %d", endpt, mode); |
||||
|
||||
if (hpriv->endpoints[endpt] < 0) { |
||||
/* Pick the right node given the control one */ |
||||
strlcpy(devnode, dpriv->devnode, sizeof(devnode)); |
||||
s = strchr(devnode, '.'); |
||||
snprintf(s, 4, ".%02d", endpt); |
||||
|
||||
/* We may need to read/write to the same endpoint later. */ |
||||
if (((fd = open(devnode, O_RDWR)) < 0) && (errno == ENXIO)) |
||||
if ((fd = open(devnode, mode)) < 0) |
||||
return (-1); |
||||
|
||||
hpriv->endpoints[endpt] = fd; |
||||
} |
||||
|
||||
return (hpriv->endpoints[endpt]); |
||||
} |
||||
|
||||
int |
||||
_sync_gen_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer; |
||||
int fd, nr = 1; |
||||
|
||||
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
|
||||
/*
|
||||
* Bulk, Interrupt or Isochronous transfer depends on the |
||||
* endpoint and thus the node to open. |
||||
*/ |
||||
if ((fd = _access_endpoint(transfer)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
if ((ioctl(fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
if (IS_XFERIN(transfer)) { |
||||
if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) |
||||
if ((ioctl(fd, USB_SET_SHORT_XFER, &nr)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
nr = read(fd, transfer->buffer, transfer->length); |
||||
} else { |
||||
nr = write(fd, transfer->buffer, transfer->length); |
||||
} |
||||
|
||||
if (nr < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
itransfer->transferred = nr; |
||||
|
||||
return (0); |
||||
} |
@ -0,0 +1,771 @@ |
||||
/*
|
||||
* Copyright © 2011-2013 Martin Pieuchot <mpi@openbsd.org> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#include <sys/time.h> |
||||
#include <sys/types.h> |
||||
|
||||
#include <errno.h> |
||||
#include <fcntl.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
|
||||
#include <dev/usb/usb.h> |
||||
|
||||
#include "libusbi.h" |
||||
|
||||
struct device_priv { |
||||
char *devname; /* name of the ugen(4) node */ |
||||
int fd; /* device file descriptor */ |
||||
|
||||
unsigned char *cdesc; /* active config descriptor */ |
||||
usb_device_descriptor_t ddesc; /* usb device descriptor */ |
||||
}; |
||||
|
||||
struct handle_priv { |
||||
int endpoints[USB_MAX_ENDPOINTS]; |
||||
}; |
||||
|
||||
/*
|
||||
* Backend functions |
||||
*/ |
||||
static int obsd_get_device_list(struct libusb_context *, |
||||
struct discovered_devs **); |
||||
static int obsd_open(struct libusb_device_handle *); |
||||
static void obsd_close(struct libusb_device_handle *); |
||||
|
||||
static int obsd_get_device_descriptor(struct libusb_device *, unsigned char *, |
||||
int *); |
||||
static int obsd_get_active_config_descriptor(struct libusb_device *, |
||||
unsigned char *, size_t, int *); |
||||
static int obsd_get_config_descriptor(struct libusb_device *, uint8_t, |
||||
unsigned char *, size_t, int *); |
||||
|
||||
static int obsd_get_configuration(struct libusb_device_handle *, int *); |
||||
static int obsd_set_configuration(struct libusb_device_handle *, int); |
||||
|
||||
static int obsd_claim_interface(struct libusb_device_handle *, int); |
||||
static int obsd_release_interface(struct libusb_device_handle *, int); |
||||
|
||||
static int obsd_set_interface_altsetting(struct libusb_device_handle *, int, |
||||
int); |
||||
static int obsd_clear_halt(struct libusb_device_handle *, unsigned char); |
||||
static int obsd_reset_device(struct libusb_device_handle *); |
||||
static void obsd_destroy_device(struct libusb_device *); |
||||
|
||||
static int obsd_submit_transfer(struct usbi_transfer *); |
||||
static int obsd_cancel_transfer(struct usbi_transfer *); |
||||
static void obsd_clear_transfer_priv(struct usbi_transfer *); |
||||
static int obsd_handle_transfer_completion(struct usbi_transfer *); |
||||
static int obsd_clock_gettime(int, struct timespec *); |
||||
|
||||
/*
|
||||
* Private functions |
||||
*/ |
||||
static int _errno_to_libusb(int); |
||||
static int _cache_active_config_descriptor(struct libusb_device *); |
||||
static int _sync_control_transfer(struct usbi_transfer *); |
||||
static int _sync_gen_transfer(struct usbi_transfer *); |
||||
static int _access_endpoint(struct libusb_transfer *); |
||||
|
||||
static int _bus_open(int); |
||||
|
||||
|
||||
const struct usbi_os_backend openbsd_backend = { |
||||
"Synchronous OpenBSD backend", |
||||
0, |
||||
NULL, /* init() */ |
||||
NULL, /* exit() */ |
||||
obsd_get_device_list, |
||||
NULL, /* hotplug_poll */ |
||||
obsd_open, |
||||
obsd_close, |
||||
|
||||
obsd_get_device_descriptor, |
||||
obsd_get_active_config_descriptor, |
||||
obsd_get_config_descriptor, |
||||
NULL, /* get_config_descriptor_by_value() */ |
||||
|
||||
obsd_get_configuration, |
||||
obsd_set_configuration, |
||||
|
||||
obsd_claim_interface, |
||||
obsd_release_interface, |
||||
|
||||
obsd_set_interface_altsetting, |
||||
obsd_clear_halt, |
||||
obsd_reset_device, |
||||
|
||||
NULL, /* alloc_streams */ |
||||
NULL, /* free_streams */ |
||||
|
||||
NULL, /* dev_mem_alloc() */ |
||||
NULL, /* dev_mem_free() */ |
||||
|
||||
NULL, /* kernel_driver_active() */ |
||||
NULL, /* detach_kernel_driver() */ |
||||
NULL, /* attach_kernel_driver() */ |
||||
|
||||
obsd_destroy_device, |
||||
|
||||
obsd_submit_transfer, |
||||
obsd_cancel_transfer, |
||||
obsd_clear_transfer_priv, |
||||
|
||||
NULL, /* handle_events() */ |
||||
obsd_handle_transfer_completion, |
||||
|
||||
obsd_clock_gettime, |
||||
sizeof(struct device_priv), |
||||
sizeof(struct handle_priv), |
||||
0, /* transfer_priv_size */ |
||||
}; |
||||
|
||||
#define DEVPATH "/dev/" |
||||
#define USBDEV DEVPATH "usb" |
||||
|
||||
int |
||||
obsd_get_device_list(struct libusb_context * ctx, |
||||
struct discovered_devs **discdevs) |
||||
{ |
||||
struct discovered_devs *ddd; |
||||
struct libusb_device *dev; |
||||
struct device_priv *dpriv; |
||||
struct usb_device_info di; |
||||
struct usb_device_ddesc dd; |
||||
unsigned long session_id; |
||||
char devices[USB_MAX_DEVICES]; |
||||
char busnode[16]; |
||||
char *udevname; |
||||
int fd, addr, i, j; |
||||
|
||||
usbi_dbg(""); |
||||
|
||||
for (i = 0; i < 8; i++) { |
||||
snprintf(busnode, sizeof(busnode), USBDEV "%d", i); |
||||
|
||||
if ((fd = open(busnode, O_RDWR)) < 0) { |
||||
if (errno != ENOENT && errno != ENXIO) |
||||
usbi_err(ctx, "could not open %s", busnode); |
||||
continue; |
||||
} |
||||
|
||||
bzero(devices, sizeof(devices)); |
||||
for (addr = 1; addr < USB_MAX_DEVICES; addr++) { |
||||
if (devices[addr]) |
||||
continue; |
||||
|
||||
di.udi_addr = addr; |
||||
if (ioctl(fd, USB_DEVICEINFO, &di) < 0) |
||||
continue; |
||||
|
||||
/*
|
||||
* XXX If ugen(4) is attached to the USB device |
||||
* it will be used. |
||||
*/ |
||||
udevname = NULL; |
||||
for (j = 0; j < USB_MAX_DEVNAMES; j++) |
||||
if (!strncmp("ugen", di.udi_devnames[j], 4)) { |
||||
udevname = strdup(di.udi_devnames[j]); |
||||
break; |
||||
} |
||||
|
||||
session_id = (di.udi_bus << 8 | di.udi_addr); |
||||
dev = usbi_get_device_by_session_id(ctx, session_id); |
||||
|
||||
if (dev == NULL) { |
||||
dev = usbi_alloc_device(ctx, session_id); |
||||
if (dev == NULL) { |
||||
close(fd); |
||||
return (LIBUSB_ERROR_NO_MEM); |
||||
} |
||||
|
||||
dev->bus_number = di.udi_bus; |
||||
dev->device_address = di.udi_addr; |
||||
dev->speed = di.udi_speed; |
||||
|
||||
dpriv = (struct device_priv *)dev->os_priv; |
||||
dpriv->fd = -1; |
||||
dpriv->cdesc = NULL; |
||||
dpriv->devname = udevname; |
||||
|
||||
dd.udd_bus = di.udi_bus; |
||||
dd.udd_addr = di.udi_addr; |
||||
if (ioctl(fd, USB_DEVICE_GET_DDESC, &dd) < 0) { |
||||
libusb_unref_device(dev); |
||||
continue; |
||||
} |
||||
dpriv->ddesc = dd.udd_desc; |
||||
|
||||
if (_cache_active_config_descriptor(dev)) { |
||||
libusb_unref_device(dev); |
||||
continue; |
||||
} |
||||
|
||||
if (usbi_sanitize_device(dev)) { |
||||
libusb_unref_device(dev); |
||||
continue; |
||||
} |
||||
} |
||||
|
||||
ddd = discovered_devs_append(*discdevs, dev); |
||||
if (ddd == NULL) { |
||||
close(fd); |
||||
return (LIBUSB_ERROR_NO_MEM); |
||||
} |
||||
libusb_unref_device(dev); |
||||
|
||||
*discdevs = ddd; |
||||
devices[addr] = 1; |
||||
} |
||||
|
||||
close(fd); |
||||
} |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
obsd_open(struct libusb_device_handle *handle) |
||||
{ |
||||
struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; |
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; |
||||
char devnode[16]; |
||||
|
||||
if (dpriv->devname) { |
||||
/*
|
||||
* Only open ugen(4) attached devices read-write, all |
||||
* read-only operations are done through the bus node. |
||||
*/ |
||||
snprintf(devnode, sizeof(devnode), DEVPATH "%s.00", |
||||
dpriv->devname); |
||||
dpriv->fd = open(devnode, O_RDWR); |
||||
if (dpriv->fd < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
usbi_dbg("open %s: fd %d", devnode, dpriv->fd); |
||||
} |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
void |
||||
obsd_close(struct libusb_device_handle *handle) |
||||
{ |
||||
struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; |
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; |
||||
|
||||
if (dpriv->devname) { |
||||
usbi_dbg("close: fd %d", dpriv->fd); |
||||
|
||||
close(dpriv->fd); |
||||
dpriv->fd = -1; |
||||
} |
||||
} |
||||
|
||||
int |
||||
obsd_get_device_descriptor(struct libusb_device *dev, unsigned char *buf, |
||||
int *host_endian) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)dev->os_priv; |
||||
|
||||
usbi_dbg(""); |
||||
|
||||
memcpy(buf, &dpriv->ddesc, DEVICE_DESC_LENGTH); |
||||
|
||||
*host_endian = 0; |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
obsd_get_active_config_descriptor(struct libusb_device *dev, |
||||
unsigned char *buf, size_t len, int *host_endian) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)dev->os_priv; |
||||
usb_config_descriptor_t *ucd = (usb_config_descriptor_t *)dpriv->cdesc; |
||||
|
||||
len = MIN(len, UGETW(ucd->wTotalLength)); |
||||
|
||||
usbi_dbg("len %d", len); |
||||
|
||||
memcpy(buf, dpriv->cdesc, len); |
||||
|
||||
*host_endian = 0; |
||||
|
||||
return (len); |
||||
} |
||||
|
||||
int |
||||
obsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx, |
||||
unsigned char *buf, size_t len, int *host_endian) |
||||
{ |
||||
struct usb_device_fdesc udf; |
||||
int fd, err; |
||||
|
||||
if ((fd = _bus_open(dev->bus_number)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
udf.udf_bus = dev->bus_number; |
||||
udf.udf_addr = dev->device_address; |
||||
udf.udf_config_index = idx; |
||||
udf.udf_size = len; |
||||
udf.udf_data = buf; |
||||
|
||||
usbi_dbg("index %d, len %d", udf.udf_config_index, len); |
||||
|
||||
if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) { |
||||
err = errno; |
||||
close(fd); |
||||
return _errno_to_libusb(err); |
||||
} |
||||
close(fd); |
||||
|
||||
*host_endian = 0; |
||||
|
||||
return (len); |
||||
} |
||||
|
||||
int |
||||
obsd_get_configuration(struct libusb_device_handle *handle, int *config) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; |
||||
usb_config_descriptor_t *ucd = (usb_config_descriptor_t *)dpriv->cdesc; |
||||
|
||||
*config = ucd->bConfigurationValue; |
||||
|
||||
usbi_dbg("bConfigurationValue %d", *config); |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
obsd_set_configuration(struct libusb_device_handle *handle, int config) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; |
||||
|
||||
if (dpriv->devname == NULL) |
||||
return (LIBUSB_ERROR_NOT_SUPPORTED); |
||||
|
||||
usbi_dbg("bConfigurationValue %d", config); |
||||
|
||||
if (ioctl(dpriv->fd, USB_SET_CONFIG, &config) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
return _cache_active_config_descriptor(handle->dev); |
||||
} |
||||
|
||||
int |
||||
obsd_claim_interface(struct libusb_device_handle *handle, int iface) |
||||
{ |
||||
struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; |
||||
int i; |
||||
|
||||
for (i = 0; i < USB_MAX_ENDPOINTS; i++) |
||||
hpriv->endpoints[i] = -1; |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
obsd_release_interface(struct libusb_device_handle *handle, int iface) |
||||
{ |
||||
struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; |
||||
int i; |
||||
|
||||
for (i = 0; i < USB_MAX_ENDPOINTS; i++) |
||||
if (hpriv->endpoints[i] >= 0) |
||||
close(hpriv->endpoints[i]); |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
obsd_set_interface_altsetting(struct libusb_device_handle *handle, int iface, |
||||
int altsetting) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; |
||||
struct usb_alt_interface intf; |
||||
|
||||
if (dpriv->devname == NULL) |
||||
return (LIBUSB_ERROR_NOT_SUPPORTED); |
||||
|
||||
usbi_dbg("iface %d, setting %d", iface, altsetting); |
||||
|
||||
memset(&intf, 0, sizeof(intf)); |
||||
|
||||
intf.uai_interface_index = iface; |
||||
intf.uai_alt_no = altsetting; |
||||
|
||||
if (ioctl(dpriv->fd, USB_SET_ALTINTERFACE, &intf) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
obsd_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint) |
||||
{ |
||||
struct usb_ctl_request req; |
||||
int fd, err; |
||||
|
||||
if ((fd = _bus_open(handle->dev->bus_number)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
usbi_dbg(""); |
||||
|
||||
req.ucr_addr = handle->dev->device_address; |
||||
req.ucr_request.bmRequestType = UT_WRITE_ENDPOINT; |
||||
req.ucr_request.bRequest = UR_CLEAR_FEATURE; |
||||
USETW(req.ucr_request.wValue, UF_ENDPOINT_HALT); |
||||
USETW(req.ucr_request.wIndex, endpoint); |
||||
USETW(req.ucr_request.wLength, 0); |
||||
|
||||
if (ioctl(fd, USB_REQUEST, &req) < 0) { |
||||
err = errno; |
||||
close(fd); |
||||
return _errno_to_libusb(err); |
||||
} |
||||
close(fd); |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
obsd_reset_device(struct libusb_device_handle *handle) |
||||
{ |
||||
usbi_dbg(""); |
||||
|
||||
return (LIBUSB_ERROR_NOT_SUPPORTED); |
||||
} |
||||
|
||||
void |
||||
obsd_destroy_device(struct libusb_device *dev) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)dev->os_priv; |
||||
|
||||
usbi_dbg(""); |
||||
|
||||
free(dpriv->cdesc); |
||||
free(dpriv->devname); |
||||
} |
||||
|
||||
int |
||||
obsd_submit_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer; |
||||
struct handle_priv *hpriv; |
||||
int err = 0; |
||||
|
||||
usbi_dbg(""); |
||||
|
||||
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
hpriv = (struct handle_priv *)transfer->dev_handle->os_priv; |
||||
|
||||
switch (transfer->type) { |
||||
case LIBUSB_TRANSFER_TYPE_CONTROL: |
||||
err = _sync_control_transfer(itransfer); |
||||
break; |
||||
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: |
||||
if (IS_XFEROUT(transfer)) { |
||||
/* Isochronous write is not supported */ |
||||
err = LIBUSB_ERROR_NOT_SUPPORTED; |
||||
break; |
||||
} |
||||
err = _sync_gen_transfer(itransfer); |
||||
break; |
||||
case LIBUSB_TRANSFER_TYPE_BULK: |
||||
case LIBUSB_TRANSFER_TYPE_INTERRUPT: |
||||
if (IS_XFEROUT(transfer) && |
||||
transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) { |
||||
err = LIBUSB_ERROR_NOT_SUPPORTED; |
||||
break; |
||||
} |
||||
err = _sync_gen_transfer(itransfer); |
||||
break; |
||||
case LIBUSB_TRANSFER_TYPE_BULK_STREAM: |
||||
err = LIBUSB_ERROR_NOT_SUPPORTED; |
||||
break; |
||||
} |
||||
|
||||
if (err) |
||||
return (err); |
||||
|
||||
usbi_signal_transfer_completion(itransfer); |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
obsd_cancel_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
usbi_dbg(""); |
||||
|
||||
return (LIBUSB_ERROR_NOT_SUPPORTED); |
||||
} |
||||
|
||||
void |
||||
obsd_clear_transfer_priv(struct usbi_transfer *itransfer) |
||||
{ |
||||
usbi_dbg(""); |
||||
|
||||
/* Nothing to do */ |
||||
} |
||||
|
||||
int |
||||
obsd_handle_transfer_completion(struct usbi_transfer *itransfer) |
||||
{ |
||||
return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_COMPLETED); |
||||
} |
||||
|
||||
int |
||||
obsd_clock_gettime(int clkid, struct timespec *tp) |
||||
{ |
||||
usbi_dbg("clock %d", clkid); |
||||
|
||||
if (clkid == USBI_CLOCK_REALTIME) |
||||
return clock_gettime(CLOCK_REALTIME, tp); |
||||
|
||||
if (clkid == USBI_CLOCK_MONOTONIC) |
||||
return clock_gettime(CLOCK_MONOTONIC, tp); |
||||
|
||||
return (LIBUSB_ERROR_INVALID_PARAM); |
||||
} |
||||
|
||||
int |
||||
_errno_to_libusb(int err) |
||||
{ |
||||
usbi_dbg("error: %s (%d)", strerror(err), err); |
||||
|
||||
switch (err) { |
||||
case EIO: |
||||
return (LIBUSB_ERROR_IO); |
||||
case EACCES: |
||||
return (LIBUSB_ERROR_ACCESS); |
||||
case ENOENT: |
||||
return (LIBUSB_ERROR_NO_DEVICE); |
||||
case ENOMEM: |
||||
return (LIBUSB_ERROR_NO_MEM); |
||||
case ETIMEDOUT: |
||||
return (LIBUSB_ERROR_TIMEOUT); |
||||
} |
||||
|
||||
return (LIBUSB_ERROR_OTHER); |
||||
} |
||||
|
||||
int |
||||
_cache_active_config_descriptor(struct libusb_device *dev) |
||||
{ |
||||
struct device_priv *dpriv = (struct device_priv *)dev->os_priv; |
||||
struct usb_device_cdesc udc; |
||||
struct usb_device_fdesc udf; |
||||
unsigned char* buf; |
||||
int fd, len, err; |
||||
|
||||
if ((fd = _bus_open(dev->bus_number)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
usbi_dbg("fd %d, addr %d", fd, dev->device_address); |
||||
|
||||
udc.udc_bus = dev->bus_number; |
||||
udc.udc_addr = dev->device_address; |
||||
udc.udc_config_index = USB_CURRENT_CONFIG_INDEX; |
||||
if (ioctl(fd, USB_DEVICE_GET_CDESC, &udc) < 0) { |
||||
err = errno; |
||||
close(fd); |
||||
return _errno_to_libusb(errno); |
||||
} |
||||
|
||||
usbi_dbg("active bLength %d", udc.udc_desc.bLength); |
||||
|
||||
len = UGETW(udc.udc_desc.wTotalLength); |
||||
buf = malloc(len); |
||||
if (buf == NULL) |
||||
return (LIBUSB_ERROR_NO_MEM); |
||||
|
||||
udf.udf_bus = dev->bus_number; |
||||
udf.udf_addr = dev->device_address; |
||||
udf.udf_config_index = udc.udc_config_index; |
||||
udf.udf_size = len; |
||||
udf.udf_data = buf; |
||||
|
||||
usbi_dbg("index %d, len %d", udf.udf_config_index, len); |
||||
|
||||
if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) { |
||||
err = errno; |
||||
close(fd); |
||||
free(buf); |
||||
return _errno_to_libusb(err); |
||||
} |
||||
close(fd); |
||||
|
||||
if (dpriv->cdesc) |
||||
free(dpriv->cdesc); |
||||
dpriv->cdesc = buf; |
||||
|
||||
return (LIBUSB_SUCCESS); |
||||
} |
||||
|
||||
int |
||||
_sync_control_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer; |
||||
struct libusb_control_setup *setup; |
||||
struct device_priv *dpriv; |
||||
struct usb_ctl_request req; |
||||
|
||||
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv; |
||||
setup = (struct libusb_control_setup *)transfer->buffer; |
||||
|
||||
usbi_dbg("type %x request %x value %x index %d length %d timeout %d", |
||||
setup->bmRequestType, setup->bRequest, |
||||
libusb_le16_to_cpu(setup->wValue), |
||||
libusb_le16_to_cpu(setup->wIndex), |
||||
libusb_le16_to_cpu(setup->wLength), transfer->timeout); |
||||
|
||||
req.ucr_addr = transfer->dev_handle->dev->device_address; |
||||
req.ucr_request.bmRequestType = setup->bmRequestType; |
||||
req.ucr_request.bRequest = setup->bRequest; |
||||
/* Don't use USETW, libusb already deals with the endianness */ |
||||
(*(uint16_t *)req.ucr_request.wValue) = setup->wValue; |
||||
(*(uint16_t *)req.ucr_request.wIndex) = setup->wIndex; |
||||
(*(uint16_t *)req.ucr_request.wLength) = setup->wLength; |
||||
req.ucr_data = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; |
||||
|
||||
if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) |
||||
req.ucr_flags = USBD_SHORT_XFER_OK; |
||||
|
||||
if (dpriv->devname == NULL) { |
||||
/*
|
||||
* XXX If the device is not attached to ugen(4) it is |
||||
* XXX still possible to submit a control transfer but |
||||
* XXX with the default timeout only. |
||||
*/ |
||||
int fd, err; |
||||
|
||||
if ((fd = _bus_open(transfer->dev_handle->dev->bus_number)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
if ((ioctl(fd, USB_REQUEST, &req)) < 0) { |
||||
err = errno; |
||||
close(fd); |
||||
return _errno_to_libusb(err); |
||||
} |
||||
close(fd); |
||||
} else { |
||||
if ((ioctl(dpriv->fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
if ((ioctl(dpriv->fd, USB_DO_REQUEST, &req)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
} |
||||
|
||||
itransfer->transferred = req.ucr_actlen; |
||||
|
||||
usbi_dbg("transferred %d", itransfer->transferred); |
||||
|
||||
return (0); |
||||
} |
||||
|
||||
int |
||||
_access_endpoint(struct libusb_transfer *transfer) |
||||
{ |
||||
struct handle_priv *hpriv; |
||||
struct device_priv *dpriv; |
||||
char devnode[16]; |
||||
int fd, endpt; |
||||
mode_t mode; |
||||
|
||||
hpriv = (struct handle_priv *)transfer->dev_handle->os_priv; |
||||
dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv; |
||||
|
||||
endpt = UE_GET_ADDR(transfer->endpoint); |
||||
mode = IS_XFERIN(transfer) ? O_RDONLY : O_WRONLY; |
||||
|
||||
usbi_dbg("endpoint %d mode %d", endpt, mode); |
||||
|
||||
if (hpriv->endpoints[endpt] < 0) { |
||||
/* Pick the right endpoint node */ |
||||
snprintf(devnode, sizeof(devnode), DEVPATH "%s.%02d", |
||||
dpriv->devname, endpt); |
||||
|
||||
/* We may need to read/write to the same endpoint later. */ |
||||
if (((fd = open(devnode, O_RDWR)) < 0) && (errno == ENXIO)) |
||||
if ((fd = open(devnode, mode)) < 0) |
||||
return (-1); |
||||
|
||||
hpriv->endpoints[endpt] = fd; |
||||
} |
||||
|
||||
return (hpriv->endpoints[endpt]); |
||||
} |
||||
|
||||
int |
||||
_sync_gen_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer; |
||||
struct device_priv *dpriv; |
||||
int fd, nr = 1; |
||||
|
||||
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv; |
||||
|
||||
if (dpriv->devname == NULL) |
||||
return (LIBUSB_ERROR_NOT_SUPPORTED); |
||||
|
||||
/*
|
||||
* Bulk, Interrupt or Isochronous transfer depends on the |
||||
* endpoint and thus the node to open. |
||||
*/ |
||||
if ((fd = _access_endpoint(transfer)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
if ((ioctl(fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
if (IS_XFERIN(transfer)) { |
||||
if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) |
||||
if ((ioctl(fd, USB_SET_SHORT_XFER, &nr)) < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
nr = read(fd, transfer->buffer, transfer->length); |
||||
} else { |
||||
nr = write(fd, transfer->buffer, transfer->length); |
||||
} |
||||
|
||||
if (nr < 0) |
||||
return _errno_to_libusb(errno); |
||||
|
||||
itransfer->transferred = nr; |
||||
|
||||
return (0); |
||||
} |
||||
|
||||
int |
||||
_bus_open(int number) |
||||
{ |
||||
char busnode[16]; |
||||
|
||||
snprintf(busnode, sizeof(busnode), USBDEV "%d", number); |
||||
|
||||
return open(busnode, O_RDWR); |
||||
} |
@ -0,0 +1,53 @@ |
||||
/*
|
||||
* poll_posix: poll compatibility wrapper for POSIX systems |
||||
* Copyright © 2013 RealVNC Ltd. |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
* |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#include <unistd.h> |
||||
#include <fcntl.h> |
||||
#include <errno.h> |
||||
#include <stdlib.h> |
||||
|
||||
#include "libusbi.h" |
||||
|
||||
int usbi_pipe(int pipefd[2]) |
||||
{ |
||||
int ret = pipe(pipefd); |
||||
if (ret != 0) { |
||||
return ret; |
||||
} |
||||
ret = fcntl(pipefd[1], F_GETFL); |
||||
if (ret == -1) { |
||||
usbi_dbg("Failed to get pipe fd flags: %d", errno); |
||||
goto err_close_pipe; |
||||
} |
||||
ret = fcntl(pipefd[1], F_SETFL, ret | O_NONBLOCK); |
||||
if (ret != 0) { |
||||
usbi_dbg("Failed to set non-blocking on new pipe: %d", errno); |
||||
goto err_close_pipe; |
||||
} |
||||
|
||||
return 0; |
||||
|
||||
err_close_pipe: |
||||
usbi_close(pipefd[0]); |
||||
usbi_close(pipefd[1]); |
||||
return ret; |
||||
} |
@ -0,0 +1,11 @@ |
||||
#ifndef LIBUSB_POLL_POSIX_H |
||||
#define LIBUSB_POLL_POSIX_H |
||||
|
||||
#define usbi_write write |
||||
#define usbi_read read |
||||
#define usbi_close close |
||||
#define usbi_poll poll |
||||
|
||||
int usbi_pipe(int pipefd[2]); |
||||
|
||||
#endif /* LIBUSB_POLL_POSIX_H */ |
@ -0,0 +1,728 @@ |
||||
/*
|
||||
* poll_windows: poll compatibility wrapper for Windows |
||||
* Copyright © 2012-2013 RealVNC Ltd. |
||||
* Copyright © 2009-2010 Pete Batard <pete@akeo.ie> |
||||
* With contributions from Michael Plante, Orin Eman et al. |
||||
* Parts of poll implementation from libusb-win32, by Stephan Meyer et al. |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
* |
||||
*/ |
||||
|
||||
/*
|
||||
* poll() and pipe() Windows compatibility layer for libusb 1.0 |
||||
* |
||||
* The way this layer works is by using OVERLAPPED with async I/O transfers, as |
||||
* OVERLAPPED have an associated event which is flagged for I/O completion. |
||||
* |
||||
* For USB pollable async I/O, you would typically: |
||||
* - obtain a Windows HANDLE to a file or device that has been opened in |
||||
* OVERLAPPED mode |
||||
* - call usbi_create_fd with this handle to obtain a custom fd. |
||||
* Note that if you need simultaneous R/W access, you need to call create_fd |
||||
* twice, once in RW_READ and once in RW_WRITE mode to obtain 2 separate |
||||
* pollable fds |
||||
* - leave the core functions call the poll routine and flag POLLIN/POLLOUT |
||||
* |
||||
* The pipe pollable synchronous I/O works using the overlapped event associated |
||||
* with a fake pipe. The read/write functions are only meant to be used in that |
||||
* context. |
||||
*/ |
||||
#include <config.h> |
||||
|
||||
#include <errno.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
|
||||
#include "libusbi.h" |
||||
|
||||
// Uncomment to debug the polling layer
|
||||
//#define DEBUG_POLL_WINDOWS
|
||||
#if defined(DEBUG_POLL_WINDOWS) |
||||
#define poll_dbg usbi_dbg |
||||
#else |
||||
// MSVC++ < 2005 cannot use a variadic argument and non MSVC
|
||||
// compilers produce warnings if parenthesis are omitted.
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1400) |
||||
#define poll_dbg |
||||
#else |
||||
#define poll_dbg(...) |
||||
#endif |
||||
#endif |
||||
|
||||
#if defined(_PREFAST_) |
||||
#pragma warning(disable:28719) |
||||
#endif |
||||
|
||||
#define CHECK_INIT_POLLING do {if(!is_polling_set) init_polling();} while(0) |
||||
|
||||
// public fd data
|
||||
const struct winfd INVALID_WINFD = {-1, INVALID_HANDLE_VALUE, NULL, NULL, NULL, RW_NONE}; |
||||
struct winfd poll_fd[MAX_FDS]; |
||||
// internal fd data
|
||||
struct { |
||||
CRITICAL_SECTION mutex; // lock for fds
|
||||
// Additional variables for XP CancelIoEx partial emulation
|
||||
HANDLE original_handle; |
||||
DWORD thread_id; |
||||
} _poll_fd[MAX_FDS]; |
||||
|
||||
// globals
|
||||
BOOLEAN is_polling_set = FALSE; |
||||
LONG pipe_number = 0; |
||||
static volatile LONG compat_spinlock = 0; |
||||
|
||||
#if !defined(_WIN32_WCE) |
||||
// CancelIoEx, available on Vista and later only, provides the ability to cancel
|
||||
// a single transfer (OVERLAPPED) when used. As it may not be part of any of the
|
||||
// platform headers, we hook into the Kernel32 system DLL directly to seek it.
|
||||
static BOOL (__stdcall *pCancelIoEx)(HANDLE, LPOVERLAPPED) = NULL; |
||||
#define Use_Duplicate_Handles (pCancelIoEx == NULL) |
||||
|
||||
static inline void setup_cancel_io(void) |
||||
{ |
||||
HMODULE hKernel32 = GetModuleHandleA("KERNEL32"); |
||||
if (hKernel32 != NULL) { |
||||
pCancelIoEx = (BOOL (__stdcall *)(HANDLE,LPOVERLAPPED)) |
||||
GetProcAddress(hKernel32, "CancelIoEx"); |
||||
} |
||||
usbi_dbg("Will use CancelIo%s for I/O cancellation", |
||||
Use_Duplicate_Handles?"":"Ex"); |
||||
} |
||||
|
||||
static inline BOOL cancel_io(int _index) |
||||
{ |
||||
if ((_index < 0) || (_index >= MAX_FDS)) { |
||||
return FALSE; |
||||
} |
||||
|
||||
if ( (poll_fd[_index].fd < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE) |
||||
|| (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) { |
||||
return TRUE; |
||||
} |
||||
if (poll_fd[_index].itransfer && poll_fd[_index].cancel_fn) { |
||||
// Cancel outstanding transfer via the specific callback
|
||||
(*poll_fd[_index].cancel_fn)(poll_fd[_index].itransfer); |
||||
return TRUE; |
||||
} |
||||
if (pCancelIoEx != NULL) { |
||||
return (*pCancelIoEx)(poll_fd[_index].handle, poll_fd[_index].overlapped); |
||||
} |
||||
if (_poll_fd[_index].thread_id == GetCurrentThreadId()) { |
||||
return CancelIo(poll_fd[_index].handle); |
||||
} |
||||
usbi_warn(NULL, "Unable to cancel I/O that was started from another thread"); |
||||
return FALSE; |
||||
} |
||||
#else |
||||
#define Use_Duplicate_Handles FALSE |
||||
|
||||
static __inline void setup_cancel_io() |
||||
{ |
||||
// No setup needed on WinCE
|
||||
} |
||||
|
||||
static __inline BOOL cancel_io(int _index) |
||||
{ |
||||
if ((_index < 0) || (_index >= MAX_FDS)) { |
||||
return FALSE; |
||||
} |
||||
if ( (poll_fd[_index].fd < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE) |
||||
|| (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) { |
||||
return TRUE; |
||||
} |
||||
if (poll_fd[_index].itransfer && poll_fd[_index].cancel_fn) { |
||||
// Cancel outstanding transfer via the specific callback
|
||||
(*poll_fd[_index].cancel_fn)(poll_fd[_index].itransfer); |
||||
} |
||||
return TRUE; |
||||
} |
||||
#endif |
||||
|
||||
// Init
|
||||
void init_polling(void) |
||||
{ |
||||
int i; |
||||
|
||||
while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { |
||||
SleepEx(0, TRUE); |
||||
} |
||||
if (!is_polling_set) { |
||||
setup_cancel_io(); |
||||
for (i=0; i<MAX_FDS; i++) { |
||||
poll_fd[i] = INVALID_WINFD; |
||||
_poll_fd[i].original_handle = INVALID_HANDLE_VALUE; |
||||
_poll_fd[i].thread_id = 0; |
||||
InitializeCriticalSection(&_poll_fd[i].mutex); |
||||
} |
||||
is_polling_set = TRUE; |
||||
} |
||||
InterlockedExchange((LONG *)&compat_spinlock, 0); |
||||
} |
||||
|
||||
// Internal function to retrieve the table index (and lock the fd mutex)
|
||||
static int _fd_to_index_and_lock(int fd) |
||||
{ |
||||
int i; |
||||
|
||||
if (fd < 0) |
||||
return -1; |
||||
|
||||
for (i=0; i<MAX_FDS; i++) { |
||||
if (poll_fd[i].fd == fd) { |
||||
EnterCriticalSection(&_poll_fd[i].mutex); |
||||
// fd might have changed before we got to critical
|
||||
if (poll_fd[i].fd != fd) { |
||||
LeaveCriticalSection(&_poll_fd[i].mutex); |
||||
continue; |
||||
} |
||||
return i; |
||||
} |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
static OVERLAPPED *create_overlapped(void) |
||||
{ |
||||
OVERLAPPED *overlapped = (OVERLAPPED*) calloc(1, sizeof(OVERLAPPED)); |
||||
if (overlapped == NULL) { |
||||
return NULL; |
||||
} |
||||
overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); |
||||
if(overlapped->hEvent == NULL) { |
||||
free (overlapped); |
||||
return NULL; |
||||
} |
||||
return overlapped; |
||||
} |
||||
|
||||
static void free_overlapped(OVERLAPPED *overlapped) |
||||
{ |
||||
if (overlapped == NULL) |
||||
return; |
||||
|
||||
if ( (overlapped->hEvent != 0) |
||||
&& (overlapped->hEvent != INVALID_HANDLE_VALUE) ) { |
||||
CloseHandle(overlapped->hEvent); |
||||
} |
||||
free(overlapped); |
||||
} |
||||
|
||||
void exit_polling(void) |
||||
{ |
||||
int i; |
||||
|
||||
while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { |
||||
SleepEx(0, TRUE); |
||||
} |
||||
if (is_polling_set) { |
||||
is_polling_set = FALSE; |
||||
|
||||
for (i=0; i<MAX_FDS; i++) { |
||||
// Cancel any async I/O (handle can be invalid)
|
||||
cancel_io(i); |
||||
// If anything was pending on that I/O, it should be
|
||||
// terminating, and we should be able to access the fd
|
||||
// mutex lock before too long
|
||||
EnterCriticalSection(&_poll_fd[i].mutex); |
||||
free_overlapped(poll_fd[i].overlapped); |
||||
if (Use_Duplicate_Handles) { |
||||
// Close duplicate handle
|
||||
if (_poll_fd[i].original_handle != INVALID_HANDLE_VALUE) { |
||||
CloseHandle(poll_fd[i].handle); |
||||
} |
||||
} |
||||
poll_fd[i] = INVALID_WINFD; |
||||
LeaveCriticalSection(&_poll_fd[i].mutex); |
||||
DeleteCriticalSection(&_poll_fd[i].mutex); |
||||
} |
||||
} |
||||
InterlockedExchange((LONG *)&compat_spinlock, 0); |
||||
} |
||||
|
||||
/*
|
||||
* Create a fake pipe. |
||||
* As libusb only uses pipes for signaling, all we need from a pipe is an |
||||
* event. To that extent, we create a single wfd and overlapped as a means |
||||
* to access that event. |
||||
*/ |
||||
int usbi_pipe(int filedes[2]) |
||||
{ |
||||
int i; |
||||
OVERLAPPED* overlapped; |
||||
|
||||
CHECK_INIT_POLLING; |
||||
|
||||
overlapped = create_overlapped(); |
||||
|
||||
if (overlapped == NULL) { |
||||
return -1; |
||||
} |
||||
// The overlapped must have status pending for signaling to work in poll
|
||||
overlapped->Internal = STATUS_PENDING; |
||||
overlapped->InternalHigh = 0; |
||||
|
||||
for (i=0; i<MAX_FDS; i++) { |
||||
if (poll_fd[i].fd < 0) { |
||||
EnterCriticalSection(&_poll_fd[i].mutex); |
||||
// fd might have been allocated before we got to critical
|
||||
if (poll_fd[i].fd >= 0) { |
||||
LeaveCriticalSection(&_poll_fd[i].mutex); |
||||
continue; |
||||
} |
||||
|
||||
// Use index as the unique fd number
|
||||
poll_fd[i].fd = i; |
||||
// Read end of the "pipe"
|
||||
filedes[0] = poll_fd[i].fd; |
||||
// We can use the same handle for both ends
|
||||
filedes[1] = filedes[0]; |
||||
|
||||
poll_fd[i].handle = DUMMY_HANDLE; |
||||
poll_fd[i].overlapped = overlapped; |
||||
// There's no polling on the write end, so we just use READ for our needs
|
||||
poll_fd[i].rw = RW_READ; |
||||
_poll_fd[i].original_handle = INVALID_HANDLE_VALUE; |
||||
LeaveCriticalSection(&_poll_fd[i].mutex); |
||||
return 0; |
||||
} |
||||
} |
||||
free_overlapped(overlapped); |
||||
return -1; |
||||
} |
||||
|
||||
/*
|
||||
* Create both an fd and an OVERLAPPED from an open Windows handle, so that |
||||
* it can be used with our polling function |
||||
* The handle MUST support overlapped transfers (usually requires CreateFile |
||||
* with FILE_FLAG_OVERLAPPED) |
||||
* Return a pollable file descriptor struct, or INVALID_WINFD on error |
||||
* |
||||
* Note that the fd returned by this function is a per-transfer fd, rather |
||||
* than a per-session fd and cannot be used for anything else but our |
||||
* custom functions (the fd itself points to the NUL: device) |
||||
* if you plan to do R/W on the same handle, you MUST create 2 fds: one for |
||||
* read and one for write. Using a single R/W fd is unsupported and will |
||||
* produce unexpected results |
||||
*/ |
||||
struct winfd usbi_create_fd(HANDLE handle, int access_mode, struct usbi_transfer *itransfer, cancel_transfer *cancel_fn) |
||||
{ |
||||
int i; |
||||
struct winfd wfd = INVALID_WINFD; |
||||
OVERLAPPED* overlapped = NULL; |
||||
|
||||
CHECK_INIT_POLLING; |
||||
|
||||
if ((handle == 0) || (handle == INVALID_HANDLE_VALUE)) { |
||||
return INVALID_WINFD; |
||||
} |
||||
|
||||
wfd.itransfer = itransfer; |
||||
wfd.cancel_fn = cancel_fn; |
||||
|
||||
if ((access_mode != RW_READ) && (access_mode != RW_WRITE)) { |
||||
usbi_warn(NULL, "only one of RW_READ or RW_WRITE are supported. " |
||||
"If you want to poll for R/W simultaneously, create multiple fds from the same handle."); |
||||
return INVALID_WINFD; |
||||
} |
||||
if (access_mode == RW_READ) { |
||||
wfd.rw = RW_READ; |
||||
} else { |
||||
wfd.rw = RW_WRITE; |
||||
} |
||||
|
||||
overlapped = create_overlapped(); |
||||
if(overlapped == NULL) { |
||||
return INVALID_WINFD; |
||||
} |
||||
|
||||
for (i=0; i<MAX_FDS; i++) { |
||||
if (poll_fd[i].fd < 0) { |
||||
EnterCriticalSection(&_poll_fd[i].mutex); |
||||
// fd might have been removed before we got to critical
|
||||
if (poll_fd[i].fd >= 0) { |
||||
LeaveCriticalSection(&_poll_fd[i].mutex); |
||||
continue; |
||||
} |
||||
// Use index as the unique fd number
|
||||
wfd.fd = i; |
||||
// Attempt to emulate some of the CancelIoEx behaviour on platforms
|
||||
// that don't have it
|
||||
if (Use_Duplicate_Handles) { |
||||
_poll_fd[i].thread_id = GetCurrentThreadId(); |
||||
if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), |
||||
&wfd.handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { |
||||
usbi_dbg("could not duplicate handle for CancelIo - using original one"); |
||||
wfd.handle = handle; |
||||
// Make sure we won't close the original handle on fd deletion then
|
||||
_poll_fd[i].original_handle = INVALID_HANDLE_VALUE; |
||||
} else { |
||||
_poll_fd[i].original_handle = handle; |
||||
} |
||||
} else { |
||||
wfd.handle = handle; |
||||
} |
||||
wfd.overlapped = overlapped; |
||||
memcpy(&poll_fd[i], &wfd, sizeof(struct winfd)); |
||||
LeaveCriticalSection(&_poll_fd[i].mutex); |
||||
return wfd; |
||||
} |
||||
} |
||||
free_overlapped(overlapped); |
||||
return INVALID_WINFD; |
||||
} |
||||
|
||||
static void _free_index(int _index) |
||||
{ |
||||
// Cancel any async IO (Don't care about the validity of our handles for this)
|
||||
cancel_io(_index); |
||||
// close the duplicate handle (if we have an actual duplicate)
|
||||
if (Use_Duplicate_Handles) { |
||||
if (_poll_fd[_index].original_handle != INVALID_HANDLE_VALUE) { |
||||
CloseHandle(poll_fd[_index].handle); |
||||
} |
||||
_poll_fd[_index].original_handle = INVALID_HANDLE_VALUE; |
||||
_poll_fd[_index].thread_id = 0; |
||||
} |
||||
free_overlapped(poll_fd[_index].overlapped); |
||||
poll_fd[_index] = INVALID_WINFD; |
||||
} |
||||
|
||||
/*
|
||||
* Release a pollable file descriptor. |
||||
* |
||||
* Note that the associated Windows handle is not closed by this call |
||||
*/ |
||||
void usbi_free_fd(struct winfd *wfd) |
||||
{ |
||||
int _index; |
||||
|
||||
CHECK_INIT_POLLING; |
||||
|
||||
_index = _fd_to_index_and_lock(wfd->fd); |
||||
if (_index < 0) { |
||||
return; |
||||
} |
||||
_free_index(_index); |
||||
*wfd = INVALID_WINFD; |
||||
LeaveCriticalSection(&_poll_fd[_index].mutex); |
||||
} |
||||
|
||||
/*
|
||||
* The functions below perform various conversions between fd, handle and OVERLAPPED |
||||
*/ |
||||
struct winfd fd_to_winfd(int fd) |
||||
{ |
||||
int i; |
||||
struct winfd wfd; |
||||
|
||||
CHECK_INIT_POLLING; |
||||
|
||||
if (fd < 0) |
||||
return INVALID_WINFD; |
||||
|
||||
for (i=0; i<MAX_FDS; i++) { |
||||
if (poll_fd[i].fd == fd) { |
||||
EnterCriticalSection(&_poll_fd[i].mutex); |
||||
// fd might have been deleted before we got to critical
|
||||
if (poll_fd[i].fd != fd) { |
||||
LeaveCriticalSection(&_poll_fd[i].mutex); |
||||
continue; |
||||
} |
||||
memcpy(&wfd, &poll_fd[i], sizeof(struct winfd)); |
||||
LeaveCriticalSection(&_poll_fd[i].mutex); |
||||
return wfd; |
||||
} |
||||
} |
||||
return INVALID_WINFD; |
||||
} |
||||
|
||||
struct winfd handle_to_winfd(HANDLE handle) |
||||
{ |
||||
int i; |
||||
struct winfd wfd; |
||||
|
||||
CHECK_INIT_POLLING; |
||||
|
||||
if ((handle == 0) || (handle == INVALID_HANDLE_VALUE)) |
||||
return INVALID_WINFD; |
||||
|
||||
for (i=0; i<MAX_FDS; i++) { |
||||
if (poll_fd[i].handle == handle) { |
||||
EnterCriticalSection(&_poll_fd[i].mutex); |
||||
// fd might have been deleted before we got to critical
|
||||
if (poll_fd[i].handle != handle) { |
||||
LeaveCriticalSection(&_poll_fd[i].mutex); |
||||
continue; |
||||
} |
||||
memcpy(&wfd, &poll_fd[i], sizeof(struct winfd)); |
||||
LeaveCriticalSection(&_poll_fd[i].mutex); |
||||
return wfd; |
||||
} |
||||
} |
||||
return INVALID_WINFD; |
||||
} |
||||
|
||||
struct winfd overlapped_to_winfd(OVERLAPPED* overlapped) |
||||
{ |
||||
int i; |
||||
struct winfd wfd; |
||||
|
||||
CHECK_INIT_POLLING; |
||||
|
||||
if (overlapped == NULL) |
||||
return INVALID_WINFD; |
||||
|
||||
for (i=0; i<MAX_FDS; i++) { |
||||
if (poll_fd[i].overlapped == overlapped) { |
||||
EnterCriticalSection(&_poll_fd[i].mutex); |
||||
// fd might have been deleted before we got to critical
|
||||
if (poll_fd[i].overlapped != overlapped) { |
||||
LeaveCriticalSection(&_poll_fd[i].mutex); |
||||
continue; |
||||
} |
||||
memcpy(&wfd, &poll_fd[i], sizeof(struct winfd)); |
||||
LeaveCriticalSection(&_poll_fd[i].mutex); |
||||
return wfd; |
||||
} |
||||
} |
||||
return INVALID_WINFD; |
||||
} |
||||
|
||||
/*
|
||||
* POSIX poll equivalent, using Windows OVERLAPPED |
||||
* Currently, this function only accepts one of POLLIN or POLLOUT per fd |
||||
* (but you can create multiple fds from the same handle for read and write) |
||||
*/ |
||||
int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout) |
||||
{ |
||||
unsigned i; |
||||
int _index, object_index, triggered; |
||||
HANDLE *handles_to_wait_on; |
||||
int *handle_to_index; |
||||
DWORD nb_handles_to_wait_on = 0; |
||||
DWORD ret; |
||||
|
||||
CHECK_INIT_POLLING; |
||||
|
||||
triggered = 0; |
||||
handles_to_wait_on = (HANDLE*) calloc(nfds+1, sizeof(HANDLE)); // +1 for fd_update
|
||||
handle_to_index = (int*) calloc(nfds, sizeof(int)); |
||||
if ((handles_to_wait_on == NULL) || (handle_to_index == NULL)) { |
||||
errno = ENOMEM; |
||||
triggered = -1; |
||||
goto poll_exit; |
||||
} |
||||
|
||||
for (i = 0; i < nfds; ++i) { |
||||
fds[i].revents = 0; |
||||
|
||||
// Only one of POLLIN or POLLOUT can be selected with this version of poll (not both)
|
||||
if ((fds[i].events & ~POLLIN) && (!(fds[i].events & POLLOUT))) { |
||||
fds[i].revents |= POLLERR; |
||||
errno = EACCES; |
||||
usbi_warn(NULL, "unsupported set of events"); |
||||
triggered = -1; |
||||
goto poll_exit; |
||||
} |
||||
|
||||
_index = _fd_to_index_and_lock(fds[i].fd); |
||||
poll_dbg("fd[%d]=%d: (overlapped=%p) got events %04X", i, poll_fd[_index].fd, poll_fd[_index].overlapped, fds[i].events); |
||||
|
||||
if ( (_index < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE) |
||||
|| (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL)) { |
||||
fds[i].revents |= POLLNVAL | POLLERR; |
||||
errno = EBADF; |
||||
if (_index >= 0) { |
||||
LeaveCriticalSection(&_poll_fd[_index].mutex); |
||||
} |
||||
usbi_warn(NULL, "invalid fd"); |
||||
triggered = -1; |
||||
goto poll_exit; |
||||
} |
||||
|
||||
// IN or OUT must match our fd direction
|
||||
if ((fds[i].events & POLLIN) && (poll_fd[_index].rw != RW_READ)) { |
||||
fds[i].revents |= POLLNVAL | POLLERR; |
||||
errno = EBADF; |
||||
usbi_warn(NULL, "attempted POLLIN on fd without READ access"); |
||||
LeaveCriticalSection(&_poll_fd[_index].mutex); |
||||
triggered = -1; |
||||
goto poll_exit; |
||||
} |
||||
|
||||
if ((fds[i].events & POLLOUT) && (poll_fd[_index].rw != RW_WRITE)) { |
||||
fds[i].revents |= POLLNVAL | POLLERR; |
||||
errno = EBADF; |
||||
usbi_warn(NULL, "attempted POLLOUT on fd without WRITE access"); |
||||
LeaveCriticalSection(&_poll_fd[_index].mutex); |
||||
triggered = -1; |
||||
goto poll_exit; |
||||
} |
||||
|
||||
// The following macro only works if overlapped I/O was reported pending
|
||||
if ( (HasOverlappedIoCompleted(poll_fd[_index].overlapped)) |
||||
|| (HasOverlappedIoCompletedSync(poll_fd[_index].overlapped)) ) { |
||||
poll_dbg(" completed"); |
||||
// checks above should ensure this works:
|
||||
fds[i].revents = fds[i].events; |
||||
triggered++; |
||||
} else { |
||||
handles_to_wait_on[nb_handles_to_wait_on] = poll_fd[_index].overlapped->hEvent; |
||||
handle_to_index[nb_handles_to_wait_on] = i; |
||||
nb_handles_to_wait_on++; |
||||
} |
||||
LeaveCriticalSection(&_poll_fd[_index].mutex); |
||||
} |
||||
|
||||
// If nothing was triggered, wait on all fds that require it
|
||||
if ((timeout != 0) && (triggered == 0) && (nb_handles_to_wait_on != 0)) { |
||||
if (timeout < 0) { |
||||
poll_dbg("starting infinite wait for %u handles...", (unsigned int)nb_handles_to_wait_on); |
||||
} else { |
||||
poll_dbg("starting %d ms wait for %u handles...", timeout, (unsigned int)nb_handles_to_wait_on); |
||||
} |
||||
ret = WaitForMultipleObjects(nb_handles_to_wait_on, handles_to_wait_on, |
||||
FALSE, (timeout<0)?INFINITE:(DWORD)timeout); |
||||
object_index = ret-WAIT_OBJECT_0; |
||||
if ((object_index >= 0) && ((DWORD)object_index < nb_handles_to_wait_on)) { |
||||
poll_dbg(" completed after wait"); |
||||
i = handle_to_index[object_index]; |
||||
_index = _fd_to_index_and_lock(fds[i].fd); |
||||
fds[i].revents = fds[i].events; |
||||
triggered++; |
||||
if (_index >= 0) { |
||||
LeaveCriticalSection(&_poll_fd[_index].mutex); |
||||
} |
||||
} else if (ret == WAIT_TIMEOUT) { |
||||
poll_dbg(" timed out"); |
||||
triggered = 0; // 0 = timeout
|
||||
} else { |
||||
errno = EIO; |
||||
triggered = -1; // error
|
||||
} |
||||
} |
||||
|
||||
poll_exit: |
||||
if (handles_to_wait_on != NULL) { |
||||
free(handles_to_wait_on); |
||||
} |
||||
if (handle_to_index != NULL) { |
||||
free(handle_to_index); |
||||
} |
||||
return triggered; |
||||
} |
||||
|
||||
/*
|
||||
* close a fake pipe fd |
||||
*/ |
||||
int usbi_close(int fd) |
||||
{ |
||||
int _index; |
||||
int r = -1; |
||||
|
||||
CHECK_INIT_POLLING; |
||||
|
||||
_index = _fd_to_index_and_lock(fd); |
||||
|
||||
if (_index < 0) { |
||||
errno = EBADF; |
||||
} else { |
||||
free_overlapped(poll_fd[_index].overlapped); |
||||
poll_fd[_index] = INVALID_WINFD; |
||||
LeaveCriticalSection(&_poll_fd[_index].mutex); |
||||
} |
||||
return r; |
||||
} |
||||
|
||||
/*
|
||||
* synchronous write for fake "pipe" signaling |
||||
*/ |
||||
ssize_t usbi_write(int fd, const void *buf, size_t count) |
||||
{ |
||||
int _index; |
||||
UNUSED(buf); |
||||
|
||||
CHECK_INIT_POLLING; |
||||
|
||||
if (count != sizeof(unsigned char)) { |
||||
usbi_err(NULL, "this function should only used for signaling"); |
||||
return -1; |
||||
} |
||||
|
||||
_index = _fd_to_index_and_lock(fd); |
||||
|
||||
if ( (_index < 0) || (poll_fd[_index].overlapped == NULL) ) { |
||||
errno = EBADF; |
||||
if (_index >= 0) { |
||||
LeaveCriticalSection(&_poll_fd[_index].mutex); |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
poll_dbg("set pipe event (fd = %d, thread = %08X)", _index, (unsigned int)GetCurrentThreadId()); |
||||
SetEvent(poll_fd[_index].overlapped->hEvent); |
||||
poll_fd[_index].overlapped->Internal = STATUS_WAIT_0; |
||||
// If two threads write on the pipe at the same time, we need to
|
||||
// process two separate reads => use the overlapped as a counter
|
||||
poll_fd[_index].overlapped->InternalHigh++; |
||||
|
||||
LeaveCriticalSection(&_poll_fd[_index].mutex); |
||||
return sizeof(unsigned char); |
||||
} |
||||
|
||||
/*
|
||||
* synchronous read for fake "pipe" signaling |
||||
*/ |
||||
ssize_t usbi_read(int fd, void *buf, size_t count) |
||||
{ |
||||
int _index; |
||||
ssize_t r = -1; |
||||
UNUSED(buf); |
||||
|
||||
CHECK_INIT_POLLING; |
||||
|
||||
if (count != sizeof(unsigned char)) { |
||||
usbi_err(NULL, "this function should only used for signaling"); |
||||
return -1; |
||||
} |
||||
|
||||
_index = _fd_to_index_and_lock(fd); |
||||
|
||||
if (_index < 0) { |
||||
errno = EBADF; |
||||
return -1; |
||||
} |
||||
|
||||
if (WaitForSingleObject(poll_fd[_index].overlapped->hEvent, INFINITE) != WAIT_OBJECT_0) { |
||||
usbi_warn(NULL, "waiting for event failed: %u", (unsigned int)GetLastError()); |
||||
errno = EIO; |
||||
goto out; |
||||
} |
||||
|
||||
poll_dbg("clr pipe event (fd = %d, thread = %08X)", _index, (unsigned int)GetCurrentThreadId()); |
||||
poll_fd[_index].overlapped->InternalHigh--; |
||||
// Don't reset unless we don't have any more events to process
|
||||
if (poll_fd[_index].overlapped->InternalHigh <= 0) { |
||||
ResetEvent(poll_fd[_index].overlapped->hEvent); |
||||
poll_fd[_index].overlapped->Internal = STATUS_PENDING; |
||||
} |
||||
|
||||
r = sizeof(unsigned char); |
||||
|
||||
out: |
||||
LeaveCriticalSection(&_poll_fd[_index].mutex); |
||||
return r; |
||||
} |
@ -0,0 +1,131 @@ |
||||
/*
|
||||
* Windows compat: POSIX compatibility wrapper |
||||
* Copyright © 2012-2013 RealVNC Ltd. |
||||
* Copyright © 2009-2010 Pete Batard <pete@akeo.ie> |
||||
* With contributions from Michael Plante, Orin Eman et al. |
||||
* Parts of poll implementation from libusb-win32, by Stephan Meyer et al. |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
* |
||||
*/ |
||||
#pragma once |
||||
|
||||
#if defined(_MSC_VER) |
||||
// disable /W4 MSVC warnings that are benign
|
||||
#pragma warning(disable:4127) // conditional expression is constant
|
||||
#endif |
||||
|
||||
// Handle synchronous completion through the overlapped structure
|
||||
#if !defined(STATUS_REPARSE) // reuse the REPARSE status code
|
||||
#define STATUS_REPARSE ((LONG)0x00000104L) |
||||
#endif |
||||
#define STATUS_COMPLETED_SYNCHRONOUSLY STATUS_REPARSE |
||||
#if defined(_WIN32_WCE) |
||||
// WinCE doesn't have a HasOverlappedIoCompleted() macro, so attempt to emulate it
|
||||
#define HasOverlappedIoCompleted(lpOverlapped) (((DWORD)(lpOverlapped)->Internal) != STATUS_PENDING) |
||||
#endif |
||||
#define HasOverlappedIoCompletedSync(lpOverlapped) (((DWORD)(lpOverlapped)->Internal) == STATUS_COMPLETED_SYNCHRONOUSLY) |
||||
|
||||
#define DUMMY_HANDLE ((HANDLE)(LONG_PTR)-2) |
||||
|
||||
/* Windows versions */ |
||||
enum windows_version { |
||||
WINDOWS_CE = -2, |
||||
WINDOWS_UNDEFINED = -1, |
||||
WINDOWS_UNSUPPORTED = 0, |
||||
WINDOWS_XP = 0x51, |
||||
WINDOWS_2003 = 0x52, // Also XP x64
|
||||
WINDOWS_VISTA = 0x60, |
||||
WINDOWS_7 = 0x61, |
||||
WINDOWS_8 = 0x62, |
||||
WINDOWS_8_1_OR_LATER = 0x63, |
||||
WINDOWS_MAX |
||||
}; |
||||
extern int windows_version; |
||||
|
||||
#define MAX_FDS 256 |
||||
|
||||
#define POLLIN 0x0001 /* There is data to read */ |
||||
#define POLLPRI 0x0002 /* There is urgent data to read */ |
||||
#define POLLOUT 0x0004 /* Writing now will not block */ |
||||
#define POLLERR 0x0008 /* Error condition */ |
||||
#define POLLHUP 0x0010 /* Hung up */ |
||||
#define POLLNVAL 0x0020 /* Invalid request: fd not open */ |
||||
|
||||
struct pollfd { |
||||
int fd; /* file descriptor */ |
||||
short events; /* requested events */ |
||||
short revents; /* returned events */ |
||||
}; |
||||
|
||||
// access modes
|
||||
enum rw_type { |
||||
RW_NONE, |
||||
RW_READ, |
||||
RW_WRITE, |
||||
}; |
||||
|
||||
// fd struct that can be used for polling on Windows
|
||||
typedef int cancel_transfer(struct usbi_transfer *itransfer); |
||||
|
||||
struct winfd { |
||||
int fd; // what's exposed to libusb core
|
||||
HANDLE handle; // what we need to attach overlapped to the I/O op, so we can poll it
|
||||
OVERLAPPED* overlapped; // what will report our I/O status
|
||||
struct usbi_transfer *itransfer; // Associated transfer, or NULL if completed
|
||||
cancel_transfer *cancel_fn; // Function pointer to cancel transfer API
|
||||
enum rw_type rw; // I/O transfer direction: read *XOR* write (NOT BOTH)
|
||||
}; |
||||
extern const struct winfd INVALID_WINFD; |
||||
|
||||
int usbi_pipe(int pipefd[2]); |
||||
int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout); |
||||
ssize_t usbi_write(int fd, const void *buf, size_t count); |
||||
ssize_t usbi_read(int fd, void *buf, size_t count); |
||||
int usbi_close(int fd); |
||||
|
||||
void init_polling(void); |
||||
void exit_polling(void); |
||||
struct winfd usbi_create_fd(HANDLE handle, int access_mode,
|
||||
struct usbi_transfer *transfer, cancel_transfer *cancel_fn); |
||||
void usbi_free_fd(struct winfd* winfd); |
||||
struct winfd fd_to_winfd(int fd); |
||||
struct winfd handle_to_winfd(HANDLE handle); |
||||
struct winfd overlapped_to_winfd(OVERLAPPED* overlapped); |
||||
|
||||
/*
|
||||
* Timeval operations |
||||
*/ |
||||
#if defined(DDKBUILD) |
||||
#include <winsock.h> // defines timeval functions on DDK |
||||
#endif |
||||
|
||||
#if !defined(TIMESPEC_TO_TIMEVAL) |
||||
#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ |
||||
(tv)->tv_sec = (long)(ts)->tv_sec; \
|
||||
(tv)->tv_usec = (long)(ts)->tv_nsec / 1000; \
|
||||
} |
||||
#endif |
||||
#if !defined(timersub) |
||||
#define timersub(a, b, result) \ |
||||
do { \
|
||||
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
||||
if ((result)->tv_usec < 0) { \
|
||||
--(result)->tv_sec; \
|
||||
(result)->tv_usec += 1000000; \
|
||||
} \
|
||||
} while (0) |
||||
#endif |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,74 @@ |
||||
/*
|
||||
* |
||||
* Copyright (c) 2016, Oracle and/or its affiliates. |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#ifndef LIBUSB_SUNOS_H |
||||
#define LIBUSB_SUNOS_H |
||||
|
||||
#include <libdevinfo.h> |
||||
#include <pthread.h> |
||||
#include "libusbi.h" |
||||
|
||||
#define READ 0 |
||||
#define WRITE 1 |
||||
|
||||
typedef struct sunos_device_priv { |
||||
uint8_t cfgvalue; /* active config value */ |
||||
uint8_t *raw_cfgdescr; /* active config descriptor */ |
||||
struct libusb_device_descriptor dev_descr; /* usb device descriptor */ |
||||
char *ugenpath; /* name of the ugen(4) node */ |
||||
char *phypath; /* physical path */ |
||||
} sunos_dev_priv_t; |
||||
|
||||
typedef struct endpoint { |
||||
int datafd; /* data file */ |
||||
int statfd; /* state file */ |
||||
} sunos_ep_priv_t; |
||||
|
||||
typedef struct sunos_device_handle_priv { |
||||
uint8_t altsetting[USB_MAXINTERFACES]; /* a interface's alt */ |
||||
uint8_t config_index; |
||||
sunos_ep_priv_t eps[USB_MAXENDPOINTS]; |
||||
sunos_dev_priv_t *dpriv; /* device private */ |
||||
} sunos_dev_handle_priv_t; |
||||
|
||||
typedef struct sunos_transfer_priv { |
||||
struct aiocb aiocb; |
||||
struct libusb_transfer *transfer; |
||||
} sunos_xfer_priv_t; |
||||
|
||||
struct node_args { |
||||
struct libusb_context *ctx; |
||||
struct discovered_devs **discdevs; |
||||
const char *last_ugenpath; |
||||
di_devlink_handle_t dlink_hdl; |
||||
}; |
||||
|
||||
struct devlink_cbarg { |
||||
struct node_args *nargs; /* di node walk arguments */ |
||||
di_node_t myself; /* the di node */ |
||||
di_minor_t minor; |
||||
}; |
||||
|
||||
/* AIO callback args */ |
||||
struct aio_callback_args{ |
||||
struct libusb_transfer *transfer; |
||||
struct aiocb aiocb; |
||||
}; |
||||
|
||||
#endif /* LIBUSB_SUNOS_H */ |
@ -0,0 +1,79 @@ |
||||
/*
|
||||
* libusb synchronization using POSIX Threads |
||||
* |
||||
* Copyright © 2011 Vitali Lovich <vlovich@aliph.com> |
||||
* Copyright © 2011 Peter Stuge <peter@stuge.se> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#include <time.h> |
||||
#if defined(__linux__) || defined(__OpenBSD__) |
||||
# if defined(__OpenBSD__) |
||||
# define _BSD_SOURCE |
||||
# endif |
||||
# include <unistd.h> |
||||
# include <sys/syscall.h> |
||||
#elif defined(__APPLE__) |
||||
# include <mach/mach.h> |
||||
#elif defined(__CYGWIN__) |
||||
# include <windows.h> |
||||
#endif |
||||
|
||||
#include "threads_posix.h" |
||||
#include "libusbi.h" |
||||
|
||||
int usbi_cond_timedwait(pthread_cond_t *cond, |
||||
pthread_mutex_t *mutex, const struct timeval *tv) |
||||
{ |
||||
struct timespec timeout; |
||||
int r; |
||||
|
||||
r = usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, &timeout); |
||||
if (r < 0) |
||||
return r; |
||||
|
||||
timeout.tv_sec += tv->tv_sec; |
||||
timeout.tv_nsec += tv->tv_usec * 1000; |
||||
while (timeout.tv_nsec >= 1000000000L) { |
||||
timeout.tv_nsec -= 1000000000L; |
||||
timeout.tv_sec++; |
||||
} |
||||
|
||||
return pthread_cond_timedwait(cond, mutex, &timeout); |
||||
} |
||||
|
||||
int usbi_get_tid(void) |
||||
{ |
||||
int ret = -1; |
||||
#if defined(__ANDROID__) |
||||
ret = gettid(); |
||||
#elif defined(__linux__) |
||||
ret = syscall(SYS_gettid); |
||||
#elif defined(__OpenBSD__) |
||||
/* The following only works with OpenBSD > 5.1 as it requires
|
||||
real thread support. For 5.1 and earlier, -1 is returned. */ |
||||
ret = syscall(SYS_getthrid); |
||||
#elif defined(__APPLE__) |
||||
ret = mach_thread_self(); |
||||
mach_port_deallocate(mach_task_self(), ret); |
||||
#elif defined(__CYGWIN__) |
||||
ret = GetCurrentThreadId(); |
||||
#endif |
||||
/* TODO: NetBSD thread ID support */ |
||||
return ret; |
||||
} |
@ -0,0 +1,55 @@ |
||||
/*
|
||||
* libusb synchronization using POSIX Threads |
||||
* |
||||
* Copyright © 2010 Peter Stuge <peter@stuge.se> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#ifndef LIBUSB_THREADS_POSIX_H |
||||
#define LIBUSB_THREADS_POSIX_H |
||||
|
||||
#include <pthread.h> |
||||
#ifdef HAVE_SYS_TIME_H |
||||
#include <sys/time.h> |
||||
#endif |
||||
|
||||
#define usbi_mutex_static_t pthread_mutex_t |
||||
#define USBI_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER |
||||
#define usbi_mutex_static_lock pthread_mutex_lock |
||||
#define usbi_mutex_static_unlock pthread_mutex_unlock |
||||
|
||||
#define usbi_mutex_t pthread_mutex_t |
||||
#define usbi_mutex_init(mutex) pthread_mutex_init((mutex), NULL) |
||||
#define usbi_mutex_lock pthread_mutex_lock |
||||
#define usbi_mutex_unlock pthread_mutex_unlock |
||||
#define usbi_mutex_trylock pthread_mutex_trylock |
||||
#define usbi_mutex_destroy pthread_mutex_destroy |
||||
|
||||
#define usbi_cond_t pthread_cond_t |
||||
#define usbi_cond_init(cond) pthread_cond_init((cond), NULL) |
||||
#define usbi_cond_wait pthread_cond_wait |
||||
#define usbi_cond_broadcast pthread_cond_broadcast |
||||
#define usbi_cond_destroy pthread_cond_destroy |
||||
|
||||
#define usbi_tls_key_t pthread_key_t |
||||
#define usbi_tls_key_create(key) pthread_key_create((key), NULL) |
||||
#define usbi_tls_key_get pthread_getspecific |
||||
#define usbi_tls_key_set pthread_setspecific |
||||
#define usbi_tls_key_delete pthread_key_delete |
||||
|
||||
int usbi_get_tid(void); |
||||
|
||||
#endif /* LIBUSB_THREADS_POSIX_H */ |
@ -0,0 +1,259 @@ |
||||
/*
|
||||
* libusb synchronization on Microsoft Windows |
||||
* |
||||
* Copyright © 2010 Michael Plante <michael.plante@gmail.com> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#include <objbase.h> |
||||
#include <errno.h> |
||||
|
||||
#include "libusbi.h" |
||||
|
||||
struct usbi_cond_perthread { |
||||
struct list_head list; |
||||
DWORD tid; |
||||
HANDLE event; |
||||
}; |
||||
|
||||
int usbi_mutex_static_lock(usbi_mutex_static_t *mutex) |
||||
{ |
||||
if (!mutex) |
||||
return EINVAL; |
||||
while (InterlockedExchange(mutex, 1) == 1) |
||||
SleepEx(0, TRUE); |
||||
return 0; |
||||
} |
||||
|
||||
int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex) |
||||
{ |
||||
if (!mutex) |
||||
return EINVAL; |
||||
InterlockedExchange(mutex, 0); |
||||
return 0; |
||||
} |
||||
|
||||
int usbi_mutex_init(usbi_mutex_t *mutex) |
||||
{ |
||||
if (!mutex) |
||||
return EINVAL; |
||||
*mutex = CreateMutex(NULL, FALSE, NULL); |
||||
if (!*mutex) |
||||
return ENOMEM; |
||||
return 0; |
||||
} |
||||
|
||||
int usbi_mutex_lock(usbi_mutex_t *mutex) |
||||
{ |
||||
DWORD result; |
||||
|
||||
if (!mutex) |
||||
return EINVAL; |
||||
result = WaitForSingleObject(*mutex, INFINITE); |
||||
if (result == WAIT_OBJECT_0 || result == WAIT_ABANDONED) |
||||
return 0; // acquired (ToDo: check that abandoned is ok)
|
||||
else |
||||
return EINVAL; // don't know how this would happen
|
||||
// so don't know proper errno
|
||||
} |
||||
|
||||
int usbi_mutex_unlock(usbi_mutex_t *mutex) |
||||
{ |
||||
if (!mutex) |
||||
return EINVAL; |
||||
if (ReleaseMutex(*mutex)) |
||||
return 0; |
||||
else |
||||
return EPERM; |
||||
} |
||||
|
||||
int usbi_mutex_trylock(usbi_mutex_t *mutex) |
||||
{ |
||||
DWORD result; |
||||
|
||||
if (!mutex) |
||||
return EINVAL; |
||||
result = WaitForSingleObject(*mutex, 0); |
||||
if (result == WAIT_OBJECT_0 || result == WAIT_ABANDONED) |
||||
return 0; // acquired (ToDo: check that abandoned is ok)
|
||||
else if (result == WAIT_TIMEOUT) |
||||
return EBUSY; |
||||
else |
||||
return EINVAL; // don't know how this would happen
|
||||
// so don't know proper error
|
||||
} |
||||
|
||||
int usbi_mutex_destroy(usbi_mutex_t *mutex) |
||||
{ |
||||
// It is not clear if CloseHandle failure is due to failure to unlock.
|
||||
// If so, this should be errno=EBUSY.
|
||||
if (!mutex || !CloseHandle(*mutex)) |
||||
return EINVAL; |
||||
*mutex = NULL; |
||||
return 0; |
||||
} |
||||
|
||||
int usbi_cond_init(usbi_cond_t *cond) |
||||
{ |
||||
if (!cond) |
||||
return EINVAL; |
||||
list_init(&cond->waiters); |
||||
list_init(&cond->not_waiting); |
||||
return 0; |
||||
} |
||||
|
||||
int usbi_cond_destroy(usbi_cond_t *cond) |
||||
{ |
||||
// This assumes no one is using this anymore. The check MAY NOT BE safe.
|
||||
struct usbi_cond_perthread *pos, *next_pos; |
||||
|
||||
if(!cond) |
||||
return EINVAL; |
||||
if (!list_empty(&cond->waiters)) |
||||
return EBUSY; // (!see above!)
|
||||
list_for_each_entry_safe(pos, next_pos, &cond->not_waiting, list, struct usbi_cond_perthread) { |
||||
CloseHandle(pos->event); |
||||
list_del(&pos->list); |
||||
free(pos); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
int usbi_cond_broadcast(usbi_cond_t *cond) |
||||
{ |
||||
// Assumes mutex is locked; this is not in keeping with POSIX spec, but
|
||||
// libusb does this anyway, so we simplify by not adding more sync
|
||||
// primitives to the CV definition!
|
||||
int fail = 0; |
||||
struct usbi_cond_perthread *pos; |
||||
|
||||
if (!cond) |
||||
return EINVAL; |
||||
list_for_each_entry(pos, &cond->waiters, list, struct usbi_cond_perthread) { |
||||
if (!SetEvent(pos->event)) |
||||
fail = 1; |
||||
} |
||||
// The wait function will remove its respective item from the list.
|
||||
return fail ? EINVAL : 0; |
||||
} |
||||
|
||||
__inline static int usbi_cond_intwait(usbi_cond_t *cond, |
||||
usbi_mutex_t *mutex, DWORD timeout_ms) |
||||
{ |
||||
struct usbi_cond_perthread *pos; |
||||
int r, found = 0; |
||||
DWORD r2, tid = GetCurrentThreadId(); |
||||
|
||||
if (!cond || !mutex) |
||||
return EINVAL; |
||||
list_for_each_entry(pos, &cond->not_waiting, list, struct usbi_cond_perthread) { |
||||
if(tid == pos->tid) { |
||||
found = 1; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (!found) { |
||||
pos = calloc(1, sizeof(struct usbi_cond_perthread)); |
||||
if (!pos) |
||||
return ENOMEM; // This errno is not POSIX-allowed.
|
||||
pos->tid = tid; |
||||
pos->event = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset.
|
||||
if (!pos->event) { |
||||
free(pos); |
||||
return ENOMEM; |
||||
} |
||||
list_add(&pos->list, &cond->not_waiting); |
||||
} |
||||
|
||||
list_del(&pos->list); // remove from not_waiting list.
|
||||
list_add(&pos->list, &cond->waiters); |
||||
|
||||
r = usbi_mutex_unlock(mutex); |
||||
if (r) |
||||
return r; |
||||
|
||||
r2 = WaitForSingleObject(pos->event, timeout_ms); |
||||
r = usbi_mutex_lock(mutex); |
||||
if (r) |
||||
return r; |
||||
|
||||
list_del(&pos->list); |
||||
list_add(&pos->list, &cond->not_waiting); |
||||
|
||||
if (r2 == WAIT_OBJECT_0) |
||||
return 0; |
||||
else if (r2 == WAIT_TIMEOUT) |
||||
return ETIMEDOUT; |
||||
else |
||||
return EINVAL; |
||||
} |
||||
// N.B.: usbi_cond_*wait() can also return ENOMEM, even though pthread_cond_*wait cannot!
|
||||
int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) |
||||
{ |
||||
return usbi_cond_intwait(cond, mutex, INFINITE); |
||||
} |
||||
|
||||
int usbi_cond_timedwait(usbi_cond_t *cond, |
||||
usbi_mutex_t *mutex, const struct timeval *tv) |
||||
{ |
||||
DWORD millis; |
||||
|
||||
millis = (DWORD)(tv->tv_sec * 1000) + (tv->tv_usec / 1000); |
||||
/* round up to next millisecond */ |
||||
if (tv->tv_usec % 1000) |
||||
millis++; |
||||
return usbi_cond_intwait(cond, mutex, millis); |
||||
} |
||||
|
||||
int usbi_tls_key_create(usbi_tls_key_t *key) |
||||
{ |
||||
if (!key) |
||||
return EINVAL; |
||||
*key = TlsAlloc(); |
||||
if (*key == TLS_OUT_OF_INDEXES) |
||||
return ENOMEM; |
||||
else |
||||
return 0; |
||||
} |
||||
|
||||
void *usbi_tls_key_get(usbi_tls_key_t key) |
||||
{ |
||||
return TlsGetValue(key); |
||||
} |
||||
|
||||
int usbi_tls_key_set(usbi_tls_key_t key, void *value) |
||||
{ |
||||
if (TlsSetValue(key, value)) |
||||
return 0; |
||||
else |
||||
return EINVAL; |
||||
} |
||||
|
||||
int usbi_tls_key_delete(usbi_tls_key_t key) |
||||
{ |
||||
if (TlsFree(key)) |
||||
return 0; |
||||
else |
||||
return EINVAL; |
||||
} |
||||
|
||||
int usbi_get_tid(void) |
||||
{ |
||||
return (int)GetCurrentThreadId(); |
||||
} |
@ -0,0 +1,76 @@ |
||||
/*
|
||||
* libusb synchronization on Microsoft Windows |
||||
* |
||||
* Copyright © 2010 Michael Plante <michael.plante@gmail.com> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#ifndef LIBUSB_THREADS_WINDOWS_H |
||||
#define LIBUSB_THREADS_WINDOWS_H |
||||
|
||||
#define usbi_mutex_static_t volatile LONG |
||||
#define USBI_MUTEX_INITIALIZER 0 |
||||
|
||||
#define usbi_mutex_t HANDLE |
||||
|
||||
typedef struct usbi_cond { |
||||
// Every time a thread touches the CV, it winds up in one of these lists.
|
||||
// It stays there until the CV is destroyed, even if the thread terminates.
|
||||
struct list_head waiters; |
||||
struct list_head not_waiting; |
||||
} usbi_cond_t; |
||||
|
||||
// We *were* getting timespec from pthread.h:
|
||||
#if (!defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED)) |
||||
#define HAVE_STRUCT_TIMESPEC 1 |
||||
#define _TIMESPEC_DEFINED 1 |
||||
struct timespec { |
||||
long tv_sec; |
||||
long tv_nsec; |
||||
}; |
||||
#endif /* HAVE_STRUCT_TIMESPEC | _TIMESPEC_DEFINED */ |
||||
|
||||
// We *were* getting ETIMEDOUT from pthread.h:
|
||||
#ifndef ETIMEDOUT |
||||
# define ETIMEDOUT 10060 /* This is the value in winsock.h. */ |
||||
#endif |
||||
|
||||
#define usbi_tls_key_t DWORD |
||||
|
||||
int usbi_mutex_static_lock(usbi_mutex_static_t *mutex); |
||||
int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex); |
||||
|
||||
int usbi_mutex_init(usbi_mutex_t *mutex); |
||||
int usbi_mutex_lock(usbi_mutex_t *mutex); |
||||
int usbi_mutex_unlock(usbi_mutex_t *mutex); |
||||
int usbi_mutex_trylock(usbi_mutex_t *mutex); |
||||
int usbi_mutex_destroy(usbi_mutex_t *mutex); |
||||
|
||||
int usbi_cond_init(usbi_cond_t *cond); |
||||
int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex); |
||||
int usbi_cond_timedwait(usbi_cond_t *cond, |
||||
usbi_mutex_t *mutex, const struct timeval *tv); |
||||
int usbi_cond_broadcast(usbi_cond_t *cond); |
||||
int usbi_cond_destroy(usbi_cond_t *cond); |
||||
|
||||
int usbi_tls_key_create(usbi_tls_key_t *key); |
||||
void *usbi_tls_key_get(usbi_tls_key_t key); |
||||
int usbi_tls_key_set(usbi_tls_key_t key, void *value); |
||||
int usbi_tls_key_delete(usbi_tls_key_t key); |
||||
|
||||
int usbi_get_tid(void); |
||||
|
||||
#endif /* LIBUSB_THREADS_WINDOWS_H */ |
@ -0,0 +1,899 @@ |
||||
/*
|
||||
* Windows CE backend for libusb 1.0 |
||||
* Copyright © 2011-2013 RealVNC Ltd. |
||||
* Large portions taken from Windows backend, which is |
||||
* Copyright © 2009-2010 Pete Batard <pbatard@gmail.com> |
||||
* With contributions from Michael Plante, Orin Eman et al. |
||||
* Parts of this code adapted from libusb-win32-v1 by Stephan Meyer |
||||
* Major code testing contribution by Xiaofan Chen |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#include <stdint.h> |
||||
#include <inttypes.h> |
||||
|
||||
#include "libusbi.h" |
||||
#include "wince_usb.h" |
||||
|
||||
// Global variables
|
||||
int windows_version = WINDOWS_CE; |
||||
static uint64_t hires_frequency, hires_ticks_to_ps; |
||||
static HANDLE driver_handle = INVALID_HANDLE_VALUE; |
||||
static int concurrent_usage = -1; |
||||
|
||||
/*
|
||||
* Converts a windows error to human readable string |
||||
* uses retval as errorcode, or, if 0, use GetLastError() |
||||
*/ |
||||
#if defined(ENABLE_LOGGING) |
||||
static const char *windows_error_str(DWORD error_code) |
||||
{ |
||||
static TCHAR wErr_string[ERR_BUFFER_SIZE]; |
||||
static char err_string[ERR_BUFFER_SIZE]; |
||||
|
||||
DWORD size; |
||||
int len; |
||||
|
||||
if (error_code == 0) |
||||
error_code = GetLastError(); |
||||
|
||||
len = sprintf(err_string, "[%u] ", (unsigned int)error_code); |
||||
|
||||
size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, |
||||
NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
||||
wErr_string, ERR_BUFFER_SIZE, NULL); |
||||
if (size == 0) { |
||||
DWORD format_error = GetLastError(); |
||||
if (format_error) |
||||
snprintf(err_string, ERR_BUFFER_SIZE, |
||||
"Windows error code %u (FormatMessage error code %u)", |
||||
(unsigned int)error_code, (unsigned int)format_error); |
||||
else |
||||
snprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", (unsigned int)error_code); |
||||
} else { |
||||
// Remove CR/LF terminators, if present
|
||||
size_t pos = size - 2; |
||||
if (wErr_string[pos] == 0x0D) |
||||
wErr_string[pos] = 0; |
||||
|
||||
if (!WideCharToMultiByte(CP_ACP, 0, wErr_string, -1, &err_string[len], ERR_BUFFER_SIZE - len, NULL, NULL)) |
||||
strcpy(err_string, "Unable to convert error string"); |
||||
} |
||||
|
||||
return err_string; |
||||
} |
||||
#endif |
||||
|
||||
static struct wince_device_priv *_device_priv(struct libusb_device *dev) |
||||
{ |
||||
return (struct wince_device_priv *)dev->os_priv; |
||||
} |
||||
|
||||
// ceusbkwrapper to libusb error code mapping
|
||||
static int translate_driver_error(DWORD error) |
||||
{ |
||||
switch (error) { |
||||
case ERROR_INVALID_PARAMETER: |
||||
return LIBUSB_ERROR_INVALID_PARAM; |
||||
case ERROR_CALL_NOT_IMPLEMENTED: |
||||
case ERROR_NOT_SUPPORTED: |
||||
return LIBUSB_ERROR_NOT_SUPPORTED; |
||||
case ERROR_NOT_ENOUGH_MEMORY: |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
case ERROR_INVALID_HANDLE: |
||||
return LIBUSB_ERROR_NO_DEVICE; |
||||
case ERROR_BUSY: |
||||
return LIBUSB_ERROR_BUSY; |
||||
|
||||
// Error codes that are either unexpected, or have
|
||||
// no suitable LIBUSB_ERROR equivalent.
|
||||
case ERROR_CANCELLED: |
||||
case ERROR_INTERNAL_ERROR: |
||||
default: |
||||
return LIBUSB_ERROR_OTHER; |
||||
} |
||||
} |
||||
|
||||
static int init_dllimports(void) |
||||
{ |
||||
DLL_GET_HANDLE(ceusbkwrapper); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwOpenDriver, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwGetDeviceList, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwReleaseDeviceList, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwGetDeviceAddress, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwGetDeviceDescriptor, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwGetConfigDescriptor, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwCloseDriver, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwCancelTransfer, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwIssueControlTransfer, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwClaimInterface, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwReleaseInterface, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwSetInterfaceAlternateSetting, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwClearHaltHost, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwClearHaltDevice, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwGetConfig, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwSetConfig, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwResetDevice, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwKernelDriverActive, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwAttachKernelDriver, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwDetachKernelDriver, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwIssueBulkTransfer, TRUE); |
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwIsPipeHalted, TRUE); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static void exit_dllimports(void) |
||||
{ |
||||
DLL_FREE_HANDLE(ceusbkwrapper); |
||||
} |
||||
|
||||
static int init_device( |
||||
struct libusb_device *dev, UKW_DEVICE drv_dev, |
||||
unsigned char bus_addr, unsigned char dev_addr) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(dev); |
||||
int r = LIBUSB_SUCCESS; |
||||
|
||||
dev->bus_number = bus_addr; |
||||
dev->device_address = dev_addr; |
||||
priv->dev = drv_dev; |
||||
|
||||
if (!UkwGetDeviceDescriptor(priv->dev, &(priv->desc))) |
||||
r = translate_driver_error(GetLastError()); |
||||
|
||||
return r; |
||||
} |
||||
|
||||
// Internal API functions
|
||||
static int wince_init(struct libusb_context *ctx) |
||||
{ |
||||
int r = LIBUSB_ERROR_OTHER; |
||||
HANDLE semaphore; |
||||
LARGE_INTEGER li_frequency; |
||||
TCHAR sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
|
||||
|
||||
_stprintf(sem_name, _T("libusb_init%08X"), (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF)); |
||||
semaphore = CreateSemaphore(NULL, 1, 1, sem_name); |
||||
if (semaphore == NULL) { |
||||
usbi_err(ctx, "could not create semaphore: %s", windows_error_str(0)); |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
} |
||||
|
||||
// A successful wait brings our semaphore count to 0 (unsignaled)
|
||||
// => any concurent wait stalls until the semaphore's release
|
||||
if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) { |
||||
usbi_err(ctx, "failure to access semaphore: %s", windows_error_str(0)); |
||||
CloseHandle(semaphore); |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
} |
||||
|
||||
// NB: concurrent usage supposes that init calls are equally balanced with
|
||||
// exit calls. If init is called more than exit, we will not exit properly
|
||||
if ( ++concurrent_usage == 0 ) { // First init?
|
||||
// Initialize pollable file descriptors
|
||||
init_polling(); |
||||
|
||||
// Load DLL imports
|
||||
if (init_dllimports() != LIBUSB_SUCCESS) { |
||||
usbi_err(ctx, "could not resolve DLL functions"); |
||||
r = LIBUSB_ERROR_NOT_SUPPORTED; |
||||
goto init_exit; |
||||
} |
||||
|
||||
// try to open a handle to the driver
|
||||
driver_handle = UkwOpenDriver(); |
||||
if (driver_handle == INVALID_HANDLE_VALUE) { |
||||
usbi_err(ctx, "could not connect to driver"); |
||||
r = LIBUSB_ERROR_NOT_SUPPORTED; |
||||
goto init_exit; |
||||
} |
||||
|
||||
// find out if we have access to a monotonic (hires) timer
|
||||
if (QueryPerformanceFrequency(&li_frequency)) { |
||||
hires_frequency = li_frequency.QuadPart; |
||||
// The hires frequency can go as high as 4 GHz, so we'll use a conversion
|
||||
// to picoseconds to compute the tv_nsecs part in clock_gettime
|
||||
hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency; |
||||
usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency); |
||||
} else { |
||||
usbi_dbg("no hires timer available on this platform"); |
||||
hires_frequency = 0; |
||||
hires_ticks_to_ps = UINT64_C(0); |
||||
} |
||||
} |
||||
// At this stage, either we went through full init successfully, or didn't need to
|
||||
r = LIBUSB_SUCCESS; |
||||
|
||||
init_exit: // Holds semaphore here.
|
||||
if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
|
||||
exit_dllimports(); |
||||
exit_polling(); |
||||
|
||||
if (driver_handle != INVALID_HANDLE_VALUE) { |
||||
UkwCloseDriver(driver_handle); |
||||
driver_handle = INVALID_HANDLE_VALUE; |
||||
} |
||||
} |
||||
|
||||
if (r != LIBUSB_SUCCESS) |
||||
--concurrent_usage; // Not expected to call libusb_exit if we failed.
|
||||
|
||||
ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
|
||||
CloseHandle(semaphore); |
||||
return r; |
||||
} |
||||
|
||||
static void wince_exit(void) |
||||
{ |
||||
HANDLE semaphore; |
||||
TCHAR sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
|
||||
|
||||
_stprintf(sem_name, _T("libusb_init%08X"), (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF)); |
||||
semaphore = CreateSemaphore(NULL, 1, 1, sem_name); |
||||
if (semaphore == NULL) |
||||
return; |
||||
|
||||
// A successful wait brings our semaphore count to 0 (unsignaled)
|
||||
// => any concurent wait stalls until the semaphore release
|
||||
if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) { |
||||
CloseHandle(semaphore); |
||||
return; |
||||
} |
||||
|
||||
// Only works if exits and inits are balanced exactly
|
||||
if (--concurrent_usage < 0) { // Last exit
|
||||
exit_dllimports(); |
||||
exit_polling(); |
||||
|
||||
if (driver_handle != INVALID_HANDLE_VALUE) { |
||||
UkwCloseDriver(driver_handle); |
||||
driver_handle = INVALID_HANDLE_VALUE; |
||||
} |
||||
} |
||||
|
||||
ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
|
||||
CloseHandle(semaphore); |
||||
} |
||||
|
||||
static int wince_get_device_list( |
||||
struct libusb_context *ctx, |
||||
struct discovered_devs **discdevs) |
||||
{ |
||||
UKW_DEVICE devices[MAX_DEVICE_COUNT]; |
||||
struct discovered_devs *new_devices = *discdevs; |
||||
DWORD count = 0, i; |
||||
struct libusb_device *dev = NULL; |
||||
unsigned char bus_addr, dev_addr; |
||||
unsigned long session_id; |
||||
BOOL success; |
||||
DWORD release_list_offset = 0; |
||||
int r = LIBUSB_SUCCESS; |
||||
|
||||
success = UkwGetDeviceList(driver_handle, devices, MAX_DEVICE_COUNT, &count); |
||||
if (!success) { |
||||
int libusbErr = translate_driver_error(GetLastError()); |
||||
usbi_err(ctx, "could not get devices: %s", windows_error_str(0)); |
||||
return libusbErr; |
||||
} |
||||
|
||||
for (i = 0; i < count; ++i) { |
||||
release_list_offset = i; |
||||
success = UkwGetDeviceAddress(devices[i], &bus_addr, &dev_addr, &session_id); |
||||
if (!success) { |
||||
r = translate_driver_error(GetLastError()); |
||||
usbi_err(ctx, "could not get device address for %u: %s", (unsigned int)i, windows_error_str(0)); |
||||
goto err_out; |
||||
} |
||||
|
||||
dev = usbi_get_device_by_session_id(ctx, session_id); |
||||
if (dev) { |
||||
usbi_dbg("using existing device for %u/%u (session %lu)", |
||||
bus_addr, dev_addr, session_id); |
||||
// Release just this element in the device list (as we already hold a
|
||||
// reference to it).
|
||||
UkwReleaseDeviceList(driver_handle, &devices[i], 1); |
||||
release_list_offset++; |
||||
} else { |
||||
usbi_dbg("allocating new device for %u/%u (session %lu)", |
||||
bus_addr, dev_addr, session_id); |
||||
dev = usbi_alloc_device(ctx, session_id); |
||||
if (!dev) { |
||||
r = LIBUSB_ERROR_NO_MEM; |
||||
goto err_out; |
||||
} |
||||
|
||||
r = init_device(dev, devices[i], bus_addr, dev_addr); |
||||
if (r < 0) |
||||
goto err_out; |
||||
|
||||
r = usbi_sanitize_device(dev); |
||||
if (r < 0) |
||||
goto err_out; |
||||
} |
||||
|
||||
new_devices = discovered_devs_append(new_devices, dev); |
||||
if (!discdevs) { |
||||
r = LIBUSB_ERROR_NO_MEM; |
||||
goto err_out; |
||||
} |
||||
|
||||
libusb_unref_device(dev); |
||||
} |
||||
|
||||
*discdevs = new_devices; |
||||
return r; |
||||
err_out: |
||||
*discdevs = new_devices; |
||||
libusb_unref_device(dev); |
||||
// Release the remainder of the unprocessed device list.
|
||||
// The devices added to new_devices already will still be passed up to libusb,
|
||||
// which can dispose of them at its leisure.
|
||||
UkwReleaseDeviceList(driver_handle, &devices[release_list_offset], count - release_list_offset); |
||||
return r; |
||||
} |
||||
|
||||
static int wince_open(struct libusb_device_handle *handle) |
||||
{ |
||||
// Nothing to do to open devices as a handle to it has
|
||||
// been retrieved by wince_get_device_list
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static void wince_close(struct libusb_device_handle *handle) |
||||
{ |
||||
// Nothing to do as wince_open does nothing.
|
||||
} |
||||
|
||||
static int wince_get_device_descriptor( |
||||
struct libusb_device *device, |
||||
unsigned char *buffer, int *host_endian) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(device); |
||||
|
||||
*host_endian = 1; |
||||
memcpy(buffer, &priv->desc, DEVICE_DESC_LENGTH); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int wince_get_active_config_descriptor( |
||||
struct libusb_device *device, |
||||
unsigned char *buffer, size_t len, int *host_endian) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(device); |
||||
DWORD actualSize = len; |
||||
|
||||
*host_endian = 0; |
||||
if (!UkwGetConfigDescriptor(priv->dev, UKW_ACTIVE_CONFIGURATION, buffer, len, &actualSize)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
return actualSize; |
||||
} |
||||
|
||||
static int wince_get_config_descriptor( |
||||
struct libusb_device *device, |
||||
uint8_t config_index, |
||||
unsigned char *buffer, size_t len, int *host_endian) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(device); |
||||
DWORD actualSize = len; |
||||
|
||||
*host_endian = 0; |
||||
if (!UkwGetConfigDescriptor(priv->dev, config_index, buffer, len, &actualSize)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
return actualSize; |
||||
} |
||||
|
||||
static int wince_get_configuration( |
||||
struct libusb_device_handle *handle, |
||||
int *config) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(handle->dev); |
||||
UCHAR cv = 0; |
||||
|
||||
if (!UkwGetConfig(priv->dev, &cv)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
(*config) = cv; |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int wince_set_configuration( |
||||
struct libusb_device_handle *handle, |
||||
int config) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(handle->dev); |
||||
// Setting configuration 0 places the device in Address state.
|
||||
// This should correspond to the "unconfigured state" required by
|
||||
// libusb when the specified configuration is -1.
|
||||
UCHAR cv = (config < 0) ? 0 : config; |
||||
if (!UkwSetConfig(priv->dev, cv)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int wince_claim_interface( |
||||
struct libusb_device_handle *handle, |
||||
int interface_number) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(handle->dev); |
||||
|
||||
if (!UkwClaimInterface(priv->dev, interface_number)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int wince_release_interface( |
||||
struct libusb_device_handle *handle, |
||||
int interface_number) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(handle->dev); |
||||
|
||||
if (!UkwSetInterfaceAlternateSetting(priv->dev, interface_number, 0)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
if (!UkwReleaseInterface(priv->dev, interface_number)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int wince_set_interface_altsetting( |
||||
struct libusb_device_handle *handle, |
||||
int interface_number, int altsetting) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(handle->dev); |
||||
|
||||
if (!UkwSetInterfaceAlternateSetting(priv->dev, interface_number, altsetting)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int wince_clear_halt( |
||||
struct libusb_device_handle *handle, |
||||
unsigned char endpoint) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(handle->dev); |
||||
|
||||
if (!UkwClearHaltHost(priv->dev, endpoint)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
if (!UkwClearHaltDevice(priv->dev, endpoint)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int wince_reset_device( |
||||
struct libusb_device_handle *handle) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(handle->dev); |
||||
|
||||
if (!UkwResetDevice(priv->dev)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int wince_kernel_driver_active( |
||||
struct libusb_device_handle *handle, |
||||
int interface_number) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(handle->dev); |
||||
BOOL result = FALSE; |
||||
|
||||
if (!UkwKernelDriverActive(priv->dev, interface_number, &result)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
return result ? 1 : 0; |
||||
} |
||||
|
||||
static int wince_detach_kernel_driver( |
||||
struct libusb_device_handle *handle, |
||||
int interface_number) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(handle->dev); |
||||
|
||||
if (!UkwDetachKernelDriver(priv->dev, interface_number)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int wince_attach_kernel_driver( |
||||
struct libusb_device_handle *handle, |
||||
int interface_number) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(handle->dev); |
||||
|
||||
if (!UkwAttachKernelDriver(priv->dev, interface_number)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static void wince_destroy_device(struct libusb_device *dev) |
||||
{ |
||||
struct wince_device_priv *priv = _device_priv(dev); |
||||
|
||||
UkwReleaseDeviceList(driver_handle, &priv->dev, 1); |
||||
} |
||||
|
||||
static void wince_clear_transfer_priv(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct wince_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); |
||||
struct winfd wfd = fd_to_winfd(transfer_priv->pollable_fd.fd); |
||||
|
||||
// No need to cancel transfer as it is either complete or abandoned
|
||||
wfd.itransfer = NULL; |
||||
CloseHandle(wfd.handle); |
||||
usbi_free_fd(&transfer_priv->pollable_fd); |
||||
} |
||||
|
||||
static int wince_cancel_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev); |
||||
struct wince_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); |
||||
|
||||
if (!UkwCancelTransfer(priv->dev, transfer_priv->pollable_fd.overlapped, UKW_TF_NO_WAIT)) |
||||
return translate_driver_error(GetLastError()); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); |
||||
struct wince_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); |
||||
struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev); |
||||
BOOL direction_in, ret; |
||||
struct winfd wfd; |
||||
DWORD flags; |
||||
HANDLE eventHandle; |
||||
PUKW_CONTROL_HEADER setup = NULL; |
||||
const BOOL control_transfer = transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL; |
||||
|
||||
transfer_priv->pollable_fd = INVALID_WINFD; |
||||
if (control_transfer) { |
||||
setup = (PUKW_CONTROL_HEADER) transfer->buffer; |
||||
direction_in = setup->bmRequestType & LIBUSB_ENDPOINT_IN; |
||||
} else { |
||||
direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN; |
||||
} |
||||
flags = direction_in ? UKW_TF_IN_TRANSFER : UKW_TF_OUT_TRANSFER; |
||||
flags |= UKW_TF_SHORT_TRANSFER_OK; |
||||
|
||||
eventHandle = CreateEvent(NULL, FALSE, FALSE, NULL); |
||||
if (eventHandle == NULL) { |
||||
usbi_err(ctx, "Failed to create event for async transfer"); |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
} |
||||
|
||||
wfd = usbi_create_fd(eventHandle, direction_in ? RW_READ : RW_WRITE, itransfer, &wince_cancel_transfer); |
||||
if (wfd.fd < 0) { |
||||
CloseHandle(eventHandle); |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
} |
||||
|
||||
transfer_priv->pollable_fd = wfd; |
||||
if (control_transfer) { |
||||
// Split out control setup header and data buffer
|
||||
DWORD bufLen = transfer->length - sizeof(UKW_CONTROL_HEADER); |
||||
PVOID buf = (PVOID) &transfer->buffer[sizeof(UKW_CONTROL_HEADER)]; |
||||
|
||||
ret = UkwIssueControlTransfer(priv->dev, flags, setup, buf, bufLen, &transfer->actual_length, wfd.overlapped); |
||||
} else { |
||||
ret = UkwIssueBulkTransfer(priv->dev, flags, transfer->endpoint, transfer->buffer, |
||||
transfer->length, &transfer->actual_length, wfd.overlapped); |
||||
} |
||||
|
||||
if (!ret) { |
||||
int libusbErr = translate_driver_error(GetLastError()); |
||||
usbi_err(ctx, "UkwIssue%sTransfer failed: error %u", |
||||
control_transfer ? "Control" : "Bulk", (unsigned int)GetLastError()); |
||||
wince_clear_transfer_priv(itransfer); |
||||
return libusbErr; |
||||
} |
||||
usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, direction_in ? POLLIN : POLLOUT); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int wince_submit_iso_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
return LIBUSB_ERROR_NOT_SUPPORTED; |
||||
} |
||||
|
||||
static int wince_submit_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
|
||||
switch (transfer->type) { |
||||
case LIBUSB_TRANSFER_TYPE_CONTROL: |
||||
case LIBUSB_TRANSFER_TYPE_BULK: |
||||
case LIBUSB_TRANSFER_TYPE_INTERRUPT: |
||||
return wince_submit_control_or_bulk_transfer(itransfer); |
||||
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: |
||||
return wince_submit_iso_transfer(itransfer); |
||||
case LIBUSB_TRANSFER_TYPE_BULK_STREAM: |
||||
return LIBUSB_ERROR_NOT_SUPPORTED; |
||||
default: |
||||
usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); |
||||
return LIBUSB_ERROR_INVALID_PARAM; |
||||
} |
||||
} |
||||
|
||||
static void wince_transfer_callback( |
||||
struct usbi_transfer *itransfer, |
||||
uint32_t io_result, uint32_t io_size) |
||||
{ |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
struct wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer); |
||||
struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev); |
||||
int status; |
||||
|
||||
usbi_dbg("handling I/O completion with errcode %u", io_result); |
||||
|
||||
if (io_result == ERROR_NOT_SUPPORTED && |
||||
transfer->type != LIBUSB_TRANSFER_TYPE_CONTROL) { |
||||
/* For functional stalls, the WinCE USB layer (and therefore the USB Kernel Wrapper
|
||||
* Driver) will report USB_ERROR_STALL/ERROR_NOT_SUPPORTED in situations where the |
||||
* endpoint isn't actually stalled. |
||||
* |
||||
* One example of this is that some devices will occasionally fail to reply to an IN |
||||
* token. The WinCE USB layer carries on with the transaction until it is completed |
||||
* (or cancelled) but then completes it with USB_ERROR_STALL. |
||||
* |
||||
* This code therefore needs to confirm that there really is a stall error, by both |
||||
* checking the pipe status and requesting the endpoint status from the device. |
||||
*/ |
||||
BOOL halted = FALSE; |
||||
usbi_dbg("checking I/O completion with errcode ERROR_NOT_SUPPORTED is really a stall"); |
||||
if (UkwIsPipeHalted(priv->dev, transfer->endpoint, &halted)) { |
||||
/* Pipe status retrieved, so now request endpoint status by sending a GET_STATUS
|
||||
* control request to the device. This is done synchronously, which is a bit |
||||
* naughty, but this is a special corner case. |
||||
*/ |
||||
WORD wStatus = 0; |
||||
DWORD written = 0; |
||||
UKW_CONTROL_HEADER ctrlHeader; |
||||
ctrlHeader.bmRequestType = LIBUSB_REQUEST_TYPE_STANDARD | |
||||
LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_ENDPOINT; |
||||
ctrlHeader.bRequest = LIBUSB_REQUEST_GET_STATUS; |
||||
ctrlHeader.wValue = 0; |
||||
ctrlHeader.wIndex = transfer->endpoint; |
||||
ctrlHeader.wLength = sizeof(wStatus); |
||||
if (UkwIssueControlTransfer(priv->dev, |
||||
UKW_TF_IN_TRANSFER | UKW_TF_SEND_TO_ENDPOINT, |
||||
&ctrlHeader, &wStatus, sizeof(wStatus), &written, NULL)) { |
||||
if (written == sizeof(wStatus) && |
||||
(wStatus & STATUS_HALT_FLAG) == 0) { |
||||
if (!halted || UkwClearHaltHost(priv->dev, transfer->endpoint)) { |
||||
usbi_dbg("Endpoint doesn't appear to be stalled, overriding error with success"); |
||||
io_result = ERROR_SUCCESS; |
||||
} else { |
||||
usbi_dbg("Endpoint doesn't appear to be stalled, but the host is halted, changing error"); |
||||
io_result = ERROR_IO_DEVICE; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
switch(io_result) { |
||||
case ERROR_SUCCESS: |
||||
itransfer->transferred += io_size; |
||||
status = LIBUSB_TRANSFER_COMPLETED; |
||||
break; |
||||
case ERROR_CANCELLED: |
||||
usbi_dbg("detected transfer cancel"); |
||||
status = LIBUSB_TRANSFER_CANCELLED; |
||||
break; |
||||
case ERROR_NOT_SUPPORTED: |
||||
case ERROR_GEN_FAILURE: |
||||
usbi_dbg("detected endpoint stall"); |
||||
status = LIBUSB_TRANSFER_STALL; |
||||
break; |
||||
case ERROR_SEM_TIMEOUT: |
||||
usbi_dbg("detected semaphore timeout"); |
||||
status = LIBUSB_TRANSFER_TIMED_OUT; |
||||
break; |
||||
case ERROR_OPERATION_ABORTED: |
||||
usbi_dbg("detected operation aborted"); |
||||
status = LIBUSB_TRANSFER_CANCELLED; |
||||
break; |
||||
default: |
||||
usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error: %s", windows_error_str(io_result)); |
||||
status = LIBUSB_TRANSFER_ERROR; |
||||
break; |
||||
} |
||||
|
||||
wince_clear_transfer_priv(itransfer); |
||||
if (status == LIBUSB_TRANSFER_CANCELLED) |
||||
usbi_handle_transfer_cancellation(itransfer); |
||||
else |
||||
usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status); |
||||
} |
||||
|
||||
static void wince_handle_callback( |
||||
struct usbi_transfer *itransfer, |
||||
uint32_t io_result, uint32_t io_size) |
||||
{ |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
|
||||
switch (transfer->type) { |
||||
case LIBUSB_TRANSFER_TYPE_CONTROL: |
||||
case LIBUSB_TRANSFER_TYPE_BULK: |
||||
case LIBUSB_TRANSFER_TYPE_INTERRUPT: |
||||
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: |
||||
wince_transfer_callback (itransfer, io_result, io_size); |
||||
break; |
||||
case LIBUSB_TRANSFER_TYPE_BULK_STREAM: |
||||
break; |
||||
default: |
||||
usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); |
||||
} |
||||
} |
||||
|
||||
static int wince_handle_events( |
||||
struct libusb_context *ctx, |
||||
struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) |
||||
{ |
||||
struct wince_transfer_priv* transfer_priv = NULL; |
||||
POLL_NFDS_TYPE i = 0; |
||||
BOOL found = FALSE; |
||||
struct usbi_transfer *transfer; |
||||
DWORD io_size, io_result; |
||||
int r = LIBUSB_SUCCESS; |
||||
|
||||
usbi_mutex_lock(&ctx->open_devs_lock); |
||||
for (i = 0; i < nfds && num_ready > 0; i++) { |
||||
|
||||
usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents); |
||||
|
||||
if (!fds[i].revents) |
||||
continue; |
||||
|
||||
num_ready--; |
||||
|
||||
// Because a Windows OVERLAPPED is used for poll emulation,
|
||||
// a pollable fd is created and stored with each transfer
|
||||
usbi_mutex_lock(&ctx->flying_transfers_lock); |
||||
list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { |
||||
transfer_priv = usbi_transfer_get_os_priv(transfer); |
||||
if (transfer_priv->pollable_fd.fd == fds[i].fd) { |
||||
found = TRUE; |
||||
break; |
||||
} |
||||
} |
||||
usbi_mutex_unlock(&ctx->flying_transfers_lock); |
||||
|
||||
if (found && HasOverlappedIoCompleted(transfer_priv->pollable_fd.overlapped)) { |
||||
io_result = (DWORD)transfer_priv->pollable_fd.overlapped->Internal; |
||||
io_size = (DWORD)transfer_priv->pollable_fd.overlapped->InternalHigh; |
||||
usbi_remove_pollfd(ctx, transfer_priv->pollable_fd.fd); |
||||
// let handle_callback free the event using the transfer wfd
|
||||
// If you don't use the transfer wfd, you run a risk of trying to free a
|
||||
// newly allocated wfd that took the place of the one from the transfer.
|
||||
wince_handle_callback(transfer, io_result, io_size); |
||||
} else if (found) { |
||||
usbi_err(ctx, "matching transfer for fd %d has not completed", fds[i]); |
||||
r = LIBUSB_ERROR_OTHER; |
||||
break; |
||||
} else { |
||||
usbi_err(ctx, "could not find a matching transfer for fd %d", fds[i]); |
||||
r = LIBUSB_ERROR_NOT_FOUND; |
||||
break; |
||||
} |
||||
} |
||||
usbi_mutex_unlock(&ctx->open_devs_lock); |
||||
|
||||
return r; |
||||
} |
||||
|
||||
/*
|
||||
* Monotonic and real time functions |
||||
*/ |
||||
static int wince_clock_gettime(int clk_id, struct timespec *tp) |
||||
{ |
||||
LARGE_INTEGER hires_counter; |
||||
ULARGE_INTEGER rtime; |
||||
FILETIME filetime; |
||||
SYSTEMTIME st; |
||||
|
||||
switch(clk_id) { |
||||
case USBI_CLOCK_MONOTONIC: |
||||
if (hires_frequency != 0 && QueryPerformanceCounter(&hires_counter)) { |
||||
tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency); |
||||
tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency) / 1000) * hires_ticks_to_ps); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
// Fall through and return real-time if monotonic read failed or was not detected @ init
|
||||
case USBI_CLOCK_REALTIME: |
||||
// We follow http://msdn.microsoft.com/en-us/library/ms724928%28VS.85%29.aspx
|
||||
// with a predef epoch time to have an epoch that starts at 1970.01.01 00:00
|
||||
// Note however that our resolution is bounded by the Windows system time
|
||||
// functions and is at best of the order of 1 ms (or, usually, worse)
|
||||
GetSystemTime(&st); |
||||
SystemTimeToFileTime(&st, &filetime); |
||||
rtime.LowPart = filetime.dwLowDateTime; |
||||
rtime.HighPart = filetime.dwHighDateTime; |
||||
rtime.QuadPart -= EPOCH_TIME; |
||||
tp->tv_sec = (long)(rtime.QuadPart / 10000000); |
||||
tp->tv_nsec = (long)((rtime.QuadPart % 10000000)*100); |
||||
return LIBUSB_SUCCESS; |
||||
default: |
||||
return LIBUSB_ERROR_INVALID_PARAM; |
||||
} |
||||
} |
||||
|
||||
const struct usbi_os_backend wince_backend = { |
||||
"Windows CE", |
||||
0, |
||||
wince_init, |
||||
wince_exit, |
||||
|
||||
wince_get_device_list, |
||||
NULL, /* hotplug_poll */ |
||||
wince_open, |
||||
wince_close, |
||||
|
||||
wince_get_device_descriptor, |
||||
wince_get_active_config_descriptor, |
||||
wince_get_config_descriptor, |
||||
NULL, /* get_config_descriptor_by_value() */ |
||||
|
||||
wince_get_configuration, |
||||
wince_set_configuration, |
||||
wince_claim_interface, |
||||
wince_release_interface, |
||||
|
||||
wince_set_interface_altsetting, |
||||
wince_clear_halt, |
||||
wince_reset_device, |
||||
|
||||
NULL, /* alloc_streams */ |
||||
NULL, /* free_streams */ |
||||
|
||||
NULL, /* dev_mem_alloc() */ |
||||
NULL, /* dev_mem_free() */ |
||||
|
||||
wince_kernel_driver_active, |
||||
wince_detach_kernel_driver, |
||||
wince_attach_kernel_driver, |
||||
|
||||
wince_destroy_device, |
||||
|
||||
wince_submit_transfer, |
||||
wince_cancel_transfer, |
||||
wince_clear_transfer_priv, |
||||
|
||||
wince_handle_events, |
||||
NULL, /* handle_transfer_completion() */ |
||||
|
||||
wince_clock_gettime, |
||||
sizeof(struct wince_device_priv), |
||||
0, |
||||
sizeof(struct wince_transfer_priv), |
||||
}; |
@ -0,0 +1,126 @@ |
||||
/*
|
||||
* Windows CE backend for libusb 1.0 |
||||
* Copyright © 2011-2013 RealVNC Ltd. |
||||
* Portions taken from Windows backend, which is |
||||
* Copyright © 2009-2010 Pete Batard <pbatard@gmail.com> |
||||
* With contributions from Michael Plante, Orin Eman et al. |
||||
* Parts of this code adapted from libusb-win32-v1 by Stephan Meyer |
||||
* Major code testing contribution by Xiaofan Chen |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
#pragma once |
||||
|
||||
#include "windows_common.h" |
||||
|
||||
#include <windows.h> |
||||
#include "poll_windows.h" |
||||
|
||||
#define MAX_DEVICE_COUNT 256 |
||||
|
||||
// This is a modified dump of the types in the ceusbkwrapper.h library header
|
||||
// with functions transformed into extern pointers.
|
||||
//
|
||||
// This backend dynamically loads ceusbkwrapper.dll and doesn't include
|
||||
// ceusbkwrapper.h directly to simplify the build process. The kernel
|
||||
// side wrapper driver is built using the platform image build tools,
|
||||
// which makes it difficult to reference directly from the libusb build
|
||||
// system.
|
||||
struct UKW_DEVICE_PRIV; |
||||
typedef struct UKW_DEVICE_PRIV *UKW_DEVICE; |
||||
typedef UKW_DEVICE *PUKW_DEVICE, *LPUKW_DEVICE; |
||||
|
||||
typedef struct { |
||||
UINT8 bLength; |
||||
UINT8 bDescriptorType; |
||||
UINT16 bcdUSB; |
||||
UINT8 bDeviceClass; |
||||
UINT8 bDeviceSubClass; |
||||
UINT8 bDeviceProtocol; |
||||
UINT8 bMaxPacketSize0; |
||||
UINT16 idVendor; |
||||
UINT16 idProduct; |
||||
UINT16 bcdDevice; |
||||
UINT8 iManufacturer; |
||||
UINT8 iProduct; |
||||
UINT8 iSerialNumber; |
||||
UINT8 bNumConfigurations; |
||||
} UKW_DEVICE_DESCRIPTOR, *PUKW_DEVICE_DESCRIPTOR, *LPUKW_DEVICE_DESCRIPTOR; |
||||
|
||||
typedef struct { |
||||
UINT8 bmRequestType; |
||||
UINT8 bRequest; |
||||
UINT16 wValue; |
||||
UINT16 wIndex; |
||||
UINT16 wLength; |
||||
} UKW_CONTROL_HEADER, *PUKW_CONTROL_HEADER, *LPUKW_CONTROL_HEADER; |
||||
|
||||
// Collection of flags which can be used when issuing transfer requests
|
||||
/* Indicates that the transfer direction is 'in' */ |
||||
#define UKW_TF_IN_TRANSFER 0x00000001 |
||||
/* Indicates that the transfer direction is 'out' */ |
||||
#define UKW_TF_OUT_TRANSFER 0x00000000 |
||||
/* Specifies that the transfer should complete as soon as possible,
|
||||
* even if no OVERLAPPED structure has been provided. */ |
||||
#define UKW_TF_NO_WAIT 0x00000100 |
||||
/* Indicates that transfers shorter than the buffer are ok */ |
||||
#define UKW_TF_SHORT_TRANSFER_OK 0x00000200 |
||||
#define UKW_TF_SEND_TO_DEVICE 0x00010000 |
||||
#define UKW_TF_SEND_TO_INTERFACE 0x00020000 |
||||
#define UKW_TF_SEND_TO_ENDPOINT 0x00040000 |
||||
/* Don't block when waiting for memory allocations */ |
||||
#define UKW_TF_DONT_BLOCK_FOR_MEM 0x00080000 |
||||
|
||||
/* Value to use when dealing with configuration values, such as UkwGetConfigDescriptor,
|
||||
* to specify the currently active configuration for the device. */ |
||||
#define UKW_ACTIVE_CONFIGURATION -1 |
||||
|
||||
DLL_DECLARE_HANDLE(ceusbkwrapper); |
||||
DLL_DECLARE_FUNC(WINAPI, HANDLE, UkwOpenDriver, ()); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetDeviceList, (HANDLE, LPUKW_DEVICE, DWORD, LPDWORD)); |
||||
DLL_DECLARE_FUNC(WINAPI, void, UkwReleaseDeviceList, (HANDLE, LPUKW_DEVICE, DWORD)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetDeviceAddress, (UKW_DEVICE, unsigned char*, unsigned char*, unsigned long*)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetDeviceDescriptor, (UKW_DEVICE, LPUKW_DEVICE_DESCRIPTOR)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetConfigDescriptor, (UKW_DEVICE, DWORD, LPVOID, DWORD, LPDWORD)); |
||||
DLL_DECLARE_FUNC(WINAPI, void, UkwCloseDriver, (HANDLE)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwCancelTransfer, (UKW_DEVICE, LPOVERLAPPED, DWORD)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwIssueControlTransfer, (UKW_DEVICE, DWORD, LPUKW_CONTROL_HEADER, LPVOID, DWORD, LPDWORD, LPOVERLAPPED)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwClaimInterface, (UKW_DEVICE, DWORD)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwReleaseInterface, (UKW_DEVICE, DWORD)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwSetInterfaceAlternateSetting, (UKW_DEVICE, DWORD, DWORD)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwClearHaltHost, (UKW_DEVICE, UCHAR)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwClearHaltDevice, (UKW_DEVICE, UCHAR)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwGetConfig, (UKW_DEVICE, PUCHAR)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwSetConfig, (UKW_DEVICE, UCHAR)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwResetDevice, (UKW_DEVICE)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwKernelDriverActive, (UKW_DEVICE, DWORD, PBOOL)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwAttachKernelDriver, (UKW_DEVICE, DWORD)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwDetachKernelDriver, (UKW_DEVICE, DWORD)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwIssueBulkTransfer, (UKW_DEVICE, DWORD, UCHAR, LPVOID, DWORD, LPDWORD, LPOVERLAPPED)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, UkwIsPipeHalted, (UKW_DEVICE, UCHAR, LPBOOL)); |
||||
|
||||
// Used to determine if an endpoint status really is halted on a failed transfer.
|
||||
#define STATUS_HALT_FLAG 0x1 |
||||
|
||||
struct wince_device_priv { |
||||
UKW_DEVICE dev; |
||||
UKW_DEVICE_DESCRIPTOR desc; |
||||
}; |
||||
|
||||
struct wince_transfer_priv { |
||||
struct winfd pollable_fd; |
||||
uint8_t interface_number; |
||||
}; |
||||
|
@ -0,0 +1,124 @@ |
||||
/*
|
||||
* Windows backend common header for libusb 1.0 |
||||
* |
||||
* This file brings together header code common between |
||||
* the desktop Windows and Windows CE backends. |
||||
* Copyright © 2012-2013 RealVNC Ltd. |
||||
* Copyright © 2009-2012 Pete Batard <pete@akeo.ie> |
||||
* With contributions from Michael Plante, Orin Eman et al. |
||||
* Parts of this code adapted from libusb-win32-v1 by Stephan Meyer |
||||
* Major code testing contribution by Xiaofan Chen |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
// Windows API default is uppercase - ugh!
|
||||
#if !defined(bool) |
||||
#define bool BOOL |
||||
#endif |
||||
#if !defined(true) |
||||
#define true TRUE |
||||
#endif |
||||
#if !defined(false) |
||||
#define false FALSE |
||||
#endif |
||||
|
||||
#define EPOCH_TIME UINT64_C(116444736000000000) // 1970.01.01 00:00:000 in MS Filetime
|
||||
|
||||
#if defined(__CYGWIN__ ) |
||||
#define _stricmp strcasecmp |
||||
#define _strdup strdup |
||||
// _beginthreadex is MSVCRT => unavailable for cygwin. Fallback to using CreateThread
|
||||
#define _beginthreadex(a, b, c, d, e, f) CreateThread(a, b, (LPTHREAD_START_ROUTINE)c, d, e, (LPDWORD)f) |
||||
#endif |
||||
|
||||
#define safe_free(p) do {if (p != NULL) {free((void *)p); p = NULL;}} while (0) |
||||
|
||||
#ifndef ARRAYSIZE |
||||
#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) |
||||
#endif |
||||
|
||||
#define ERR_BUFFER_SIZE 256 |
||||
|
||||
/*
|
||||
* API macros - leveraged from libusb-win32 1.x |
||||
*/ |
||||
#ifndef _WIN32_WCE |
||||
#define DLL_STRINGIFY(s) #s |
||||
#define DLL_LOAD_LIBRARY(name) LoadLibraryA(DLL_STRINGIFY(name)) |
||||
#else |
||||
#define DLL_STRINGIFY(s) L#s |
||||
#define DLL_LOAD_LIBRARY(name) LoadLibrary(DLL_STRINGIFY(name)) |
||||
#endif |
||||
|
||||
/*
|
||||
* Macros for handling DLL themselves |
||||
*/ |
||||
#define DLL_DECLARE_HANDLE(name) \ |
||||
static HMODULE __dll_##name##_handle = NULL |
||||
|
||||
#define DLL_GET_HANDLE(name) \ |
||||
do { \
|
||||
__dll_##name##_handle = DLL_LOAD_LIBRARY(name); \
|
||||
if (!__dll_##name##_handle) \
|
||||
return LIBUSB_ERROR_OTHER; \
|
||||
} while (0) |
||||
|
||||
#define DLL_FREE_HANDLE(name) \ |
||||
do { \
|
||||
if (__dll_##name##_handle) { \
|
||||
FreeLibrary(__dll_##name##_handle); \
|
||||
__dll_##name##_handle = NULL; \
|
||||
} \
|
||||
} while(0) |
||||
|
||||
|
||||
/*
|
||||
* Macros for handling functions within a DLL |
||||
*/ |
||||
#define DLL_DECLARE_FUNC_PREFIXNAME(api, ret, prefixname, name, args) \ |
||||
typedef ret (api * __dll_##name##_func_t)args; \
|
||||
static __dll_##name##_func_t prefixname = NULL |
||||
|
||||
#define DLL_DECLARE_FUNC(api, ret, name, args) \ |
||||
DLL_DECLARE_FUNC_PREFIXNAME(api, ret, name, name, args) |
||||
#define DLL_DECLARE_FUNC_PREFIXED(api, ret, prefix, name, args) \ |
||||
DLL_DECLARE_FUNC_PREFIXNAME(api, ret, prefix##name, name, args) |
||||
|
||||
#define DLL_LOAD_FUNC_PREFIXNAME(dll, prefixname, name, ret_on_failure) \ |
||||
do { \
|
||||
HMODULE h = __dll_##dll##_handle; \
|
||||
prefixname = (__dll_##name##_func_t)GetProcAddress(h, \
|
||||
DLL_STRINGIFY(name)); \
|
||||
if (prefixname) \
|
||||
break; \
|
||||
prefixname = (__dll_##name##_func_t)GetProcAddress(h, \
|
||||
DLL_STRINGIFY(name) DLL_STRINGIFY(A)); \
|
||||
if (prefixname) \
|
||||
break; \
|
||||
prefixname = (__dll_##name##_func_t)GetProcAddress(h, \
|
||||
DLL_STRINGIFY(name) DLL_STRINGIFY(W)); \
|
||||
if (prefixname) \
|
||||
break; \
|
||||
if (ret_on_failure) \
|
||||
return LIBUSB_ERROR_NOT_FOUND; \
|
||||
} while(0) |
||||
|
||||
#define DLL_LOAD_FUNC(dll, name, ret_on_failure) \ |
||||
DLL_LOAD_FUNC_PREFIXNAME(dll, name, name, ret_on_failure) |
||||
#define DLL_LOAD_FUNC_PREFIXED(dll, prefix, name, ret_on_failure) \ |
||||
DLL_LOAD_FUNC_PREFIXNAME(dll, prefix##name, name, ret_on_failure) |
591
vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_nt_common.c
generated
vendored
591
vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_nt_common.c
generated
vendored
@ -0,0 +1,591 @@ |
||||
/*
|
||||
* windows backend for libusb 1.0 |
||||
* Copyright © 2009-2012 Pete Batard <pete@akeo.ie> |
||||
* With contributions from Michael Plante, Orin Eman et al. |
||||
* Parts of this code adapted from libusb-win32-v1 by Stephan Meyer |
||||
* HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software |
||||
* Hash table functions adapted from glibc, by Ulrich Drepper et al. |
||||
* Major code testing contribution by Xiaofan Chen |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#include <inttypes.h> |
||||
#include <process.h> |
||||
#include <stdio.h> |
||||
|
||||
#include "libusbi.h" |
||||
#include "windows_common.h" |
||||
#include "windows_nt_common.h" |
||||
|
||||
// Global variables for clock_gettime mechanism
|
||||
static uint64_t hires_ticks_to_ps; |
||||
static uint64_t hires_frequency; |
||||
|
||||
#define TIMER_REQUEST_RETRY_MS 100 |
||||
#define WM_TIMER_REQUEST (WM_USER + 1) |
||||
#define WM_TIMER_EXIT (WM_USER + 2) |
||||
|
||||
// used for monotonic clock_gettime()
|
||||
struct timer_request { |
||||
struct timespec *tp; |
||||
HANDLE event; |
||||
}; |
||||
|
||||
// Timer thread
|
||||
static HANDLE timer_thread = NULL; |
||||
static DWORD timer_thread_id = 0; |
||||
|
||||
/* User32 dependencies */ |
||||
DLL_DECLARE_HANDLE(User32); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, GetMessageA, (LPMSG, HWND, UINT, UINT)); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, PeekMessageA, (LPMSG, HWND, UINT, UINT, UINT)); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, PostThreadMessageA, (DWORD, UINT, WPARAM, LPARAM)); |
||||
|
||||
static unsigned __stdcall windows_clock_gettime_threaded(void *param); |
||||
|
||||
/*
|
||||
* Converts a windows error to human readable string |
||||
* uses retval as errorcode, or, if 0, use GetLastError() |
||||
*/ |
||||
#if defined(ENABLE_LOGGING) |
||||
const char *windows_error_str(DWORD error_code) |
||||
{ |
||||
static char err_string[ERR_BUFFER_SIZE]; |
||||
|
||||
DWORD size; |
||||
int len; |
||||
|
||||
if (error_code == 0) |
||||
error_code = GetLastError(); |
||||
|
||||
len = sprintf(err_string, "[%u] ", (unsigned int)error_code); |
||||
|
||||
// Translate codes returned by SetupAPI. The ones we are dealing with are either
|
||||
// in 0x0000xxxx or 0xE000xxxx and can be distinguished from standard error codes.
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/hardware/ff545011.aspx
|
||||
switch (error_code & 0xE0000000) { |
||||
case 0: |
||||
error_code = HRESULT_FROM_WIN32(error_code); // Still leaves ERROR_SUCCESS unmodified
|
||||
break; |
||||
case 0xE0000000: |
||||
error_code = 0x80000000 | (FACILITY_SETUPAPI << 16) | (error_code & 0x0000FFFF); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
|
||||
size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, |
||||
NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
||||
&err_string[len], ERR_BUFFER_SIZE - len, NULL); |
||||
if (size == 0) { |
||||
DWORD format_error = GetLastError(); |
||||
if (format_error) |
||||
snprintf(err_string, ERR_BUFFER_SIZE, |
||||
"Windows error code %u (FormatMessage error code %u)", |
||||
(unsigned int)error_code, (unsigned int)format_error); |
||||
else |
||||
snprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", (unsigned int)error_code); |
||||
} else { |
||||
// Remove CRLF from end of message, if present
|
||||
size_t pos = len + size - 2; |
||||
if (err_string[pos] == '\r') |
||||
err_string[pos] = '\0'; |
||||
} |
||||
|
||||
return err_string; |
||||
} |
||||
#endif |
||||
|
||||
/* Hash table functions - modified From glibc 2.3.2:
|
||||
[Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986 |
||||
[Knuth] The Art of Computer Programming, part 3 (6.4) */ |
||||
|
||||
#define HTAB_SIZE 1021UL // *MUST* be a prime number!!
|
||||
|
||||
typedef struct htab_entry { |
||||
unsigned long used; |
||||
char *str; |
||||
} htab_entry; |
||||
|
||||
static htab_entry *htab_table = NULL; |
||||
static usbi_mutex_t htab_mutex = NULL; |
||||
static unsigned long htab_filled; |
||||
|
||||
/* Before using the hash table we must allocate memory for it.
|
||||
We allocate one element more as the found prime number says. |
||||
This is done for more effective indexing as explained in the |
||||
comment for the hash function. */ |
||||
static bool htab_create(struct libusb_context *ctx) |
||||
{ |
||||
if (htab_table != NULL) { |
||||
usbi_err(ctx, "hash table already allocated"); |
||||
return true; |
||||
} |
||||
|
||||
// Create a mutex
|
||||
usbi_mutex_init(&htab_mutex); |
||||
|
||||
usbi_dbg("using %lu entries hash table", HTAB_SIZE); |
||||
htab_filled = 0; |
||||
|
||||
// allocate memory and zero out.
|
||||
htab_table = calloc(HTAB_SIZE + 1, sizeof(htab_entry)); |
||||
if (htab_table == NULL) { |
||||
usbi_err(ctx, "could not allocate space for hash table"); |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/* After using the hash table it has to be destroyed. */ |
||||
static void htab_destroy(void) |
||||
{ |
||||
unsigned long i; |
||||
|
||||
if (htab_table == NULL) |
||||
return; |
||||
|
||||
for (i = 0; i < HTAB_SIZE; i++) |
||||
free(htab_table[i].str); |
||||
|
||||
safe_free(htab_table); |
||||
|
||||
usbi_mutex_destroy(&htab_mutex); |
||||
} |
||||
|
||||
/* This is the search function. It uses double hashing with open addressing.
|
||||
We use a trick to speed up the lookup. The table is created with one |
||||
more element available. This enables us to use the index zero special. |
||||
This index will never be used because we store the first hash index in |
||||
the field used where zero means not used. Every other value means used. |
||||
The used field can be used as a first fast comparison for equality of |
||||
the stored and the parameter value. This helps to prevent unnecessary |
||||
expensive calls of strcmp. */ |
||||
unsigned long htab_hash(const char *str) |
||||
{ |
||||
unsigned long hval, hval2; |
||||
unsigned long idx; |
||||
unsigned long r = 5381; |
||||
int c; |
||||
const char *sz = str; |
||||
|
||||
if (str == NULL) |
||||
return 0; |
||||
|
||||
// Compute main hash value (algorithm suggested by Nokia)
|
||||
while ((c = *sz++) != 0) |
||||
r = ((r << 5) + r) + c; |
||||
if (r == 0) |
||||
++r; |
||||
|
||||
// compute table hash: simply take the modulus
|
||||
hval = r % HTAB_SIZE; |
||||
if (hval == 0) |
||||
++hval; |
||||
|
||||
// Try the first index
|
||||
idx = hval; |
||||
|
||||
// Mutually exclusive access (R/W lock would be better)
|
||||
usbi_mutex_lock(&htab_mutex); |
||||
|
||||
if (htab_table[idx].used) { |
||||
if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0)) |
||||
goto out_unlock; // existing hash
|
||||
|
||||
usbi_dbg("hash collision ('%s' vs '%s')", str, htab_table[idx].str); |
||||
|
||||
// Second hash function, as suggested in [Knuth]
|
||||
hval2 = 1 + hval % (HTAB_SIZE - 2); |
||||
|
||||
do { |
||||
// Because size is prime this guarantees to step through all available indexes
|
||||
if (idx <= hval2) |
||||
idx = HTAB_SIZE + idx - hval2; |
||||
else |
||||
idx -= hval2; |
||||
|
||||
// If we visited all entries leave the loop unsuccessfully
|
||||
if (idx == hval) |
||||
break; |
||||
|
||||
// If entry is found use it.
|
||||
if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0)) |
||||
goto out_unlock; |
||||
} while (htab_table[idx].used); |
||||
} |
||||
|
||||
// Not found => New entry
|
||||
|
||||
// If the table is full return an error
|
||||
if (htab_filled >= HTAB_SIZE) { |
||||
usbi_err(NULL, "hash table is full (%lu entries)", HTAB_SIZE); |
||||
idx = 0; |
||||
goto out_unlock; |
||||
} |
||||
|
||||
htab_table[idx].str = _strdup(str); |
||||
if (htab_table[idx].str == NULL) { |
||||
usbi_err(NULL, "could not duplicate string for hash table"); |
||||
idx = 0; |
||||
goto out_unlock; |
||||
} |
||||
|
||||
htab_table[idx].used = hval; |
||||
++htab_filled; |
||||
|
||||
out_unlock: |
||||
usbi_mutex_unlock(&htab_mutex); |
||||
|
||||
return idx; |
||||
} |
||||
|
||||
static int windows_init_dlls(void) |
||||
{ |
||||
DLL_GET_HANDLE(User32); |
||||
DLL_LOAD_FUNC_PREFIXED(User32, p, GetMessageA, TRUE); |
||||
DLL_LOAD_FUNC_PREFIXED(User32, p, PeekMessageA, TRUE); |
||||
DLL_LOAD_FUNC_PREFIXED(User32, p, PostThreadMessageA, TRUE); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static void windows_exit_dlls(void) |
||||
{ |
||||
DLL_FREE_HANDLE(User32); |
||||
} |
||||
|
||||
static bool windows_init_clock(struct libusb_context *ctx) |
||||
{ |
||||
DWORD_PTR affinity, dummy; |
||||
HANDLE event = NULL; |
||||
LARGE_INTEGER li_frequency; |
||||
int i; |
||||
|
||||
if (QueryPerformanceFrequency(&li_frequency)) { |
||||
// Load DLL imports
|
||||
if (windows_init_dlls() != LIBUSB_SUCCESS) { |
||||
usbi_err(ctx, "could not resolve DLL functions"); |
||||
return false; |
||||
} |
||||
|
||||
// The hires frequency can go as high as 4 GHz, so we'll use a conversion
|
||||
// to picoseconds to compute the tv_nsecs part in clock_gettime
|
||||
hires_frequency = li_frequency.QuadPart; |
||||
hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency; |
||||
usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency); |
||||
|
||||
// Because QueryPerformanceCounter might report different values when
|
||||
// running on different cores, we create a separate thread for the timer
|
||||
// calls, which we glue to the first available core always to prevent timing discrepancies.
|
||||
if (!GetProcessAffinityMask(GetCurrentProcess(), &affinity, &dummy) || (affinity == 0)) { |
||||
usbi_err(ctx, "could not get process affinity: %s", windows_error_str(0)); |
||||
return false; |
||||
} |
||||
|
||||
// The process affinity mask is a bitmask where each set bit represents a core on
|
||||
// which this process is allowed to run, so we find the first set bit
|
||||
for (i = 0; !(affinity & (DWORD_PTR)(1 << i)); i++); |
||||
affinity = (DWORD_PTR)(1 << i); |
||||
|
||||
usbi_dbg("timer thread will run on core #%d", i); |
||||
|
||||
event = CreateEvent(NULL, FALSE, FALSE, NULL); |
||||
if (event == NULL) { |
||||
usbi_err(ctx, "could not create event: %s", windows_error_str(0)); |
||||
return false; |
||||
} |
||||
|
||||
timer_thread = (HANDLE)_beginthreadex(NULL, 0, windows_clock_gettime_threaded, (void *)event, |
||||
0, (unsigned int *)&timer_thread_id); |
||||
if (timer_thread == NULL) { |
||||
usbi_err(ctx, "unable to create timer thread - aborting"); |
||||
CloseHandle(event); |
||||
return false; |
||||
} |
||||
|
||||
if (!SetThreadAffinityMask(timer_thread, affinity)) |
||||
usbi_warn(ctx, "unable to set timer thread affinity, timer discrepancies may arise"); |
||||
|
||||
// Wait for timer thread to init before continuing.
|
||||
if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) { |
||||
usbi_err(ctx, "failed to wait for timer thread to become ready - aborting"); |
||||
CloseHandle(event); |
||||
return false; |
||||
} |
||||
|
||||
CloseHandle(event); |
||||
} else { |
||||
usbi_dbg("no hires timer available on this platform"); |
||||
hires_frequency = 0; |
||||
hires_ticks_to_ps = UINT64_C(0); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void windows_destroy_clock(void) |
||||
{ |
||||
if (timer_thread) { |
||||
// actually the signal to quit the thread.
|
||||
if (!pPostThreadMessageA(timer_thread_id, WM_TIMER_EXIT, 0, 0) |
||||
|| (WaitForSingleObject(timer_thread, INFINITE) != WAIT_OBJECT_0)) { |
||||
usbi_dbg("could not wait for timer thread to quit"); |
||||
TerminateThread(timer_thread, 1); |
||||
// shouldn't happen, but we're destroying
|
||||
// all objects it might have held anyway.
|
||||
} |
||||
CloseHandle(timer_thread); |
||||
timer_thread = NULL; |
||||
timer_thread_id = 0; |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* Monotonic and real time functions |
||||
*/ |
||||
static unsigned __stdcall windows_clock_gettime_threaded(void *param) |
||||
{ |
||||
struct timer_request *request; |
||||
LARGE_INTEGER hires_counter; |
||||
MSG msg; |
||||
|
||||
// The following call will create this thread's message queue
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms644946.aspx
|
||||
pPeekMessageA(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); |
||||
|
||||
// Signal windows_init_clock() that we're ready to service requests
|
||||
if (!SetEvent((HANDLE)param)) |
||||
usbi_dbg("SetEvent failed for timer init event: %s", windows_error_str(0)); |
||||
param = NULL; |
||||
|
||||
// Main loop - wait for requests
|
||||
while (1) { |
||||
if (pGetMessageA(&msg, NULL, WM_TIMER_REQUEST, WM_TIMER_EXIT) == -1) { |
||||
usbi_err(NULL, "GetMessage failed for timer thread: %s", windows_error_str(0)); |
||||
return 1; |
||||
} |
||||
|
||||
switch (msg.message) { |
||||
case WM_TIMER_REQUEST: |
||||
// Requests to this thread are for hires always
|
||||
// Microsoft says that this function always succeeds on XP and later
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904.aspx
|
||||
request = (struct timer_request *)msg.lParam; |
||||
QueryPerformanceCounter(&hires_counter); |
||||
request->tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency); |
||||
request->tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency) / 1000) * hires_ticks_to_ps); |
||||
if (!SetEvent(request->event)) |
||||
usbi_err(NULL, "SetEvent failed for timer request: %s", windows_error_str(0)); |
||||
break; |
||||
case WM_TIMER_EXIT: |
||||
usbi_dbg("timer thread quitting"); |
||||
return 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
int windows_clock_gettime(int clk_id, struct timespec *tp) |
||||
{ |
||||
struct timer_request request; |
||||
#if !defined(_MSC_VER) || (_MSC_VER < 1900) |
||||
FILETIME filetime; |
||||
ULARGE_INTEGER rtime; |
||||
#endif |
||||
DWORD r; |
||||
|
||||
switch (clk_id) { |
||||
case USBI_CLOCK_MONOTONIC: |
||||
if (timer_thread) { |
||||
request.tp = tp; |
||||
request.event = CreateEvent(NULL, FALSE, FALSE, NULL); |
||||
if (request.event == NULL) |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
|
||||
if (!pPostThreadMessageA(timer_thread_id, WM_TIMER_REQUEST, 0, (LPARAM)&request)) { |
||||
usbi_err(NULL, "PostThreadMessage failed for timer thread: %s", windows_error_str(0)); |
||||
CloseHandle(request.event); |
||||
return LIBUSB_ERROR_OTHER; |
||||
} |
||||
|
||||
do { |
||||
r = WaitForSingleObject(request.event, TIMER_REQUEST_RETRY_MS); |
||||
if (r == WAIT_TIMEOUT) |
||||
usbi_dbg("could not obtain a timer value within reasonable timeframe - too much load?"); |
||||
else if (r == WAIT_FAILED) |
||||
usbi_err(NULL, "WaitForSingleObject failed: %s", windows_error_str(0)); |
||||
} while (r == WAIT_TIMEOUT); |
||||
CloseHandle(request.event); |
||||
|
||||
if (r == WAIT_OBJECT_0) |
||||
return LIBUSB_SUCCESS; |
||||
else |
||||
return LIBUSB_ERROR_OTHER; |
||||
} |
||||
// Fall through and return real-time if monotonic was not detected @ timer init
|
||||
case USBI_CLOCK_REALTIME: |
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1900) |
||||
timespec_get(tp, TIME_UTC); |
||||
#else |
||||
// We follow http://msdn.microsoft.com/en-us/library/ms724928%28VS.85%29.aspx
|
||||
// with a predef epoch time to have an epoch that starts at 1970.01.01 00:00
|
||||
// Note however that our resolution is bounded by the Windows system time
|
||||
// functions and is at best of the order of 1 ms (or, usually, worse)
|
||||
GetSystemTimeAsFileTime(&filetime); |
||||
rtime.LowPart = filetime.dwLowDateTime; |
||||
rtime.HighPart = filetime.dwHighDateTime; |
||||
rtime.QuadPart -= EPOCH_TIME; |
||||
tp->tv_sec = (long)(rtime.QuadPart / 10000000); |
||||
tp->tv_nsec = (long)((rtime.QuadPart % 10000000) * 100); |
||||
#endif |
||||
return LIBUSB_SUCCESS; |
||||
default: |
||||
return LIBUSB_ERROR_INVALID_PARAM; |
||||
} |
||||
} |
||||
|
||||
static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) |
||||
{ |
||||
int status, istatus; |
||||
|
||||
usbi_dbg("handling I/O completion with errcode %u, size %u", io_result, io_size); |
||||
|
||||
switch (io_result) { |
||||
case NO_ERROR: |
||||
status = windows_copy_transfer_data(itransfer, io_size); |
||||
break; |
||||
case ERROR_GEN_FAILURE: |
||||
usbi_dbg("detected endpoint stall"); |
||||
status = LIBUSB_TRANSFER_STALL; |
||||
break; |
||||
case ERROR_SEM_TIMEOUT: |
||||
usbi_dbg("detected semaphore timeout"); |
||||
status = LIBUSB_TRANSFER_TIMED_OUT; |
||||
break; |
||||
case ERROR_OPERATION_ABORTED: |
||||
istatus = windows_copy_transfer_data(itransfer, io_size); |
||||
if (istatus != LIBUSB_TRANSFER_COMPLETED) |
||||
usbi_dbg("Failed to copy partial data in aborted operation: %d", istatus); |
||||
|
||||
usbi_dbg("detected operation aborted"); |
||||
status = LIBUSB_TRANSFER_CANCELLED; |
||||
break; |
||||
default: |
||||
usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error %u: %s", io_result, windows_error_str(io_result)); |
||||
status = LIBUSB_TRANSFER_ERROR; |
||||
break; |
||||
} |
||||
windows_clear_transfer_priv(itransfer); // Cancel polling
|
||||
if (status == LIBUSB_TRANSFER_CANCELLED) |
||||
usbi_handle_transfer_cancellation(itransfer); |
||||
else |
||||
usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status); |
||||
} |
||||
|
||||
void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) |
||||
{ |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
|
||||
switch (transfer->type) { |
||||
case LIBUSB_TRANSFER_TYPE_CONTROL: |
||||
case LIBUSB_TRANSFER_TYPE_BULK: |
||||
case LIBUSB_TRANSFER_TYPE_INTERRUPT: |
||||
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: |
||||
windows_transfer_callback(itransfer, io_result, io_size); |
||||
break; |
||||
case LIBUSB_TRANSFER_TYPE_BULK_STREAM: |
||||
usbi_warn(ITRANSFER_CTX(itransfer), "bulk stream transfers are not yet supported on this platform"); |
||||
break; |
||||
default: |
||||
usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); |
||||
} |
||||
} |
||||
|
||||
int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) |
||||
{ |
||||
POLL_NFDS_TYPE i; |
||||
bool found = false; |
||||
struct usbi_transfer *transfer; |
||||
struct winfd *pollable_fd = NULL; |
||||
DWORD io_size, io_result; |
||||
int r = LIBUSB_SUCCESS; |
||||
|
||||
usbi_mutex_lock(&ctx->open_devs_lock); |
||||
for (i = 0; i < nfds && num_ready > 0; i++) { |
||||
|
||||
usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents); |
||||
|
||||
if (!fds[i].revents) |
||||
continue; |
||||
|
||||
num_ready--; |
||||
|
||||
// Because a Windows OVERLAPPED is used for poll emulation,
|
||||
// a pollable fd is created and stored with each transfer
|
||||
usbi_mutex_lock(&ctx->flying_transfers_lock); |
||||
found = false; |
||||
list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { |
||||
pollable_fd = windows_get_fd(transfer); |
||||
if (pollable_fd->fd == fds[i].fd) { |
||||
found = true; |
||||
break; |
||||
} |
||||
} |
||||
usbi_mutex_unlock(&ctx->flying_transfers_lock); |
||||
|
||||
if (found) { |
||||
windows_get_overlapped_result(transfer, pollable_fd, &io_result, &io_size); |
||||
|
||||
usbi_remove_pollfd(ctx, pollable_fd->fd); |
||||
// let handle_callback free the event using the transfer wfd
|
||||
// If you don't use the transfer wfd, you run a risk of trying to free a
|
||||
// newly allocated wfd that took the place of the one from the transfer.
|
||||
windows_handle_callback(transfer, io_result, io_size); |
||||
} else { |
||||
usbi_err(ctx, "could not find a matching transfer for fd %d", fds[i]); |
||||
r = LIBUSB_ERROR_NOT_FOUND; |
||||
break; |
||||
} |
||||
} |
||||
usbi_mutex_unlock(&ctx->open_devs_lock); |
||||
|
||||
return r; |
||||
} |
||||
|
||||
int windows_common_init(struct libusb_context *ctx) |
||||
{ |
||||
if (!windows_init_clock(ctx)) |
||||
goto error_roll_back; |
||||
|
||||
if (!htab_create(ctx)) |
||||
goto error_roll_back; |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
|
||||
error_roll_back: |
||||
windows_common_exit(); |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
} |
||||
|
||||
void windows_common_exit(void) |
||||
{ |
||||
htab_destroy(); |
||||
windows_destroy_clock(); |
||||
windows_exit_dlls(); |
||||
} |
63
vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_nt_common.h
generated
vendored
63
vendor/github.com/karalabe/gousb/internal/libusb/libusb/os/windows_nt_common.h
generated
vendored
@ -0,0 +1,63 @@ |
||||
/*
|
||||
* Windows backend common header for libusb 1.0 |
||||
* |
||||
* This file brings together header code common between |
||||
* the desktop Windows backends. |
||||
* Copyright © 2012-2013 RealVNC Ltd. |
||||
* Copyright © 2009-2012 Pete Batard <pete@akeo.ie> |
||||
* With contributions from Michael Plante, Orin Eman et al. |
||||
* Parts of this code adapted from libusb-win32-v1 by Stephan Meyer |
||||
* Major code testing contribution by Xiaofan Chen |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
// Missing from MinGW
|
||||
#if !defined(FACILITY_SETUPAPI) |
||||
#define FACILITY_SETUPAPI 15 |
||||
#endif |
||||
|
||||
typedef struct USB_CONFIGURATION_DESCRIPTOR { |
||||
UCHAR bLength; |
||||
UCHAR bDescriptorType; |
||||
USHORT wTotalLength; |
||||
UCHAR bNumInterfaces; |
||||
UCHAR bConfigurationValue; |
||||
UCHAR iConfiguration; |
||||
UCHAR bmAttributes; |
||||
UCHAR MaxPower; |
||||
} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; |
||||
|
||||
typedef struct libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR; |
||||
|
||||
int windows_common_init(struct libusb_context *ctx); |
||||
void windows_common_exit(void); |
||||
|
||||
unsigned long htab_hash(const char *str); |
||||
int windows_clock_gettime(int clk_id, struct timespec *tp); |
||||
|
||||
void windows_clear_transfer_priv(struct usbi_transfer *itransfer); |
||||
int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size); |
||||
struct winfd *windows_get_fd(struct usbi_transfer *transfer); |
||||
void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size); |
||||
|
||||
void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size); |
||||
int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready); |
||||
|
||||
#if defined(ENABLE_LOGGING) |
||||
const char *windows_error_str(DWORD error_code); |
||||
#endif |
@ -0,0 +1,905 @@ |
||||
/*
|
||||
* windows UsbDk backend for libusb 1.0 |
||||
* Copyright © 2014 Red Hat, Inc. |
||||
|
||||
* Authors: |
||||
* Dmitry Fleytman <dmitry@daynix.com> |
||||
* Pavel Gurvich <pavel@daynix.com> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#if defined(USE_USBDK) |
||||
|
||||
#include <windows.h> |
||||
#include <cfgmgr32.h> |
||||
#include <stdio.h> |
||||
|
||||
#include "libusbi.h" |
||||
#include "windows_common.h" |
||||
#include "windows_nt_common.h" |
||||
|
||||
#define ULONG64 uint64_t |
||||
#define PVOID64 uint64_t |
||||
|
||||
typedef CONST WCHAR *PCWCHAR; |
||||
#define wcsncpy_s wcsncpy |
||||
|
||||
#include "windows_usbdk.h" |
||||
|
||||
#if !defined(STATUS_SUCCESS) |
||||
typedef LONG NTSTATUS; |
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) |
||||
#endif |
||||
|
||||
#if !defined(STATUS_CANCELLED) |
||||
#define STATUS_CANCELLED ((NTSTATUS)0xC0000120L) |
||||
#endif |
||||
|
||||
#if !defined(STATUS_REQUEST_CANCELED) |
||||
#define STATUS_REQUEST_CANCELED ((NTSTATUS)0xC0000703L) |
||||
#endif |
||||
|
||||
#if !defined(USBD_SUCCESS) |
||||
typedef int32_t USBD_STATUS; |
||||
#define USBD_SUCCESS(Status) ((USBD_STATUS) (Status) >= 0) |
||||
#define USBD_PENDING(Status) ((ULONG) (Status) >> 30 == 1) |
||||
#define USBD_ERROR(Status) ((USBD_STATUS) (Status) < 0) |
||||
#define USBD_STATUS_STALL_PID ((USBD_STATUS) 0xc0000004) |
||||
#define USBD_STATUS_ENDPOINT_HALTED ((USBD_STATUS) 0xc0000030) |
||||
#define USBD_STATUS_BAD_START_FRAME ((USBD_STATUS) 0xc0000a00) |
||||
#define USBD_STATUS_TIMEOUT ((USBD_STATUS) 0xc0006000) |
||||
#define USBD_STATUS_CANCELED ((USBD_STATUS) 0xc0010000) |
||||
#endif |
||||
|
||||
static int concurrent_usage = -1; |
||||
|
||||
struct usbdk_device_priv { |
||||
USB_DK_DEVICE_INFO info; |
||||
PUSB_CONFIGURATION_DESCRIPTOR *config_descriptors; |
||||
HANDLE redirector_handle; |
||||
uint8_t active_configuration; |
||||
}; |
||||
|
||||
struct usbdk_transfer_priv { |
||||
USB_DK_TRANSFER_REQUEST request; |
||||
struct winfd pollable_fd; |
||||
PULONG64 IsochronousPacketsArray; |
||||
PUSB_DK_ISO_TRANSFER_RESULT IsochronousResultsArray; |
||||
}; |
||||
|
||||
static inline struct usbdk_device_priv *_usbdk_device_priv(struct libusb_device *dev) |
||||
{ |
||||
return (struct usbdk_device_priv *)dev->os_priv; |
||||
} |
||||
|
||||
static inline struct usbdk_transfer_priv *_usbdk_transfer_priv(struct usbi_transfer *itransfer) |
||||
{ |
||||
return (struct usbdk_transfer_priv *)usbi_transfer_get_os_priv(itransfer); |
||||
} |
||||
|
||||
static struct { |
||||
HMODULE module; |
||||
|
||||
USBDK_GET_DEVICES_LIST GetDevicesList; |
||||
USBDK_RELEASE_DEVICES_LIST ReleaseDevicesList; |
||||
USBDK_START_REDIRECT StartRedirect; |
||||
USBDK_STOP_REDIRECT StopRedirect; |
||||
USBDK_GET_CONFIGURATION_DESCRIPTOR GetConfigurationDescriptor; |
||||
USBDK_RELEASE_CONFIGURATION_DESCRIPTOR ReleaseConfigurationDescriptor; |
||||
USBDK_READ_PIPE ReadPipe; |
||||
USBDK_WRITE_PIPE WritePipe; |
||||
USBDK_ABORT_PIPE AbortPipe; |
||||
USBDK_RESET_PIPE ResetPipe; |
||||
USBDK_SET_ALTSETTING SetAltsetting; |
||||
USBDK_RESET_DEVICE ResetDevice; |
||||
USBDK_GET_REDIRECTOR_SYSTEM_HANDLE GetRedirectorSystemHandle; |
||||
} usbdk_helper; |
||||
|
||||
static FARPROC get_usbdk_proc_addr(struct libusb_context *ctx, LPCSTR api_name) |
||||
{ |
||||
FARPROC api_ptr = GetProcAddress(usbdk_helper.module, api_name); |
||||
|
||||
if (api_ptr == NULL) |
||||
usbi_err(ctx, "UsbDkHelper API %s not found, error %d", api_name, GetLastError()); |
||||
|
||||
return api_ptr; |
||||
} |
||||
|
||||
static void unload_usbdk_helper_dll(void) |
||||
{ |
||||
if (usbdk_helper.module != NULL) { |
||||
FreeLibrary(usbdk_helper.module); |
||||
usbdk_helper.module = NULL; |
||||
} |
||||
} |
||||
|
||||
static int load_usbdk_helper_dll(struct libusb_context *ctx) |
||||
{ |
||||
usbdk_helper.module = LoadLibraryA("UsbDkHelper"); |
||||
if (usbdk_helper.module == NULL) { |
||||
usbi_err(ctx, "Failed to load UsbDkHelper.dll, error %d", GetLastError()); |
||||
return LIBUSB_ERROR_NOT_FOUND; |
||||
} |
||||
|
||||
usbdk_helper.GetDevicesList = (USBDK_GET_DEVICES_LIST)get_usbdk_proc_addr(ctx, "UsbDk_GetDevicesList"); |
||||
if (usbdk_helper.GetDevicesList == NULL) |
||||
goto error_unload; |
||||
|
||||
usbdk_helper.ReleaseDevicesList = (USBDK_RELEASE_DEVICES_LIST)get_usbdk_proc_addr(ctx, "UsbDk_ReleaseDevicesList"); |
||||
if (usbdk_helper.ReleaseDevicesList == NULL) |
||||
goto error_unload; |
||||
|
||||
usbdk_helper.StartRedirect = (USBDK_START_REDIRECT)get_usbdk_proc_addr(ctx, "UsbDk_StartRedirect"); |
||||
if (usbdk_helper.StartRedirect == NULL) |
||||
goto error_unload; |
||||
|
||||
usbdk_helper.StopRedirect = (USBDK_STOP_REDIRECT)get_usbdk_proc_addr(ctx, "UsbDk_StopRedirect"); |
||||
if (usbdk_helper.StopRedirect == NULL) |
||||
goto error_unload; |
||||
|
||||
usbdk_helper.GetConfigurationDescriptor = (USBDK_GET_CONFIGURATION_DESCRIPTOR)get_usbdk_proc_addr(ctx, "UsbDk_GetConfigurationDescriptor"); |
||||
if (usbdk_helper.GetConfigurationDescriptor == NULL) |
||||
goto error_unload; |
||||
|
||||
usbdk_helper.ReleaseConfigurationDescriptor = (USBDK_RELEASE_CONFIGURATION_DESCRIPTOR)get_usbdk_proc_addr(ctx, "UsbDk_ReleaseConfigurationDescriptor"); |
||||
if (usbdk_helper.ReleaseConfigurationDescriptor == NULL) |
||||
goto error_unload; |
||||
|
||||
usbdk_helper.ReadPipe = (USBDK_READ_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_ReadPipe"); |
||||
if (usbdk_helper.ReadPipe == NULL) |
||||
goto error_unload; |
||||
|
||||
usbdk_helper.WritePipe = (USBDK_WRITE_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_WritePipe"); |
||||
if (usbdk_helper.WritePipe == NULL) |
||||
goto error_unload; |
||||
|
||||
usbdk_helper.AbortPipe = (USBDK_ABORT_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_AbortPipe"); |
||||
if (usbdk_helper.AbortPipe == NULL) |
||||
goto error_unload; |
||||
|
||||
usbdk_helper.ResetPipe = (USBDK_RESET_PIPE)get_usbdk_proc_addr(ctx, "UsbDk_ResetPipe"); |
||||
if (usbdk_helper.ResetPipe == NULL) |
||||
goto error_unload; |
||||
|
||||
usbdk_helper.SetAltsetting = (USBDK_SET_ALTSETTING)get_usbdk_proc_addr(ctx, "UsbDk_SetAltsetting"); |
||||
if (usbdk_helper.SetAltsetting == NULL) |
||||
goto error_unload; |
||||
|
||||
usbdk_helper.ResetDevice = (USBDK_RESET_DEVICE)get_usbdk_proc_addr(ctx, "UsbDk_ResetDevice"); |
||||
if (usbdk_helper.ResetDevice == NULL) |
||||
goto error_unload; |
||||
|
||||
usbdk_helper.GetRedirectorSystemHandle = (USBDK_GET_REDIRECTOR_SYSTEM_HANDLE)get_usbdk_proc_addr(ctx, "UsbDk_GetRedirectorSystemHandle"); |
||||
if (usbdk_helper.GetRedirectorSystemHandle == NULL) |
||||
goto error_unload; |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
|
||||
error_unload: |
||||
FreeLibrary(usbdk_helper.module); |
||||
usbdk_helper.module = NULL; |
||||
return LIBUSB_ERROR_NOT_FOUND; |
||||
} |
||||
|
||||
static int usbdk_init(struct libusb_context *ctx) |
||||
{ |
||||
int r; |
||||
|
||||
if (++concurrent_usage == 0) { // First init?
|
||||
r = load_usbdk_helper_dll(ctx); |
||||
if (r) |
||||
goto init_exit; |
||||
|
||||
init_polling(); |
||||
|
||||
r = windows_common_init(ctx); |
||||
if (r) |
||||
goto init_exit; |
||||
} |
||||
// At this stage, either we went through full init successfully, or didn't need to
|
||||
r = LIBUSB_SUCCESS; |
||||
|
||||
init_exit: |
||||
if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
|
||||
exit_polling(); |
||||
windows_common_exit(); |
||||
unload_usbdk_helper_dll(); |
||||
} |
||||
|
||||
if (r != LIBUSB_SUCCESS) |
||||
--concurrent_usage; // Not expected to call libusb_exit if we failed.
|
||||
|
||||
return r; |
||||
} |
||||
|
||||
static int usbdk_get_session_id_for_device(struct libusb_context *ctx, |
||||
PUSB_DK_DEVICE_ID id, unsigned long *session_id) |
||||
{ |
||||
char dev_identity[ARRAYSIZE(id->DeviceID) + ARRAYSIZE(id->InstanceID)]; |
||||
|
||||
if (sprintf(dev_identity, "%S%S", id->DeviceID, id->InstanceID) == -1) { |
||||
usbi_warn(ctx, "cannot form device identity", id->DeviceID); |
||||
return LIBUSB_ERROR_NOT_SUPPORTED; |
||||
} |
||||
|
||||
*session_id = htab_hash(dev_identity); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static void usbdk_release_config_descriptors(struct usbdk_device_priv *p, uint8_t count) |
||||
{ |
||||
uint8_t i; |
||||
|
||||
for (i = 0; i < count; i++) |
||||
usbdk_helper.ReleaseConfigurationDescriptor(p->config_descriptors[i]); |
||||
|
||||
free(p->config_descriptors); |
||||
p->config_descriptors = NULL; |
||||
} |
||||
|
||||
static int usbdk_cache_config_descriptors(struct libusb_context *ctx, |
||||
struct usbdk_device_priv *p, PUSB_DK_DEVICE_INFO info) |
||||
{ |
||||
uint8_t i; |
||||
USB_DK_CONFIG_DESCRIPTOR_REQUEST Request; |
||||
Request.ID = info->ID; |
||||
|
||||
p->config_descriptors = calloc(info->DeviceDescriptor.bNumConfigurations, sizeof(PUSB_CONFIGURATION_DESCRIPTOR)); |
||||
if (p->config_descriptors == NULL) { |
||||
usbi_err(ctx, "failed to allocate configuration descriptors holder"); |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
} |
||||
|
||||
for (i = 0; i < info->DeviceDescriptor.bNumConfigurations; i++) { |
||||
ULONG Length; |
||||
|
||||
Request.Index = i; |
||||
if (!usbdk_helper.GetConfigurationDescriptor(&Request, &p->config_descriptors[i], &Length)) { |
||||
usbi_err(ctx, "failed to retrieve configuration descriptors"); |
||||
usbdk_release_config_descriptors(p, i); |
||||
return LIBUSB_ERROR_OTHER; |
||||
} |
||||
} |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static inline int usbdk_device_priv_init(struct libusb_context *ctx, struct libusb_device *dev, PUSB_DK_DEVICE_INFO info) |
||||
{ |
||||
struct usbdk_device_priv *p = _usbdk_device_priv(dev); |
||||
|
||||
p->info = *info; |
||||
p->active_configuration = 0; |
||||
|
||||
return usbdk_cache_config_descriptors(ctx, p, info); |
||||
} |
||||
|
||||
static void usbdk_device_init(libusb_device *dev, PUSB_DK_DEVICE_INFO info) |
||||
{ |
||||
dev->bus_number = (uint8_t)info->FilterID; |
||||
dev->port_number = (uint8_t)info->Port; |
||||
dev->parent_dev = NULL; |
||||
|
||||
//Addresses in libusb are 1-based
|
||||
dev->device_address = (uint8_t)(info->Port + 1); |
||||
|
||||
dev->num_configurations = info->DeviceDescriptor.bNumConfigurations; |
||||
dev->device_descriptor = info->DeviceDescriptor; |
||||
|
||||
switch (info->Speed) { |
||||
case LowSpeed: |
||||
dev->speed = LIBUSB_SPEED_LOW; |
||||
break; |
||||
case FullSpeed: |
||||
dev->speed = LIBUSB_SPEED_FULL; |
||||
break; |
||||
case HighSpeed: |
||||
dev->speed = LIBUSB_SPEED_HIGH; |
||||
break; |
||||
case SuperSpeed: |
||||
dev->speed = LIBUSB_SPEED_SUPER; |
||||
break; |
||||
case NoSpeed: |
||||
default: |
||||
dev->speed = LIBUSB_SPEED_UNKNOWN; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
static int usbdk_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) |
||||
{ |
||||
int r = LIBUSB_SUCCESS; |
||||
ULONG i; |
||||
struct discovered_devs *discdevs = NULL; |
||||
ULONG dev_number; |
||||
PUSB_DK_DEVICE_INFO devices; |
||||
|
||||
if(!usbdk_helper.GetDevicesList(&devices, &dev_number)) |
||||
return LIBUSB_ERROR_OTHER; |
||||
|
||||
for (i = 0; i < dev_number; i++) { |
||||
unsigned long session_id; |
||||
struct libusb_device *dev = NULL; |
||||
|
||||
if (usbdk_get_session_id_for_device(ctx, &devices[i].ID, &session_id)) |
||||
continue; |
||||
|
||||
dev = usbi_get_device_by_session_id(ctx, session_id); |
||||
if (dev == NULL) { |
||||
dev = usbi_alloc_device(ctx, session_id); |
||||
if (dev == NULL) { |
||||
usbi_err(ctx, "failed to allocate a new device structure"); |
||||
continue; |
||||
} |
||||
|
||||
usbdk_device_init(dev, &devices[i]); |
||||
if (usbdk_device_priv_init(ctx, dev, &devices[i]) != LIBUSB_SUCCESS) { |
||||
libusb_unref_device(dev); |
||||
continue; |
||||
} |
||||
} |
||||
|
||||
discdevs = discovered_devs_append(*_discdevs, dev); |
||||
libusb_unref_device(dev); |
||||
if (!discdevs) { |
||||
usbi_err(ctx, "cannot append new device to list"); |
||||
r = LIBUSB_ERROR_NO_MEM; |
||||
goto func_exit; |
||||
} |
||||
|
||||
*_discdevs = discdevs; |
||||
} |
||||
|
||||
func_exit: |
||||
usbdk_helper.ReleaseDevicesList(devices); |
||||
return r; |
||||
} |
||||
|
||||
static void usbdk_exit(void) |
||||
{ |
||||
if (--concurrent_usage < 0) { |
||||
windows_common_exit(); |
||||
exit_polling(); |
||||
unload_usbdk_helper_dll(); |
||||
} |
||||
} |
||||
|
||||
static int usbdk_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) |
||||
{ |
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev); |
||||
|
||||
memcpy(buffer, &priv->info.DeviceDescriptor, DEVICE_DESC_LENGTH); |
||||
*host_endian = 0; |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int usbdk_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) |
||||
{ |
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev); |
||||
PUSB_CONFIGURATION_DESCRIPTOR config_header; |
||||
size_t size; |
||||
|
||||
if (config_index >= dev->num_configurations) |
||||
return LIBUSB_ERROR_INVALID_PARAM; |
||||
|
||||
config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptors[config_index]; |
||||
|
||||
size = min(config_header->wTotalLength, len); |
||||
memcpy(buffer, config_header, size); |
||||
*host_endian = 0; |
||||
|
||||
return (int)size; |
||||
} |
||||
|
||||
static inline int usbdk_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) |
||||
{ |
||||
return usbdk_get_config_descriptor(dev, _usbdk_device_priv(dev)->active_configuration, |
||||
buffer, len, host_endian); |
||||
} |
||||
|
||||
static int usbdk_open(struct libusb_device_handle *dev_handle) |
||||
{ |
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); |
||||
|
||||
priv->redirector_handle = usbdk_helper.StartRedirect(&priv->info.ID); |
||||
if (priv->redirector_handle == INVALID_HANDLE_VALUE) { |
||||
usbi_err(DEVICE_CTX(dev_handle->dev), "Redirector startup failed"); |
||||
return LIBUSB_ERROR_OTHER; |
||||
} |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static void usbdk_close(struct libusb_device_handle *dev_handle) |
||||
{ |
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); |
||||
|
||||
if (!usbdk_helper.StopRedirect(priv->redirector_handle)) { |
||||
struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); |
||||
usbi_err(ctx, "Redirector shutdown failed"); |
||||
} |
||||
} |
||||
|
||||
static int usbdk_get_configuration(struct libusb_device_handle *dev_handle, int *config) |
||||
{ |
||||
*config = _usbdk_device_priv(dev_handle->dev)->active_configuration; |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int usbdk_set_configuration(struct libusb_device_handle *dev_handle, int config) |
||||
{ |
||||
UNUSED(dev_handle); |
||||
UNUSED(config); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int usbdk_claim_interface(struct libusb_device_handle *dev_handle, int iface) |
||||
{ |
||||
UNUSED(dev_handle); |
||||
UNUSED(iface); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int usbdk_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) |
||||
{ |
||||
struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); |
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); |
||||
|
||||
if (!usbdk_helper.SetAltsetting(priv->redirector_handle, iface, altsetting)) { |
||||
usbi_err(ctx, "SetAltsetting failed: %s", windows_error_str(0)); |
||||
return LIBUSB_ERROR_NO_DEVICE; |
||||
} |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int usbdk_release_interface(struct libusb_device_handle *dev_handle, int iface) |
||||
{ |
||||
UNUSED(dev_handle); |
||||
UNUSED(iface); |
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int usbdk_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) |
||||
{ |
||||
struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); |
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); |
||||
|
||||
if (!usbdk_helper.ResetPipe(priv->redirector_handle, endpoint)) { |
||||
usbi_err(ctx, "ResetPipe failed: %s", windows_error_str(0)); |
||||
return LIBUSB_ERROR_NO_DEVICE; |
||||
} |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int usbdk_reset_device(struct libusb_device_handle *dev_handle) |
||||
{ |
||||
struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); |
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev); |
||||
|
||||
if (!usbdk_helper.ResetDevice(priv->redirector_handle)) { |
||||
usbi_err(ctx, "ResetDevice failed: %s", windows_error_str(0)); |
||||
return LIBUSB_ERROR_NO_DEVICE; |
||||
} |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int usbdk_kernel_driver_active(struct libusb_device_handle *dev_handle, int iface) |
||||
{ |
||||
UNUSED(dev_handle); |
||||
UNUSED(iface); |
||||
return LIBUSB_ERROR_NOT_SUPPORTED; |
||||
} |
||||
|
||||
static int usbdk_attach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) |
||||
{ |
||||
UNUSED(dev_handle); |
||||
UNUSED(iface); |
||||
return LIBUSB_ERROR_NOT_SUPPORTED; |
||||
} |
||||
|
||||
static int usbdk_detach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) |
||||
{ |
||||
UNUSED(dev_handle); |
||||
UNUSED(iface); |
||||
return LIBUSB_ERROR_NOT_SUPPORTED; |
||||
} |
||||
|
||||
static void usbdk_destroy_device(struct libusb_device *dev) |
||||
{ |
||||
struct usbdk_device_priv* p = _usbdk_device_priv(dev); |
||||
|
||||
if (p->config_descriptors != NULL) |
||||
usbdk_release_config_descriptors(p, p->info.DeviceDescriptor.bNumConfigurations); |
||||
} |
||||
|
||||
void windows_clear_transfer_priv(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
|
||||
usbi_free_fd(&transfer_priv->pollable_fd); |
||||
|
||||
if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { |
||||
safe_free(transfer_priv->IsochronousPacketsArray); |
||||
safe_free(transfer_priv->IsochronousResultsArray); |
||||
} |
||||
} |
||||
|
||||
static int usbdk_do_control_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); |
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); |
||||
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); |
||||
struct winfd wfd; |
||||
ULONG Length; |
||||
TransferResult transResult; |
||||
HANDLE sysHandle; |
||||
|
||||
sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle); |
||||
|
||||
wfd = usbi_create_fd(sysHandle, RW_READ, NULL, NULL); |
||||
// Always use the handle returned from usbi_create_fd (wfd.handle)
|
||||
if (wfd.fd < 0) |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
|
||||
transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer; |
||||
transfer_priv->request.BufferLength = transfer->length; |
||||
transfer_priv->request.TransferType = ControlTransferType; |
||||
transfer_priv->pollable_fd = INVALID_WINFD; |
||||
Length = (ULONG)transfer->length; |
||||
|
||||
if (IS_XFERIN(transfer)) |
||||
transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); |
||||
else |
||||
transResult = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); |
||||
|
||||
switch (transResult) { |
||||
case TransferSuccess: |
||||
wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; |
||||
wfd.overlapped->InternalHigh = (DWORD)Length; |
||||
break; |
||||
case TransferSuccessAsync: |
||||
break; |
||||
case TransferFailure: |
||||
usbi_err(ctx, "ControlTransfer failed: %s", windows_error_str(0)); |
||||
usbi_free_fd(&wfd); |
||||
return LIBUSB_ERROR_IO; |
||||
} |
||||
|
||||
// Use priv_transfer to store data needed for async polling
|
||||
transfer_priv->pollable_fd = wfd; |
||||
usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int usbdk_do_bulk_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); |
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); |
||||
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); |
||||
struct winfd wfd; |
||||
TransferResult transferRes; |
||||
HANDLE sysHandle; |
||||
|
||||
transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer; |
||||
transfer_priv->request.BufferLength = transfer->length; |
||||
transfer_priv->request.EndpointAddress = transfer->endpoint; |
||||
|
||||
switch (transfer->type) { |
||||
case LIBUSB_TRANSFER_TYPE_BULK: |
||||
transfer_priv->request.TransferType = BulkTransferType; |
||||
break; |
||||
case LIBUSB_TRANSFER_TYPE_INTERRUPT: |
||||
transfer_priv->request.TransferType = IntertuptTransferType; |
||||
break; |
||||
default: |
||||
usbi_err(ctx, "Wrong transfer type (%d) in usbdk_do_bulk_transfer. %s", transfer->type, windows_error_str(0)); |
||||
return LIBUSB_ERROR_INVALID_PARAM; |
||||
} |
||||
|
||||
transfer_priv->pollable_fd = INVALID_WINFD; |
||||
|
||||
sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle); |
||||
|
||||
wfd = usbi_create_fd(sysHandle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL); |
||||
// Always use the handle returned from usbi_create_fd (wfd.handle)
|
||||
if (wfd.fd < 0) |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
|
||||
if (IS_XFERIN(transfer)) |
||||
transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); |
||||
else |
||||
transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); |
||||
|
||||
switch (transferRes) { |
||||
case TransferSuccess: |
||||
wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; |
||||
break; |
||||
case TransferSuccessAsync: |
||||
break; |
||||
case TransferFailure: |
||||
usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0)); |
||||
usbi_free_fd(&wfd); |
||||
return LIBUSB_ERROR_IO; |
||||
} |
||||
|
||||
transfer_priv->pollable_fd = wfd; |
||||
usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, IS_XFERIN(transfer) ? POLLIN : POLLOUT); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int usbdk_do_iso_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); |
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); |
||||
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); |
||||
struct winfd wfd; |
||||
TransferResult transferRes; |
||||
int i; |
||||
HANDLE sysHandle; |
||||
|
||||
transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer; |
||||
transfer_priv->request.BufferLength = transfer->length; |
||||
transfer_priv->request.EndpointAddress = transfer->endpoint; |
||||
transfer_priv->request.TransferType = IsochronousTransferType; |
||||
transfer_priv->request.IsochronousPacketsArraySize = transfer->num_iso_packets; |
||||
transfer_priv->IsochronousPacketsArray = malloc(transfer->num_iso_packets * sizeof(ULONG64)); |
||||
transfer_priv->request.IsochronousPacketsArray = (PVOID64)(uintptr_t)transfer_priv->IsochronousPacketsArray; |
||||
if (!transfer_priv->IsochronousPacketsArray) { |
||||
usbi_err(ctx, "Allocation of IsochronousPacketsArray is failed, %s", windows_error_str(0)); |
||||
return LIBUSB_ERROR_IO; |
||||
} |
||||
|
||||
transfer_priv->IsochronousResultsArray = malloc(transfer->num_iso_packets * sizeof(USB_DK_ISO_TRANSFER_RESULT)); |
||||
transfer_priv->request.Result.IsochronousResultsArray = (PVOID64)(uintptr_t)transfer_priv->IsochronousResultsArray; |
||||
if (!transfer_priv->IsochronousResultsArray) { |
||||
usbi_err(ctx, "Allocation of isochronousResultsArray is failed, %s", windows_error_str(0)); |
||||
free(transfer_priv->IsochronousPacketsArray); |
||||
return LIBUSB_ERROR_IO; |
||||
} |
||||
|
||||
for (i = 0; i < transfer->num_iso_packets; i++) |
||||
transfer_priv->IsochronousPacketsArray[i] = transfer->iso_packet_desc[i].length; |
||||
|
||||
transfer_priv->pollable_fd = INVALID_WINFD; |
||||
|
||||
sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle); |
||||
|
||||
wfd = usbi_create_fd(sysHandle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL); |
||||
// Always use the handle returned from usbi_create_fd (wfd.handle)
|
||||
if (wfd.fd < 0) { |
||||
free(transfer_priv->IsochronousPacketsArray); |
||||
free(transfer_priv->IsochronousResultsArray); |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
} |
||||
|
||||
if (IS_XFERIN(transfer)) |
||||
transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); |
||||
else |
||||
transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); |
||||
|
||||
switch (transferRes) { |
||||
case TransferSuccess: |
||||
wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; |
||||
break; |
||||
case TransferSuccessAsync: |
||||
break; |
||||
case TransferFailure: |
||||
usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0)); |
||||
usbi_free_fd(&wfd); |
||||
free(transfer_priv->IsochronousPacketsArray); |
||||
free(transfer_priv->IsochronousResultsArray); |
||||
return LIBUSB_ERROR_IO; |
||||
} |
||||
|
||||
transfer_priv->pollable_fd = wfd; |
||||
usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, IS_XFERIN(transfer) ? POLLIN : POLLOUT); |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int usbdk_submit_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
|
||||
switch (transfer->type) { |
||||
case LIBUSB_TRANSFER_TYPE_CONTROL: |
||||
return usbdk_do_control_transfer(itransfer); |
||||
case LIBUSB_TRANSFER_TYPE_BULK: |
||||
case LIBUSB_TRANSFER_TYPE_INTERRUPT: |
||||
if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET)) |
||||
return LIBUSB_ERROR_NOT_SUPPORTED; //TODO: Check whether we can support this in UsbDk
|
||||
else |
||||
return usbdk_do_bulk_transfer(itransfer); |
||||
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: |
||||
return usbdk_do_iso_transfer(itransfer); |
||||
default: |
||||
usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); |
||||
return LIBUSB_ERROR_INVALID_PARAM; |
||||
} |
||||
} |
||||
|
||||
static int usbdk_abort_transfers(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); |
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); |
||||
|
||||
if (!usbdk_helper.AbortPipe(priv->redirector_handle, transfer->endpoint)) { |
||||
usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0)); |
||||
return LIBUSB_ERROR_NO_DEVICE; |
||||
} |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
static int usbdk_cancel_transfer(struct usbi_transfer *itransfer) |
||||
{ |
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); |
||||
|
||||
switch (transfer->type) { |
||||
case LIBUSB_TRANSFER_TYPE_CONTROL: |
||||
// Control transfers cancelled by IoCancelXXX() API
|
||||
// No special treatment needed
|
||||
return LIBUSB_SUCCESS; |
||||
case LIBUSB_TRANSFER_TYPE_BULK: |
||||
case LIBUSB_TRANSFER_TYPE_INTERRUPT: |
||||
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: |
||||
return usbdk_abort_transfers(itransfer); |
||||
default: |
||||
usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); |
||||
return LIBUSB_ERROR_INVALID_PARAM; |
||||
} |
||||
} |
||||
|
||||
int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) |
||||
{ |
||||
itransfer->transferred += io_size; |
||||
return LIBUSB_TRANSFER_COMPLETED; |
||||
} |
||||
|
||||
struct winfd *windows_get_fd(struct usbi_transfer *transfer) |
||||
{ |
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer); |
||||
return &transfer_priv->pollable_fd; |
||||
} |
||||
|
||||
static DWORD usbdk_translate_usbd_status(USBD_STATUS UsbdStatus) |
||||
{ |
||||
if (USBD_SUCCESS(UsbdStatus)) |
||||
return NO_ERROR; |
||||
|
||||
switch (UsbdStatus) { |
||||
case USBD_STATUS_STALL_PID: |
||||
case USBD_STATUS_ENDPOINT_HALTED: |
||||
case USBD_STATUS_BAD_START_FRAME: |
||||
return ERROR_GEN_FAILURE; |
||||
case USBD_STATUS_TIMEOUT: |
||||
return ERROR_SEM_TIMEOUT; |
||||
case USBD_STATUS_CANCELED: |
||||
return ERROR_OPERATION_ABORTED; |
||||
default: |
||||
return ERROR_FUNCTION_FAILED; |
||||
} |
||||
} |
||||
|
||||
void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size) |
||||
{ |
||||
if (HasOverlappedIoCompletedSync(pollable_fd->overlapped) // Handle async requests that completed synchronously first
|
||||
|| GetOverlappedResult(pollable_fd->handle, pollable_fd->overlapped, io_size, false)) { // Regular async overlapped
|
||||
struct libusb_transfer *ltransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer); |
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer); |
||||
|
||||
if (ltransfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { |
||||
int i; |
||||
for (i = 0; i < transfer_priv->request.IsochronousPacketsArraySize; i++) { |
||||
struct libusb_iso_packet_descriptor *lib_desc = <ransfer->iso_packet_desc[i]; |
||||
|
||||
switch (transfer_priv->IsochronousResultsArray[i].TransferResult) { |
||||
case STATUS_SUCCESS: |
||||
case STATUS_CANCELLED: |
||||
case STATUS_REQUEST_CANCELED: |
||||
lib_desc->status = LIBUSB_TRANSFER_COMPLETED; // == ERROR_SUCCESS
|
||||
break; |
||||
default: |
||||
lib_desc->status = LIBUSB_TRANSFER_ERROR; // ERROR_UNKNOWN_EXCEPTION;
|
||||
break; |
||||
} |
||||
|
||||
lib_desc->actual_length = (unsigned int)transfer_priv->IsochronousResultsArray[i].ActualLength; |
||||
} |
||||
} |
||||
|
||||
*io_size = (DWORD) transfer_priv->request.Result.GenResult.BytesTransferred; |
||||
*io_result = usbdk_translate_usbd_status((USBD_STATUS) transfer_priv->request.Result.GenResult.UsbdStatus); |
||||
} |
||||
else { |
||||
*io_result = GetLastError(); |
||||
} |
||||
} |
||||
|
||||
static int usbdk_clock_gettime(int clk_id, struct timespec *tp) |
||||
{ |
||||
return windows_clock_gettime(clk_id, tp); |
||||
} |
||||
|
||||
const struct usbi_os_backend usbdk_backend = { |
||||
"Windows", |
||||
USBI_CAP_HAS_HID_ACCESS, |
||||
usbdk_init, |
||||
usbdk_exit, |
||||
|
||||
usbdk_get_device_list, |
||||
NULL, |
||||
usbdk_open, |
||||
usbdk_close, |
||||
|
||||
usbdk_get_device_descriptor, |
||||
usbdk_get_active_config_descriptor, |
||||
usbdk_get_config_descriptor, |
||||
NULL, |
||||
|
||||
usbdk_get_configuration, |
||||
usbdk_set_configuration, |
||||
usbdk_claim_interface, |
||||
usbdk_release_interface, |
||||
|
||||
usbdk_set_interface_altsetting, |
||||
usbdk_clear_halt, |
||||
usbdk_reset_device, |
||||
|
||||
NULL, |
||||
NULL, |
||||
|
||||
NULL, // dev_mem_alloc()
|
||||
NULL, // dev_mem_free()
|
||||
|
||||
usbdk_kernel_driver_active, |
||||
usbdk_detach_kernel_driver, |
||||
usbdk_attach_kernel_driver, |
||||
|
||||
usbdk_destroy_device, |
||||
|
||||
usbdk_submit_transfer, |
||||
usbdk_cancel_transfer, |
||||
windows_clear_transfer_priv, |
||||
|
||||
windows_handle_events, |
||||
NULL, |
||||
|
||||
usbdk_clock_gettime, |
||||
#if defined(USBI_TIMERFD_AVAILABLE) |
||||
NULL, |
||||
#endif |
||||
sizeof(struct usbdk_device_priv), |
||||
0, |
||||
sizeof(struct usbdk_transfer_priv), |
||||
}; |
||||
|
||||
#endif /* USE_USBDK */ |
@ -0,0 +1,146 @@ |
||||
/*
|
||||
* windows UsbDk backend for libusb 1.0 |
||||
* Copyright © 2014 Red Hat, Inc. |
||||
|
||||
* Authors: |
||||
* Dmitry Fleytman <dmitry@daynix.com> |
||||
* Pavel Gurvich <pavel@daynix.com> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
typedef struct tag_USB_DK_DEVICE_ID { |
||||
WCHAR DeviceID[MAX_DEVICE_ID_LEN]; |
||||
WCHAR InstanceID[MAX_DEVICE_ID_LEN]; |
||||
} USB_DK_DEVICE_ID, *PUSB_DK_DEVICE_ID; |
||||
|
||||
static inline void UsbDkFillIDStruct(USB_DK_DEVICE_ID *ID, PCWCHAR DeviceID, PCWCHAR InstanceID) |
||||
{ |
||||
wcsncpy_s(ID->DeviceID, DeviceID, MAX_DEVICE_ID_LEN); |
||||
wcsncpy_s(ID->InstanceID, InstanceID, MAX_DEVICE_ID_LEN); |
||||
} |
||||
|
||||
typedef struct tag_USB_DK_DEVICE_INFO { |
||||
USB_DK_DEVICE_ID ID; |
||||
ULONG64 FilterID; |
||||
ULONG64 Port; |
||||
ULONG64 Speed; |
||||
USB_DEVICE_DESCRIPTOR DeviceDescriptor; |
||||
} USB_DK_DEVICE_INFO, *PUSB_DK_DEVICE_INFO; |
||||
|
||||
typedef struct tag_USB_DK_CONFIG_DESCRIPTOR_REQUEST { |
||||
USB_DK_DEVICE_ID ID; |
||||
ULONG64 Index; |
||||
} USB_DK_CONFIG_DESCRIPTOR_REQUEST, *PUSB_DK_CONFIG_DESCRIPTOR_REQUEST; |
||||
|
||||
typedef struct tag_USB_DK_ISO_TRANSFER_RESULT { |
||||
ULONG64 ActualLength; |
||||
ULONG64 TransferResult; |
||||
} USB_DK_ISO_TRANSFER_RESULT, *PUSB_DK_ISO_TRANSFER_RESULT; |
||||
|
||||
typedef struct tag_USB_DK_GEN_TRANSFER_RESULT { |
||||
ULONG64 BytesTransferred; |
||||
ULONG64 UsbdStatus; // USBD_STATUS code
|
||||
} USB_DK_GEN_TRANSFER_RESULT, *PUSB_DK_GEN_TRANSFER_RESULT; |
||||
|
||||
typedef struct tag_USB_DK_TRANSFER_RESULT { |
||||
USB_DK_GEN_TRANSFER_RESULT GenResult; |
||||
PVOID64 IsochronousResultsArray; // array of USB_DK_ISO_TRANSFER_RESULT
|
||||
} USB_DK_TRANSFER_RESULT, *PUSB_DK_TRANSFER_RESULT; |
||||
|
||||
typedef struct tag_USB_DK_TRANSFER_REQUEST { |
||||
ULONG64 EndpointAddress; |
||||
PVOID64 Buffer; |
||||
ULONG64 BufferLength; |
||||
ULONG64 TransferType; |
||||
ULONG64 IsochronousPacketsArraySize; |
||||
PVOID64 IsochronousPacketsArray; |
||||
|
||||
USB_DK_TRANSFER_RESULT Result; |
||||
} USB_DK_TRANSFER_REQUEST, *PUSB_DK_TRANSFER_REQUEST; |
||||
|
||||
typedef enum { |
||||
TransferFailure = 0, |
||||
TransferSuccess, |
||||
TransferSuccessAsync |
||||
} TransferResult; |
||||
|
||||
typedef enum { |
||||
NoSpeed = 0, |
||||
LowSpeed, |
||||
FullSpeed, |
||||
HighSpeed, |
||||
SuperSpeed |
||||
} USB_DK_DEVICE_SPEED; |
||||
|
||||
typedef enum { |
||||
ControlTransferType, |
||||
BulkTransferType, |
||||
IntertuptTransferType, |
||||
IsochronousTransferType |
||||
} USB_DK_TRANSFER_TYPE; |
||||
|
||||
typedef BOOL (__cdecl *USBDK_GET_DEVICES_LIST)( |
||||
PUSB_DK_DEVICE_INFO *DeviceInfo, |
||||
PULONG DeviceNumber |
||||
); |
||||
typedef void (__cdecl *USBDK_RELEASE_DEVICES_LIST)( |
||||
PUSB_DK_DEVICE_INFO DeviceInfo |
||||
); |
||||
typedef HANDLE (__cdecl *USBDK_START_REDIRECT)( |
||||
PUSB_DK_DEVICE_ID DeviceId |
||||
); |
||||
typedef BOOL (__cdecl *USBDK_STOP_REDIRECT)( |
||||
HANDLE DeviceHandle |
||||
); |
||||
typedef BOOL (__cdecl *USBDK_GET_CONFIGURATION_DESCRIPTOR)( |
||||
PUSB_DK_CONFIG_DESCRIPTOR_REQUEST Request, |
||||
PUSB_CONFIGURATION_DESCRIPTOR *Descriptor, |
||||
PULONG Length |
||||
); |
||||
typedef void (__cdecl *USBDK_RELEASE_CONFIGURATION_DESCRIPTOR)( |
||||
PUSB_CONFIGURATION_DESCRIPTOR Descriptor |
||||
); |
||||
typedef TransferResult (__cdecl *USBDK_WRITE_PIPE)( |
||||
HANDLE DeviceHandle, |
||||
PUSB_DK_TRANSFER_REQUEST Request, |
||||
LPOVERLAPPED lpOverlapped |
||||
); |
||||
typedef TransferResult (__cdecl *USBDK_READ_PIPE)( |
||||
HANDLE DeviceHandle, |
||||
PUSB_DK_TRANSFER_REQUEST Request, |
||||
LPOVERLAPPED lpOverlapped |
||||
); |
||||
typedef BOOL (__cdecl *USBDK_ABORT_PIPE)( |
||||
HANDLE DeviceHandle, |
||||
ULONG64 PipeAddress |
||||
); |
||||
typedef BOOL (__cdecl *USBDK_RESET_PIPE)( |
||||
HANDLE DeviceHandle, |
||||
ULONG64 PipeAddress |
||||
); |
||||
typedef BOOL (__cdecl *USBDK_SET_ALTSETTING)( |
||||
HANDLE DeviceHandle, |
||||
ULONG64 InterfaceIdx, |
||||
ULONG64 AltSettingIdx |
||||
); |
||||
typedef BOOL (__cdecl *USBDK_RESET_DEVICE)( |
||||
HANDLE DeviceHandle |
||||
); |
||||
typedef HANDLE (__cdecl *USBDK_GET_REDIRECTOR_SYSTEM_HANDLE)( |
||||
HANDLE DeviceHandle |
||||
); |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,937 @@ |
||||
/*
|
||||
* Windows backend for libusb 1.0 |
||||
* Copyright © 2009-2012 Pete Batard <pete@akeo.ie> |
||||
* With contributions from Michael Plante, Orin Eman et al. |
||||
* Parts of this code adapted from libusb-win32-v1 by Stephan Meyer |
||||
* Major code testing contribution by Xiaofan Chen |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include "windows_common.h" |
||||
#include "windows_nt_common.h" |
||||
|
||||
#if defined(_MSC_VER) |
||||
// disable /W4 MSVC warnings that are benign
|
||||
#pragma warning(disable:4100) // unreferenced formal parameter
|
||||
#pragma warning(disable:4127) // conditional expression is constant
|
||||
#pragma warning(disable:4201) // nameless struct/union
|
||||
#pragma warning(disable:4214) // bit field types other than int
|
||||
#pragma warning(disable:4996) // deprecated API calls
|
||||
#pragma warning(disable:28159) // more deprecated API calls
|
||||
#endif |
||||
|
||||
// Missing from MSVC6 setupapi.h
|
||||
#if !defined(SPDRP_ADDRESS) |
||||
#define SPDRP_ADDRESS 28 |
||||
#endif |
||||
#if !defined(SPDRP_INSTALL_STATE) |
||||
#define SPDRP_INSTALL_STATE 34 |
||||
#endif |
||||
|
||||
#define MAX_CTRL_BUFFER_LENGTH 4096 |
||||
#define MAX_USB_DEVICES 256 |
||||
#define MAX_USB_STRING_LENGTH 128 |
||||
#define MAX_HID_REPORT_SIZE 1024 |
||||
#define MAX_HID_DESCRIPTOR_SIZE 256 |
||||
#define MAX_GUID_STRING_LENGTH 40 |
||||
#define MAX_PATH_LENGTH 128 |
||||
#define MAX_KEY_LENGTH 256 |
||||
#define LIST_SEPARATOR ';' |
||||
|
||||
// Handle code for HID interface that have been claimed ("dibs")
|
||||
#define INTERFACE_CLAIMED ((HANDLE)(intptr_t)0xD1B5) |
||||
// Additional return code for HID operations that completed synchronously
|
||||
#define LIBUSB_COMPLETED (LIBUSB_SUCCESS + 1) |
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ff545978.aspx
|
||||
// http://msdn.microsoft.com/en-us/library/ff545972.aspx
|
||||
// http://msdn.microsoft.com/en-us/library/ff545982.aspx
|
||||
#if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER) |
||||
const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27} }; |
||||
#endif |
||||
#if !defined(GUID_DEVINTERFACE_USB_DEVICE) |
||||
const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} }; |
||||
#endif |
||||
#if !defined(GUID_DEVINTERFACE_USB_HUB) |
||||
const GUID GUID_DEVINTERFACE_USB_HUB = { 0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8} }; |
||||
#endif |
||||
#if !defined(GUID_DEVINTERFACE_LIBUSB0_FILTER) |
||||
const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9} }; |
||||
#endif |
||||
|
||||
|
||||
/*
|
||||
* Multiple USB API backend support |
||||
*/ |
||||
#define USB_API_UNSUPPORTED 0 |
||||
#define USB_API_HUB 1 |
||||
#define USB_API_COMPOSITE 2 |
||||
#define USB_API_WINUSBX 3 |
||||
#define USB_API_HID 4 |
||||
#define USB_API_MAX 5 |
||||
// The following is used to indicate if the HID or composite extra props have already been set.
|
||||
#define USB_API_SET (1 << USB_API_MAX) |
||||
|
||||
// Sub-APIs for WinUSB-like driver APIs (WinUSB, libusbK, libusb-win32 through the libusbK DLL)
|
||||
// Must have the same values as the KUSB_DRVID enum from libusbk.h
|
||||
#define SUB_API_NOTSET -1 |
||||
#define SUB_API_LIBUSBK 0 |
||||
#define SUB_API_LIBUSB0 1 |
||||
#define SUB_API_WINUSB 2 |
||||
#define SUB_API_MAX 3 |
||||
|
||||
#define WINUSBX_DRV_NAMES {"libusbK", "libusb0", "WinUSB"} |
||||
|
||||
struct windows_usb_api_backend { |
||||
const uint8_t id; |
||||
const char *designation; |
||||
const char **driver_name_list; // Driver name, without .sys, e.g. "usbccgp"
|
||||
const uint8_t nb_driver_names; |
||||
int (*init)(int sub_api, struct libusb_context *ctx); |
||||
int (*exit)(int sub_api); |
||||
int (*open)(int sub_api, struct libusb_device_handle *dev_handle); |
||||
void (*close)(int sub_api, struct libusb_device_handle *dev_handle); |
||||
int (*configure_endpoints)(int sub_api, struct libusb_device_handle *dev_handle, int iface); |
||||
int (*claim_interface)(int sub_api, struct libusb_device_handle *dev_handle, int iface); |
||||
int (*set_interface_altsetting)(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting); |
||||
int (*release_interface)(int sub_api, struct libusb_device_handle *dev_handle, int iface); |
||||
int (*clear_halt)(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); |
||||
int (*reset_device)(int sub_api, struct libusb_device_handle *dev_handle); |
||||
int (*submit_bulk_transfer)(int sub_api, struct usbi_transfer *itransfer); |
||||
int (*submit_iso_transfer)(int sub_api, struct usbi_transfer *itransfer); |
||||
int (*submit_control_transfer)(int sub_api, struct usbi_transfer *itransfer); |
||||
int (*abort_control)(int sub_api, struct usbi_transfer *itransfer); |
||||
int (*abort_transfers)(int sub_api, struct usbi_transfer *itransfer); |
||||
int (*copy_transfer_data)(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size); |
||||
}; |
||||
|
||||
extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX]; |
||||
|
||||
#define PRINT_UNSUPPORTED_API(fname) \ |
||||
usbi_dbg("unsupported API call for '" \
|
||||
#fname "' (unrecognized device driver)"); \ |
||||
return LIBUSB_ERROR_NOT_SUPPORTED; |
||||
|
||||
/*
|
||||
* private structures definition |
||||
* with inline pseudo constructors/destructors |
||||
*/ |
||||
|
||||
// TODO (v2+): move hid desc to libusb.h?
|
||||
struct libusb_hid_descriptor { |
||||
uint8_t bLength; |
||||
uint8_t bDescriptorType; |
||||
uint16_t bcdHID; |
||||
uint8_t bCountryCode; |
||||
uint8_t bNumDescriptors; |
||||
uint8_t bClassDescriptorType; |
||||
uint16_t wClassDescriptorLength; |
||||
}; |
||||
|
||||
#define LIBUSB_DT_HID_SIZE 9 |
||||
#define HID_MAX_CONFIG_DESC_SIZE (LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE \ |
||||
+ LIBUSB_DT_HID_SIZE + 2 * LIBUSB_DT_ENDPOINT_SIZE) |
||||
#define HID_MAX_REPORT_SIZE 1024 |
||||
#define HID_IN_EP 0x81 |
||||
#define HID_OUT_EP 0x02 |
||||
#define LIBUSB_REQ_RECIPIENT(request_type) ((request_type) & 0x1F) |
||||
#define LIBUSB_REQ_TYPE(request_type) ((request_type) & (0x03 << 5)) |
||||
#define LIBUSB_REQ_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN) |
||||
#define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type)) |
||||
|
||||
// The following are used for HID reports IOCTLs
|
||||
#define HID_CTL_CODE(id) \ |
||||
CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_NEITHER, FILE_ANY_ACCESS) |
||||
#define HID_BUFFER_CTL_CODE(id) \ |
||||
CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_BUFFERED, FILE_ANY_ACCESS) |
||||
#define HID_IN_CTL_CODE(id) \ |
||||
CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_IN_DIRECT, FILE_ANY_ACCESS) |
||||
#define HID_OUT_CTL_CODE(id) \ |
||||
CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) |
||||
|
||||
#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) |
||||
#define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104) |
||||
#define IOCTL_HID_SET_FEATURE HID_IN_CTL_CODE(100) |
||||
#define IOCTL_HID_SET_OUTPUT_REPORT HID_IN_CTL_CODE(101) |
||||
|
||||
enum libusb_hid_request_type { |
||||
HID_REQ_GET_REPORT = 0x01, |
||||
HID_REQ_GET_IDLE = 0x02, |
||||
HID_REQ_GET_PROTOCOL = 0x03, |
||||
HID_REQ_SET_REPORT = 0x09, |
||||
HID_REQ_SET_IDLE = 0x0A, |
||||
HID_REQ_SET_PROTOCOL = 0x0B |
||||
}; |
||||
|
||||
enum libusb_hid_report_type { |
||||
HID_REPORT_TYPE_INPUT = 0x01, |
||||
HID_REPORT_TYPE_OUTPUT = 0x02, |
||||
HID_REPORT_TYPE_FEATURE = 0x03 |
||||
}; |
||||
|
||||
struct hid_device_priv { |
||||
uint16_t vid; |
||||
uint16_t pid; |
||||
uint8_t config; |
||||
uint8_t nb_interfaces; |
||||
bool uses_report_ids[3]; // input, ouptput, feature
|
||||
uint16_t input_report_size; |
||||
uint16_t output_report_size; |
||||
uint16_t feature_report_size; |
||||
WCHAR string[3][MAX_USB_STRING_LENGTH]; |
||||
uint8_t string_index[3]; // man, prod, ser
|
||||
}; |
||||
|
||||
struct windows_device_priv { |
||||
uint8_t depth; // distance to HCD
|
||||
uint8_t port; // port number on the hub
|
||||
uint8_t active_config; |
||||
struct windows_usb_api_backend const *apib; |
||||
char *path; // device interface path
|
||||
int sub_api; // for WinUSB-like APIs
|
||||
struct { |
||||
char *path; // each interface needs a device interface path,
|
||||
struct windows_usb_api_backend const *apib; // an API backend (multiple drivers support),
|
||||
int sub_api; |
||||
int8_t nb_endpoints; // and a set of endpoint addresses (USB_MAXENDPOINTS)
|
||||
uint8_t *endpoint; |
||||
bool restricted_functionality; // indicates if the interface functionality is restricted
|
||||
// by Windows (eg. HID keyboards or mice cannot do R/W)
|
||||
} usb_interface[USB_MAXINTERFACES]; |
||||
struct hid_device_priv *hid; |
||||
USB_DEVICE_DESCRIPTOR dev_descriptor; |
||||
unsigned char **config_descriptor; // list of pointers to the cached config descriptors
|
||||
}; |
||||
|
||||
static inline struct windows_device_priv *_device_priv(struct libusb_device *dev) |
||||
{ |
||||
return (struct windows_device_priv *)dev->os_priv; |
||||
} |
||||
|
||||
static inline struct windows_device_priv *windows_device_priv_init(struct libusb_device *dev) |
||||
{ |
||||
struct windows_device_priv *p = _device_priv(dev); |
||||
int i; |
||||
|
||||
p->apib = &usb_api_backend[USB_API_UNSUPPORTED]; |
||||
p->sub_api = SUB_API_NOTSET; |
||||
for (i = 0; i < USB_MAXINTERFACES; i++) { |
||||
p->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED]; |
||||
p->usb_interface[i].sub_api = SUB_API_NOTSET; |
||||
} |
||||
|
||||
return p; |
||||
} |
||||
|
||||
static inline void windows_device_priv_release(struct libusb_device *dev) |
||||
{ |
||||
struct windows_device_priv *p = _device_priv(dev); |
||||
int i; |
||||
|
||||
free(p->path); |
||||
if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) { |
||||
for (i = 0; i < dev->num_configurations; i++) |
||||
free(p->config_descriptor[i]); |
||||
} |
||||
free(p->config_descriptor); |
||||
free(p->hid); |
||||
for (i = 0; i < USB_MAXINTERFACES; i++) { |
||||
free(p->usb_interface[i].path); |
||||
free(p->usb_interface[i].endpoint); |
||||
} |
||||
} |
||||
|
||||
struct interface_handle_t { |
||||
HANDLE dev_handle; // WinUSB needs an extra handle for the file
|
||||
HANDLE api_handle; // used by the API to communicate with the device
|
||||
}; |
||||
|
||||
struct windows_device_handle_priv { |
||||
int active_interface; |
||||
struct interface_handle_t interface_handle[USB_MAXINTERFACES]; |
||||
int autoclaim_count[USB_MAXINTERFACES]; // For auto-release
|
||||
}; |
||||
|
||||
static inline struct windows_device_handle_priv *_device_handle_priv( |
||||
struct libusb_device_handle *handle) |
||||
{ |
||||
return (struct windows_device_handle_priv *)handle->os_priv; |
||||
} |
||||
|
||||
// used for async polling functions
|
||||
struct windows_transfer_priv { |
||||
struct winfd pollable_fd; |
||||
uint8_t interface_number; |
||||
uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID
|
||||
uint8_t *hid_dest; // transfer buffer destination, required for HID
|
||||
size_t hid_expected_size; |
||||
}; |
||||
|
||||
// used to match a device driver (including filter drivers) against a supported API
|
||||
struct driver_lookup { |
||||
char list[MAX_KEY_LENGTH + 1]; // REG_MULTI_SZ list of services (driver) names
|
||||
const DWORD reg_prop; // SPDRP registry key to use to retrieve list
|
||||
const char* designation; // internal designation (for debug output)
|
||||
}; |
||||
|
||||
/* OLE32 dependency */ |
||||
DLL_DECLARE_HANDLE(OLE32); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID)); |
||||
|
||||
/* Kernel32 dependencies */ |
||||
DLL_DECLARE_HANDLE(Kernel32); |
||||
/* This call is only available from XP SP2 */ |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, IsWow64Process, (HANDLE, PBOOL)); |
||||
|
||||
/* SetupAPI dependencies */ |
||||
DLL_DECLARE_HANDLE(SetupAPI); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (const GUID*, PCSTR, HWND, DWORD)); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInfo, (HDEVINFO, DWORD, PSP_DEVINFO_DATA)); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInterfaces, (HDEVINFO, PSP_DEVINFO_DATA, |
||||
const GUID*, DWORD, PSP_DEVICE_INTERFACE_DATA)); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceInterfaceDetailA, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, |
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA_A, DWORD, PDWORD, PSP_DEVINFO_DATA)); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVINFO)); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDevRegKey, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM)); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceRegistryPropertyA, (HDEVINFO, |
||||
PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD)); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDeviceInterfaceRegKey, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, DWORD, DWORD)); |
||||
|
||||
/* AdvAPI32 dependencies */ |
||||
DLL_DECLARE_HANDLE(AdvAPI32); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD)); |
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY)); |
||||
|
||||
/*
|
||||
* Windows DDK API definitions. Most of it copied from MinGW's includes |
||||
*/ |
||||
typedef DWORD DEVNODE, DEVINST; |
||||
typedef DEVNODE *PDEVNODE, *PDEVINST; |
||||
typedef DWORD RETURN_TYPE; |
||||
typedef RETURN_TYPE CONFIGRET; |
||||
|
||||
#define CR_SUCCESS 0x00000000 |
||||
#define CR_NO_SUCH_DEVNODE 0x0000000D |
||||
|
||||
#define USB_DEVICE_DESCRIPTOR_TYPE LIBUSB_DT_DEVICE |
||||
#define USB_CONFIGURATION_DESCRIPTOR_TYPE LIBUSB_DT_CONFIG |
||||
#define USB_STRING_DESCRIPTOR_TYPE LIBUSB_DT_STRING |
||||
#define USB_INTERFACE_DESCRIPTOR_TYPE LIBUSB_DT_INTERFACE |
||||
#define USB_ENDPOINT_DESCRIPTOR_TYPE LIBUSB_DT_ENDPOINT |
||||
|
||||
#define USB_REQUEST_GET_STATUS LIBUSB_REQUEST_GET_STATUS |
||||
#define USB_REQUEST_CLEAR_FEATURE LIBUSB_REQUEST_CLEAR_FEATURE |
||||
#define USB_REQUEST_SET_FEATURE LIBUSB_REQUEST_SET_FEATURE |
||||
#define USB_REQUEST_SET_ADDRESS LIBUSB_REQUEST_SET_ADDRESS |
||||
#define USB_REQUEST_GET_DESCRIPTOR LIBUSB_REQUEST_GET_DESCRIPTOR |
||||
#define USB_REQUEST_SET_DESCRIPTOR LIBUSB_REQUEST_SET_DESCRIPTOR |
||||
#define USB_REQUEST_GET_CONFIGURATION LIBUSB_REQUEST_GET_CONFIGURATION |
||||
#define USB_REQUEST_SET_CONFIGURATION LIBUSB_REQUEST_SET_CONFIGURATION |
||||
#define USB_REQUEST_GET_INTERFACE LIBUSB_REQUEST_GET_INTERFACE |
||||
#define USB_REQUEST_SET_INTERFACE LIBUSB_REQUEST_SET_INTERFACE |
||||
#define USB_REQUEST_SYNC_FRAME LIBUSB_REQUEST_SYNCH_FRAME |
||||
|
||||
#define USB_GET_NODE_INFORMATION 258 |
||||
#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260 |
||||
#define USB_GET_NODE_CONNECTION_NAME 261 |
||||
#define USB_GET_HUB_CAPABILITIES 271 |
||||
#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX) |
||||
#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274 |
||||
#endif |
||||
#if !defined(USB_GET_HUB_CAPABILITIES_EX) |
||||
#define USB_GET_HUB_CAPABILITIES_EX 276 |
||||
#endif |
||||
#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX_V2) |
||||
#define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279 |
||||
#endif |
||||
|
||||
#ifndef METHOD_BUFFERED |
||||
#define METHOD_BUFFERED 0 |
||||
#endif |
||||
#ifndef FILE_ANY_ACCESS |
||||
#define FILE_ANY_ACCESS 0x00000000 |
||||
#endif |
||||
#ifndef FILE_DEVICE_UNKNOWN |
||||
#define FILE_DEVICE_UNKNOWN 0x00000022 |
||||
#endif |
||||
#ifndef FILE_DEVICE_USB |
||||
#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN |
||||
#endif |
||||
|
||||
#ifndef CTL_CODE |
||||
#define CTL_CODE(DeviceType, Function, Method, Access) \ |
||||
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) |
||||
#endif |
||||
|
||||
typedef enum USB_CONNECTION_STATUS { |
||||
NoDeviceConnected, |
||||
DeviceConnected, |
||||
DeviceFailedEnumeration, |
||||
DeviceGeneralFailure, |
||||
DeviceCausedOvercurrent, |
||||
DeviceNotEnoughPower, |
||||
DeviceNotEnoughBandwidth, |
||||
DeviceHubNestedTooDeeply, |
||||
DeviceInLegacyHub |
||||
} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS; |
||||
|
||||
typedef enum USB_HUB_NODE { |
||||
UsbHub, |
||||
UsbMIParent |
||||
} USB_HUB_NODE; |
||||
|
||||
/* Cfgmgr32.dll interface */ |
||||
DLL_DECLARE_HANDLE(Cfgmgr32); |
||||
DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG)); |
||||
DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG)); |
||||
DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG)); |
||||
DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG)); |
||||
|
||||
#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \ |
||||
CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) |
||||
|
||||
#define IOCTL_USB_GET_HUB_CAPABILITIES \ |
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS) |
||||
|
||||
#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \ |
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, METHOD_BUFFERED, FILE_ANY_ACCESS) |
||||
|
||||
#define IOCTL_USB_GET_ROOT_HUB_NAME \ |
||||
CTL_CODE(FILE_DEVICE_USB, HCD_GET_ROOT_HUB_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) |
||||
|
||||
#define IOCTL_USB_GET_NODE_INFORMATION \ |
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS) |
||||
|
||||
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \ |
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) |
||||
|
||||
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \ |
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, METHOD_BUFFERED, FILE_ANY_ACCESS) |
||||
|
||||
#define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \ |
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS) |
||||
|
||||
#define IOCTL_USB_GET_NODE_CONNECTION_NAME \ |
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) |
||||
|
||||
// Most of the structures below need to be packed
|
||||
#pragma pack(push, 1) |
||||
|
||||
typedef struct USB_INTERFACE_DESCRIPTOR { |
||||
UCHAR bLength; |
||||
UCHAR bDescriptorType; |
||||
UCHAR bInterfaceNumber; |
||||
UCHAR bAlternateSetting; |
||||
UCHAR bNumEndpoints; |
||||
UCHAR bInterfaceClass; |
||||
UCHAR bInterfaceSubClass; |
||||
UCHAR bInterfaceProtocol; |
||||
UCHAR iInterface; |
||||
} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; |
||||
|
||||
typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT { |
||||
struct { |
||||
ULONG ConnectionIndex; |
||||
struct { |
||||
UCHAR bmRequest; |
||||
UCHAR bRequest; |
||||
USHORT wValue; |
||||
USHORT wIndex; |
||||
USHORT wLength; |
||||
} SetupPacket; |
||||
} req; |
||||
USB_CONFIGURATION_DESCRIPTOR data; |
||||
} USB_CONFIGURATION_DESCRIPTOR_SHORT; |
||||
|
||||
typedef struct USB_ENDPOINT_DESCRIPTOR { |
||||
UCHAR bLength; |
||||
UCHAR bDescriptorType; |
||||
UCHAR bEndpointAddress; |
||||
UCHAR bmAttributes; |
||||
USHORT wMaxPacketSize; |
||||
UCHAR bInterval; |
||||
} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR; |
||||
|
||||
typedef struct USB_DESCRIPTOR_REQUEST { |
||||
ULONG ConnectionIndex; |
||||
struct { |
||||
UCHAR bmRequest; |
||||
UCHAR bRequest; |
||||
USHORT wValue; |
||||
USHORT wIndex; |
||||
USHORT wLength; |
||||
} SetupPacket; |
||||
// UCHAR Data[0];
|
||||
} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST; |
||||
|
||||
typedef struct USB_HUB_DESCRIPTOR { |
||||
UCHAR bDescriptorLength; |
||||
UCHAR bDescriptorType; |
||||
UCHAR bNumberOfPorts; |
||||
USHORT wHubCharacteristics; |
||||
UCHAR bPowerOnToPowerGood; |
||||
UCHAR bHubControlCurrent; |
||||
UCHAR bRemoveAndPowerMask[64]; |
||||
} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR; |
||||
|
||||
typedef struct USB_ROOT_HUB_NAME { |
||||
ULONG ActualLength; |
||||
WCHAR RootHubName[1]; |
||||
} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME; |
||||
|
||||
typedef struct USB_ROOT_HUB_NAME_FIXED { |
||||
ULONG ActualLength; |
||||
WCHAR RootHubName[MAX_PATH_LENGTH]; |
||||
} USB_ROOT_HUB_NAME_FIXED; |
||||
|
||||
typedef struct USB_NODE_CONNECTION_NAME { |
||||
ULONG ConnectionIndex; |
||||
ULONG ActualLength; |
||||
WCHAR NodeName[1]; |
||||
} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME; |
||||
|
||||
typedef struct USB_NODE_CONNECTION_NAME_FIXED { |
||||
ULONG ConnectionIndex; |
||||
ULONG ActualLength; |
||||
WCHAR NodeName[MAX_PATH_LENGTH]; |
||||
} USB_NODE_CONNECTION_NAME_FIXED; |
||||
|
||||
typedef struct USB_HUB_NAME_FIXED { |
||||
union { |
||||
USB_ROOT_HUB_NAME_FIXED root; |
||||
USB_NODE_CONNECTION_NAME_FIXED node; |
||||
} u; |
||||
} USB_HUB_NAME_FIXED; |
||||
|
||||
typedef struct USB_HUB_INFORMATION { |
||||
USB_HUB_DESCRIPTOR HubDescriptor; |
||||
BOOLEAN HubIsBusPowered; |
||||
} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION; |
||||
|
||||
typedef struct USB_MI_PARENT_INFORMATION { |
||||
ULONG NumberOfInterfaces; |
||||
} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION; |
||||
|
||||
typedef struct USB_NODE_INFORMATION { |
||||
USB_HUB_NODE NodeType; |
||||
union { |
||||
USB_HUB_INFORMATION HubInformation; |
||||
USB_MI_PARENT_INFORMATION MiParentInformation; |
||||
} u; |
||||
} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION; |
||||
|
||||
typedef struct USB_PIPE_INFO { |
||||
USB_ENDPOINT_DESCRIPTOR EndpointDescriptor; |
||||
ULONG ScheduleOffset; |
||||
} USB_PIPE_INFO, *PUSB_PIPE_INFO; |
||||
|
||||
typedef struct USB_NODE_CONNECTION_INFORMATION_EX { |
||||
ULONG ConnectionIndex; |
||||
USB_DEVICE_DESCRIPTOR DeviceDescriptor; |
||||
UCHAR CurrentConfigurationValue; |
||||
UCHAR Speed; |
||||
BOOLEAN DeviceIsHub; |
||||
USHORT DeviceAddress; |
||||
ULONG NumberOfOpenPipes; |
||||
USB_CONNECTION_STATUS ConnectionStatus; |
||||
// USB_PIPE_INFO PipeList[0];
|
||||
} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX; |
||||
|
||||
typedef union _USB_PROTOCOLS { |
||||
ULONG ul; |
||||
struct { |
||||
ULONG Usb110:1; |
||||
ULONG Usb200:1; |
||||
ULONG Usb300:1; |
||||
ULONG ReservedMBZ:29; |
||||
}; |
||||
} USB_PROTOCOLS, *PUSB_PROTOCOLS; |
||||
|
||||
typedef union _USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS { |
||||
ULONG ul; |
||||
struct { |
||||
ULONG DeviceIsOperatingAtSuperSpeedOrHigher:1; |
||||
ULONG DeviceIsSuperSpeedCapableOrHigher:1; |
||||
ULONG ReservedMBZ:30; |
||||
}; |
||||
} USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS; |
||||
|
||||
typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 { |
||||
ULONG ConnectionIndex; |
||||
ULONG Length; |
||||
USB_PROTOCOLS SupportedUsbProtocols; |
||||
USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS Flags; |
||||
} USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2; |
||||
|
||||
typedef struct USB_HUB_CAP_FLAGS { |
||||
ULONG HubIsHighSpeedCapable:1; |
||||
ULONG HubIsHighSpeed:1; |
||||
ULONG HubIsMultiTtCapable:1; |
||||
ULONG HubIsMultiTt:1; |
||||
ULONG HubIsRoot:1; |
||||
ULONG HubIsArmedWakeOnConnect:1; |
||||
ULONG ReservedMBZ:26; |
||||
} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS; |
||||
|
||||
typedef struct USB_HUB_CAPABILITIES { |
||||
ULONG HubIs2xCapable:1; |
||||
} USB_HUB_CAPABILITIES, *PUSB_HUB_CAPABILITIES; |
||||
|
||||
typedef struct USB_HUB_CAPABILITIES_EX { |
||||
USB_HUB_CAP_FLAGS CapabilityFlags; |
||||
} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX; |
||||
|
||||
#pragma pack(pop) |
||||
|
||||
/* winusb.dll interface */ |
||||
|
||||
#define SHORT_PACKET_TERMINATE 0x01 |
||||
#define AUTO_CLEAR_STALL 0x02 |
||||
#define PIPE_TRANSFER_TIMEOUT 0x03 |
||||
#define IGNORE_SHORT_PACKETS 0x04 |
||||
#define ALLOW_PARTIAL_READS 0x05 |
||||
#define AUTO_FLUSH 0x06 |
||||
#define RAW_IO 0x07 |
||||
#define MAXIMUM_TRANSFER_SIZE 0x08 |
||||
#define AUTO_SUSPEND 0x81 |
||||
#define SUSPEND_DELAY 0x83 |
||||
#define DEVICE_SPEED 0x01 |
||||
#define LowSpeed 0x01 |
||||
#define FullSpeed 0x02 |
||||
#define HighSpeed 0x03 |
||||
|
||||
typedef enum USBD_PIPE_TYPE { |
||||
UsbdPipeTypeControl, |
||||
UsbdPipeTypeIsochronous, |
||||
UsbdPipeTypeBulk, |
||||
UsbdPipeTypeInterrupt |
||||
} USBD_PIPE_TYPE; |
||||
|
||||
typedef struct { |
||||
USBD_PIPE_TYPE PipeType; |
||||
UCHAR PipeId; |
||||
USHORT MaximumPacketSize; |
||||
UCHAR Interval; |
||||
} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION; |
||||
|
||||
#pragma pack(1) |
||||
typedef struct { |
||||
UCHAR request_type; |
||||
UCHAR request; |
||||
USHORT value; |
||||
USHORT index; |
||||
USHORT length; |
||||
} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET; |
||||
#pragma pack() |
||||
|
||||
typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; |
||||
|
||||
typedef BOOL (WINAPI *WinUsb_AbortPipe_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
UCHAR PipeID |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_ControlTransfer_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
WINUSB_SETUP_PACKET SetupPacket, |
||||
PUCHAR Buffer, |
||||
ULONG BufferLength, |
||||
PULONG LengthTransferred, |
||||
LPOVERLAPPED Overlapped |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_FlushPipe_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
UCHAR PipeID |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_Free_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_GetAssociatedInterface_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
UCHAR AssociatedInterfaceIndex, |
||||
PWINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_GetCurrentAlternateSetting_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
PUCHAR AlternateSetting |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_GetDescriptor_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
UCHAR DescriptorType, |
||||
UCHAR Index, |
||||
USHORT LanguageID, |
||||
PUCHAR Buffer, |
||||
ULONG BufferLength, |
||||
PULONG LengthTransferred |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_GetOverlappedResult_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
LPOVERLAPPED lpOverlapped, |
||||
LPDWORD lpNumberOfBytesTransferred, |
||||
BOOL bWait |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_GetPipePolicy_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
UCHAR PipeID, |
||||
ULONG PolicyType, |
||||
PULONG ValueLength, |
||||
PVOID Value |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_GetPowerPolicy_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
ULONG PolicyType, |
||||
PULONG ValueLength, |
||||
PVOID Value |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_Initialize_t)( |
||||
HANDLE DeviceHandle, |
||||
PWINUSB_INTERFACE_HANDLE InterfaceHandle |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_QueryDeviceInformation_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
ULONG InformationType, |
||||
PULONG BufferLength, |
||||
PVOID Buffer |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_QueryInterfaceSettings_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
UCHAR AlternateSettingNumber, |
||||
PUSB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_QueryPipe_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
UCHAR AlternateInterfaceNumber, |
||||
UCHAR PipeIndex, |
||||
PWINUSB_PIPE_INFORMATION PipeInformation |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_ReadPipe_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
UCHAR PipeID, |
||||
PUCHAR Buffer, |
||||
ULONG BufferLength, |
||||
PULONG LengthTransferred, |
||||
LPOVERLAPPED Overlapped |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_ResetPipe_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
UCHAR PipeID |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_SetCurrentAlternateSetting_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
UCHAR AlternateSetting |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_SetPipePolicy_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
UCHAR PipeID, |
||||
ULONG PolicyType, |
||||
ULONG ValueLength, |
||||
PVOID Value |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_SetPowerPolicy_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
ULONG PolicyType, |
||||
ULONG ValueLength, |
||||
PVOID Value |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_WritePipe_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle, |
||||
UCHAR PipeID, |
||||
PUCHAR Buffer, |
||||
ULONG BufferLength, |
||||
PULONG LengthTransferred, |
||||
LPOVERLAPPED Overlapped |
||||
); |
||||
typedef BOOL (WINAPI *WinUsb_ResetDevice_t)( |
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle |
||||
); |
||||
|
||||
/* /!\ These must match the ones from the official libusbk.h */ |
||||
typedef enum _KUSB_FNID { |
||||
KUSB_FNID_Init, |
||||
KUSB_FNID_Free, |
||||
KUSB_FNID_ClaimInterface, |
||||
KUSB_FNID_ReleaseInterface, |
||||
KUSB_FNID_SetAltInterface, |
||||
KUSB_FNID_GetAltInterface, |
||||
KUSB_FNID_GetDescriptor, |
||||
KUSB_FNID_ControlTransfer, |
||||
KUSB_FNID_SetPowerPolicy, |
||||
KUSB_FNID_GetPowerPolicy, |
||||
KUSB_FNID_SetConfiguration, |
||||
KUSB_FNID_GetConfiguration, |
||||
KUSB_FNID_ResetDevice, |
||||
KUSB_FNID_Initialize, |
||||
KUSB_FNID_SelectInterface, |
||||
KUSB_FNID_GetAssociatedInterface, |
||||
KUSB_FNID_Clone, |
||||
KUSB_FNID_QueryInterfaceSettings, |
||||
KUSB_FNID_QueryDeviceInformation, |
||||
KUSB_FNID_SetCurrentAlternateSetting, |
||||
KUSB_FNID_GetCurrentAlternateSetting, |
||||
KUSB_FNID_QueryPipe, |
||||
KUSB_FNID_SetPipePolicy, |
||||
KUSB_FNID_GetPipePolicy, |
||||
KUSB_FNID_ReadPipe, |
||||
KUSB_FNID_WritePipe, |
||||
KUSB_FNID_ResetPipe, |
||||
KUSB_FNID_AbortPipe, |
||||
KUSB_FNID_FlushPipe, |
||||
KUSB_FNID_IsoReadPipe, |
||||
KUSB_FNID_IsoWritePipe, |
||||
KUSB_FNID_GetCurrentFrameNumber, |
||||
KUSB_FNID_GetOverlappedResult, |
||||
KUSB_FNID_GetProperty, |
||||
KUSB_FNID_COUNT, |
||||
} KUSB_FNID; |
||||
|
||||
typedef struct _KLIB_VERSION { |
||||
INT Major; |
||||
INT Minor; |
||||
INT Micro; |
||||
INT Nano; |
||||
} KLIB_VERSION; |
||||
typedef KLIB_VERSION* PKLIB_VERSION; |
||||
|
||||
typedef BOOL (WINAPI *LibK_GetProcAddress_t)( |
||||
PVOID *ProcAddress, |
||||
ULONG DriverID, |
||||
ULONG FunctionID |
||||
); |
||||
|
||||
typedef VOID (WINAPI *LibK_GetVersion_t)( |
||||
PKLIB_VERSION Version |
||||
); |
||||
|
||||
struct winusb_interface { |
||||
bool initialized; |
||||
WinUsb_AbortPipe_t AbortPipe; |
||||
WinUsb_ControlTransfer_t ControlTransfer; |
||||
WinUsb_FlushPipe_t FlushPipe; |
||||
WinUsb_Free_t Free; |
||||
WinUsb_GetAssociatedInterface_t GetAssociatedInterface; |
||||
WinUsb_GetCurrentAlternateSetting_t GetCurrentAlternateSetting; |
||||
WinUsb_GetDescriptor_t GetDescriptor; |
||||
WinUsb_GetOverlappedResult_t GetOverlappedResult; |
||||
WinUsb_GetPipePolicy_t GetPipePolicy; |
||||
WinUsb_GetPowerPolicy_t GetPowerPolicy; |
||||
WinUsb_Initialize_t Initialize; |
||||
WinUsb_QueryDeviceInformation_t QueryDeviceInformation; |
||||
WinUsb_QueryInterfaceSettings_t QueryInterfaceSettings; |
||||
WinUsb_QueryPipe_t QueryPipe; |
||||
WinUsb_ReadPipe_t ReadPipe; |
||||
WinUsb_ResetPipe_t ResetPipe; |
||||
WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting; |
||||
WinUsb_SetPipePolicy_t SetPipePolicy; |
||||
WinUsb_SetPowerPolicy_t SetPowerPolicy; |
||||
WinUsb_WritePipe_t WritePipe; |
||||
WinUsb_ResetDevice_t ResetDevice; |
||||
}; |
||||
|
||||
/* hid.dll interface */ |
||||
|
||||
#define HIDP_STATUS_SUCCESS 0x110000 |
||||
typedef void * PHIDP_PREPARSED_DATA; |
||||
|
||||
#pragma pack(1) |
||||
typedef struct { |
||||
ULONG Size; |
||||
USHORT VendorID; |
||||
USHORT ProductID; |
||||
USHORT VersionNumber; |
||||
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; |
||||
#pragma pack() |
||||
|
||||
typedef USHORT USAGE; |
||||
typedef struct { |
||||
USAGE Usage; |
||||
USAGE UsagePage; |
||||
USHORT InputReportByteLength; |
||||
USHORT OutputReportByteLength; |
||||
USHORT FeatureReportByteLength; |
||||
USHORT Reserved[17]; |
||||
USHORT NumberLinkCollectionNodes; |
||||
USHORT NumberInputButtonCaps; |
||||
USHORT NumberInputValueCaps; |
||||
USHORT NumberInputDataIndices; |
||||
USHORT NumberOutputButtonCaps; |
||||
USHORT NumberOutputValueCaps; |
||||
USHORT NumberOutputDataIndices; |
||||
USHORT NumberFeatureButtonCaps; |
||||
USHORT NumberFeatureValueCaps; |
||||
USHORT NumberFeatureDataIndices; |
||||
} HIDP_CAPS, *PHIDP_CAPS; |
||||
|
||||
typedef enum _HIDP_REPORT_TYPE { |
||||
HidP_Input, |
||||
HidP_Output, |
||||
HidP_Feature |
||||
} HIDP_REPORT_TYPE; |
||||
|
||||
typedef struct _HIDP_VALUE_CAPS { |
||||
USAGE UsagePage; |
||||
UCHAR ReportID; |
||||
BOOLEAN IsAlias; |
||||
USHORT BitField; |
||||
USHORT LinkCollection; |
||||
USAGE LinkUsage; |
||||
USAGE LinkUsagePage; |
||||
BOOLEAN IsRange; |
||||
BOOLEAN IsStringRange; |
||||
BOOLEAN IsDesignatorRange; |
||||
BOOLEAN IsAbsolute; |
||||
BOOLEAN HasNull; |
||||
UCHAR Reserved; |
||||
USHORT BitSize; |
||||
USHORT ReportCount; |
||||
USHORT Reserved2[5]; |
||||
ULONG UnitsExp; |
||||
ULONG Units; |
||||
LONG LogicalMin, LogicalMax; |
||||
LONG PhysicalMin, PhysicalMax; |
||||
union { |
||||
struct { |
||||
USAGE UsageMin, UsageMax; |
||||
USHORT StringMin, StringMax; |
||||
USHORT DesignatorMin, DesignatorMax; |
||||
USHORT DataIndexMin, DataIndexMax; |
||||
} Range; |
||||
struct { |
||||
USAGE Usage, Reserved1; |
||||
USHORT StringIndex, Reserved2; |
||||
USHORT DesignatorIndex, Reserved3; |
||||
USHORT DataIndex, Reserved4; |
||||
} NotRange; |
||||
} u; |
||||
} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; |
||||
|
||||
DLL_DECLARE_HANDLE(hid); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetAttributes, (HANDLE, PHIDD_ATTRIBUTES)); |
||||
DLL_DECLARE_FUNC(WINAPI, VOID, HidD_GetHidGuid, (LPGUID)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetPreparsedData, (HANDLE, PHIDP_PREPARSED_DATA *)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_FreePreparsedData, (PHIDP_PREPARSED_DATA)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetManufacturerString, (HANDLE, PVOID, ULONG)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetProductString, (HANDLE, PVOID, ULONG)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetSerialNumberString, (HANDLE, PVOID, ULONG)); |
||||
DLL_DECLARE_FUNC(WINAPI, LONG, HidP_GetCaps, (PHIDP_PREPARSED_DATA, PHIDP_CAPS)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetNumInputBuffers, (HANDLE, ULONG)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetFeature, (HANDLE, PVOID, ULONG)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetFeature, (HANDLE, PVOID, ULONG)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetPhysicalDescriptor, (HANDLE, PVOID, ULONG)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetInputReport, (HANDLE, PVOID, ULONG)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetOutputReport, (HANDLE, PVOID, ULONG)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_FlushQueue, (HANDLE)); |
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidP_GetValueCaps, (HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PULONG, PHIDP_PREPARSED_DATA)); |
@ -0,0 +1,202 @@ |
||||
/*
|
||||
* libusb strerror code |
||||
* Copyright © 2013 Hans de Goede <hdegoede@redhat.com> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#include <locale.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#if defined(HAVE_STRINGS_H) |
||||
#include <strings.h> |
||||
#endif |
||||
|
||||
#include "libusbi.h" |
||||
|
||||
#if defined(_MSC_VER) |
||||
#define strncasecmp _strnicmp |
||||
#endif |
||||
|
||||
static size_t usbi_locale = 0; |
||||
|
||||
/** \ingroup libusb_misc
|
||||
* How to add a new \ref libusb_strerror() translation: |
||||
* <ol> |
||||
* <li> Download the latest \c strerror.c from:<br> |
||||
* https://raw.github.com/libusb/libusb/master/libusb/sterror.c </li>
|
||||
* <li> Open the file in an UTF-8 capable editor </li> |
||||
* <li> Add the 2 letter <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes">ISO 639-1</a> |
||||
* code for your locale at the end of \c usbi_locale_supported[]<br> |
||||
* Eg. for Chinese, you would add "zh" so that: |
||||
* \code... usbi_locale_supported[] = { "en", "nl", "fr" };\endcode |
||||
* becomes: |
||||
* \code... usbi_locale_supported[] = { "en", "nl", "fr", "zh" };\endcode </li> |
||||
* <li> Copy the <tt>{ / * English (en) * / ... }</tt> section and add it at the end of \c usbi_localized_errors<br> |
||||
* Eg. for Chinese, the last section of \c usbi_localized_errors could look like: |
||||
* \code |
||||
* }, { / * Chinese (zh) * / |
||||
* "Success", |
||||
* ... |
||||
* "Other error", |
||||
* } |
||||
* };\endcode </li> |
||||
* <li> Translate each of the English messages from the section you copied into your language </li> |
||||
* <li> Save the file (in UTF-8 format) and send it to \c libusb-devel\@lists.sourceforge.net </li> |
||||
* </ol> |
||||
*/ |
||||
|
||||
static const char* usbi_locale_supported[] = { "en", "nl", "fr", "ru" }; |
||||
static const char* usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUSB_ERROR_COUNT] = { |
||||
{ /* English (en) */ |
||||
"Success", |
||||
"Input/Output Error", |
||||
"Invalid parameter", |
||||
"Access denied (insufficient permissions)", |
||||
"No such device (it may have been disconnected)", |
||||
"Entity not found", |
||||
"Resource busy", |
||||
"Operation timed out", |
||||
"Overflow", |
||||
"Pipe error", |
||||
"System call interrupted (perhaps due to signal)", |
||||
"Insufficient memory", |
||||
"Operation not supported or unimplemented on this platform", |
||||
"Other error", |
||||
}, { /* Dutch (nl) */ |
||||
"Gelukt", |
||||
"Invoer-/uitvoerfout", |
||||
"Ongeldig argument", |
||||
"Toegang geweigerd (onvoldoende toegangsrechten)", |
||||
"Apparaat bestaat niet (verbinding met apparaat verbroken?)", |
||||
"Niet gevonden", |
||||
"Apparaat of hulpbron is bezig", |
||||
"Bewerking verlopen", |
||||
"Waarde is te groot", |
||||
"Gebroken pijp", |
||||
"Onderbroken systeemaanroep", |
||||
"Onvoldoende geheugen beschikbaar", |
||||
"Bewerking wordt niet ondersteund", |
||||
"Andere fout", |
||||
}, { /* French (fr) */ |
||||
"Succès", |
||||
"Erreur d'entrée/sortie", |
||||
"Paramètre invalide", |
||||
"Accès refusé (permissions insuffisantes)", |
||||
"Périphérique introuvable (peut-être déconnecté)", |
||||
"Elément introuvable", |
||||
"Resource déjà occupée", |
||||
"Operation expirée", |
||||
"Débordement", |
||||
"Erreur de pipe", |
||||
"Appel système abandonné (peut-être à cause d’un signal)", |
||||
"Mémoire insuffisante", |
||||
"Opération non supportée or non implémentée sur cette plateforme", |
||||
"Autre erreur", |
||||
}, { /* Russian (ru) */ |
||||
"Успех", |
||||
"Ошибка ввода/вывода", |
||||
"Неверный параметр", |
||||
"Доступ запрещён (не хватает прав)", |
||||
"Устройство отсутствует (возможно, оно было отсоединено)", |
||||
"Элемент не найден", |
||||
"Ресурс занят", |
||||
"Истекло время ожидания операции", |
||||
"Переполнение", |
||||
"Ошибка канала", |
||||
"Системный вызов прерван (возможно, сигналом)", |
||||
"Память исчерпана", |
||||
"Операция не поддерживается данной платформой", |
||||
"Неизвестная ошибка" |
||||
} |
||||
}; |
||||
|
||||
/** \ingroup libusb_misc
|
||||
* Set the language, and only the language, not the encoding! used for |
||||
* translatable libusb messages. |
||||
* |
||||
* This takes a locale string in the default setlocale format: lang[-region] |
||||
* or lang[_country_region][.codeset]. Only the lang part of the string is |
||||
* used, and only 2 letter ISO 639-1 codes are accepted for it, such as "de". |
||||
* The optional region, country_region or codeset parts are ignored. This |
||||
* means that functions which return translatable strings will NOT honor the |
||||
* specified encoding.
|
||||
* All strings returned are encoded as UTF-8 strings. |
||||
* |
||||
* If libusb_setlocale() is not called, all messages will be in English. |
||||
* |
||||
* The following functions return translatable strings: libusb_strerror(). |
||||
* Note that the libusb log messages controlled through libusb_set_debug() |
||||
* are not translated, they are always in English. |
||||
* |
||||
* For POSIX UTF-8 environments if you want libusb to follow the standard |
||||
* locale settings, call libusb_setlocale(setlocale(LC_MESSAGES, NULL)), |
||||
* after your app has done its locale setup. |
||||
* |
||||
* \param locale locale-string in the form of lang[_country_region][.codeset] |
||||
* or lang[-region], where lang is a 2 letter ISO 639-1 code |
||||
* \returns LIBUSB_SUCCESS on success |
||||
* \returns LIBUSB_ERROR_INVALID_PARAM if the locale doesn't meet the requirements |
||||
* \returns LIBUSB_ERROR_NOT_FOUND if the requested language is not supported |
||||
* \returns a LIBUSB_ERROR code on other errors |
||||
*/ |
||||
|
||||
int API_EXPORTED libusb_setlocale(const char *locale) |
||||
{ |
||||
size_t i; |
||||
|
||||
if ( (locale == NULL) || (strlen(locale) < 2) |
||||
|| ((strlen(locale) > 2) && (locale[2] != '-') && (locale[2] != '_') && (locale[2] != '.')) ) |
||||
return LIBUSB_ERROR_INVALID_PARAM; |
||||
|
||||
for (i=0; i<ARRAYSIZE(usbi_locale_supported); i++) { |
||||
if (strncasecmp(usbi_locale_supported[i], locale, 2) == 0) |
||||
break; |
||||
} |
||||
if (i >= ARRAYSIZE(usbi_locale_supported)) { |
||||
return LIBUSB_ERROR_NOT_FOUND; |
||||
} |
||||
|
||||
usbi_locale = i; |
||||
|
||||
return LIBUSB_SUCCESS; |
||||
} |
||||
|
||||
/** \ingroup libusb_misc
|
||||
* Returns a constant string with a short description of the given error code, |
||||
* this description is intended for displaying to the end user and will be in |
||||
* the language set by libusb_setlocale(). |
||||
* |
||||
* The returned string is encoded in UTF-8. |
||||
* |
||||
* The messages always start with a capital letter and end without any dot. |
||||
* The caller must not free() the returned string. |
||||
* |
||||
* \param errcode the error code whose description is desired |
||||
* \returns a short description of the error code in UTF-8 encoding |
||||
*/ |
||||
DEFAULT_VISIBILITY const char* LIBUSB_CALL libusb_strerror(enum libusb_error errcode) |
||||
{ |
||||
int errcode_index = -errcode; |
||||
|
||||
if ((errcode_index < 0) || (errcode_index >= LIBUSB_ERROR_COUNT)) { |
||||
/* "Other Error", which should always be our last message, is returned */ |
||||
errcode_index = LIBUSB_ERROR_COUNT - 1; |
||||
} |
||||
|
||||
return usbi_localized_errors[usbi_locale][errcode_index]; |
||||
} |
@ -0,0 +1,327 @@ |
||||
/*
|
||||
* Synchronous I/O functions for libusb |
||||
* Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#include <errno.h> |
||||
#include <stdint.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#include "libusbi.h" |
||||
|
||||
/**
|
||||
* @defgroup libusb_syncio Synchronous device I/O |
||||
* |
||||
* This page documents libusb's synchronous (blocking) API for USB device I/O. |
||||
* This interface is easy to use but has some limitations. More advanced users |
||||
* may wish to consider using the \ref libusb_asyncio "asynchronous I/O API" instead. |
||||
*/ |
||||
|
||||
static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer) |
||||
{ |
||||
int *completed = transfer->user_data; |
||||
*completed = 1; |
||||
usbi_dbg("actual_length=%d", transfer->actual_length); |
||||
/* caller interprets result and frees transfer */ |
||||
} |
||||
|
||||
static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) |
||||
{ |
||||
int r, *completed = transfer->user_data; |
||||
struct libusb_context *ctx = HANDLE_CTX(transfer->dev_handle); |
||||
|
||||
while (!*completed) { |
||||
r = libusb_handle_events_completed(ctx, completed); |
||||
if (r < 0) { |
||||
if (r == LIBUSB_ERROR_INTERRUPTED) |
||||
continue; |
||||
usbi_err(ctx, "libusb_handle_events failed: %s, cancelling transfer and retrying", |
||||
libusb_error_name(r)); |
||||
libusb_cancel_transfer(transfer); |
||||
continue; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** \ingroup libusb_syncio
|
||||
* Perform a USB control transfer. |
||||
* |
||||
* The direction of the transfer is inferred from the bmRequestType field of |
||||
* the setup packet. |
||||
* |
||||
* The wValue, wIndex and wLength fields values should be given in host-endian |
||||
* byte order. |
||||
* |
||||
* \param dev_handle a handle for the device to communicate with |
||||
* \param bmRequestType the request type field for the setup packet |
||||
* \param bRequest the request field for the setup packet |
||||
* \param wValue the value field for the setup packet |
||||
* \param wIndex the index field for the setup packet |
||||
* \param data a suitably-sized data buffer for either input or output |
||||
* (depending on direction bits within bmRequestType) |
||||
* \param wLength the length field for the setup packet. The data buffer should |
||||
* be at least this size. |
||||
* \param timeout timeout (in millseconds) that this function should wait |
||||
* before giving up due to no response being received. For an unlimited |
||||
* timeout, use value 0. |
||||
* \returns on success, the number of bytes actually transferred |
||||
* \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out |
||||
* \returns LIBUSB_ERROR_PIPE if the control request was not supported by the |
||||
* device |
||||
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected |
||||
* \returns LIBUSB_ERROR_BUSY if called from event handling context |
||||
* \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than |
||||
* the operating system and/or hardware can support |
||||
* \returns another LIBUSB_ERROR code on other failures |
||||
*/ |
||||
int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle, |
||||
uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, |
||||
unsigned char *data, uint16_t wLength, unsigned int timeout) |
||||
{ |
||||
struct libusb_transfer *transfer; |
||||
unsigned char *buffer; |
||||
int completed = 0; |
||||
int r; |
||||
|
||||
if (usbi_handling_events(HANDLE_CTX(dev_handle))) |
||||
return LIBUSB_ERROR_BUSY; |
||||
|
||||
transfer = libusb_alloc_transfer(0); |
||||
if (!transfer) |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
|
||||
buffer = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength); |
||||
if (!buffer) { |
||||
libusb_free_transfer(transfer); |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
} |
||||
|
||||
libusb_fill_control_setup(buffer, bmRequestType, bRequest, wValue, wIndex, |
||||
wLength); |
||||
if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) |
||||
memcpy(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, wLength); |
||||
|
||||
libusb_fill_control_transfer(transfer, dev_handle, buffer, |
||||
sync_transfer_cb, &completed, timeout); |
||||
transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER; |
||||
r = libusb_submit_transfer(transfer); |
||||
if (r < 0) { |
||||
libusb_free_transfer(transfer); |
||||
return r; |
||||
} |
||||
|
||||
sync_transfer_wait_for_completion(transfer); |
||||
|
||||
if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) |
||||
memcpy(data, libusb_control_transfer_get_data(transfer), |
||||
transfer->actual_length); |
||||
|
||||
switch (transfer->status) { |
||||
case LIBUSB_TRANSFER_COMPLETED: |
||||
r = transfer->actual_length; |
||||
break; |
||||
case LIBUSB_TRANSFER_TIMED_OUT: |
||||
r = LIBUSB_ERROR_TIMEOUT; |
||||
break; |
||||
case LIBUSB_TRANSFER_STALL: |
||||
r = LIBUSB_ERROR_PIPE; |
||||
break; |
||||
case LIBUSB_TRANSFER_NO_DEVICE: |
||||
r = LIBUSB_ERROR_NO_DEVICE; |
||||
break; |
||||
case LIBUSB_TRANSFER_OVERFLOW: |
||||
r = LIBUSB_ERROR_OVERFLOW; |
||||
break; |
||||
case LIBUSB_TRANSFER_ERROR: |
||||
case LIBUSB_TRANSFER_CANCELLED: |
||||
r = LIBUSB_ERROR_IO; |
||||
break; |
||||
default: |
||||
usbi_warn(HANDLE_CTX(dev_handle), |
||||
"unrecognised status code %d", transfer->status); |
||||
r = LIBUSB_ERROR_OTHER; |
||||
} |
||||
|
||||
libusb_free_transfer(transfer); |
||||
return r; |
||||
} |
||||
|
||||
static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, |
||||
unsigned char endpoint, unsigned char *buffer, int length, |
||||
int *transferred, unsigned int timeout, unsigned char type) |
||||
{ |
||||
struct libusb_transfer *transfer; |
||||
int completed = 0; |
||||
int r; |
||||
|
||||
if (usbi_handling_events(HANDLE_CTX(dev_handle))) |
||||
return LIBUSB_ERROR_BUSY; |
||||
|
||||
transfer = libusb_alloc_transfer(0); |
||||
if (!transfer) |
||||
return LIBUSB_ERROR_NO_MEM; |
||||
|
||||
libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length, |
||||
sync_transfer_cb, &completed, timeout); |
||||
transfer->type = type; |
||||
|
||||
r = libusb_submit_transfer(transfer); |
||||
if (r < 0) { |
||||
libusb_free_transfer(transfer); |
||||
return r; |
||||
} |
||||
|
||||
sync_transfer_wait_for_completion(transfer); |
||||
|
||||
if (transferred) |
||||
*transferred = transfer->actual_length; |
||||
|
||||
switch (transfer->status) { |
||||
case LIBUSB_TRANSFER_COMPLETED: |
||||
r = 0; |
||||
break; |
||||
case LIBUSB_TRANSFER_TIMED_OUT: |
||||
r = LIBUSB_ERROR_TIMEOUT; |
||||
break; |
||||
case LIBUSB_TRANSFER_STALL: |
||||
r = LIBUSB_ERROR_PIPE; |
||||
break; |
||||
case LIBUSB_TRANSFER_OVERFLOW: |
||||
r = LIBUSB_ERROR_OVERFLOW; |
||||
break; |
||||
case LIBUSB_TRANSFER_NO_DEVICE: |
||||
r = LIBUSB_ERROR_NO_DEVICE; |
||||
break; |
||||
case LIBUSB_TRANSFER_ERROR: |
||||
case LIBUSB_TRANSFER_CANCELLED: |
||||
r = LIBUSB_ERROR_IO; |
||||
break; |
||||
default: |
||||
usbi_warn(HANDLE_CTX(dev_handle), |
||||
"unrecognised status code %d", transfer->status); |
||||
r = LIBUSB_ERROR_OTHER; |
||||
} |
||||
|
||||
libusb_free_transfer(transfer); |
||||
return r; |
||||
} |
||||
|
||||
/** \ingroup libusb_syncio
|
||||
* Perform a USB bulk transfer. The direction of the transfer is inferred from |
||||
* the direction bits of the endpoint address. |
||||
* |
||||
* For bulk reads, the <tt>length</tt> field indicates the maximum length of |
||||
* data you are expecting to receive. If less data arrives than expected, |
||||
* this function will return that data, so be sure to check the |
||||
* <tt>transferred</tt> output parameter. |
||||
* |
||||
* You should also check the <tt>transferred</tt> parameter for bulk writes. |
||||
* Not all of the data may have been written. |
||||
* |
||||
* Also check <tt>transferred</tt> when dealing with a timeout error code. |
||||
* libusb may have to split your transfer into a number of chunks to satisfy |
||||
* underlying O/S requirements, meaning that the timeout may expire after |
||||
* the first few chunks have completed. libusb is careful not to lose any data |
||||
* that may have been transferred; do not assume that timeout conditions |
||||
* indicate a complete lack of I/O. |
||||
* |
||||
* \param dev_handle a handle for the device to communicate with |
||||
* \param endpoint the address of a valid endpoint to communicate with |
||||
* \param data a suitably-sized data buffer for either input or output |
||||
* (depending on endpoint) |
||||
* \param length for bulk writes, the number of bytes from data to be sent. for |
||||
* bulk reads, the maximum number of bytes to receive into the data buffer. |
||||
* \param transferred output location for the number of bytes actually |
||||
* transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105), |
||||
* it is legal to pass a NULL pointer if you do not wish to receive this |
||||
* information. |
||||
* \param timeout timeout (in millseconds) that this function should wait |
||||
* before giving up due to no response being received. For an unlimited |
||||
* timeout, use value 0. |
||||
* |
||||
* \returns 0 on success (and populates <tt>transferred</tt>) |
||||
* \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates |
||||
* <tt>transferred</tt>) |
||||
* \returns LIBUSB_ERROR_PIPE if the endpoint halted |
||||
* \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see |
||||
* \ref libusb_packetoverflow |
||||
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected |
||||
* \returns LIBUSB_ERROR_BUSY if called from event handling context |
||||
* \returns another LIBUSB_ERROR code on other failures |
||||
*/ |
||||
int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle, |
||||
unsigned char endpoint, unsigned char *data, int length, int *transferred, |
||||
unsigned int timeout) |
||||
{ |
||||
return do_sync_bulk_transfer(dev_handle, endpoint, data, length, |
||||
transferred, timeout, LIBUSB_TRANSFER_TYPE_BULK); |
||||
} |
||||
|
||||
/** \ingroup libusb_syncio
|
||||
* Perform a USB interrupt transfer. The direction of the transfer is inferred |
||||
* from the direction bits of the endpoint address. |
||||
* |
||||
* For interrupt reads, the <tt>length</tt> field indicates the maximum length |
||||
* of data you are expecting to receive. If less data arrives than expected, |
||||
* this function will return that data, so be sure to check the |
||||
* <tt>transferred</tt> output parameter. |
||||
* |
||||
* You should also check the <tt>transferred</tt> parameter for interrupt |
||||
* writes. Not all of the data may have been written. |
||||
* |
||||
* Also check <tt>transferred</tt> when dealing with a timeout error code. |
||||
* libusb may have to split your transfer into a number of chunks to satisfy |
||||
* underlying O/S requirements, meaning that the timeout may expire after |
||||
* the first few chunks have completed. libusb is careful not to lose any data |
||||
* that may have been transferred; do not assume that timeout conditions |
||||
* indicate a complete lack of I/O. |
||||
* |
||||
* The default endpoint bInterval value is used as the polling interval. |
||||
* |
||||
* \param dev_handle a handle for the device to communicate with |
||||
* \param endpoint the address of a valid endpoint to communicate with |
||||
* \param data a suitably-sized data buffer for either input or output |
||||
* (depending on endpoint) |
||||
* \param length for bulk writes, the number of bytes from data to be sent. for |
||||
* bulk reads, the maximum number of bytes to receive into the data buffer. |
||||
* \param transferred output location for the number of bytes actually |
||||
* transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105), |
||||
* it is legal to pass a NULL pointer if you do not wish to receive this |
||||
* information. |
||||
* \param timeout timeout (in millseconds) that this function should wait |
||||
* before giving up due to no response being received. For an unlimited |
||||
* timeout, use value 0. |
||||
* |
||||
* \returns 0 on success (and populates <tt>transferred</tt>) |
||||
* \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out |
||||
* \returns LIBUSB_ERROR_PIPE if the endpoint halted |
||||
* \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see |
||||
* \ref libusb_packetoverflow |
||||
* \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected |
||||
* \returns LIBUSB_ERROR_BUSY if called from event handling context |
||||
* \returns another LIBUSB_ERROR code on other error |
||||
*/ |
||||
int API_EXPORTED libusb_interrupt_transfer( |
||||
struct libusb_device_handle *dev_handle, unsigned char endpoint, |
||||
unsigned char *data, int length, int *transferred, unsigned int timeout) |
||||
{ |
||||
return do_sync_bulk_transfer(dev_handle, endpoint, data, length, |
||||
transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); |
||||
} |
@ -0,0 +1,18 @@ |
||||
/* This file is parsed by m4 and windres and RC.EXE so please keep it simple. */ |
||||
#include "version_nano.h" |
||||
#ifndef LIBUSB_MAJOR |
||||
#define LIBUSB_MAJOR 1 |
||||
#endif |
||||
#ifndef LIBUSB_MINOR |
||||
#define LIBUSB_MINOR 0 |
||||
#endif |
||||
#ifndef LIBUSB_MICRO |
||||
#define LIBUSB_MICRO 21 |
||||
#endif |
||||
#ifndef LIBUSB_NANO |
||||
#define LIBUSB_NANO 0 |
||||
#endif |
||||
/* LIBUSB_RC is the release candidate suffix. Should normally be empty. */ |
||||
#ifndef LIBUSB_RC |
||||
#define LIBUSB_RC "" |
||||
#endif |
@ -0,0 +1 @@ |
||||
#define LIBUSB_NANO 11182 |
@ -0,0 +1,153 @@ |
||||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package usb |
||||
|
||||
/* |
||||
#ifndef OS_WINDOWS |
||||
#include "os/threads_posix.h" |
||||
#endif |
||||
#include "libusbi.h" |
||||
#include "libusb.h" |
||||
*/ |
||||
import "C" |
||||
|
||||
import ( |
||||
"fmt" |
||||
"reflect" |
||||
"unsafe" |
||||
) |
||||
|
||||
type EndpointInfo struct { |
||||
Address uint8 |
||||
Attributes uint8 |
||||
MaxPacketSize uint16 |
||||
MaxIsoPacket uint32 |
||||
PollInterval uint8 |
||||
RefreshRate uint8 |
||||
SynchAddress uint8 |
||||
} |
||||
|
||||
func (e EndpointInfo) Number() int { |
||||
return int(e.Address) & ENDPOINT_NUM_MASK |
||||
} |
||||
|
||||
func (e EndpointInfo) Direction() EndpointDirection { |
||||
return EndpointDirection(e.Address) & ENDPOINT_DIR_MASK |
||||
} |
||||
|
||||
func (e EndpointInfo) String() string { |
||||
return fmt.Sprintf("Endpoint %d %-3s %s - %s %s [%d %d]", |
||||
e.Number(), e.Direction(), |
||||
TransferType(e.Attributes)&TRANSFER_TYPE_MASK, |
||||
IsoSyncType(e.Attributes)&ISO_SYNC_TYPE_MASK, |
||||
IsoUsageType(e.Attributes)&ISO_USAGE_TYPE_MASK, |
||||
e.MaxPacketSize, e.MaxIsoPacket, |
||||
) |
||||
} |
||||
|
||||
type InterfaceInfo struct { |
||||
Number uint8 |
||||
Setups []InterfaceSetup |
||||
} |
||||
|
||||
func (i InterfaceInfo) String() string { |
||||
return fmt.Sprintf("Interface %02x (%d setups)", i.Number, len(i.Setups)) |
||||
} |
||||
|
||||
type InterfaceSetup struct { |
||||
Number uint8 |
||||
Alternate uint8 |
||||
IfClass uint8 |
||||
IfSubClass uint8 |
||||
IfProtocol uint8 |
||||
Endpoints []EndpointInfo |
||||
} |
||||
|
||||
func (a InterfaceSetup) String() string { |
||||
return fmt.Sprintf("Interface %02x Setup %02x", a.Number, a.Alternate) |
||||
} |
||||
|
||||
type ConfigInfo struct { |
||||
Config uint8 |
||||
Attributes uint8 |
||||
MaxPower uint8 |
||||
Interfaces []InterfaceInfo |
||||
} |
||||
|
||||
func (c ConfigInfo) String() string { |
||||
return fmt.Sprintf("Config %02x", c.Config) |
||||
} |
||||
|
||||
func newConfig(dev *C.libusb_device, cfg *C.struct_libusb_config_descriptor) ConfigInfo { |
||||
c := ConfigInfo{ |
||||
Config: uint8(cfg.bConfigurationValue), |
||||
Attributes: uint8(cfg.bmAttributes), |
||||
MaxPower: uint8(cfg.MaxPower), |
||||
} |
||||
|
||||
var ifaces []C.struct_libusb_interface |
||||
*(*reflect.SliceHeader)(unsafe.Pointer(&ifaces)) = reflect.SliceHeader{ |
||||
Data: uintptr(unsafe.Pointer(cfg._interface)), |
||||
Len: int(cfg.bNumInterfaces), |
||||
Cap: int(cfg.bNumInterfaces), |
||||
} |
||||
c.Interfaces = make([]InterfaceInfo, 0, len(ifaces)) |
||||
for _, iface := range ifaces { |
||||
if iface.num_altsetting == 0 { |
||||
continue |
||||
} |
||||
|
||||
var alts []C.struct_libusb_interface_descriptor |
||||
*(*reflect.SliceHeader)(unsafe.Pointer(&alts)) = reflect.SliceHeader{ |
||||
Data: uintptr(unsafe.Pointer(iface.altsetting)), |
||||
Len: int(iface.num_altsetting), |
||||
Cap: int(iface.num_altsetting), |
||||
} |
||||
descs := make([]InterfaceSetup, 0, len(alts)) |
||||
for _, alt := range alts { |
||||
i := InterfaceSetup{ |
||||
Number: uint8(alt.bInterfaceNumber), |
||||
Alternate: uint8(alt.bAlternateSetting), |
||||
IfClass: uint8(alt.bInterfaceClass), |
||||
IfSubClass: uint8(alt.bInterfaceSubClass), |
||||
IfProtocol: uint8(alt.bInterfaceProtocol), |
||||
} |
||||
var ends []C.struct_libusb_endpoint_descriptor |
||||
*(*reflect.SliceHeader)(unsafe.Pointer(&ends)) = reflect.SliceHeader{ |
||||
Data: uintptr(unsafe.Pointer(alt.endpoint)), |
||||
Len: int(alt.bNumEndpoints), |
||||
Cap: int(alt.bNumEndpoints), |
||||
} |
||||
i.Endpoints = make([]EndpointInfo, 0, len(ends)) |
||||
for _, end := range ends { |
||||
i.Endpoints = append(i.Endpoints, EndpointInfo{ |
||||
Address: uint8(end.bEndpointAddress), |
||||
Attributes: uint8(end.bmAttributes), |
||||
MaxPacketSize: uint16(end.wMaxPacketSize), |
||||
//MaxIsoPacket: uint32(C.libusb_get_max_iso_packet_size(dev, C.uchar(end.bEndpointAddress))),
|
||||
PollInterval: uint8(end.bInterval), |
||||
RefreshRate: uint8(end.bRefresh), |
||||
SynchAddress: uint8(end.bSynchAddress), |
||||
}) |
||||
} |
||||
descs = append(descs, i) |
||||
} |
||||
c.Interfaces = append(c.Interfaces, InterfaceInfo{ |
||||
Number: descs[0].Number, |
||||
Setups: descs, |
||||
}) |
||||
} |
||||
return c |
||||
} |
@ -0,0 +1,183 @@ |
||||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package usb |
||||
|
||||
// #include "libusb.h"
|
||||
import "C" |
||||
|
||||
type Class uint8 |
||||
|
||||
const ( |
||||
CLASS_PER_INTERFACE Class = C.LIBUSB_CLASS_PER_INTERFACE |
||||
CLASS_AUDIO Class = C.LIBUSB_CLASS_AUDIO |
||||
CLASS_COMM Class = C.LIBUSB_CLASS_COMM |
||||
CLASS_HID Class = C.LIBUSB_CLASS_HID |
||||
CLASS_PRINTER Class = C.LIBUSB_CLASS_PRINTER |
||||
CLASS_PTP Class = C.LIBUSB_CLASS_PTP |
||||
CLASS_MASS_STORAGE Class = C.LIBUSB_CLASS_MASS_STORAGE |
||||
CLASS_HUB Class = C.LIBUSB_CLASS_HUB |
||||
CLASS_DATA Class = C.LIBUSB_CLASS_DATA |
||||
CLASS_WIRELESS Class = C.LIBUSB_CLASS_WIRELESS |
||||
CLASS_APPLICATION Class = C.LIBUSB_CLASS_APPLICATION |
||||
CLASS_VENDOR_SPEC Class = C.LIBUSB_CLASS_VENDOR_SPEC |
||||
) |
||||
|
||||
var classDescription = map[Class]string{ |
||||
CLASS_PER_INTERFACE: "per-interface", |
||||
CLASS_AUDIO: "audio", |
||||
CLASS_COMM: "communications", |
||||
CLASS_HID: "human interface device", |
||||
CLASS_PRINTER: "printer dclass", |
||||
CLASS_PTP: "picture transfer protocol", |
||||
CLASS_MASS_STORAGE: "mass storage", |
||||
CLASS_HUB: "hub", |
||||
CLASS_DATA: "data", |
||||
CLASS_WIRELESS: "wireless", |
||||
CLASS_APPLICATION: "application", |
||||
CLASS_VENDOR_SPEC: "vendor-specific", |
||||
} |
||||
|
||||
func (c Class) String() string { |
||||
return classDescription[c] |
||||
} |
||||
|
||||
type DescriptorType uint8 |
||||
|
||||
const ( |
||||
DT_DEVICE DescriptorType = C.LIBUSB_DT_DEVICE |
||||
DT_CONFIG DescriptorType = C.LIBUSB_DT_CONFIG |
||||
DT_STRING DescriptorType = C.LIBUSB_DT_STRING |
||||
DT_INTERFACE DescriptorType = C.LIBUSB_DT_INTERFACE |
||||
DT_ENDPOINT DescriptorType = C.LIBUSB_DT_ENDPOINT |
||||
DT_HID DescriptorType = C.LIBUSB_DT_HID |
||||
DT_REPORT DescriptorType = C.LIBUSB_DT_REPORT |
||||
DT_PHYSICAL DescriptorType = C.LIBUSB_DT_PHYSICAL |
||||
DT_HUB DescriptorType = C.LIBUSB_DT_HUB |
||||
) |
||||
|
||||
var descriptorTypeDescription = map[DescriptorType]string{ |
||||
DT_DEVICE: "device", |
||||
DT_CONFIG: "configuration", |
||||
DT_STRING: "string", |
||||
DT_INTERFACE: "interface", |
||||
DT_ENDPOINT: "endpoint", |
||||
DT_HID: "HID", |
||||
DT_REPORT: "HID report", |
||||
DT_PHYSICAL: "physical", |
||||
DT_HUB: "hub", |
||||
} |
||||
|
||||
func (dt DescriptorType) String() string { |
||||
return descriptorTypeDescription[dt] |
||||
} |
||||
|
||||
type EndpointDirection uint8 |
||||
|
||||
const ( |
||||
ENDPOINT_NUM_MASK = 0x03 |
||||
ENDPOINT_DIR_IN EndpointDirection = C.LIBUSB_ENDPOINT_IN |
||||
ENDPOINT_DIR_OUT EndpointDirection = C.LIBUSB_ENDPOINT_OUT |
||||
ENDPOINT_DIR_MASK EndpointDirection = 0x80 |
||||
) |
||||
|
||||
var endpointDirectionDescription = map[EndpointDirection]string{ |
||||
ENDPOINT_DIR_IN: "IN", |
||||
ENDPOINT_DIR_OUT: "OUT", |
||||
} |
||||
|
||||
func (ed EndpointDirection) String() string { |
||||
return endpointDirectionDescription[ed] |
||||
} |
||||
|
||||
type TransferType uint8 |
||||
|
||||
const ( |
||||
TRANSFER_TYPE_CONTROL TransferType = C.LIBUSB_TRANSFER_TYPE_CONTROL |
||||
TRANSFER_TYPE_ISOCHRONOUS TransferType = C.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS |
||||
TRANSFER_TYPE_BULK TransferType = C.LIBUSB_TRANSFER_TYPE_BULK |
||||
TRANSFER_TYPE_INTERRUPT TransferType = C.LIBUSB_TRANSFER_TYPE_INTERRUPT |
||||
TRANSFER_TYPE_MASK TransferType = 0x03 |
||||
) |
||||
|
||||
var transferTypeDescription = map[TransferType]string{ |
||||
TRANSFER_TYPE_CONTROL: "control", |
||||
TRANSFER_TYPE_ISOCHRONOUS: "isochronous", |
||||
TRANSFER_TYPE_BULK: "bulk", |
||||
TRANSFER_TYPE_INTERRUPT: "interrupt", |
||||
} |
||||
|
||||
func (tt TransferType) String() string { |
||||
return transferTypeDescription[tt] |
||||
} |
||||
|
||||
type IsoSyncType uint8 |
||||
|
||||
const ( |
||||
ISO_SYNC_TYPE_NONE IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_NONE << 2 |
||||
ISO_SYNC_TYPE_ASYNC IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ASYNC << 2 |
||||
ISO_SYNC_TYPE_ADAPTIVE IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_ADAPTIVE << 2 |
||||
ISO_SYNC_TYPE_SYNC IsoSyncType = C.LIBUSB_ISO_SYNC_TYPE_SYNC << 2 |
||||
ISO_SYNC_TYPE_MASK IsoSyncType = 0x0C |
||||
) |
||||
|
||||
var isoSyncTypeDescription = map[IsoSyncType]string{ |
||||
ISO_SYNC_TYPE_NONE: "unsynchronized", |
||||
ISO_SYNC_TYPE_ASYNC: "asynchronous", |
||||
ISO_SYNC_TYPE_ADAPTIVE: "adaptive", |
||||
ISO_SYNC_TYPE_SYNC: "synchronous", |
||||
} |
||||
|
||||
func (ist IsoSyncType) String() string { |
||||
return isoSyncTypeDescription[ist] |
||||
} |
||||
|
||||
type IsoUsageType uint8 |
||||
|
||||
const ( |
||||
ISO_USAGE_TYPE_DATA IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_DATA << 4 |
||||
ISO_USAGE_TYPE_FEEDBACK IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_FEEDBACK << 4 |
||||
ISO_USAGE_TYPE_IMPLICIT IsoUsageType = C.LIBUSB_ISO_USAGE_TYPE_IMPLICIT << 4 |
||||
ISO_USAGE_TYPE_MASK IsoUsageType = 0x30 |
||||
) |
||||
|
||||
var isoUsageTypeDescription = map[IsoUsageType]string{ |
||||
ISO_USAGE_TYPE_DATA: "data", |
||||
ISO_USAGE_TYPE_FEEDBACK: "feedback", |
||||
ISO_USAGE_TYPE_IMPLICIT: "implicit data", |
||||
} |
||||
|
||||
func (iut IsoUsageType) String() string { |
||||
return isoUsageTypeDescription[iut] |
||||
} |
||||
|
||||
type RequestType uint8 |
||||
|
||||
const ( |
||||
REQUEST_TYPE_STANDARD = C.LIBUSB_REQUEST_TYPE_STANDARD |
||||
REQUEST_TYPE_CLASS = C.LIBUSB_REQUEST_TYPE_CLASS |
||||
REQUEST_TYPE_VENDOR = C.LIBUSB_REQUEST_TYPE_VENDOR |
||||
REQUEST_TYPE_RESERVED = C.LIBUSB_REQUEST_TYPE_RESERVED |
||||
) |
||||
|
||||
var requestTypeDescription = map[RequestType]string{ |
||||
REQUEST_TYPE_STANDARD: "standard", |
||||
REQUEST_TYPE_CLASS: "class", |
||||
REQUEST_TYPE_VENDOR: "vendor", |
||||
REQUEST_TYPE_RESERVED: "reserved", |
||||
} |
||||
|
||||
func (rt RequestType) String() string { |
||||
return requestTypeDescription[rt] |
||||
} |
@ -0,0 +1,36 @@ |
||||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package usb |
||||
|
||||
// To enable internal debugging:
|
||||
// -ldflags "-X github.com/karalabe/gousb/usb.debugInternal true"
|
||||
|
||||
import ( |
||||
"io" |
||||
"io/ioutil" |
||||
"log" // TODO(kevlar): make a logger
|
||||
"os" |
||||
) |
||||
|
||||
var debug *log.Logger |
||||
var debugInternal string |
||||
|
||||
func init() { |
||||
var out io.Writer = ioutil.Discard |
||||
if debugInternal != "" { |
||||
out = os.Stderr |
||||
} |
||||
debug = log.New(out, "usb", log.LstdFlags|log.Lshortfile) |
||||
} |
@ -0,0 +1,77 @@ |
||||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package usb |
||||
|
||||
/* |
||||
#ifndef OS_WINDOWS |
||||
#include "os/threads_posix.h" |
||||
#endif |
||||
#include "libusbi.h" |
||||
#include "libusb.h" |
||||
*/ |
||||
import "C" |
||||
|
||||
type Descriptor struct { |
||||
// Bus information
|
||||
Bus uint8 // The bus on which the device was detected
|
||||
Address uint8 // The address of the device on the bus
|
||||
|
||||
// Version information
|
||||
Spec BCD // USB Specification Release Number
|
||||
Device BCD // The device version
|
||||
|
||||
// Product information
|
||||
Vendor ID // The Vendor identifer
|
||||
Product ID // The Product identifier
|
||||
|
||||
// Protocol information
|
||||
Class uint8 // The class of this device
|
||||
SubClass uint8 // The sub-class (within the class) of this device
|
||||
Protocol uint8 // The protocol (within the sub-class) of this device
|
||||
|
||||
// Configuration information
|
||||
Configs []ConfigInfo |
||||
} |
||||
|
||||
func newDescriptor(dev *C.libusb_device) (*Descriptor, error) { |
||||
var desc C.struct_libusb_device_descriptor |
||||
if errno := C.libusb_get_device_descriptor(dev, &desc); errno < 0 { |
||||
return nil, usbError(errno) |
||||
} |
||||
|
||||
// Enumerate configurations
|
||||
var cfgs []ConfigInfo |
||||
for i := 0; i < int(desc.bNumConfigurations); i++ { |
||||
var cfg *C.struct_libusb_config_descriptor |
||||
if errno := C.libusb_get_config_descriptor(dev, C.uint8_t(i), &cfg); errno < 0 { |
||||
return nil, usbError(errno) |
||||
} |
||||
cfgs = append(cfgs, newConfig(dev, cfg)) |
||||
C.libusb_free_config_descriptor(cfg) |
||||
} |
||||
|
||||
return &Descriptor{ |
||||
Bus: uint8(C.libusb_get_bus_number(dev)), |
||||
Address: uint8(C.libusb_get_device_address(dev)), |
||||
Spec: BCD(desc.bcdUSB), |
||||
Device: BCD(desc.bcdDevice), |
||||
Vendor: ID(desc.idVendor), |
||||
Product: ID(desc.idProduct), |
||||
Class: uint8(desc.bDeviceClass), |
||||
SubClass: uint8(desc.bDeviceSubClass), |
||||
Protocol: uint8(desc.bDeviceProtocol), |
||||
Configs: cfgs, |
||||
}, nil |
||||
} |
@ -0,0 +1,295 @@ |
||||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package usb |
||||
|
||||
/* |
||||
#ifndef OS_WINDOWS |
||||
#include "os/threads_posix.h" |
||||
#endif |
||||
#include "libusbi.h" |
||||
#include "libusb.h" |
||||
*/ |
||||
import "C" |
||||
|
||||
import ( |
||||
"fmt" |
||||
"reflect" |
||||
"sync" |
||||
"time" |
||||
"unsafe" |
||||
) |
||||
|
||||
var DefaultReadTimeout = 1 * time.Second |
||||
var DefaultWriteTimeout = 1 * time.Second |
||||
var DefaultControlTimeout = 250 * time.Millisecond //5 * time.Second
|
||||
|
||||
type Device struct { |
||||
handle *C.libusb_device_handle |
||||
|
||||
// Embed the device information for easy access
|
||||
*Descriptor |
||||
|
||||
// Timeouts
|
||||
ReadTimeout time.Duration |
||||
WriteTimeout time.Duration |
||||
ControlTimeout time.Duration |
||||
|
||||
// Claimed interfaces
|
||||
lock *sync.Mutex |
||||
claimed map[uint8]int |
||||
|
||||
// Detached kernel interfaces
|
||||
detached map[uint8]int |
||||
} |
||||
|
||||
func newDevice(handle *C.libusb_device_handle, desc *Descriptor) (*Device, error) { |
||||
ifaces := 0 |
||||
d := &Device{ |
||||
handle: handle, |
||||
Descriptor: desc, |
||||
ReadTimeout: DefaultReadTimeout, |
||||
WriteTimeout: DefaultWriteTimeout, |
||||
ControlTimeout: DefaultControlTimeout, |
||||
lock: new(sync.Mutex), |
||||
claimed: make(map[uint8]int, ifaces), |
||||
detached: make(map[uint8]int), |
||||
} |
||||
|
||||
if err := d.detachKernelDriver(); err != nil { |
||||
d.Close() |
||||
return nil, err |
||||
} |
||||
|
||||
return d, nil |
||||
} |
||||
|
||||
// detachKernelDriver detaches any active kernel drivers, if supported by the platform.
|
||||
// If there are any errors, like Context.ListDevices, only the final one will be returned.
|
||||
func (d *Device) detachKernelDriver() (err error) { |
||||
for _, cfg := range d.Configs { |
||||
for _, iface := range cfg.Interfaces { |
||||
switch activeErr := C.libusb_kernel_driver_active(d.handle, C.int(iface.Number)); activeErr { |
||||
case C.LIBUSB_ERROR_NOT_SUPPORTED: |
||||
// no need to do any futher checking, no platform support
|
||||
return |
||||
case 0: |
||||
continue |
||||
case 1: |
||||
switch detachErr := C.libusb_detach_kernel_driver(d.handle, C.int(iface.Number)); detachErr { |
||||
case C.LIBUSB_ERROR_NOT_SUPPORTED: |
||||
// shouldn't ever get here, should be caught by the outer switch
|
||||
return |
||||
case 0: |
||||
d.detached[iface.Number]++ |
||||
case C.LIBUSB_ERROR_NOT_FOUND: |
||||
// this status is returned if libusb's driver is already attached to the device
|
||||
d.detached[iface.Number]++ |
||||
default: |
||||
err = fmt.Errorf("usb: detach kernel driver: %s", usbError(detachErr)) |
||||
} |
||||
default: |
||||
err = fmt.Errorf("usb: active kernel driver check: %s", usbError(activeErr)) |
||||
} |
||||
} |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
// attachKernelDriver re-attaches kernel drivers to any previously detached interfaces, if supported by the platform.
|
||||
// If there are any errors, like Context.ListDevices, only the final one will be returned.
|
||||
func (d *Device) attachKernelDriver() (err error) { |
||||
for iface := range d.detached { |
||||
switch attachErr := C.libusb_attach_kernel_driver(d.handle, C.int(iface)); attachErr { |
||||
case C.LIBUSB_ERROR_NOT_SUPPORTED: |
||||
// no need to do any futher checking, no platform support
|
||||
return |
||||
case 0: |
||||
continue |
||||
default: |
||||
err = fmt.Errorf("usb: attach kernel driver: %s", usbError(attachErr)) |
||||
} |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
func (d *Device) Reset() error { |
||||
if errno := C.libusb_reset_device(d.handle); errno != 0 { |
||||
return usbError(errno) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (int, error) { |
||||
//log.Printf("control xfer: %d:%d/%d:%d %x", idx, rType, request, val, string(data))
|
||||
dataSlice := (*reflect.SliceHeader)(unsafe.Pointer(&data)) |
||||
n := C.libusb_control_transfer( |
||||
d.handle, |
||||
C.uint8_t(rType), |
||||
C.uint8_t(request), |
||||
C.uint16_t(val), |
||||
C.uint16_t(idx), |
||||
(*C.uchar)(unsafe.Pointer(dataSlice.Data)), |
||||
C.uint16_t(len(data)), |
||||
C.uint(d.ControlTimeout/time.Millisecond)) |
||||
if n < 0 { |
||||
return int(n), usbError(n) |
||||
} |
||||
return int(n), nil |
||||
} |
||||
|
||||
// ActiveConfig returns the config id (not the index) of the active configuration.
|
||||
// This corresponds to the ConfigInfo.Config field.
|
||||
func (d *Device) ActiveConfig() (uint8, error) { |
||||
var cfg C.int |
||||
if errno := C.libusb_get_configuration(d.handle, &cfg); errno < 0 { |
||||
return 0, usbError(errno) |
||||
} |
||||
return uint8(cfg), nil |
||||
} |
||||
|
||||
// SetConfig attempts to change the active configuration.
|
||||
// The cfg provided is the config id (not the index) of the configuration to set,
|
||||
// which corresponds to the ConfigInfo.Config field.
|
||||
func (d *Device) SetConfig(cfg uint8) error { |
||||
if errno := C.libusb_set_configuration(d.handle, C.int(cfg)); errno < 0 { |
||||
return usbError(errno) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// Close the device.
|
||||
func (d *Device) Close() error { |
||||
if d.handle == nil { |
||||
return fmt.Errorf("usb: double close on device") |
||||
} |
||||
d.lock.Lock() |
||||
defer d.lock.Unlock() |
||||
for iface := range d.claimed { |
||||
C.libusb_release_interface(d.handle, C.int(iface)) |
||||
} |
||||
d.attachKernelDriver() |
||||
C.libusb_close(d.handle) |
||||
d.handle = nil |
||||
return nil |
||||
} |
||||
|
||||
func (d *Device) OpenEndpoint(conf, iface, setup, epoint uint8) (Endpoint, error) { |
||||
end := &endpoint{ |
||||
Device: d, |
||||
} |
||||
|
||||
var setAlternate bool |
||||
for _, c := range d.Configs { |
||||
if c.Config != conf { |
||||
continue |
||||
} |
||||
debug.Printf("found conf: %#v\n", c) |
||||
for _, i := range c.Interfaces { |
||||
if i.Number != iface { |
||||
continue |
||||
} |
||||
debug.Printf("found iface: %#v\n", i) |
||||
for i, s := range i.Setups { |
||||
if s.Alternate != setup { |
||||
continue |
||||
} |
||||
setAlternate = i != 0 |
||||
|
||||
debug.Printf("found setup: %#v [default: %v]\n", s, !setAlternate) |
||||
for _, e := range s.Endpoints { |
||||
debug.Printf("ep %02x search: %#v\n", epoint, s) |
||||
if e.Address != epoint { |
||||
continue |
||||
} |
||||
end.InterfaceSetup = s |
||||
end.EndpointInfo = e |
||||
switch tt := TransferType(e.Attributes) & TRANSFER_TYPE_MASK; tt { |
||||
case TRANSFER_TYPE_BULK: |
||||
end.xfer = bulk_xfer |
||||
case TRANSFER_TYPE_INTERRUPT: |
||||
end.xfer = interrupt_xfer |
||||
case TRANSFER_TYPE_ISOCHRONOUS: |
||||
end.xfer = isochronous_xfer |
||||
default: |
||||
return nil, fmt.Errorf("usb: %s transfer is unsupported", tt) |
||||
} |
||||
goto found |
||||
} |
||||
return nil, fmt.Errorf("usb: unknown endpoint %02x", epoint) |
||||
} |
||||
return nil, fmt.Errorf("usb: unknown setup %02x", setup) |
||||
} |
||||
return nil, fmt.Errorf("usb: unknown interface %02x", iface) |
||||
} |
||||
return nil, fmt.Errorf("usb: unknown configuration %02x", conf) |
||||
|
||||
found: |
||||
|
||||
// Set the configuration
|
||||
var activeConf C.int |
||||
if errno := C.libusb_get_configuration(d.handle, &activeConf); errno < 0 { |
||||
return nil, fmt.Errorf("usb: getcfg: %s", usbError(errno)) |
||||
} |
||||
if int(activeConf) != int(conf) { |
||||
if errno := C.libusb_set_configuration(d.handle, C.int(conf)); errno < 0 { |
||||
return nil, fmt.Errorf("usb: setcfg: %s", usbError(errno)) |
||||
} |
||||
} |
||||
|
||||
// Claim the interface
|
||||
if errno := C.libusb_claim_interface(d.handle, C.int(iface)); errno < 0 { |
||||
return nil, fmt.Errorf("usb: claim: %s", usbError(errno)) |
||||
} |
||||
|
||||
// Increment the claim count
|
||||
d.lock.Lock() |
||||
d.claimed[iface]++ |
||||
d.lock.Unlock() // unlock immediately because the next calls may block
|
||||
|
||||
// Choose the alternate
|
||||
if setAlternate { |
||||
if errno := C.libusb_set_interface_alt_setting(d.handle, C.int(iface), C.int(setup)); errno < 0 { |
||||
debug.Printf("altsetting error: %s", usbError(errno)) |
||||
return nil, fmt.Errorf("usb: setalt: %s", usbError(errno)) |
||||
} |
||||
} |
||||
|
||||
return end, nil |
||||
} |
||||
|
||||
func (d *Device) GetStringDescriptor(desc_index int) (string, error) { |
||||
|
||||
// allocate 200-byte array limited the length of string descriptor
|
||||
goBuffer := make([]byte, 200) |
||||
|
||||
// get string descriptor from libusb. if errno < 0 then there are any errors.
|
||||
// if errno >= 0; it is a length of result string descriptor
|
||||
errno := C.libusb_get_string_descriptor_ascii( |
||||
d.handle, |
||||
C.uint8_t(desc_index), |
||||
(*C.uchar)(unsafe.Pointer(&goBuffer[0])), |
||||
200) |
||||
|
||||
// if any errors occur
|
||||
if errno < 0 { |
||||
return "", fmt.Errorf("usb: getstr: %s", usbError(errno)) |
||||
} |
||||
// convert slice of byte to string with limited length from errno
|
||||
stringDescriptor := string(goBuffer[:errno]) |
||||
|
||||
return stringDescriptor, nil |
||||
} |
@ -0,0 +1,100 @@ |
||||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package usb |
||||
|
||||
// #include "libusb.h"
|
||||
import "C" |
||||
|
||||
import ( |
||||
"fmt" |
||||
"reflect" |
||||
"time" |
||||
"unsafe" |
||||
) |
||||
|
||||
type Endpoint interface { |
||||
Read(b []byte) (int, error) |
||||
Write(b []byte) (int, error) |
||||
Interface() InterfaceSetup |
||||
Info() EndpointInfo |
||||
} |
||||
|
||||
type endpoint struct { |
||||
*Device |
||||
InterfaceSetup |
||||
EndpointInfo |
||||
xfer func(*endpoint, []byte, time.Duration) (int, error) |
||||
} |
||||
|
||||
func (e *endpoint) Read(buf []byte) (int, error) { |
||||
if EndpointDirection(e.Address)&ENDPOINT_DIR_MASK != ENDPOINT_DIR_IN { |
||||
return 0, fmt.Errorf("usb: read: not an IN endpoint") |
||||
} |
||||
|
||||
return e.xfer(e, buf, e.ReadTimeout) |
||||
} |
||||
|
||||
func (e *endpoint) Write(buf []byte) (int, error) { |
||||
if EndpointDirection(e.Address)&ENDPOINT_DIR_MASK != ENDPOINT_DIR_OUT { |
||||
return 0, fmt.Errorf("usb: write: not an OUT endpoint") |
||||
} |
||||
|
||||
return e.xfer(e, buf, e.WriteTimeout) |
||||
} |
||||
|
||||
func (e *endpoint) Interface() InterfaceSetup { return e.InterfaceSetup } |
||||
func (e *endpoint) Info() EndpointInfo { return e.EndpointInfo } |
||||
|
||||
// TODO(kevlar): (*Endpoint).Close
|
||||
|
||||
func bulk_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { |
||||
if len(buf) == 0 { |
||||
return 0, nil |
||||
} |
||||
|
||||
data := (*reflect.SliceHeader)(unsafe.Pointer(&buf)).Data |
||||
|
||||
var cnt C.int |
||||
if errno := C.libusb_bulk_transfer( |
||||
e.handle, |
||||
C.uchar(e.Address), |
||||
(*C.uchar)(unsafe.Pointer(data)), |
||||
C.int(len(buf)), |
||||
&cnt, |
||||
C.uint(timeout/time.Millisecond)); errno < 0 { |
||||
return 0, usbError(errno) |
||||
} |
||||
return int(cnt), nil |
||||
} |
||||
|
||||
func interrupt_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { |
||||
if len(buf) == 0 { |
||||
return 0, nil |
||||
} |
||||
|
||||
data := (*reflect.SliceHeader)(unsafe.Pointer(&buf)).Data |
||||
|
||||
var cnt C.int |
||||
if errno := C.libusb_interrupt_transfer( |
||||
e.handle, |
||||
C.uchar(e.Address), |
||||
(*C.uchar)(unsafe.Pointer(data)), |
||||
C.int(len(buf)), |
||||
&cnt, |
||||
C.uint(timeout/time.Millisecond)); errno < 0 { |
||||
return 0, usbError(errno) |
||||
} |
||||
return int(cnt), nil |
||||
} |
@ -0,0 +1,92 @@ |
||||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package usb |
||||
|
||||
import ( |
||||
"fmt" |
||||
) |
||||
|
||||
// #include "libusb.h"
|
||||
import "C" |
||||
|
||||
type usbError C.int |
||||
|
||||
func (e usbError) Error() string { |
||||
return fmt.Sprintf("libusb: %s [code %d]", usbErrorString[e], int(e)) |
||||
} |
||||
|
||||
const ( |
||||
SUCCESS usbError = C.LIBUSB_SUCCESS |
||||
ERROR_IO usbError = C.LIBUSB_ERROR_IO |
||||
ERROR_INVALID_PARAM usbError = C.LIBUSB_ERROR_INVALID_PARAM |
||||
ERROR_ACCESS usbError = C.LIBUSB_ERROR_ACCESS |
||||
ERROR_NO_DEVICE usbError = C.LIBUSB_ERROR_NO_DEVICE |
||||
ERROR_NOT_FOUND usbError = C.LIBUSB_ERROR_NOT_FOUND |
||||
ERROR_BUSY usbError = C.LIBUSB_ERROR_BUSY |
||||
ERROR_TIMEOUT usbError = C.LIBUSB_ERROR_TIMEOUT |
||||
ERROR_OVERFLOW usbError = C.LIBUSB_ERROR_OVERFLOW |
||||
ERROR_PIPE usbError = C.LIBUSB_ERROR_PIPE |
||||
ERROR_INTERRUPTED usbError = C.LIBUSB_ERROR_INTERRUPTED |
||||
ERROR_NO_MEM usbError = C.LIBUSB_ERROR_NO_MEM |
||||
ERROR_NOT_SUPPORTED usbError = C.LIBUSB_ERROR_NOT_SUPPORTED |
||||
ERROR_OTHER usbError = C.LIBUSB_ERROR_OTHER |
||||
) |
||||
|
||||
var usbErrorString = map[usbError]string{ |
||||
C.LIBUSB_SUCCESS: "success", |
||||
C.LIBUSB_ERROR_IO: "i/o error", |
||||
C.LIBUSB_ERROR_INVALID_PARAM: "invalid param", |
||||
C.LIBUSB_ERROR_ACCESS: "bad access", |
||||
C.LIBUSB_ERROR_NO_DEVICE: "no device", |
||||
C.LIBUSB_ERROR_NOT_FOUND: "not found", |
||||
C.LIBUSB_ERROR_BUSY: "device or resource busy", |
||||
C.LIBUSB_ERROR_TIMEOUT: "timeout", |
||||
C.LIBUSB_ERROR_OVERFLOW: "overflow", |
||||
C.LIBUSB_ERROR_PIPE: "pipe error", |
||||
C.LIBUSB_ERROR_INTERRUPTED: "interrupted", |
||||
C.LIBUSB_ERROR_NO_MEM: "out of memory", |
||||
C.LIBUSB_ERROR_NOT_SUPPORTED: "not supported", |
||||
C.LIBUSB_ERROR_OTHER: "unknown error", |
||||
} |
||||
|
||||
type TransferStatus uint8 |
||||
|
||||
const ( |
||||
LIBUSB_TRANSFER_COMPLETED TransferStatus = C.LIBUSB_TRANSFER_COMPLETED |
||||
LIBUSB_TRANSFER_ERROR TransferStatus = C.LIBUSB_TRANSFER_ERROR |
||||
LIBUSB_TRANSFER_TIMED_OUT TransferStatus = C.LIBUSB_TRANSFER_TIMED_OUT |
||||
LIBUSB_TRANSFER_CANCELLED TransferStatus = C.LIBUSB_TRANSFER_CANCELLED |
||||
LIBUSB_TRANSFER_STALL TransferStatus = C.LIBUSB_TRANSFER_STALL |
||||
LIBUSB_TRANSFER_NO_DEVICE TransferStatus = C.LIBUSB_TRANSFER_NO_DEVICE |
||||
LIBUSB_TRANSFER_OVERFLOW TransferStatus = C.LIBUSB_TRANSFER_OVERFLOW |
||||
) |
||||
|
||||
var transferStatusDescription = map[TransferStatus]string{ |
||||
LIBUSB_TRANSFER_COMPLETED: "transfer completed without error", |
||||
LIBUSB_TRANSFER_ERROR: "transfer failed", |
||||
LIBUSB_TRANSFER_TIMED_OUT: "transfer timed out", |
||||
LIBUSB_TRANSFER_CANCELLED: "transfer was cancelled", |
||||
LIBUSB_TRANSFER_STALL: "halt condition detected (endpoint stalled) or control request not supported", |
||||
LIBUSB_TRANSFER_NO_DEVICE: "device was disconnected", |
||||
LIBUSB_TRANSFER_OVERFLOW: "device sent more data than requested", |
||||
} |
||||
|
||||
func (ts TransferStatus) String() string { |
||||
return transferStatusDescription[ts] |
||||
} |
||||
|
||||
func (ts TransferStatus) Error() string { |
||||
return "libusb: " + ts.String() |
||||
} |
@ -0,0 +1,79 @@ |
||||
#include "libusb.h" |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
|
||||
void print_xfer(struct libusb_transfer *xfer); |
||||
void iso_callback(void *); |
||||
|
||||
void callback(struct libusb_transfer *xfer) { |
||||
//printf("Callback!\n");
|
||||
//print_xfer(xfer);
|
||||
iso_callback(xfer->user_data); |
||||
} |
||||
|
||||
int submit(struct libusb_transfer *xfer) { |
||||
xfer->callback = &callback; |
||||
xfer->status = -1; |
||||
//print_xfer(xfer);
|
||||
//printf("Transfer submitted\n");
|
||||
|
||||
/* fake
|
||||
strcpy(xfer->buffer, "hello"); |
||||
xfer->actual_length = 5; |
||||
callback(xfer); |
||||
return 0; */ |
||||
return libusb_submit_transfer(xfer); |
||||
} |
||||
|
||||
void print_xfer(struct libusb_transfer *xfer) { |
||||
int i; |
||||
|
||||
printf("Transfer:\n"); |
||||
printf(" dev_handle: %p\n", xfer->dev_handle); |
||||
printf(" flags: %08x\n", xfer->flags); |
||||
printf(" endpoint: %x\n", xfer->endpoint); |
||||
printf(" type: %x\n", xfer->type); |
||||
printf(" timeout: %dms\n", xfer->timeout); |
||||
printf(" status: %x\n", xfer->status); |
||||
printf(" length: %d (act: %d)\n", xfer->length, xfer->actual_length); |
||||
printf(" callback: %p\n", xfer->callback); |
||||
printf(" user_data: %p\n", xfer->user_data); |
||||
printf(" buffer: %p\n", xfer->buffer); |
||||
printf(" num_iso_pkts: %d\n", xfer->num_iso_packets); |
||||
printf(" packets:\n"); |
||||
for (i = 0; i < xfer->num_iso_packets; i++) { |
||||
printf(" [%04d] %d (act: %d) %x\n", i, |
||||
xfer->iso_packet_desc[i].length, |
||||
xfer->iso_packet_desc[i].actual_length, |
||||
xfer->iso_packet_desc[i].status); |
||||
} |
||||
} |
||||
|
||||
int extract_data(struct libusb_transfer *xfer, void *raw, int max, unsigned char *status) { |
||||
int i; |
||||
int copied = 0; |
||||
unsigned char *in = xfer->buffer; |
||||
unsigned char *out = raw; |
||||
for (i = 0; i < xfer->num_iso_packets; i++) { |
||||
struct libusb_iso_packet_descriptor pkt = xfer->iso_packet_desc[i]; |
||||
|
||||
// Copy the data
|
||||
int len = pkt.actual_length; |
||||
if (len > max) { |
||||
len = max; |
||||
} |
||||
memcpy(out, in, len); |
||||
copied += len; |
||||
|
||||
// Increment offsets
|
||||
in += pkt.length; |
||||
out += len; |
||||
|
||||
// Extract first error
|
||||
if (pkt.status == 0 || *status != 0) { |
||||
continue; |
||||
} |
||||
*status = pkt.status; |
||||
} |
||||
return copied; |
||||
} |
@ -0,0 +1,148 @@ |
||||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package usb |
||||
|
||||
/* |
||||
#include "libusb.h" |
||||
|
||||
int submit(struct libusb_transfer *xfer); |
||||
void print_xfer(struct libusb_transfer *xfer); |
||||
int extract_data(struct libusb_transfer *xfer, void *data, int max, unsigned char *status); |
||||
*/ |
||||
import "C" |
||||
|
||||
import ( |
||||
"fmt" |
||||
"log" |
||||
"time" |
||||
"unsafe" |
||||
) |
||||
|
||||
//export iso_callback
|
||||
func iso_callback(cptr unsafe.Pointer) { |
||||
ch := *(*chan struct{})(cptr) |
||||
close(ch) |
||||
} |
||||
|
||||
func (end *endpoint) allocTransfer() *Transfer { |
||||
// Use libusb_get_max_iso_packet_size ?
|
||||
const ( |
||||
iso_packets = 8 // 128 // 242
|
||||
packet_size = 2 * 960 // 1760
|
||||
) |
||||
|
||||
xfer := C.libusb_alloc_transfer(C.int(iso_packets)) |
||||
if xfer == nil { |
||||
log.Printf("usb: transfer allocation failed?!") |
||||
return nil |
||||
} |
||||
|
||||
buf := make([]byte, iso_packets*packet_size) |
||||
done := make(chan struct{}, 1) |
||||
|
||||
xfer.dev_handle = end.Device.handle |
||||
xfer.endpoint = C.uchar(end.Address) |
||||
xfer._type = C.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS |
||||
|
||||
xfer.buffer = (*C.uchar)((unsafe.Pointer)(&buf[0])) |
||||
xfer.length = C.int(len(buf)) |
||||
xfer.num_iso_packets = iso_packets |
||||
|
||||
C.libusb_set_iso_packet_lengths(xfer, packet_size) |
||||
/* |
||||
pkts := *(*[]C.struct_libusb_packet_descriptor)(unsafe.Pointer(&reflect.SliceHeader{ |
||||
Data: uintptr(unsafe.Pointer(&xfer.iso_packet_desc)), |
||||
Len: iso_packets, |
||||
Cap: iso_packets, |
||||
})) |
||||
*/ |
||||
|
||||
t := &Transfer{ |
||||
xfer: xfer, |
||||
done: done, |
||||
buf: buf, |
||||
} |
||||
xfer.user_data = (unsafe.Pointer)(&t.done) |
||||
|
||||
return t |
||||
} |
||||
|
||||
type Transfer struct { |
||||
xfer *C.struct_libusb_transfer |
||||
pkts []*C.struct_libusb_packet_descriptor |
||||
done chan struct{} |
||||
buf []byte |
||||
} |
||||
|
||||
func (t *Transfer) Submit(timeout time.Duration) error { |
||||
//log.Printf("iso: submitting %#v", t.xfer)
|
||||
t.xfer.timeout = C.uint(timeout / time.Millisecond) |
||||
if errno := C.submit(t.xfer); errno < 0 { |
||||
return usbError(errno) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func (t *Transfer) Wait(b []byte) (n int, err error) { |
||||
select { |
||||
case <-time.After(10 * time.Second): |
||||
return 0, fmt.Errorf("wait timed out after 10s") |
||||
case <-t.done: |
||||
} |
||||
// Non-iso transfers:
|
||||
//n = int(t.xfer.actual_length)
|
||||
//copy(b, ((*[1 << 16]byte)(unsafe.Pointer(t.xfer.buffer)))[:n])
|
||||
|
||||
//C.print_xfer(t.xfer)
|
||||
/* |
||||
buf, offset := ((*[1 << 16]byte)(unsafe.Pointer(t.xfer.buffer))), 0 |
||||
for i, pkt := range *t.pkts { |
||||
log.Printf("Type is %T", t.pkts) |
||||
n += copy(b[n:], buf[offset:][:pkt.actual_length]) |
||||
offset += pkt.Length |
||||
if pkt.status != 0 && err == nil { |
||||
err = error(TransferStatus(pkt.status)) |
||||
} |
||||
} |
||||
*/ |
||||
var status uint8 |
||||
n = int(C.extract_data(t.xfer, unsafe.Pointer(&b[0]), C.int(len(b)), (*C.uchar)(unsafe.Pointer(&status)))) |
||||
if status != 0 { |
||||
err = TransferStatus(status) |
||||
} |
||||
return n, err |
||||
} |
||||
|
||||
func (t *Transfer) Close() error { |
||||
C.libusb_free_transfer(t.xfer) |
||||
return nil |
||||
} |
||||
|
||||
func isochronous_xfer(e *endpoint, buf []byte, timeout time.Duration) (int, error) { |
||||
t := e.allocTransfer() |
||||
defer t.Close() |
||||
|
||||
if err := t.Submit(timeout); err != nil { |
||||
log.Printf("iso: xfer failed to submit: %s", err) |
||||
return 0, err |
||||
} |
||||
|
||||
n, err := t.Wait(buf) |
||||
if err != nil { |
||||
log.Printf("iso: xfer failed: %s", err) |
||||
return 0, err |
||||
} |
||||
return n, err |
||||
} |
@ -0,0 +1,47 @@ |
||||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package usb |
||||
|
||||
import ( |
||||
"fmt" |
||||
) |
||||
|
||||
type BCD uint16 |
||||
|
||||
const ( |
||||
USB_2_0 BCD = 0x0200 |
||||
USB_1_1 BCD = 0x0110 |
||||
USB_1_0 BCD = 0x0100 |
||||
) |
||||
|
||||
func (d BCD) Int() (i int) { |
||||
ten := 1 |
||||
for o := uint(0); o < 4; o++ { |
||||
n := ((0xF << (o * 4)) & d) >> (o * 4) |
||||
i += int(n) * ten |
||||
ten *= 10 |
||||
} |
||||
return |
||||
} |
||||
|
||||
func (d BCD) String() string { |
||||
return fmt.Sprintf("%02x.%02x", int(d>>8), int(d&0xFF)) |
||||
} |
||||
|
||||
type ID uint16 |
||||
|
||||
func (id ID) String() string { |
||||
return fmt.Sprintf("%04x", int(id)) |
||||
} |
@ -0,0 +1,178 @@ |
||||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package usb provides a wrapper around libusb-1.0.
|
||||
package usb |
||||
|
||||
/* |
||||
#cgo CFLAGS: -I../internal/libusb/libusb |
||||
#cgo CFLAGS: -DDEFAULT_VISIBILITY="" |
||||
#cgo linux CFLAGS: -DOS_LINUX -D_GNU_SOURCE -DPOLL_NFDS_TYPE=int |
||||
#cgo darwin CFLAGS: -DOS_DARWIN -DPOLL_NFDS_TYPE=int |
||||
#cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit -lobjc |
||||
#cgo openbsd CFLAGS: -DOS_OPENBSD -DPOLL_NFDS_TYPE=int |
||||
#cgo windows CFLAGS: -DOS_WINDOWS -DUSE_USBDK -DPOLL_NFDS_TYPE=int |
||||
|
||||
#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) |
||||
#include <sys/poll.h> |
||||
#include "os/threads_posix.c" |
||||
#include "os/poll_posix.c" |
||||
#elif defined(OS_WINDOWS) |
||||
#include "os/threads_windows.c" |
||||
#include "os/poll_windows.c" |
||||
#endif |
||||
|
||||
#ifdef OS_LINUX |
||||
#include "os/linux_usbfs.c" |
||||
#include "os/linux_netlink.c" |
||||
#elif OS_DARWIN |
||||
#include "os/darwin_usb.c" |
||||
#elif OS_OPENBSD |
||||
#include "os/openbsd_usb.c" |
||||
#elif OS_WINDOWS |
||||
#include "os/windows_nt_common.c" |
||||
#include "os/windows_usbdk.c" |
||||
#endif |
||||
|
||||
#include "core.c" |
||||
#include "descriptor.c" |
||||
#include "hotplug.c" |
||||
#include "io.c" |
||||
#include "strerror.c" |
||||
#include "sync.c" |
||||
*/ |
||||
import "C" |
||||
|
||||
import ( |
||||
"log" |
||||
"reflect" |
||||
"unsafe" |
||||
) |
||||
|
||||
type Context struct { |
||||
ctx *C.libusb_context |
||||
done chan struct{} |
||||
} |
||||
|
||||
func (c *Context) Debug(level int) { |
||||
C.libusb_set_debug(c.ctx, C.int(level)) |
||||
} |
||||
|
||||
func NewContext() (*Context, error) { |
||||
c := &Context{ |
||||
done: make(chan struct{}), |
||||
} |
||||
|
||||
if errno := C.libusb_init(&c.ctx); errno != 0 { |
||||
return nil, usbError(errno) |
||||
} |
||||
|
||||
go func() { |
||||
tv := C.struct_timeval{ |
||||
tv_sec: 0, |
||||
tv_usec: 100000, |
||||
} |
||||
for { |
||||
select { |
||||
case <-c.done: |
||||
return |
||||
default: |
||||
} |
||||
if errno := C.libusb_handle_events_timeout_completed(c.ctx, &tv, nil); errno < 0 { |
||||
log.Printf("handle_events: error: %s", usbError(errno)) |
||||
continue |
||||
} |
||||
//log.Printf("handle_events returned")
|
||||
} |
||||
}() |
||||
|
||||
return c, nil |
||||
} |
||||
|
||||
// ListDevices calls each with each enumerated device.
|
||||
// If the function returns true, the device is opened and a Device is returned if the operation succeeds.
|
||||
// Every Device returned (whether an error is also returned or not) must be closed.
|
||||
// If there are any errors enumerating the devices,
|
||||
// the final one is returned along with any successfully opened devices.
|
||||
func (c *Context) ListDevices(each func(desc *Descriptor) bool) ([]*Device, error) { |
||||
var list **C.libusb_device |
||||
cnt := C.libusb_get_device_list(c.ctx, &list) |
||||
if cnt < 0 { |
||||
return nil, usbError(cnt) |
||||
} |
||||
defer C.libusb_free_device_list(list, 1) |
||||
|
||||
var slice []*C.libusb_device |
||||
*(*reflect.SliceHeader)(unsafe.Pointer(&slice)) = reflect.SliceHeader{ |
||||
Data: uintptr(unsafe.Pointer(list)), |
||||
Len: int(cnt), |
||||
Cap: int(cnt), |
||||
} |
||||
|
||||
var reterr error |
||||
var ret []*Device |
||||
for _, dev := range slice { |
||||
desc, err := newDescriptor(dev) |
||||
if err != nil { |
||||
reterr = err |
||||
continue |
||||
} |
||||
|
||||
if each(desc) { |
||||
var handle *C.libusb_device_handle |
||||
if errno := C.libusb_open(dev, &handle); errno != 0 { |
||||
reterr = usbError(errno) |
||||
continue |
||||
} |
||||
if dev, err := newDevice(handle, desc); err != nil { |
||||
reterr = err |
||||
} else { |
||||
ret = append(ret, dev) |
||||
} |
||||
} |
||||
} |
||||
return ret, reterr |
||||
} |
||||
|
||||
// OpenDeviceWithVidPid opens Device from specific VendorId and ProductId.
|
||||
// If there are any errors, it'll returns at second value.
|
||||
func (c *Context) OpenDeviceWithVidPid(vid, pid int) (*Device, error) { |
||||
|
||||
handle := C.libusb_open_device_with_vid_pid(c.ctx, (C.uint16_t)(vid), (C.uint16_t)(pid)) |
||||
if handle == nil { |
||||
return nil, ERROR_NOT_FOUND |
||||
} |
||||
|
||||
dev := C.libusb_get_device(handle) |
||||
if dev == nil { |
||||
return nil, ERROR_NO_DEVICE |
||||
} |
||||
|
||||
desc, err := newDescriptor(dev) |
||||
|
||||
// return an error from nil-handle and nil-device
|
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return newDevice(handle, desc) |
||||
} |
||||
|
||||
func (c *Context) Close() error { |
||||
close(c.done) |
||||
if c.ctx != nil { |
||||
C.libusb_exit(c.ctx) |
||||
} |
||||
c.ctx = nil |
||||
return nil |
||||
} |
Loading…
Reference in new issue