refactor: refactor UMS mediums and links, add internals docs
This commit is contained in:
parent
9a49056522
commit
6b211a8f40
|
@ -184,10 +184,9 @@ Link management related function declarations
|
|||
|
||||
.. warning::
|
||||
|
||||
This cannot be used if :c:macro:`CAHUTE_SERIAL_RECEIVER` is not
|
||||
set and :c:macro:`CAHUTE_SERIAL_NOCHECK` is set, as the
|
||||
sender / active side tweaks the checking flow to determine the
|
||||
protocol of the other side.
|
||||
This cannot be used if :c:macro:`CAHUTE_SERIAL_NOCHECK` is set,
|
||||
as we tweak the checking flow to determine the protocol of the
|
||||
other side.
|
||||
|
||||
.. c:macro:: CAHUTE_SERIAL_PROTOCOL_CASIOLINK
|
||||
|
||||
|
@ -339,9 +338,8 @@ Link management related function declarations
|
|||
|
||||
.. warning::
|
||||
|
||||
This cannot be used if :c:macro:`CAHUTE_SERIAL_RECEIVER` is not
|
||||
set and :c:macro:`CAHUTE_SERIAL_PROTOCOL_AUTO` is used, as the
|
||||
sender / active side tweaks the checking flow to determine the
|
||||
This cannot be used if :c:macro:`CAHUTE_SERIAL_PROTOCOL_AUTO`
|
||||
is used, as we tweak the checking flow to determine the
|
||||
protocol of the other side.
|
||||
|
||||
.. c:macro:: CAHUTE_SERIAL_NODISC
|
||||
|
|
|
@ -28,6 +28,7 @@ This documentation is organized using `Diátaxis`_' structure.
|
|||
cli-guides
|
||||
developer-guides
|
||||
topics
|
||||
internals
|
||||
project
|
||||
cli
|
||||
headers
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
Cahute internals
|
||||
================
|
||||
|
||||
In this section, we will explore discussion topics regarding the project's
|
||||
design and inner workings. These topics provide keys to understanding what is
|
||||
happening in this project, and why it is happening.
|
||||
|
||||
.. note::
|
||||
|
||||
The following sections are hands-on, it is recommended to open the code
|
||||
at the same time.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
internals/links
|
|
@ -0,0 +1,681 @@
|
|||
Links and mediums
|
||||
=================
|
||||
|
||||
All communication implementations are centered around resources called links.
|
||||
Internally, links are mostly constituted of:
|
||||
|
||||
* A **medium**, which is an interface with a set of resources to communicate
|
||||
with the underlying medium opened with the system or hardware.
|
||||
* A **protocol**, which is a set of resources and functions that do **not**
|
||||
constitute an interface, since protocols may obey different logics.
|
||||
|
||||
A link only requires one memory allocation (except for system resources that
|
||||
are allocated / opened using different functions), and the medium
|
||||
and the protocol are initialized together using link opening functions.
|
||||
|
||||
Mediums
|
||||
-------
|
||||
|
||||
Mediums define a common set of interfaces that can be used by protocols to
|
||||
communicate with the device or host.
|
||||
|
||||
Medium interface
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Most mediums support a stream-like interface with the following functions:
|
||||
|
||||
.. c:function:: int cahute_read_from_link(cahute_link *link, cahute_u8 *buf, \
|
||||
size_t size, unsigned long first_timeout, unsigned long next_timeout)
|
||||
|
||||
Read exactly ``size`` bytes of data into the buffer.
|
||||
|
||||
This function uses the medium read buffer to store any incoming excess
|
||||
data, for it to be processed first next time before using the underlying
|
||||
buffer to read more data.
|
||||
|
||||
.. warning::
|
||||
|
||||
This function does not provide the number of bytes that have been
|
||||
read in case of error (with the exception of
|
||||
:c:macro:`CAHUTE_ERROR_TIMEOUT_START`, which implies that no bytes
|
||||
have been read).
|
||||
|
||||
This is to simplify as much as possible protocol
|
||||
implementations, but it also means that the medium should be
|
||||
considered irrecoverable in such cases.
|
||||
|
||||
Errors to be expected from this function are the following:
|
||||
|
||||
:c:macro:`CAHUTE_ERROR_TIMEOUT_START`
|
||||
The first byte was not received in a timely matter.
|
||||
This can only occur if ``first_timeout`` was not set to 0.
|
||||
|
||||
:c:macro:`CAHUTE_ERROR_TIMEOUT`
|
||||
A byte past the first one was not received in a timely matter.
|
||||
This can only occur if ``next_timeout`` was not set to 0.
|
||||
|
||||
:c:macro:`CAHUTE_ERROR_GONE`
|
||||
The device is no longer present, usually either because the USB
|
||||
cable has been unplugged on one end or the other, or the serial
|
||||
adapter has been unplugged from the host.
|
||||
|
||||
:c:macro:`CAHUTE_ERROR_UNKNOWN`
|
||||
The medium-specific operations have yielded an error code that
|
||||
Cahute did not interpret. Some details can usually be found in
|
||||
the logs.
|
||||
|
||||
:param link: Link to the device.
|
||||
:param buf: Buffer in which to write the received data.
|
||||
:param size: Size of the data to write to the buffer.
|
||||
:param first_timeout: Maximum delay to wait before the first byte of the
|
||||
data, in milliseconds. If this is set to 0, the first byte will be
|
||||
awaited indefinitely.
|
||||
:param next_timeout: Maximum delay to wait between two bytes of the data,
|
||||
or before the last byte, in milliseconds. If this is set to 0, next
|
||||
bytes will be awaited indefinitely.
|
||||
:return: Error, or :c:macro:`CAHUTE_OK`.
|
||||
|
||||
.. c:function:: int cahute_skip_from_link(cahute_link *link, size_t size, \
|
||||
unsigned long first_timeout, unsigned long next_timeout)
|
||||
|
||||
Skip exactly ``size`` bytes of data.
|
||||
|
||||
This function is a convenience function for protocol implementations.
|
||||
It uses :c:func:`cahute_read_from_link` to read into a trashable
|
||||
buffer, and thus, comes with the same risks and errors.
|
||||
|
||||
:param link: Link to the device.
|
||||
:param size: Size of the data to receive and skip.
|
||||
:param first_timeout: Maximum delay to wait before the first byte of the
|
||||
data, in milliseconds. If this is set to 0, the first byte will be
|
||||
awaited indefinitely.
|
||||
:param next_timeout: Maximum delay to wait between two bytes of the data,
|
||||
or before the last byte, in milliseconds. If this is set to 0, next
|
||||
bytes will be awaited indefinitely.
|
||||
:return: Error, or :c:macro:`CAHUTE_OK`.
|
||||
|
||||
.. c:function:: int cahute_write_to_link(cahute_link *link, \
|
||||
cahute_u8 const *buf, size_t size)
|
||||
|
||||
Write exactly ``size`` bytes of data to the link.
|
||||
|
||||
Errors to be expected from this function are the following:
|
||||
|
||||
:c:macro:`CAHUTE_ERROR_GONE`
|
||||
The device is no longer present, usually either because the USB
|
||||
cable has been unplugged on one end or the other, or the serial
|
||||
adapter has been unplugged from the host.
|
||||
|
||||
:c:macro:`CAHUTE_ERROR_UNKNOWN`
|
||||
The medium-specific operations have yielded an error code that
|
||||
Cahute did not interpret. Some details can usually be found in
|
||||
the logs.
|
||||
|
||||
:param link: Link to the device.
|
||||
:param buf: Buffer from which to read the data to send.
|
||||
:param size: Size of the data to read and send.
|
||||
:return: Error, or :c:macro:`CAHUTE_OK`.
|
||||
|
||||
Serial mediums such as :c:macro:`CAHUTE_LINK_MEDIUM_POSIX_SERIAL` or
|
||||
:c:macro:`CAHUTE_LINK_MEDIUM_WIN32_SERIAL` support changing the parameters
|
||||
of the serial link using the following function:
|
||||
|
||||
.. c:function:: int cahute_set_serial_params_to_link(cahute_link *link, \
|
||||
unsigned long flags, unsigned long speed)
|
||||
|
||||
Set the serial parameters to the medium.
|
||||
|
||||
Accepted flags are a subset of the flags for :c:func:`cahute_open_serial`:
|
||||
|
||||
* ``CAHUTE_SERIAL_STOP_*`` (stop bits);
|
||||
* ``CAHUTE_SERIAL_PARITY_*`` (parity);
|
||||
* ``CAHUTE_SERIAL_XONXOFF_*`` (XON/XOFF software control);
|
||||
* ``CAHUTE_SERIAL_DTR_*`` (DTR hardware control);
|
||||
* ``CAHUTE_SERIAL_RTS_*`` (RTS hardware control).
|
||||
|
||||
:param link: Link to the device.
|
||||
:param flags: Flags to set to the medium.
|
||||
:param speed: Speed to set to the medium.
|
||||
:return: Error, or :c:macro:`CAHUTE_OK`.
|
||||
|
||||
USB Mass Storage mediums support an interface capable of making SCSI requests,
|
||||
with the following functions:
|
||||
|
||||
.. c:function:: int cahute_scsi_request_to_link(cahute_link *link, \
|
||||
cahute_u8 const *command, size_t command_size, cahute_u8 const *data, \
|
||||
size_t data_size, int *statusp)
|
||||
|
||||
Emit an SCSI request to the medium, with or without data.
|
||||
|
||||
:param link: Link to the device.
|
||||
:param command: Command to send.
|
||||
:param command_size: Size of the command to send.
|
||||
:param data: Optional data to send along with the command.
|
||||
This can be set to ``NULL`` if ``data_size`` is set to 0.
|
||||
:param data_size: Size of the data to send along with the command.
|
||||
:param statusp: Pointer to the status code to set to the one returned by
|
||||
the device.
|
||||
:return: Error, or :c:macro:`CAHUTE_OK`.
|
||||
|
||||
.. c:function:: int cahute_scsi_request_from_link(cahute_link *link, \
|
||||
cahute_u8 const *command, size_t command_size, cahute_u8 *buf, \
|
||||
size_t buf_size, int *statusp)
|
||||
|
||||
Emit an SCSI request to the medium, while requesting data.
|
||||
|
||||
:param link: Link to the device.
|
||||
:param command: Command to send.
|
||||
:param command_size: Size of the command to send.
|
||||
:param buf: Buffer to fill with the requested data.
|
||||
:param buf_size: Size of the data to request.
|
||||
:param statusp: Pointer to the status code to set to the one returned by
|
||||
the device.
|
||||
:return: Error, or :c:macro:`CAHUTE_OK`.
|
||||
|
||||
Available mediums
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Mediums are represented as ``CAHUTE_LINK_MEDIUM_*`` constants internally.
|
||||
|
||||
.. warning::
|
||||
|
||||
The medium constants are only represented **if they are available on the
|
||||
current configuration**. This is a simple way for medium-specific
|
||||
implementations to be defined or not, with ``#ifdef``.
|
||||
|
||||
Available mediums are the following:
|
||||
|
||||
.. c:macro:: CAHUTE_LINK_MEDIUM_POSIX_SERIAL
|
||||
|
||||
Serial medium using the POSIX STREAMS API, with a file descriptor (*fd*):
|
||||
|
||||
* Closing using `close(2) <https://linux.die.net/man/2/close>`_;
|
||||
* Receiving uses `select(2) <https://linux.die.net/man/2/select>`_ and
|
||||
`read(2) <https://linux.die.net/man/2/read>`_;
|
||||
* Sending uses `write(2) <https://linux.die.net/man/2/write>`_;
|
||||
* Serial params setting uses
|
||||
`termios(3) <https://linux.die.net/man/3/termios>`_, including
|
||||
``tcdrain()``, and
|
||||
`tty_ioctl(4) <https://linux.die.net/man/4/tty_ioctl>`_, especially
|
||||
``TIOCMGET`` and ``TIOCMSET``.
|
||||
|
||||
Only available on platforms considered POSIX, including Apple's OS X
|
||||
explicitely (since they do not define the ``__unix__`` constant like
|
||||
Linux does).
|
||||
|
||||
Available protocols on this medium are the following:
|
||||
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_AUTO`;
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_CASIOLINK`;
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN`;
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN_OHP`.
|
||||
|
||||
.. c:macro:: CAHUTE_LINK_MEDIUM_WIN32_SERIAL
|
||||
|
||||
Serial medium using the Windows API, with a |HANDLE|_ and
|
||||
`Overlapped I/O`_:
|
||||
|
||||
* Closing uses |CloseHandle|_;
|
||||
* Receiving uses |ReadFile|_ and |WaitForSingleObject|_, and depending
|
||||
on whether the second function succeeded or not, either
|
||||
|GetOverlappedResult|_ or |CancelIo|_, to ensure we don't have any
|
||||
buffer writes post-freeing the link;
|
||||
* Sending uses |WriteFile|_ and |WaitForSingleObject|_, and depending
|
||||
on whether the second function succeeded or not, either
|
||||
|GetOverlappedResult|_ or |CancelIo|_, to ensure we don't have any
|
||||
buffer reads post-freeing the link;
|
||||
* Serial params setting uses |SetCommState|_.
|
||||
|
||||
For more information, see `Serial Communications in Win32`_.
|
||||
|
||||
Available protocols on this medium are the following:
|
||||
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_AUTO`;
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_CASIOLINK`;
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN`;
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN_OHP`.
|
||||
|
||||
.. c:macro:: CAHUTE_LINK_MEDIUM_WIN32_CESG
|
||||
|
||||
USB device used as a host through CASIO's CESG502 driver using the
|
||||
Windows API.
|
||||
|
||||
As described in :ref:`usb-detection-windows`, we must detect if the
|
||||
device driver is CESG502 or a libusb-compatible driver by using
|
||||
SetupAPI_ or CfgMgr32_, and use this medium in the first case.
|
||||
|
||||
It is used with a |HANDLE|_ and `Overlapped I/O`_:
|
||||
|
||||
* Closing uses |CloseHandle|_;
|
||||
* Receiving uses |ReadFile|_ and |WaitForSingleObject|_, and depending
|
||||
on whether the second function succeeded or not, either
|
||||
|GetOverlappedResult|_ or |CancelIo|_, to ensure we don't have any
|
||||
buffer writes post-freeing the link;
|
||||
* Sending uses |WriteFile|_ and |WaitForSingleObject|_, and depending
|
||||
on whether the second function succeeded or not, either
|
||||
|GetOverlappedResult|_ or |CancelIo|_, to ensure we don't have any
|
||||
buffer reads post-freeing the link.
|
||||
|
||||
Note that CESG502 waits for calculator input by default, and always
|
||||
requires a buffer bigger than the actual input it receives (4 KiB is
|
||||
usually enough). It also abstracts away whether it using bulk transfers
|
||||
directly, or USB Mass Storage, into a stream interface; this however
|
||||
does not allow you to make SCSI requests directly.
|
||||
|
||||
Available protocols on this medium are the following:
|
||||
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_USB_SEVEN`;
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP`.
|
||||
|
||||
.. c:macro:: CAHUTE_LINK_MEDIUM_LIBUSB
|
||||
|
||||
USB device used as a host through libusb, with bulk transport.
|
||||
|
||||
It is used with a |libusb_device_handle|_, opened using a
|
||||
|libusb_context|_:
|
||||
|
||||
* Closing uses |libusb_close|_ on the device handle, and |libusb_exit|_
|
||||
on the libusb context;
|
||||
* Receiving and sending uses |libusb_bulk_transfer|_.
|
||||
|
||||
Available protocols on this medium are the following:
|
||||
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_USB_SEVEN`;
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP`.
|
||||
|
||||
.. c:macro:: CAHUTE_LINK_MEDIUM_LIBUSB_UMS
|
||||
|
||||
USB device used as a host through libusb, implementing USB Mass Storage
|
||||
(UMS) with Bulk-only transport.
|
||||
|
||||
As for :c:macro:`CAHUTE_LINK_MEDIUM_LIBUSB`, it is used with a
|
||||
|libusb_device_handle|_, opened using a |libusb_context|_:
|
||||
|
||||
* Closing uses |libusb_close|_ on the device handle, and |libusb_exit|_
|
||||
on the libusb context;
|
||||
* Requesting using SCSI uses |libusb_bulk_transfer|_ with manual reading
|
||||
and writing of the Command Block Wrapper (CBW) and
|
||||
Command Status Wrapper (CSW).
|
||||
|
||||
See `USB Mass Storage Class, Bulk-Only Transport`_ for more information
|
||||
on CBW and CSW format and protocol in general.
|
||||
|
||||
Available protocols on this medium are the following:
|
||||
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_USB_MASS_STORAGE`;
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP`.
|
||||
|
||||
Protocols
|
||||
---------
|
||||
|
||||
Protocols define what operations and logics are available, and how to
|
||||
implement these operations and logics.
|
||||
|
||||
All protocols may use the **protocol buffer**, which is in the link directly,
|
||||
and for which the use varies depending on the selected protocol:
|
||||
|
||||
* CASIOLINK uses the protocol buffer when receiving data, for storing both
|
||||
the headers and raw data.
|
||||
* Protocol 7.00 uses the protocol buffer to store unpadded data from
|
||||
data packets.
|
||||
* Protocol 7.00 Screenstreaming uses the protocol buffer to store raw
|
||||
picture data.
|
||||
|
||||
.. todo::
|
||||
|
||||
The role of the protocol buffer for Protocol 7.00 should actually
|
||||
change to store the received data.
|
||||
|
||||
Available protocols are:
|
||||
|
||||
.. c:macro:: CAHUTE_LINK_PROTOCOL_SERIAL_AUTO
|
||||
|
||||
Automatic protocol detection on a serial medium.
|
||||
|
||||
Note that this doesn't outlive link protocol initialization, and gets
|
||||
replaced by the actual protocol afterwards; see
|
||||
:ref:`internals-link-protocol-initialization` for more details.
|
||||
|
||||
.. c:macro:: CAHUTE_LINK_PROTOCOL_SERIAL_CASIOLINK
|
||||
|
||||
CASIOLINK protocol over a serial medium.
|
||||
|
||||
See :ref:`protocol-casiolink` for more information.
|
||||
|
||||
Note that in this case, the CASIOLINK variant is set in the
|
||||
``protocol_state.casiolink.variant`` property of the link.
|
||||
|
||||
.. c:macro:: CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN
|
||||
|
||||
Protocol 7.00 over a serial medium.
|
||||
|
||||
See :ref:`protocol-seven` for more information.
|
||||
|
||||
This differs from :c:macro:`CAHUTE_LINK_PROTOCOL_USB_SEVEN` by the
|
||||
availability of command :ref:`seven-command-02`.
|
||||
|
||||
.. c:macro:: CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN_OHP
|
||||
|
||||
Protocol 7.00 Screenstreaming over a serial medium.
|
||||
|
||||
See :ref:`protocol-seven-ohp` for more information.
|
||||
|
||||
.. c:macro:: CAHUTE_LINK_PROTOCOL_USB_SEVEN
|
||||
|
||||
Protocol 7.00 over USB bulk transport or USB Mass Storage or
|
||||
USB Mass Storage commands.
|
||||
|
||||
See :ref:`protocol-seven` and :ref:`protocol-ums` for more information.
|
||||
|
||||
.. c:macro:: CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP
|
||||
|
||||
Protocol 7.00 Screenstreaming over USB bulk transport or USB Mass Storage
|
||||
extended commands.
|
||||
|
||||
See :ref:`protocol-seven-ohp` and :ref:`protocol-ums` for more information.
|
||||
|
||||
.. c:macro:: CAHUTE_LINK_PROTOCOL_USB_MASS_STORAGE
|
||||
|
||||
USB Mass Storage without extensions.
|
||||
|
||||
Opening behaviours
|
||||
------------------
|
||||
|
||||
In this section, we will describe the behaviour of link opening functions.
|
||||
|
||||
:c:func:`cahute_open_serial_link`
|
||||
This function first validates all params to ensure compatibility, e.g.
|
||||
throws an error in case of unsupported flag, speed, or combination.
|
||||
|
||||
.. note::
|
||||
|
||||
The protocol is selected, depending on the flags, to one of the
|
||||
following:
|
||||
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_AUTO`;
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_CASIOLINK`;
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN`;
|
||||
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN_OHP`.
|
||||
|
||||
Then, depending on the platform:
|
||||
|
||||
* On POSIX and compatible, it will attempt to open the serial device
|
||||
using `open(2) <https://linux.die.net/man/2/open>`_.
|
||||
If this succeeds, the medium of the created link will be set to
|
||||
:c:macro:`CAHUTE_LINK_MEDIUM_POSIX_SERIAL`;
|
||||
* On Windows, it will attempt to open the serial device using
|
||||
|CreateFile|_, then, if it succeeds, call |SetCommTimeouts|_
|
||||
with ``ReadTimeoutInterval`` set to ``MAXDWORD`` in order to only read
|
||||
what is directly available, and create the event for the overlapped
|
||||
object using |CreateEvent|_. If this succeeds, the medium of the
|
||||
created link will be set to :c:macro:`CAHUTE_LINK_MEDIUM_WIN32_SERIAL`;
|
||||
* Otherwise, it will return :c:macro:`CAHUTE_ERROR_IMPL`.
|
||||
|
||||
If the underlying medium has successfully been opened, it will allocate
|
||||
the link and call :c:func:`cahute_set_serial_params_to_link` to set
|
||||
the initial serial parameters to it.
|
||||
|
||||
It will then initialize the protocol using the common protocol
|
||||
initialization procedure; see
|
||||
:ref:`internals-link-protocol-initialization`.
|
||||
|
||||
:c:func:`cahute_open_usb_link`
|
||||
This function first validates all params to ensure compatibility, e.g.
|
||||
throws an error in case of unsupported flag or combination.
|
||||
|
||||
If Cahute runs on Windows, this function makes use of cfgmgr32_ to
|
||||
look for USB device interfaces, and get the associated USB devices.
|
||||
If it finds one matching the provided bus and address numbers, it
|
||||
checks if the device driver is CESG502, and if this is the case, it
|
||||
opens the device interface using |CreateFile|_, and creates the link
|
||||
with the :c:macro:`CAHUTE_LINK_MEDIUM_WIN32_CESG` medium, and:
|
||||
|
||||
* Either the :c:macro:`CAHUTE_LINK_PROTOCOL_SEVEN_OHP` protocol if the
|
||||
:c:macro:`CAHUTE_USB_OHP` flag has been passed;
|
||||
* Or the :c:macro:`CAHUTE_LINK_PROTOCOL_SEVEN` protocol otherwise.
|
||||
|
||||
.. note::
|
||||
|
||||
In order to get bus and address numbers for USB devices that are
|
||||
equivalent to what is obtained through libusb,
|
||||
|DEVPKEY_Device_LocationInfo|_ is used.
|
||||
|
||||
This property returns a string of the form ``Port_#0002.Hub_#000D``,
|
||||
which must be parsed to obtain the address number (here, 2) and
|
||||
the bus number (here, 13).
|
||||
|
||||
Otherwise, and on other platforms, if libusb support is enabled, this
|
||||
function creates a context using |libusb_init|_, gets the device list
|
||||
using |libusb_get_device_list|_, and finds one matching the provided bus
|
||||
and address numbers using |libusb_get_bus_number|_ and
|
||||
|libusb_get_device_address|_ on every entry.
|
||||
|
||||
If a matching device is found, the configuration is obtained using
|
||||
|libusb_get_device_descriptor|_ and |libusb_get_active_config_descriptor|_,
|
||||
in order to:
|
||||
|
||||
* Get the vendor (VID) and product (PID) identifiers, to ensure they match
|
||||
one of the known combinations for CASIO calculators.
|
||||
* Get the interface class (``bInterfaceClass``) to determine the protocol
|
||||
and medium type.
|
||||
* In both cases, ensure that the bulk IN and OUT endpoints exist, and
|
||||
get their endpoint identifiers.
|
||||
|
||||
.. note::
|
||||
|
||||
While historical implementations of CASIO's protocols using libusb
|
||||
hardcode 0x82 as Bulk IN and 0x01 as Bulk OUT, this has proven to
|
||||
change on other platforms such as OS X; see `#3 (comment 1823215641)
|
||||
<https://gitlab.com/cahuteproject/cahute/-/issues/3#note_1823215641>`_
|
||||
for more context.
|
||||
|
||||
The interface class and :c:macro:`CAHUTE_USB_OHP` flag presence to
|
||||
protocol and medium type mapping is the following:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:width: 100%
|
||||
|
||||
* - (in) Intf. class
|
||||
- (in) ``OHP`` flag
|
||||
- (out) Medium
|
||||
- (out) Protocol
|
||||
* - 8
|
||||
- absent
|
||||
- :c:macro:`CAHUTE_LINK_MEDIUM_LIBUSB_UMS`
|
||||
- :c:macro:`CAHUTE_LINK_PROTOCOL_USB_MASS_STORAGE`
|
||||
* - 8
|
||||
- present
|
||||
- :c:macro:`CAHUTE_LINK_MEDIUM_LIBUSB_UMS`
|
||||
- :c:macro:`CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP`
|
||||
* - 255
|
||||
- absent
|
||||
- :c:macro:`CAHUTE_LINK_MEDIUM_LIBUSB`
|
||||
- :c:macro:`CAHUTE_LINK_PROTOCOL_USB_SEVEN`
|
||||
* - 255
|
||||
- present
|
||||
- :c:macro:`CAHUTE_LINK_MEDIUM_LIBUSB`
|
||||
- :c:macro:`CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP`
|
||||
|
||||
See :ref:`usb-detection` for more information.
|
||||
|
||||
Once all metadata has been gathered, the function opens the device using
|
||||
|libusb_open|_, and attempt to claim its interface using
|
||||
|libusb_claim_interface|_ and |libusb_detach_kernel_driver|_.
|
||||
|
||||
.. note::
|
||||
|
||||
Access errors, i.e. any of these two functions returning
|
||||
``LIBUSB_ERROR_ACCESS``, are ignored, since libusb is still
|
||||
able to communicate with the device on some platforms afterwards.
|
||||
|
||||
See `#3 <https://gitlab.com/cahuteproject/cahute/-/issues/3>`_
|
||||
for more context.
|
||||
|
||||
Once this is done, the link is created with the previously selected
|
||||
medium and protocol.
|
||||
|
||||
Otherwise, if libusb support has been disabled, the function returns
|
||||
:c:macro:`CAHUTE_ERROR_IMPL`.
|
||||
|
||||
If medium initialization has been successful, the function will then
|
||||
initialize the protocol using the common protocol initialization
|
||||
procedure; see :ref:`internals-link-protocol-initialization`.
|
||||
|
||||
:c:func:`cahute_open_simple_usb_link`
|
||||
This function is a convenience function, using mostly public functions
|
||||
to work:
|
||||
|
||||
* It detects available USB devices using :c:func:`cahute_detect_usb`.
|
||||
If it finds none, it sleeps and retries until it has no attempts left.
|
||||
If it finds multiple, it fails with error
|
||||
:c:macro:`CAHUTE_ERROR_TOO_MANY`.
|
||||
* It opens the found USB device using :c:func:`cahute_open_usb_link`.
|
||||
|
||||
It used to be to the program or library to define by itself, and was in
|
||||
the guides, but this behaviour is found in most simple scripts that
|
||||
use the Cahute library, so it was decided to include it within the library.
|
||||
|
||||
.. _internals-link-protocol-initialization:
|
||||
|
||||
Protocol initialization
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The common protocol initialization procedure is defined by a function named
|
||||
``init_link`` in ``link/open.c``.
|
||||
|
||||
First of all, if the selected protocol is
|
||||
:c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_AUTO`, the communication initialization
|
||||
is used to determine the protocol in which both devices should communicate.
|
||||
|
||||
.. note::
|
||||
|
||||
Since the initialization step is necessary for automatic protocol
|
||||
discovery to take place, the :c:macro:`CAHUTE_SERIAL_NOCHECK` flag
|
||||
is forbidden with :c:macro:`CAHUTE_SERIAL_PROTOCOL_AUTO`.
|
||||
This is described in :c:func:`cahute_open_serial_link`'s flags description.
|
||||
|
||||
Then, the initialization sequence is run depending on the protocol and role
|
||||
(sender or receiver, depending on the presence of the
|
||||
:c:macro:`CAHUTE_SERIAL_RECEIVER` :c:macro:`CAHUTE_USB_RECEIVER` in the flags
|
||||
of the original function).
|
||||
|
||||
.. |HANDLE| replace:: ``HANDLE``
|
||||
.. |CreateFile| replace:: ``CreateFile``
|
||||
.. |SetCommTimeouts| replace:: ``SetCommTimeouts``
|
||||
.. |CreateEvent| replace:: ``CreateEvent``
|
||||
.. |ReadFile| replace:: ``ReadFile``
|
||||
.. |WriteFile| replace:: ``WriteFile``
|
||||
.. |WaitForSingleObject| replace:: ``WaitForSingleObject``
|
||||
.. |GetOverlappedResult| replace:: ``GetOverlappedResult``
|
||||
.. |CancelIo| replace:: ``CancelIo``
|
||||
.. |CloseHandle| replace:: ``CloseHandle``
|
||||
.. |SetCommState| replace:: ``SetCommState``
|
||||
.. |DEVPKEY_Device_LocationInfo| replace:: ``DEVPKEY_Device_LocationInfo``
|
||||
|
||||
.. |libusb_context| replace:: ``libusb_context``
|
||||
.. |libusb_init| replace:: ``libusb_init``
|
||||
.. |libusb_exit| replace:: ``libusb_exit``
|
||||
.. |libusb_device_handle| replace:: ``libusb_device_handle``
|
||||
.. |libusb_get_device_list| replace:: ``libusb_get_device_list``
|
||||
.. |libusb_get_bus_number| replace:: ``libusb_get_bus_number``
|
||||
.. |libusb_get_device_address| replace:: ``libusb_get_device_address``
|
||||
.. |libusb_get_device_descriptor| replace:: ``libusb_get_device_descriptor``
|
||||
.. |libusb_get_active_config_descriptor|
|
||||
replace:: ``libusb_get_active_config_descriptor``
|
||||
.. |libusb_detach_kernel_driver| replace:: ``_libusb_detach_kernel_driver``
|
||||
.. |libusb_claim_interface| replace:: ``libusb_claim_interface``
|
||||
.. |libusb_open| replace:: ``libusb_open``
|
||||
.. |libusb_close| replace:: ``libusb_close``
|
||||
.. |libusb_bulk_transfer| replace:: ``libusb_bulk_transfer``
|
||||
|
||||
.. _HANDLE:
|
||||
https://learn.microsoft.com/en-us/windows/win32/sysinfo/handles-and-objects
|
||||
.. _Overlapped I/O:
|
||||
https://learn.microsoft.com/en-us/windows/win32/sync/
|
||||
synchronization-and-overlapped-input-and-output
|
||||
.. _CreateFile:
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/
|
||||
fileapi/nf-fileapi-createfilea
|
||||
.. _SetCommTimeouts:
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/
|
||||
winbase/nf-winbase-setcommtimeouts
|
||||
.. _CreateEvent:
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/
|
||||
synchapi/nf-synchapi-createeventa
|
||||
.. _ReadFile:
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/
|
||||
fileapi/nf-fileapi-readfile
|
||||
.. _WriteFile:
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/
|
||||
fileapi/nf-fileapi-writefile
|
||||
.. _WaitForSingleObject:
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/
|
||||
synchapi/nf-synchapi-waitforsingleobject
|
||||
.. _GetOverlappedResult:
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/
|
||||
ioapiset/nf-ioapiset-getoverlappedresult
|
||||
.. _CancelIo:
|
||||
https://learn.microsoft.com/en-us/windows/win32/fileio/cancelio
|
||||
.. _CloseHandle:
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/
|
||||
handleapi/nf-handleapi-closehandle
|
||||
.. _SetCommState:
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/
|
||||
winbase/nf-winbase-setcommstate
|
||||
.. _DEVPKEY_Device_LocationInfo:
|
||||
https://learn.microsoft.com/en-us/windows-hardware/
|
||||
drivers/install/devpkey-device-locationinfo
|
||||
.. _Serial Communications in Win32:
|
||||
https://learn.microsoft.com/en-us/previous-versions/ms810467(v=msdn.10)
|
||||
|
||||
.. _SetupAPI:
|
||||
https://learn.microsoft.com/en-us/windows-hardware/drivers/install/setupapi
|
||||
.. _cfgmgr32:
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/cfgmgr32/
|
||||
|
||||
.. _libusb_context:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__lib.html
|
||||
#ga4ec088aa7b79c4a9599e39bf36a72833
|
||||
.. _libusb_init:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__lib.html
|
||||
#ga7deaef521cfb1a5b3f8d6c01be11a795
|
||||
.. _libusb_exit:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__lib.html
|
||||
#gadc174de608932caeb2fc15d94fa0844d
|
||||
.. _libusb_device_handle:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html
|
||||
#ga7df95821d20d27b5597f1d783749d6a4
|
||||
.. _libusb_get_device_list:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html
|
||||
#gac0fe4b65914c5ed036e6cbec61cb0b97
|
||||
.. _libusb_get_bus_number:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html
|
||||
#gaf2718609d50c8ded2704e4051b3d2925
|
||||
.. _libusb_get_device_address:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html
|
||||
#gab6d4e39ac483ebaeb108f2954715305d
|
||||
.. _libusb_get_device_descriptor:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__desc.html
|
||||
#ga5e9ab08d490a7704cf3a9b0439f16f00
|
||||
.. _libusb_get_active_config_descriptor:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__desc.html
|
||||
#ga425885149172b53b3975a07629c8dab3
|
||||
.. _libusb_detach_kernel_driver:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html
|
||||
#ga5e0cc1d666097e915748593effdc634a
|
||||
.. _libusb_claim_interface:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html
|
||||
#gaee5076addf5de77c7962138397fd5b1a
|
||||
.. _libusb_open:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html
|
||||
#ga3f184a8be4488a767b2e0ae07e76d1b0
|
||||
.. _libusb_close:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html
|
||||
#ga779bc4f1316bdb0ac383bddbd538620e
|
||||
.. _libusb_bulk_transfer:
|
||||
https://libusb.sourceforge.io/api-1.0/group__libusb__syncio.html
|
||||
#ga2f90957ccc1285475ae96ad2ceb1f58c
|
||||
|
||||
.. _USB Mass Storage Class, Bulk-Only Transport:
|
||||
https://www.usb.org/sites/default/files/usbmassbulk_10.pdf
|
|
@ -1,9 +1,9 @@
|
|||
Discussion topics
|
||||
=================
|
||||
|
||||
In this section, we will explore discussion topics regarding the project's
|
||||
environment and design. These topics provide keys to understanding what is
|
||||
happening in this project, and why it is happening.
|
||||
In this section, we will explore topics regarding the project's environment,
|
||||
i.e. the protocols and formats implemented or to be implemented,
|
||||
rationales behind those, and some of Cahute's external-facing logics.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
@ -13,6 +13,5 @@ happening in this project, and why it is happening.
|
|||
topics/text-encodings
|
||||
topics/file-formats
|
||||
topics/protocols
|
||||
topics/data
|
||||
topics/usb-detection
|
||||
topics/logging
|
||||
|
|
|
@ -48,6 +48,8 @@ when the file system and main memory are presented using SCSI.
|
|||
Note however that these should be used through the system's serial
|
||||
bus interface rather than directly.
|
||||
|
||||
.. _usb-detection-windows:
|
||||
|
||||
Driver detection on Microsoft Windows
|
||||
-------------------------------------
|
||||
|
||||
|
|
|
@ -152,10 +152,9 @@ CAHUTE_INLINE(void) log_windows_error(char const *func_name, DWORD code) {
|
|||
#define CAHUTE_LINK_MEDIUM_READ_BUFFER_SIZE 32768
|
||||
|
||||
/* Flags that can be present on a link at runtime. */
|
||||
#define CAHUTE_LINK_FLAG_CLOSE_MEDIUM 0x00000001
|
||||
#define CAHUTE_LINK_FLAG_CLOSE_PROTOCOL 0x00000002
|
||||
#define CAHUTE_LINK_FLAG_TERMINATE 0x00000004 /* Should terminate. */
|
||||
#define CAHUTE_LINK_FLAG_RECEIVER 0x00000008 /* Act as a receiver. */
|
||||
#define CAHUTE_LINK_FLAG_CLOSE_MEDIUM 0x00000001
|
||||
#define CAHUTE_LINK_FLAG_TERMINATE 0x00000002 /* Should terminate. */
|
||||
#define CAHUTE_LINK_FLAG_RECEIVER 0x00000004 /* Act as a receiver. */
|
||||
|
||||
#define CAHUTE_LINK_FLAG_GONE 0x00000100 /* Underlying medium gone. */
|
||||
#define CAHUTE_LINK_FLAG_TERMINATED 0x00000200 /* Was terminated! */
|
||||
|
@ -170,7 +169,8 @@ CAHUTE_INLINE(void) log_windows_error(char const *func_name, DWORD code) {
|
|||
# define CAHUTE_LINK_MEDIUM_WIN32_CESG 3
|
||||
#endif
|
||||
#if LIBUSB_ENABLED
|
||||
# define CAHUTE_LINK_MEDIUM_LIBUSB 4
|
||||
# define CAHUTE_LINK_MEDIUM_LIBUSB 4
|
||||
# define CAHUTE_LINK_MEDIUM_LIBUSB_UMS 5
|
||||
#endif
|
||||
|
||||
/* Protocol selection for 'initialize_link_protocol()'. */
|
||||
|
@ -180,8 +180,7 @@ CAHUTE_INLINE(void) log_windows_error(char const *func_name, DWORD code) {
|
|||
#define CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN_OHP 3
|
||||
#define CAHUTE_LINK_PROTOCOL_USB_SEVEN 4
|
||||
#define CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP 5
|
||||
#define CAHUTE_LINK_PROTOCOL_UMS 6
|
||||
#define CAHUTE_LINK_PROTOCOL_UMS_OHP 7
|
||||
#define CAHUTE_LINK_PROTOCOL_USB_MASS_STORAGE 6
|
||||
|
||||
/* CASIOLINK variant selection for the same function. */
|
||||
#define CAHUTE_CASIOLINK_VARIANT_AUTO 0
|
||||
|
|
|
@ -85,7 +85,6 @@ cahute_receive_screen(
|
|||
|
||||
case CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN_OHP:
|
||||
case CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP:
|
||||
case CAHUTE_LINK_PROTOCOL_UMS_OHP:
|
||||
return cahute_seven_ohp_receive_screen(link, frame, timeout);
|
||||
}
|
||||
|
||||
|
|
144
lib/linkopen.c
144
lib/linkopen.c
|
@ -77,10 +77,41 @@ CAHUTE_INLINE(char const *) get_protocol_name(int protocol) {
|
|||
return "Protocol 7.00 (USB)";
|
||||
case CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP:
|
||||
return "Protocol 7.00 Screenstreaming (USB)";
|
||||
case CAHUTE_LINK_PROTOCOL_UMS:
|
||||
case CAHUTE_LINK_PROTOCOL_USB_MASS_STORAGE:
|
||||
return "USB Mass Storage";
|
||||
case CAHUTE_LINK_PROTOCOL_UMS_OHP:
|
||||
return "USB Mass Storage (Screenstreaming)";
|
||||
default:
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of a link medium.
|
||||
*
|
||||
* @param medium Medium identifier, as a constant.
|
||||
* @return Textual name of the medium.
|
||||
*/
|
||||
CAHUTE_LOCAL(char const *) get_medium_name(int medium) {
|
||||
switch (medium) {
|
||||
#ifdef CAHUTE_LINK_MEDIUM_POSIX_SERIAL
|
||||
case CAHUTE_LINK_MEDIUM_POSIX_SERIAL:
|
||||
return "Serial (POSIX)";
|
||||
#endif
|
||||
#ifdef CAHUTE_LINK_MEDIUM_WIN32_SERIAL
|
||||
case CAHUTE_LINK_MEDIUM_WIN32_SERIAL:
|
||||
return "Serial (Win32)";
|
||||
#endif
|
||||
#ifdef CAHUTE_LINK_MEDIUM_WIN32_CESG
|
||||
case CAHUTE_LINK_MEDIUM_WIN32_CESG:
|
||||
return "CESG502 (Win32)";
|
||||
#endif
|
||||
#ifdef CAHUTE_LINK_MEDIUM_LIBUSB
|
||||
case CAHUTE_LINK_MEDIUM_LIBUSB:
|
||||
return "USB Bulk (libusb)";
|
||||
#endif
|
||||
#ifdef CAHUTE_LINK_MEDIUM_LIBUSB_UMS
|
||||
case CAHUTE_LINK_MEDIUM_LIBUSB_UMS:
|
||||
return "USB Mass Storage (libusb)";
|
||||
#endif
|
||||
default:
|
||||
return "(unknown)";
|
||||
}
|
||||
|
@ -247,11 +278,7 @@ fail:
|
|||
* @return Cahute error, or CAHUTE_OK if no error has occurred.
|
||||
*/
|
||||
CAHUTE_LOCAL(int)
|
||||
initialize_link_protocol(
|
||||
cahute_link *link,
|
||||
unsigned long flags,
|
||||
int casiolink_variant
|
||||
) {
|
||||
init_link(cahute_link *link, unsigned long flags, int casiolink_variant) {
|
||||
struct cahute_casiolink_state *casiolink_state;
|
||||
struct cahute_seven_state *seven_state;
|
||||
struct cahute_seven_ohp_state *seven_ohp_state;
|
||||
|
@ -277,7 +304,10 @@ initialize_link_protocol(
|
|||
flags |= PROTOCOL_FLAG_NOCHECK;
|
||||
}
|
||||
|
||||
msg(ll_info, "Using %s.", get_protocol_name(protocol));
|
||||
msg(ll_info,
|
||||
"Using %s over %s.",
|
||||
get_protocol_name(protocol),
|
||||
get_medium_name(link->medium));
|
||||
msg(ll_info,
|
||||
"Playing the role of %s.",
|
||||
flags & PROTOCOL_FLAG_RECEIVER ? "receiver / passive side"
|
||||
|
@ -341,7 +371,6 @@ initialize_link_protocol(
|
|||
|
||||
case CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN_OHP:
|
||||
case CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP:
|
||||
case CAHUTE_LINK_PROTOCOL_UMS_OHP:
|
||||
seven_ohp_state = &link->protocol_state.seven_ohp;
|
||||
|
||||
/* No need to guarantee a minimum protocol buffer size here;
|
||||
|
@ -360,35 +389,6 @@ initialize_link_protocol(
|
|||
return CAHUTE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* De-initialize a link's protocol state.
|
||||
*
|
||||
* @param link Link to deinitialize.
|
||||
* @return Cahute error, or CAHUTE_OK if no error has occurred.
|
||||
*/
|
||||
CAHUTE_LOCAL(int) deinitialize_link_protocol(cahute_link *link) {
|
||||
if (!(link->flags
|
||||
& (CAHUTE_LINK_FLAG_IRRECOVERABLE | CAHUTE_LINK_FLAG_TERMINATED
|
||||
| CAHUTE_LINK_FLAG_GONE | CAHUTE_LINK_FLAG_RECEIVER))) {
|
||||
switch (link->protocol) {
|
||||
case CAHUTE_LINK_PROTOCOL_SERIAL_CASIOLINK:
|
||||
if (link->flags & CAHUTE_LINK_FLAG_TERMINATE)
|
||||
cahute_casiolink_terminate(link);
|
||||
|
||||
break;
|
||||
|
||||
case CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN:
|
||||
case CAHUTE_LINK_PROTOCOL_USB_SEVEN:
|
||||
if (link->flags & CAHUTE_LINK_FLAG_TERMINATE)
|
||||
cahute_seven_terminate(link);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return CAHUTE_OK;
|
||||
}
|
||||
|
||||
#if WIN32_ENABLED
|
||||
# include <initguid.h>
|
||||
# include <cfgmgr32.h>
|
||||
|
@ -466,11 +466,10 @@ cahute_open_serial_link(
|
|||
|
||||
switch (flags & CAHUTE_SERIAL_PROTOCOL_MASK) {
|
||||
case CAHUTE_SERIAL_PROTOCOL_AUTO:
|
||||
/* If we are to be the sender or active side, and are not allowed
|
||||
* to initiate the connection, we cannot test different things,
|
||||
* therefore this cannot be used with ``CAHUTE_SERIAL_NOCHECK``. */
|
||||
if ((~flags & CAHUTE_SERIAL_RECEIVER)
|
||||
&& (flags & CAHUTE_SERIAL_NOCHECK)) {
|
||||
/* If we are not allowed to initiate the connection, we cannot test
|
||||
* different things, therefore this cannot be used with
|
||||
* ``CAHUTE_SERIAL_NOCHECK``. */
|
||||
if (flags & CAHUTE_SERIAL_NOCHECK) {
|
||||
msg(ll_error, "We need the check flow to determine the protocol.");
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
@ -773,14 +772,12 @@ cahute_open_serial_link(
|
|||
if (flags & CAHUTE_SERIAL_RECEIVER)
|
||||
protocol_flags |= PROTOCOL_FLAG_RECEIVER;
|
||||
|
||||
err = initialize_link_protocol(link, protocol_flags, casiolink_variant);
|
||||
err = init_link(link, protocol_flags, casiolink_variant);
|
||||
if (err) {
|
||||
cahute_close_link(link);
|
||||
return err;
|
||||
}
|
||||
|
||||
link->flags |= CAHUTE_LINK_FLAG_CLOSE_PROTOCOL;
|
||||
|
||||
*linkp = link;
|
||||
return CAHUTE_OK;
|
||||
}
|
||||
|
@ -803,7 +800,7 @@ cahute_open_usb_link(
|
|||
int address
|
||||
) {
|
||||
cahute_link *link = NULL;
|
||||
int protocol;
|
||||
int protocol = CAHUTE_LINK_PROTOCOL_USB_SEVEN;
|
||||
unsigned long protocol_flags = 0;
|
||||
int i, err = CAHUTE_ERROR_UNKNOWN;
|
||||
|
||||
|
@ -822,6 +819,7 @@ cahute_open_usb_link(
|
|||
struct libusb_config_descriptor *config_descriptor = NULL;
|
||||
libusb_device_handle *device_handle = NULL;
|
||||
int device_count, libusberr, bulk_in = -1, bulk_out = -1;
|
||||
int medium = CAHUTE_LINK_MEDIUM_LIBUSB;
|
||||
#endif
|
||||
|
||||
if (flags & CAHUTE_USB_OHP) {
|
||||
|
@ -1017,8 +1015,6 @@ cahute_open_usb_link(
|
|||
/* The device has been found and is running the CESG502 driver! */
|
||||
if (flags & CAHUTE_USB_OHP)
|
||||
protocol = CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP;
|
||||
else
|
||||
protocol = CAHUTE_LINK_PROTOCOL_USB_SEVEN;
|
||||
|
||||
msg(ll_info, "Opening the following CESG502 interface:");
|
||||
msg(ll_info, "%ls", device_interface);
|
||||
|
@ -1118,18 +1114,19 @@ cahute_open_usb_link(
|
|||
interface_descriptor = config_descriptor->interface[0].altsetting;
|
||||
interface_class = interface_descriptor->bInterfaceClass;
|
||||
|
||||
if (interface_class == 8) {
|
||||
if (flags & CAHUTE_USB_OHP)
|
||||
protocol = CAHUTE_LINK_PROTOCOL_UMS_OHP;
|
||||
else
|
||||
protocol = CAHUTE_LINK_PROTOCOL_UMS;
|
||||
} else if (interface_class == 255) {
|
||||
if (flags & CAHUTE_USB_OHP)
|
||||
protocol = CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP;
|
||||
else
|
||||
protocol = CAHUTE_LINK_PROTOCOL_USB_SEVEN;
|
||||
} else
|
||||
if (interface_class == 8)
|
||||
medium = CAHUTE_LINK_MEDIUM_LIBUSB_UMS;
|
||||
else if (interface_class == 255)
|
||||
medium = CAHUTE_LINK_MEDIUM_LIBUSB;
|
||||
else {
|
||||
msg(ll_error, "Unsupported interface class %d", interface_class);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (flags & CAHUTE_USB_OHP)
|
||||
protocol = CAHUTE_LINK_PROTOCOL_USB_SEVEN_OHP;
|
||||
else if (medium == CAHUTE_LINK_MEDIUM_LIBUSB_UMS)
|
||||
protocol = CAHUTE_LINK_PROTOCOL_USB_MASS_STORAGE;
|
||||
|
||||
/* Find bulk in and out endpoints.
|
||||
* This search is in case they vary between host platforms. */
|
||||
|
@ -1287,7 +1284,7 @@ ready:
|
|||
|
||||
#if defined(CAHUTE_LINK_MEDIUM_LIBUSB)
|
||||
link->flags = CAHUTE_LINK_FLAG_CLOSE_MEDIUM;
|
||||
link->medium = CAHUTE_LINK_MEDIUM_LIBUSB;
|
||||
link->medium = medium;
|
||||
link->medium_state.libusb.context = context;
|
||||
link->medium_state.libusb.handle = device_handle;
|
||||
link->medium_state.libusb.bulk_in = bulk_in;
|
||||
|
@ -1324,12 +1321,10 @@ prepared:
|
|||
if (flags & CAHUTE_USB_NOTERM)
|
||||
protocol_flags |= PROTOCOL_FLAG_NOTERM;
|
||||
|
||||
if ((err = initialize_link_protocol(link, protocol_flags, 0)))
|
||||
if ((err = init_link(link, protocol_flags, 0)))
|
||||
goto fail;
|
||||
|
||||
link->flags |= CAHUTE_LINK_FLAG_CLOSE_PROTOCOL;
|
||||
*linkp = link;
|
||||
|
||||
return CAHUTE_OK;
|
||||
|
||||
fail:
|
||||
|
@ -1479,8 +1474,23 @@ CAHUTE_EXTERN(void) cahute_close_link(cahute_link *link) {
|
|||
if (link->cached_device_info)
|
||||
free(link->cached_device_info);
|
||||
|
||||
if (link->flags & CAHUTE_LINK_FLAG_CLOSE_PROTOCOL)
|
||||
deinitialize_link_protocol(link);
|
||||
if ((link->flags & CAHUTE_LINK_FLAG_TERMINATE)
|
||||
&& !(
|
||||
link->flags
|
||||
& (CAHUTE_LINK_FLAG_IRRECOVERABLE | CAHUTE_LINK_FLAG_TERMINATED
|
||||
| CAHUTE_LINK_FLAG_GONE | CAHUTE_LINK_FLAG_RECEIVER)
|
||||
)) {
|
||||
switch (link->protocol) {
|
||||
case CAHUTE_LINK_PROTOCOL_SERIAL_CASIOLINK:
|
||||
cahute_casiolink_terminate(link);
|
||||
break;
|
||||
|
||||
case CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN:
|
||||
case CAHUTE_LINK_PROTOCOL_USB_SEVEN:
|
||||
cahute_seven_terminate(link);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (link->flags & CAHUTE_LINK_FLAG_CLOSE_MEDIUM) {
|
||||
switch (link->medium) {
|
||||
|
|
616
lib/medium.c
616
lib/medium.c
|
@ -209,8 +209,184 @@ cahute_read_from_link(
|
|||
* ``target_size`` (while the caller only requires ``size``).
|
||||
* It must set ``bytes_read`` to the actual number of bytes read
|
||||
* this pass. */
|
||||
if (link->protocol == CAHUTE_LINK_PROTOCOL_UMS
|
||||
|| link->protocol == CAHUTE_LINK_PROTOCOL_UMS_OHP) {
|
||||
switch (link->medium) {
|
||||
#ifdef CAHUTE_LINK_MEDIUM_POSIX_SERIAL
|
||||
case CAHUTE_LINK_MEDIUM_POSIX_SERIAL: {
|
||||
ssize_t ret;
|
||||
|
||||
if (timeout > 0) {
|
||||
/* Use select() to wait for input to be present. */
|
||||
fd_set read_fds, write_fds, except_fds;
|
||||
struct timeval timeout_tv;
|
||||
int select_ret;
|
||||
|
||||
FD_ZERO(&read_fds);
|
||||
FD_ZERO(&write_fds);
|
||||
FD_ZERO(&except_fds);
|
||||
FD_SET(link->medium_state.posix.fd, &read_fds);
|
||||
|
||||
timeout_tv.tv_sec = timeout / 1000;
|
||||
timeout_tv.tv_usec = (timeout % 1000) * 1000;
|
||||
|
||||
select_ret = select(
|
||||
link->medium_state.posix.fd + 1,
|
||||
&read_fds,
|
||||
&write_fds,
|
||||
&except_fds,
|
||||
&timeout_tv
|
||||
);
|
||||
|
||||
switch (select_ret) {
|
||||
case 1:
|
||||
/* Input is ready for us to read! */
|
||||
break;
|
||||
|
||||
case 0:
|
||||
goto time_out;
|
||||
|
||||
default:
|
||||
msg(ll_error,
|
||||
"An error occurred while calling select() %s (%d)",
|
||||
strerror(errno),
|
||||
errno);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
ret = read(link->medium_state.posix.fd, dest, target_size);
|
||||
|
||||
if (ret < 0)
|
||||
switch (errno) {
|
||||
case 0:
|
||||
continue;
|
||||
|
||||
case ENODEV:
|
||||
case EIO:
|
||||
link->flags |= CAHUTE_LINK_FLAG_GONE;
|
||||
return CAHUTE_ERROR_GONE;
|
||||
|
||||
default:
|
||||
msg(ll_error,
|
||||
"An error occurred while calling read() %s (%d)",
|
||||
strerror(errno),
|
||||
errno);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
bytes_read = (size_t)ret;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CAHUTE_LINK_MEDIUM_WIN32_SERIAL
|
||||
case CAHUTE_LINK_MEDIUM_WIN32_SERIAL:
|
||||
case CAHUTE_LINK_MEDIUM_WIN32_CESG: {
|
||||
DWORD received = 0;
|
||||
BOOL ret;
|
||||
|
||||
ret = ReadFile(
|
||||
link->medium_state.windows.handle,
|
||||
dest,
|
||||
target_size,
|
||||
&received,
|
||||
&link->medium_state.windows.overlapped
|
||||
);
|
||||
if (!ret) {
|
||||
DWORD werr = GetLastError();
|
||||
|
||||
if (werr == ERROR_IO_PENDING) {
|
||||
ret = WaitForSingleObject(
|
||||
link->medium_state.windows.overlapped.hEvent,
|
||||
timeout ? timeout : INFINITE
|
||||
);
|
||||
switch (ret) {
|
||||
case WAIT_OBJECT_0:
|
||||
ret = GetOverlappedResult(
|
||||
link->medium_state.windows.handle,
|
||||
&link->medium_state.windows.overlapped,
|
||||
&received,
|
||||
FALSE
|
||||
);
|
||||
|
||||
if (!ret) {
|
||||
werr = GetLastError();
|
||||
if (werr == ERROR_GEN_FAILURE)
|
||||
return CAHUTE_ERROR_GONE;
|
||||
|
||||
log_windows_error("GetOverlappedResult", werr);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
break;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
CancelIo(link->medium_state.windows.handle);
|
||||
goto time_out;
|
||||
|
||||
default:
|
||||
log_windows_error(
|
||||
"WaitForSingleObject",
|
||||
GetLastError()
|
||||
);
|
||||
CancelIo(link->medium_state.windows.handle);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
log_windows_error("ReadFile", GetLastError());
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
bytes_read = (size_t)received;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CAHUTE_LINK_MEDIUM_LIBUSB
|
||||
case CAHUTE_LINK_MEDIUM_LIBUSB: {
|
||||
int libusberr;
|
||||
int received;
|
||||
|
||||
libusberr = libusb_bulk_transfer(
|
||||
link->medium_state.libusb.handle,
|
||||
link->medium_state.libusb.bulk_in,
|
||||
dest,
|
||||
target_size,
|
||||
&received,
|
||||
timeout
|
||||
);
|
||||
|
||||
switch (libusberr) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case LIBUSB_ERROR_PIPE:
|
||||
case LIBUSB_ERROR_NO_DEVICE:
|
||||
case LIBUSB_ERROR_IO:
|
||||
msg(ll_error, "USB device is no longer available.");
|
||||
link->flags |= CAHUTE_LINK_FLAG_GONE;
|
||||
return CAHUTE_ERROR_GONE;
|
||||
|
||||
case LIBUSB_ERROR_TIMEOUT:
|
||||
goto time_out;
|
||||
|
||||
default:
|
||||
msg(ll_error,
|
||||
"libusb_bulk_transfer returned %d: %s",
|
||||
libusberr,
|
||||
libusb_error_name(libusberr));
|
||||
if (libusberr == LIBUSB_ERROR_OVERFLOW)
|
||||
msg(ll_error, "Required buffer size was %d.", received);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
bytes_read = (size_t)received;
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#ifdef CAHUTE_LINK_MEDIUM_LIBUSB_UMS
|
||||
case CAHUTE_LINK_MEDIUM_LIBUSB_UMS: {
|
||||
cahute_u8 status_buf[16];
|
||||
cahute_u8 payload[16];
|
||||
size_t avail;
|
||||
|
@ -274,189 +450,11 @@ cahute_read_from_link(
|
|||
return err;
|
||||
|
||||
bytes_read = avail;
|
||||
} else {
|
||||
switch (link->medium) {
|
||||
#ifdef CAHUTE_LINK_MEDIUM_POSIX_SERIAL
|
||||
case CAHUTE_LINK_MEDIUM_POSIX_SERIAL: {
|
||||
ssize_t ret;
|
||||
|
||||
if (timeout > 0) {
|
||||
/* Use select() to wait for input to be present. */
|
||||
fd_set read_fds, write_fds, except_fds;
|
||||
struct timeval timeout_tv;
|
||||
int select_ret;
|
||||
|
||||
FD_ZERO(&read_fds);
|
||||
FD_ZERO(&write_fds);
|
||||
FD_ZERO(&except_fds);
|
||||
FD_SET(link->medium_state.posix.fd, &read_fds);
|
||||
|
||||
timeout_tv.tv_sec = timeout / 1000;
|
||||
timeout_tv.tv_usec = (timeout % 1000) * 1000;
|
||||
|
||||
select_ret = select(
|
||||
link->medium_state.posix.fd + 1,
|
||||
&read_fds,
|
||||
&write_fds,
|
||||
&except_fds,
|
||||
&timeout_tv
|
||||
);
|
||||
|
||||
switch (select_ret) {
|
||||
case 1:
|
||||
/* Input is ready for us to read! */
|
||||
break;
|
||||
|
||||
case 0:
|
||||
goto time_out;
|
||||
|
||||
default:
|
||||
msg(ll_error,
|
||||
"An error occurred while calling select() %s (%d)",
|
||||
strerror(errno),
|
||||
errno);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
ret = read(link->medium_state.posix.fd, dest, target_size);
|
||||
|
||||
if (ret < 0)
|
||||
switch (errno) {
|
||||
case 0:
|
||||
continue;
|
||||
|
||||
case ENODEV:
|
||||
case EIO:
|
||||
link->flags |= CAHUTE_LINK_FLAG_GONE;
|
||||
return CAHUTE_ERROR_GONE;
|
||||
|
||||
default:
|
||||
msg(ll_error,
|
||||
"An error occurred while calling read() %s (%d)",
|
||||
strerror(errno),
|
||||
errno);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
bytes_read = (size_t)ret;
|
||||
}
|
||||
|
||||
break;
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#ifdef CAHUTE_LINK_MEDIUM_WIN32_SERIAL
|
||||
case CAHUTE_LINK_MEDIUM_WIN32_SERIAL:
|
||||
case CAHUTE_LINK_MEDIUM_WIN32_CESG: {
|
||||
DWORD received = 0;
|
||||
BOOL ret;
|
||||
|
||||
ret = ReadFile(
|
||||
link->medium_state.windows.handle,
|
||||
dest,
|
||||
target_size,
|
||||
&received,
|
||||
&link->medium_state.windows.overlapped
|
||||
);
|
||||
if (!ret) {
|
||||
DWORD werr = GetLastError();
|
||||
|
||||
if (werr == ERROR_IO_PENDING) {
|
||||
ret = WaitForSingleObject(
|
||||
link->medium_state.windows.overlapped.hEvent,
|
||||
timeout ? timeout : INFINITE
|
||||
);
|
||||
switch (ret) {
|
||||
case WAIT_OBJECT_0:
|
||||
ret = GetOverlappedResult(
|
||||
link->medium_state.windows.handle,
|
||||
&link->medium_state.windows.overlapped,
|
||||
&received,
|
||||
FALSE
|
||||
);
|
||||
|
||||
if (!ret) {
|
||||
werr = GetLastError();
|
||||
if (werr == ERROR_GEN_FAILURE)
|
||||
return CAHUTE_ERROR_GONE;
|
||||
|
||||
log_windows_error("GetOverlappedResult", werr);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
break;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
CancelIo(link->medium_state.windows.handle);
|
||||
goto time_out;
|
||||
|
||||
default:
|
||||
log_windows_error(
|
||||
"WaitForSingleObject",
|
||||
GetLastError()
|
||||
);
|
||||
CancelIo(link->medium_state.windows.handle);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
log_windows_error("ReadFile", GetLastError());
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
bytes_read = (size_t)received;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CAHUTE_LINK_MEDIUM_LIBUSB
|
||||
case CAHUTE_LINK_MEDIUM_LIBUSB: {
|
||||
int libusberr;
|
||||
int received;
|
||||
|
||||
libusberr = libusb_bulk_transfer(
|
||||
link->medium_state.libusb.handle,
|
||||
link->medium_state.libusb.bulk_in,
|
||||
dest,
|
||||
target_size,
|
||||
&received,
|
||||
timeout
|
||||
);
|
||||
|
||||
switch (libusberr) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case LIBUSB_ERROR_PIPE:
|
||||
case LIBUSB_ERROR_NO_DEVICE:
|
||||
case LIBUSB_ERROR_IO:
|
||||
msg(ll_error, "USB device is no longer available.");
|
||||
link->flags |= CAHUTE_LINK_FLAG_GONE;
|
||||
return CAHUTE_ERROR_GONE;
|
||||
|
||||
case LIBUSB_ERROR_TIMEOUT:
|
||||
goto time_out;
|
||||
|
||||
default:
|
||||
msg(ll_error,
|
||||
"libusb_bulk_transfer returned %d: %s",
|
||||
libusberr,
|
||||
libusb_error_name(libusberr));
|
||||
if (libusberr == LIBUSB_ERROR_OVERFLOW)
|
||||
msg(ll_error, "Required buffer size was %d.", received
|
||||
);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
bytes_read = (size_t)received;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
CAHUTE_RETURN_IMPL("No method available for reading.");
|
||||
}
|
||||
default:
|
||||
CAHUTE_RETURN_IMPL("No method available for reading.");
|
||||
}
|
||||
|
||||
if (!bytes_read)
|
||||
|
@ -552,8 +550,126 @@ cahute_write_to_link(cahute_link *link, cahute_u8 const *buf, size_t size) {
|
|||
*
|
||||
* This way, if only a partial write was achieved, the
|
||||
* implementation-specific write function can be called again. */
|
||||
if (link->protocol == CAHUTE_LINK_PROTOCOL_UMS
|
||||
|| link->protocol == CAHUTE_LINK_PROTOCOL_UMS_OHP) {
|
||||
switch (link->medium) {
|
||||
#ifdef CAHUTE_LINK_MEDIUM_POSIX_SERIAL
|
||||
case CAHUTE_LINK_MEDIUM_POSIX_SERIAL: {
|
||||
ssize_t ret;
|
||||
|
||||
ret = write(link->medium_state.posix.fd, buf, size);
|
||||
if (ret < 0)
|
||||
switch (errno) {
|
||||
case ENODEV:
|
||||
link->flags |= CAHUTE_LINK_FLAG_GONE;
|
||||
return CAHUTE_ERROR_GONE;
|
||||
|
||||
default:
|
||||
msg(ll_fatal, "errno was %d: %s", errno, strerror(errno));
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
bytes_written = (size_t)ret;
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#ifdef CAHUTE_LINK_MEDIUM_WIN32_SERIAL
|
||||
case CAHUTE_LINK_MEDIUM_WIN32_SERIAL:
|
||||
case CAHUTE_LINK_MEDIUM_WIN32_CESG: {
|
||||
DWORD sent;
|
||||
BOOL ret;
|
||||
|
||||
ret = WriteFile(
|
||||
link->medium_state.windows.handle,
|
||||
buf,
|
||||
size,
|
||||
&sent,
|
||||
&link->medium_state.windows.overlapped
|
||||
);
|
||||
if (!ret) {
|
||||
DWORD werr = GetLastError();
|
||||
|
||||
if (werr == ERROR_IO_PENDING) {
|
||||
ret = WaitForSingleObject(
|
||||
link->medium_state.windows.overlapped.hEvent,
|
||||
INFINITE
|
||||
);
|
||||
switch (ret) {
|
||||
case WAIT_OBJECT_0:
|
||||
ret = GetOverlappedResult(
|
||||
link->medium_state.windows.handle,
|
||||
&link->medium_state.windows.overlapped,
|
||||
&sent,
|
||||
FALSE
|
||||
);
|
||||
if (!ret) {
|
||||
werr = GetLastError();
|
||||
if (werr == ERROR_GEN_FAILURE)
|
||||
return CAHUTE_ERROR_GONE;
|
||||
|
||||
log_windows_error("GetOverlappedResult", werr);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
log_windows_error(
|
||||
"WaitForSingleObject",
|
||||
GetLastError()
|
||||
);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
log_windows_error("WriteFile", werr);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
bytes_written = (size_t)sent;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CAHUTE_LINK_MEDIUM_LIBUSB
|
||||
case CAHUTE_LINK_MEDIUM_LIBUSB: {
|
||||
int libusberr;
|
||||
int sent;
|
||||
|
||||
libusberr = libusb_bulk_transfer(
|
||||
link->medium_state.libusb.handle,
|
||||
link->medium_state.libusb.bulk_out,
|
||||
(cahute_u8 *)buf,
|
||||
size,
|
||||
&sent,
|
||||
0 /* Unlimited timeout. */
|
||||
);
|
||||
|
||||
switch (libusberr) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case LIBUSB_ERROR_PIPE:
|
||||
case LIBUSB_ERROR_NO_DEVICE:
|
||||
case LIBUSB_ERROR_IO:
|
||||
msg(ll_error, "USB device is no longer available.");
|
||||
link->flags |= CAHUTE_LINK_FLAG_GONE;
|
||||
return CAHUTE_ERROR_GONE;
|
||||
|
||||
default:
|
||||
msg(ll_error,
|
||||
"libusb_bulk_transfer returned %d: %s",
|
||||
libusberr,
|
||||
libusb_error_name(libusberr));
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
bytes_written = (size_t)sent;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CAHUTE_LINK_MEDIUM_LIBUSB_UMS
|
||||
case CAHUTE_LINK_MEDIUM_LIBUSB_UMS: {
|
||||
size_t to_send = size > 0xFFFF ? 0xFFFF : size;
|
||||
cahute_u8 payload[16], status_buf[16];
|
||||
int err;
|
||||
|
@ -593,131 +709,11 @@ cahute_write_to_link(cahute_link *link, cahute_u8 const *buf, size_t size) {
|
|||
return err;
|
||||
|
||||
bytes_written = to_send;
|
||||
} else {
|
||||
switch (link->medium) {
|
||||
#ifdef CAHUTE_LINK_MEDIUM_POSIX_SERIAL
|
||||
case CAHUTE_LINK_MEDIUM_POSIX_SERIAL: {
|
||||
ssize_t ret;
|
||||
|
||||
ret = write(link->medium_state.posix.fd, buf, size);
|
||||
if (ret < 0)
|
||||
switch (errno) {
|
||||
case ENODEV:
|
||||
link->flags |= CAHUTE_LINK_FLAG_GONE;
|
||||
return CAHUTE_ERROR_GONE;
|
||||
|
||||
default:
|
||||
msg(ll_fatal,
|
||||
"errno was %d: %s",
|
||||
errno,
|
||||
strerror(errno));
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
bytes_written = (size_t)ret;
|
||||
} break;
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#ifdef CAHUTE_LINK_MEDIUM_WIN32_SERIAL
|
||||
case CAHUTE_LINK_MEDIUM_WIN32_SERIAL:
|
||||
case CAHUTE_LINK_MEDIUM_WIN32_CESG: {
|
||||
DWORD sent;
|
||||
BOOL ret;
|
||||
|
||||
ret = WriteFile(
|
||||
link->medium_state.windows.handle,
|
||||
buf,
|
||||
size,
|
||||
&sent,
|
||||
&link->medium_state.windows.overlapped
|
||||
);
|
||||
if (!ret) {
|
||||
DWORD werr = GetLastError();
|
||||
|
||||
if (werr == ERROR_IO_PENDING) {
|
||||
ret = WaitForSingleObject(
|
||||
link->medium_state.windows.overlapped.hEvent,
|
||||
INFINITE
|
||||
);
|
||||
switch (ret) {
|
||||
case WAIT_OBJECT_0:
|
||||
ret = GetOverlappedResult(
|
||||
link->medium_state.windows.handle,
|
||||
&link->medium_state.windows.overlapped,
|
||||
&sent,
|
||||
FALSE
|
||||
);
|
||||
if (!ret) {
|
||||
werr = GetLastError();
|
||||
if (werr == ERROR_GEN_FAILURE)
|
||||
return CAHUTE_ERROR_GONE;
|
||||
|
||||
log_windows_error("GetOverlappedResult", werr);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
log_windows_error(
|
||||
"WaitForSingleObject",
|
||||
GetLastError()
|
||||
);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
log_windows_error("WriteFile", werr);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
bytes_written = (size_t)sent;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CAHUTE_LINK_MEDIUM_LIBUSB
|
||||
case CAHUTE_LINK_MEDIUM_LIBUSB: {
|
||||
int libusberr;
|
||||
int sent;
|
||||
|
||||
libusberr = libusb_bulk_transfer(
|
||||
link->medium_state.libusb.handle,
|
||||
link->medium_state.libusb.bulk_out,
|
||||
(cahute_u8 *)buf,
|
||||
size,
|
||||
&sent,
|
||||
0 /* Unlimited timeout. */
|
||||
);
|
||||
|
||||
switch (libusberr) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case LIBUSB_ERROR_PIPE:
|
||||
case LIBUSB_ERROR_NO_DEVICE:
|
||||
case LIBUSB_ERROR_IO:
|
||||
msg(ll_error, "USB device is no longer available.");
|
||||
link->flags |= CAHUTE_LINK_FLAG_GONE;
|
||||
return CAHUTE_ERROR_GONE;
|
||||
|
||||
default:
|
||||
msg(ll_error,
|
||||
"libusb_bulk_transfer returned %d: %s",
|
||||
libusberr,
|
||||
libusb_error_name(libusberr));
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
bytes_written = (size_t)sent;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
CAHUTE_RETURN_IMPL("No method available for writing.");
|
||||
}
|
||||
default:
|
||||
CAHUTE_RETURN_IMPL("No method available for writing.");
|
||||
}
|
||||
|
||||
if (bytes_written >= size)
|
||||
|
@ -1057,8 +1053,8 @@ cahute_scsi_request(
|
|||
/* The medium-specific implementation must store the status in the
|
||||
* ``status`` variable (NOT ``*statusp``). */
|
||||
switch (link->medium) {
|
||||
#ifdef CAHUTE_LINK_MEDIUM_LIBUSB
|
||||
case CAHUTE_LINK_MEDIUM_LIBUSB: {
|
||||
#ifdef CAHUTE_LINK_MEDIUM_LIBUSB_UMS
|
||||
case CAHUTE_LINK_MEDIUM_LIBUSB_UMS: {
|
||||
cahute_u8 cbw_buf[32];
|
||||
int libusberr, sent = 0;
|
||||
|
||||
|
|
Loading…
Reference in New Issue