From ea32784273825c16a2b2472e5eb625a4bca7e4ea Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 7 Apr 2008 12:15:12 +0000 Subject: [PATCH] Added the initial support for infrared connections on Windows and Linux. --- irda.c | 396 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ irda.h | 38 ++++++ 2 files changed, 434 insertions(+) create mode 100644 irda.c create mode 100644 irda.h diff --git a/irda.c b/irda.c new file mode 100644 index 0000000..bafc564 --- /dev/null +++ b/irda.c @@ -0,0 +1,396 @@ +#include // malloc, free +#include // snprintf +#ifdef _WIN32 + #include + #include + #include +#else + #include // strerror + #include // errno + #include // close + #include // socket, getsockopt + #include // socket, getsockopt + #include // irda + #include // irda + #include // select +#endif + +#include "irda.h" +#include "utils.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 + + +struct irda { +#ifdef _WIN32 + SOCKET fd; +#else + int fd; +#endif + long timeout; +}; + + +int irda_errcode () +{ +#ifdef _WIN32 + return WSAGetLastError (); +#else + return errno; +#endif +} + + +const char* irda_errmsg () +{ +#ifdef _WIN32 + static char buffer[256] = {0}; + unsigned int size = sizeof (buffer) / sizeof (char); + + DWORD errcode = WSAGetLastError (); + DWORD rc = FormatMessage (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 () +{ +#ifdef _WIN32 + WSADATA wsaData = {0}; + 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 () +{ +#ifdef _WIN32 + if (WSACleanup () != 0) { + TRACE ("WSACleanup"); + return -1; + } +#endif + + return 0; +} + + +int +irda_socket_open (irda **out) +{ + if (out == NULL) + return -1; // EINVAL (Invalid argument) + + // Allocate memory. + struct irda *device = malloc (sizeof (struct irda)); + 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 *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 *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 *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 n = 0; + while ((rc = getsockopt (device->fd, SOL_IRLMP, IRLMP_ENUMDEVICES, data, &size)) != 0) { +#ifdef _WIN32 + if (WSAGetLastError() != WSAEWOULDBLOCK) { +#else + if (errno != EAGAIN) { +#endif + TRACE ("getsockopt"); + return -1; // Error during getsockopt call. + } + + if (n >= DISCOVER_MAX_RETRIES) + return 0; + + n++; + +#ifdef _WIN32 + Sleep (1000); +#else + sleep (1); +#endif + } + + if (callback) { +#ifdef _WIN32 + for (unsigned int i = 0; i < list->numDevice; ++i) { + unsigned int address = (list->Device[i].irdaDeviceID[0] << 24) + + (list->Device[i].irdaDeviceID[1] << 16) + + (list->Device[i].irdaDeviceID[2] << 8) + + list->Device[i].irdaDeviceID[3]; + 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 = (list->dev[i].hints[0] << 8) + + list->dev[i].hints[1]; + callback (list->dev[i].daddr, + list->dev[i].info, + list->dev[i].charset, + hints, + userdata); + } +#endif + } + + return 0; +} + + +int +irda_socket_connect_name (irda *device, unsigned int address, const char *name) +{ + if (device == NULL) + return -1; + +#ifdef _WIN32 + SOCKADDR_IRDA peer = {0}; + 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 = {0}; + 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 *device, unsigned int address, unsigned int lsap) +{ + if (device == NULL) + return -1; + +#ifdef _WIN32 + SOCKADDR_IRDA peer = {0}; + 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 = {0}; + 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_read (irda* device, void* data, unsigned int size) +{ + if (device == NULL) + return -1; // EINVAL (Invalid argument) + + struct timeval tv = {0}; + 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); + int rc = select (device->fd + 1, &fds, NULL, NULL, (device->timeout >= 0 ? &tv : NULL)); + if (rc < 0) { + TRACE ("select"); + return -1; // Error. + } else if (rc == 0) { + return 0; // Timeout. + } + + int n = recv (device->fd, data, size, 0); + if (n < 0) { + TRACE ("recv"); + return -1; + } + + return n; +} + + +int +irda_socket_write (irda* device, const void *data, unsigned int size) +{ + if (device == NULL) + return -1; // EINVAL (Invalid argument) + + int n = send (device->fd, data, size, 0); + if (n < 0) { + TRACE ("send"); + return -1; + } + + return n; +} diff --git a/irda.h b/irda.h new file mode 100644 index 0000000..0d7573c --- /dev/null +++ b/irda.h @@ -0,0 +1,38 @@ +#ifndef IRDA_H +#define IRDA_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct irda irda; + +typedef void (*irda_callback_t) (unsigned int address, const char *name, unsigned int charset, unsigned int hints, void *userdata); + +int irda_errcode (); + +const char* irda_errmsg (); + +int irda_init (); + +int irda_cleanup (); + +int irda_socket_open (irda **device); + +int irda_socket_close (irda *device); + +int irda_socket_set_timeout (irda *device, long timeout); + +int irda_socket_discover (irda *device, irda_callback_t callback, void *userdata); + +int irda_socket_connect_name (irda *device, unsigned int address, const char *name); +int irda_socket_connect_lsap (irda *device, unsigned int address, unsigned int lsap); + +int irda_socket_read (irda* device, void* data, unsigned int size); + +int irda_socket_write (irda* device, const void *data, unsigned int size); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* IRDA_H */