Windows and unix use a diferent data type for representing socket file
descriptors (SOCKET vs int). This mismatch results in a compiler warning
when comparing to S_INVALID.
Replace the small helper function to retrieve the function pointer and
then call the function, with another helper function to call the filter
function directly. This way the function pointer doesn't need to be
exposed at all.
The set_latency function is the perfect example of a feature that should
be implemented as an ioctl: it's only implemented by a single driver,
and the functionality is also highly platform specific.
This new ioctl function allows to perform I/O stream specific requests
through a generic interface. This provides an easy way to extend the I/O
interface with some driver specific features, without having to modify
the public api.
The Linux implementation is very straighforward and just a lightweight
wrapper around the select function. But the Windows implementation is
much more complex, because the Windows event notification mechanism
behaves very different:
The WaitCommEvent function does not support a timeout and is always a
blocking call. The only way to implement a timeout is to use
asynchronous I/O (or overlapped I/O as it's called in the Windows API),
to run the operation in the background. This requires some additional
book keeping to keep track of the pending background operation.
The event mechanism is also edge triggered instead of level triggered,
and reading the event with the WaitCommEvent function clears the pending
event. Therefore, the state of the input buffer needs to be checked with
the ClearCommError function before and after the WaitCommEvent call.
The check before is necessary in case the event is already cleared by a
previous WaitCommEvent call, while there is still data present in the
input buffer. In this case, WaitCommEvent should not be called at all,
because it would wait until more data arrives.
The check afterwards is necessary in case WaitCommEvent reports a
pending event, while the data in the input buffer has already been
consumed. In this case, the current event must be ignored and
WaitCommEvent needs to be called again, to wait for the next event.
For most I/O stream implementations the serial communication specific
functions are meaningless. Implementing them as no-ops allows the dive
computer backends the call the I/O stream functions unconditionally.
However, implementing the no-op with a dummy function returning
DC_STATUS_SUCCESS, does not only add some (small) overhead at runtime,
but also requires many such functions. This is inconvenient and the same
result can easily be obtained by using a NULL pointer instead.
The consequence is that the logic is reversed now. To obtain the
previous behaviour of returning the DC_STATUS_UNSUPPORTED error code
again, you'll need to implement a dummy function. But that's fine
because it's the less common case.
Add a function to query the underlying transport type. This allows the
dive computer backends to implement transport specific behaviour where
necessary.
For the built-in I/O implementations, the transport type is obviously
always hardcoded, but for a custom I/O implementation the application
needs to provide the correct type. Hence the transport type can't be
hardcoded in the vtable and needs to be passed as a parameter.
There is no need to expose the two step connection setup of the
underlying socket interface in the public api. Doing so may complicate
the implementation on platforms where the native api is not based on the
socket interface (e.g. Mac OS X).
Note that the function to connect based on the IrDA service name is
removed. It's not used anywhere in libdivecomputer and since IrDA is an
outdated technology nowadays, it's unlikely we'll need it in the future.
The device descriptors are extended with a filter function. During the
device discovery, this filter function is used to return only devices
that match a known dive computer.
The filtering is optional, and can be disabled by passing a NULL pointer
for the device descriptor when creating the iterator with one of the
dc_xxx_iterator_new() functions.
Replacing the callback based interface with an iterator based interface,
results in a more extensible abstraction with a common interface for
each of the built-in I/O implementations (serial, usbhid, irda and
bluetooth).
The Linux kernel uses the sir_name as a standard C string (in one
instance copying it into a 60 char buffer using kstrncpy with a length
limit of 60), we therefore need to ensure that it is 0 terminated.
Since the existing code didn't notify the caller if we were truncating
the string at 25 characters, I didn't add such a warning/error for
truncating at 24 characters.
I was not able to find documentation on how Windows uses irdaServiceName
but since this is implementing the same standard, the same change was
made to the Windows code.
In both cases I replaced the hardcoded length of 25 with a sizeof()
argument (but both Linux and Windows hard code that length in their
headers, so it seems unlikely this would ever change).
Coverity CID 207790
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
For the socket based I/O stream implementations (IrDA and bluetooth) the
serial communication specific functions are meaningless. Implementing
them as no-ops allows the dive computer backends the call the I/O stream
functions unconditionally.
This is important for the bluetooth implementation, because bluetooth
enabled dive computers will be able to use both the native bluetooth
communication and the legacy bluetooth serial port emulation.
The dummy IrDA implementation is integrated in the main file. The
appropriate implementation is selected using conditional compilation
based on the features detect by the autotools build system.
The select system call modifies the file descriptor set, and depending
on the underlying implementation also the timeout. Therefore these
parameters should be re-initialized before every call.
The existing code also didn't handle EINTR and EAGAIN correct.
Errors reported by system calls are now converted to the corresponding
libdivecomputer status code. This results in a more descriptive and
meaningfull return value.
The low level serial and IrDA functions are modified to:
- Use the libdivecomputer namespace prefix.
- Return a more detailed status code instead of the zero on success and
negative on error return value. This will allow to return more
fine-grained error codes.
- The read and write functions have an additional output parameter to
return the actual number of bytes transferred. Since these functions
are not atomic, some data might still be transferred successfully if
an error occurs.
The dive computer backends are updated to use the new api.
Unlike the other socket functions, the WSAStartup() function returns the
extended error code directly. A call to the WSAGetLastError() function
is not needed and should not be used.
When the close function returns, all resources should be freed,
regardless of whether an error has occured or not. The error code is
purely informative.
However, in order to return the first error code, which is usually the
most interesting one, the current implementation is unnecessary
complicated. If an error occurs, there is no need to exit immediately.
Simply store the error code unless there is already a previous one, and
then continue.
This is only a cosmetic change, to make the IrDA device address on
Windows consistent with the address Linux. There is no functional
difference because the address is always serialized and deserialized
internally, and applications shouldn't care about the actual number.
Anyway, the difference in endianness is easy to notice because the
Uwatec Smart family of devices use the serial number as the IrDA device
address. On Linux, the address was identical to the serial number, while
on Windows the byte order was reversed.
Apparantly, the windows wingdi.h header file already defines the
ERROR macro. By defining the NOGDI macro before including the
windows.h header file, we can prevent the wingdi.h file from being
included and thus avoid the warning. We don't need that header for
anything anyway.
Because the libusb header file includes the windows.h file
explicitly, it needs the same fix.
The Windows WSAStartup() and WSACleanup() functions are now called
automatically when opening and closing IrDA sockets. This causes no
problems because these functions are reference counted and can be called
multiple times.
In practice nothing changes because the Uwatec Smart backend already
called these functions for every connection.
The public header files are moved to a new subdirectory, to separate
the definition of the public interface from the actual implementation.
Using an identical directory layout as the final installation has the
advantage that the example code can be build outside the project tree
without any modifications to the #include statements.