Jef Driesen e65025b501 Separate private and public headers.
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.
2012-06-21 21:39:24 +02:00

470 lines
9.7 KiB
C

/*
* libdivecomputer
*
* Copyright (C) 2008 Jef Driesen
*
* 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 <stdlib.h> // malloc, free
#include <stdio.h> // snprintf
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <af_irda.h>
#else
#include <string.h> // strerror
#include <errno.h> // errno
#include <unistd.h> // close
#include <sys/types.h> // socket, getsockopt
#include <sys/socket.h> // socket, getsockopt
#include <linux/types.h> // irda
#include <linux/irda.h> // irda
#include <sys/select.h> // select
#include <sys/ioctl.h> // ioctl
#endif
#include <libdivecomputer/utils.h>
#include "irda.h"
#include "array.h"
#ifdef _WIN32
#define TRACE(expr) \
{ \
DWORD error = WSAGetLastError (); \
message ("TRACE (%s:%d, %s): %s (%d)\n", __FILE__, __LINE__, \
expr, irda_errmsg (), error); \
WSASetLastError (error); \
}
#else
#define TRACE(expr) \
{ \
int error = errno; \
message ("TRACE (%s:%d, %s): %s (%d)\n", __FILE__, __LINE__, \
expr, strerror (errno), errno); \
errno = error; \
}
#endif
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
struct irda_t {
#ifdef _WIN32
SOCKET fd;
#else
int fd;
#endif
long timeout;
};
int irda_errcode (void)
{
#ifdef _WIN32
return WSAGetLastError ();
#else
return errno;
#endif
}
const char* irda_errmsg (void)
{
#ifdef _WIN32
static char buffer[256] = {0};
unsigned int size = sizeof (buffer) / sizeof (char);
DWORD errcode = WSAGetLastError ();
DWORD rc = FormatMessageA (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errcode, 0, buffer, size, NULL);
// Remove certain characters ('\r', '\n' and '.')
// at the end of the error message.
while (rc > 0 && (
buffer[rc-1] == '\n' ||
buffer[rc-1] == '\r' ||
buffer[rc-1] == '.')) {
buffer[rc-1] = '\0';
rc--;
}
if (rc) {
return buffer;
} else {
return NULL;
}
#else
return strerror (errno);
#endif
}
int irda_init (void)
{
#ifdef _WIN32
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD (2, 2);
if (WSAStartup (wVersionRequested, &wsaData) != 0) {
TRACE ("WSAStartup");
return -1;
}
// Confirm that the WinSock DLL supports 2.2.
// Note that if the DLL supports versions greater
// than 2.2 in addition to 2.2, it will still return
// 2.2 in wVersion since that is the version we requested.
if (LOBYTE (wsaData.wVersion) != 2 ||
HIBYTE (wsaData.wVersion) != 2) {
TRACE ("wsaData.wVersion");
WSACleanup ();
return -1;
}
#endif
return 0;
}
int irda_cleanup (void)
{
#ifdef _WIN32
if (WSACleanup () != 0) {
TRACE ("WSACleanup");
return -1;
}
#endif
return 0;
}
int
irda_socket_open (irda_t **out)
{
if (out == NULL)
return -1; // EINVAL (Invalid argument)
// Allocate memory.
irda_t *device = (irda_t *) malloc (sizeof (irda_t));
if (device == NULL) {
TRACE ("malloc");
return -1; // ENOMEM (Not enough space)
}
// Default to blocking reads.
device->timeout = -1;
// Open the socket.
device->fd = socket (AF_IRDA, SOCK_STREAM, 0);
#ifdef _WIN32
if (device->fd == INVALID_SOCKET) {
#else
if (device->fd == -1) {
#endif
TRACE ("socket");
free (device);
return -1;
}
*out = device;
return 0;
}
int
irda_socket_close (irda_t *device)
{
if (device == NULL)
return -1;
// Terminate all send and receive operations.
shutdown (device->fd, 0);
// Close the socket.
#ifdef _WIN32
if (closesocket (device->fd) != 0) {
TRACE ("closesocket");
#else
if (close (device->fd) != 0) {
TRACE ("close");
#endif
free (device);
return -1;
}
// Free memory.
free (device);
return 0;
}
int
irda_socket_set_timeout (irda_t *device, long timeout)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
device->timeout = timeout;
return 0;
}
#define DISCOVER_MAX_DEVICES 16 // Maximum number of devices.
#define DISCOVER_MAX_RETRIES 4 // Maximum number of retries.
#ifdef _WIN32
#define DISCOVER_BUFSIZE sizeof (DEVICELIST) + \
sizeof (IRDA_DEVICE_INFO) * (DISCOVER_MAX_DEVICES - 1)
#else
#define DISCOVER_BUFSIZE sizeof (struct irda_device_list) + \
sizeof (struct irda_device_info) * (DISCOVER_MAX_DEVICES - 1)
#endif
int
irda_socket_discover (irda_t *device, irda_callback_t callback, void *userdata)
{
if (device == NULL)
return -1;
unsigned char data[DISCOVER_BUFSIZE] = {0};
#ifdef _WIN32
DEVICELIST *list = (DEVICELIST *) data;
int size = sizeof (data);
#else
struct irda_device_list *list = (struct irda_device_list *) data;
socklen_t size = sizeof (data);
#endif
int rc = 0;
unsigned int nretries = 0;
while ((rc = getsockopt (device->fd, SOL_IRLMP, IRLMP_ENUMDEVICES, (char*) data, &size)) != 0 ||
#ifdef _WIN32
list->numDevice == 0)
#else
list->len == 0)
#endif
{
// Automatically retry the discovery when no devices were found.
// On Linux, getsockopt fails with EAGAIN when no devices are
// discovered, while on Windows it succeeds and sets the number
// of devices to zero. Both situations are handled the same here.
if (rc != 0) {
#ifdef _WIN32
if (WSAGetLastError() != WSAEWOULDBLOCK) {
#else
if (errno != EAGAIN) {
#endif
TRACE ("getsockopt");
return -1; // Error during getsockopt call.
}
}
// Abort if the maximum number of retries is reached.
if (nretries++ >= DISCOVER_MAX_RETRIES)
return 0;
// Restore the size parameter in case it was
// modified by the previous getsockopt call.
size = sizeof (data);
#ifdef _WIN32
Sleep (1000);
#else
sleep (1);
#endif
}
if (callback) {
#ifdef _WIN32
for (unsigned int i = 0; i < list->numDevice; ++i) {
unsigned int address = array_uint32_be (list->Device[i].irdaDeviceID);
unsigned int hints = (list->Device[i].irdaDeviceHints1 << 8) +
list->Device[i].irdaDeviceHints2;
callback (address,
list->Device[i].irdaDeviceName,
list->Device[i].irdaCharSet,
hints,
userdata);
}
#else
for (unsigned int i = 0; i < list->len; ++i) {
unsigned int hints = array_uint16_be (list->dev[i].hints);
callback (list->dev[i].daddr,
list->dev[i].info,
list->dev[i].charset,
hints,
userdata);
}
#endif
}
return 0;
}
int
irda_socket_connect_name (irda_t *device, unsigned int address, const char *name)
{
if (device == NULL)
return -1;
#ifdef _WIN32
SOCKADDR_IRDA peer;
peer.irdaAddressFamily = AF_IRDA;
peer.irdaDeviceID[0] = (address >> 24) & 0xFF;
peer.irdaDeviceID[1] = (address >> 16) & 0xFF;
peer.irdaDeviceID[2] = (address >> 8) & 0xFF;
peer.irdaDeviceID[3] = (address ) & 0xFF;
if (name)
strncpy (peer.irdaServiceName, name, 25);
else
memset (peer.irdaServiceName, 0x00, 25);
#else
struct sockaddr_irda peer;
peer.sir_family = AF_IRDA;
peer.sir_addr = address;
if (name)
strncpy (peer.sir_name, name, 25);
else
memset (peer.sir_name, 0x00, 25);
#endif
if (connect (device->fd, (struct sockaddr *) &peer, sizeof (peer)) != 0) {
TRACE ("connect");
return -1;
}
return 0;
}
int
irda_socket_connect_lsap (irda_t *device, unsigned int address, unsigned int lsap)
{
if (device == NULL)
return -1;
#ifdef _WIN32
SOCKADDR_IRDA peer;
peer.irdaAddressFamily = AF_IRDA;
peer.irdaDeviceID[0] = (address >> 24) & 0xFF;
peer.irdaDeviceID[1] = (address >> 16) & 0xFF;
peer.irdaDeviceID[2] = (address >> 8) & 0xFF;
peer.irdaDeviceID[3] = (address ) & 0xFF;
snprintf (peer.irdaServiceName, 25, "LSAP-SEL%u", lsap);
#else
struct sockaddr_irda peer;
peer.sir_family = AF_IRDA;
peer.sir_addr = address;
peer.sir_lsap_sel = lsap;
memset (peer.sir_name, 0x00, 25);
#endif
if (connect (device->fd, (struct sockaddr *) &peer, sizeof (peer)) != 0) {
TRACE ("connect");
return -1;
}
return 0;
}
int
irda_socket_available (irda_t *device)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
#ifdef _WIN32
unsigned long bytes = 0;
if (ioctlsocket (device->fd, FIONREAD, &bytes) != 0) {
TRACE ("ioctlsocket");
#else
int bytes = 0;
if (ioctl (device->fd, FIONREAD, &bytes) != 0) {
TRACE ("ioctl");
#endif
return -1;
}
return bytes;
}
int
irda_socket_read (irda_t *device, void *data, unsigned int size)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
struct timeval tv;
if (device->timeout >= 0) {
tv.tv_sec = (device->timeout / 1000);
tv.tv_usec = (device->timeout % 1000) * 1000;
}
fd_set fds;
FD_ZERO (&fds);
FD_SET (device->fd, &fds);
unsigned int nbytes = 0;
while (nbytes < size) {
int rc = select (device->fd + 1, &fds, NULL, NULL, (device->timeout >= 0 ? &tv : NULL));
if (rc < 0) {
TRACE ("select");
return -1; // Error during select call.
} else if (rc == 0) {
break; // Timeout.
}
int n = recv (device->fd, (char*) data + nbytes, size - nbytes, 0);
if (n < 0) {
TRACE ("recv");
return -1; // Error during recv call.
} else if (n == 0) {
break; // EOF reached.
}
nbytes += n;
}
return nbytes;
}
int
irda_socket_write (irda_t *device, const void *data, unsigned int size)
{
if (device == NULL)
return -1; // EINVAL (Invalid argument)
unsigned int nbytes = 0;
while (nbytes < size) {
int n = send (device->fd, (char*) data + nbytes, size - nbytes, 0);
if (n < 0) {
TRACE ("send");
return -1; // Error during send call.
}
nbytes += n;
}
return nbytes;
}