Move the socket code to a common file

A large part of the irda and bluetooth code is the Windows and BSD
socket code. Moving this code to a common file reduces code duplication.
This commit is contained in:
Jef Driesen 2017-09-07 22:17:03 +02:00
parent 283eaa1ca6
commit 823303980e
6 changed files with 475 additions and 617 deletions

View File

@ -426,6 +426,10 @@
RelativePath="..\src\shearwater_predator_parser.c"
>
</File>
<File
RelativePath="..\src\socket.c"
>
</File>
<File
RelativePath="..\src\suunto_common.c"
>
@ -768,6 +772,10 @@
RelativePath="..\src\shearwater_predator.h"
>
</File>
<File
RelativePath="..\src\socket.h"
>
</File>
<File
RelativePath="..\src\suunto_common.h"
>

View File

@ -78,6 +78,7 @@ else
libdivecomputer_la_SOURCES += serial.h serial_posix.c
endif
libdivecomputer_la_SOURCES += socket.h socket.c
libdivecomputer_la_SOURCES += irda.h irda.c
libdivecomputer_la_SOURCES += usbhid.h usbhid.c
libdivecomputer_la_SOURCES += bluetooth.h bluetooth.c

View File

@ -25,22 +25,14 @@
#include <stdlib.h> // malloc, free
#include "socket.h"
#ifdef _WIN32
#define NOGDI
#include <winsock2.h>
#include <windows.h>
#ifdef HAVE_WS2BTH_H
#define BLUETOOTH
#include <ws2bth.h>
#endif
#else
#include <errno.h> // errno
#include <unistd.h> // close
#include <sys/types.h> // socket, getsockopt
#include <sys/socket.h> // socket, getsockopt
#include <sys/select.h> // select
#include <sys/ioctl.h> // ioctl
#include <sys/time.h>
#ifdef HAVE_BLUEZ
#define BLUETOOTH
#include <bluetooth/bluetooth.h>
@ -56,34 +48,6 @@
#include "context-private.h"
#include "iostream-private.h"
#ifdef _WIN32
typedef int s_ssize_t;
typedef DWORD s_errcode_t;
#define S_ERRNO WSAGetLastError ()
#define S_EINTR WSAEINTR
#define S_EAGAIN WSAEWOULDBLOCK
#define S_ENOMEM WSA_NOT_ENOUGH_MEMORY
#define S_EINVAL WSAEINVAL
#define S_EACCES WSAEACCES
#define S_EAFNOSUPPORT WSAEAFNOSUPPORT
#define S_INVALID INVALID_SOCKET
#define S_IOCTL ioctlsocket
#define S_CLOSE closesocket
#else
typedef ssize_t s_ssize_t;
typedef int s_errcode_t;
#define S_ERRNO errno
#define S_EINTR EINTR
#define S_EAGAIN EAGAIN
#define S_ENOMEM ENOMEM
#define S_EINVAL EINVAL
#define S_EACCES EACCES
#define S_EAFNOSUPPORT EAFNOSUPPORT
#define S_INVALID -1
#define S_IOCTL ioctl
#define S_CLOSE close
#endif
#ifdef _WIN32
#define DC_ADDRESS_FORMAT "%012I64X"
#else
@ -98,58 +62,25 @@ typedef int s_errcode_t;
#define ISINSTANCE(device) dc_iostream_isinstance((device), &dc_bluetooth_vtable)
#ifdef BLUETOOTH
static dc_status_t dc_bluetooth_set_timeout (dc_iostream_t *iostream, int timeout);
static dc_status_t dc_bluetooth_get_available (dc_iostream_t *iostream, size_t *value);
static dc_status_t dc_bluetooth_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual);
static dc_status_t dc_bluetooth_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual);
static dc_status_t dc_bluetooth_close (dc_iostream_t *iostream);
typedef struct dc_bluetooth_t {
dc_iostream_t base;
#ifdef _WIN32
SOCKET fd;
#else
int fd;
#endif
int timeout;
} dc_bluetooth_t;
static const dc_iostream_vtable_t dc_bluetooth_vtable = {
sizeof(dc_bluetooth_t),
dc_bluetooth_set_timeout, /* set_timeout */
sizeof(dc_socket_t),
dc_socket_set_timeout, /* set_timeout */
NULL, /* set_latency */
NULL, /* set_halfduplex */
NULL, /* set_break */
NULL, /* set_dtr */
NULL, /* set_rts */
NULL, /* get_lines */
dc_bluetooth_get_available, /* get_received */
dc_socket_get_available, /* get_received */
NULL, /* configure */
dc_bluetooth_read, /* read */
dc_bluetooth_write, /* write */
dc_socket_read, /* read */
dc_socket_write, /* write */
NULL, /* flush */
NULL, /* purge */
NULL, /* sleep */
dc_bluetooth_close, /* close */
dc_socket_close, /* close */
};
static dc_status_t
syserror(s_errcode_t errcode)
{
switch (errcode) {
case S_EINVAL:
return DC_STATUS_INVALIDARGS;
case S_ENOMEM:
return DC_STATUS_NOMEMORY;
case S_EACCES:
return DC_STATUS_NOACCESS;
case S_EAFNOSUPPORT:
return DC_STATUS_UNSUPPORTED;
default:
return DC_STATUS_IO;
}
}
#ifdef HAVE_BLUEZ
static dc_bluetooth_address_t
dc_address_get (const bdaddr_t *ba)
@ -182,65 +113,33 @@ dc_bluetooth_open (dc_iostream_t **out, dc_context_t *context)
{
#ifdef BLUETOOTH
dc_status_t status = DC_STATUS_SUCCESS;
dc_bluetooth_t *device = NULL;
dc_socket_t *device = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
device = (dc_bluetooth_t *) dc_iostream_allocate (context, &dc_bluetooth_vtable);
device = (dc_socket_t *) dc_iostream_allocate (context, &dc_bluetooth_vtable);
if (device == NULL) {
SYSERROR (context, S_ENOMEM);
return DC_STATUS_NOMEMORY;
}
// Default to blocking reads.
device->timeout = -1;
#ifdef _WIN32
// Initialize the winsock dll.
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD (2, 2);
int rc = WSAStartup (wVersionRequested, &wsaData);
if (rc != 0) {
SYSERROR (context, rc);
status = DC_STATUS_UNSUPPORTED;
goto error_free;
}
// Confirm that the winsock dll supports version 2.2.
// Note that if the dll supports versions greater than 2.2 in addition to
// 2.2, it will still return 2.2 since that is the version we requested.
if (LOBYTE (wsaData.wVersion) != 2 ||
HIBYTE (wsaData.wVersion) != 2) {
ERROR (context, "Incorrect winsock version.");
status = DC_STATUS_UNSUPPORTED;
goto error_wsacleanup;
}
#endif
// Open the socket.
#ifdef _WIN32
device->fd = socket (AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
status = dc_socket_open (&device->base, AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
#else
device->fd = socket (AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
status = dc_socket_open (&device->base, AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
#endif
if (device->fd == S_INVALID) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (context, errcode);
status = syserror(errcode);
goto error_wsacleanup;
if (status != DC_STATUS_SUCCESS) {
goto error_free;
}
*out = (dc_iostream_t *) device;
return DC_STATUS_SUCCESS;
error_wsacleanup:
#ifdef _WIN32
WSACleanup ();
error_free:
#endif
dc_iostream_deallocate ((dc_iostream_t *) device);
return status;
#else
@ -248,46 +147,6 @@ error_free:
#endif
}
#ifdef BLUETOOTH
static dc_status_t
dc_bluetooth_close (dc_iostream_t *abstract)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_bluetooth_t *device = (dc_bluetooth_t *) abstract;
// Terminate all send and receive operations.
shutdown (device->fd, 0);
// Close the socket.
if (S_CLOSE (device->fd) != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
dc_status_set_error(&status, syserror(errcode));
}
#ifdef _WIN32
// Terminate the winsock dll.
if (WSACleanup () != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
dc_status_set_error(&status, syserror(errcode));
}
#endif
return status;
}
static dc_status_t
dc_bluetooth_set_timeout (dc_iostream_t *abstract, int timeout)
{
dc_bluetooth_t *device = (dc_bluetooth_t *) abstract;
device->timeout = timeout;
return DC_STATUS_SUCCESS;
}
#endif
dc_status_t
dc_bluetooth_discover (dc_iostream_t *abstract, dc_bluetooth_callback_t callback, void *userdata)
{
@ -312,7 +171,7 @@ dc_bluetooth_discover (dc_iostream_t *abstract, dc_bluetooth_callback_t callback
status = DC_STATUS_SUCCESS;
} else {
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
status = dc_socket_syserror(errcode);
}
goto error_exit;
}
@ -332,7 +191,7 @@ dc_bluetooth_discover (dc_iostream_t *abstract, dc_bluetooth_callback_t callback
break; // No more results.
}
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
status = dc_socket_syserror(errcode);
goto error_close;
}
@ -362,7 +221,7 @@ error_close:
if (dev < 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
status = dc_socket_syserror(errcode);
goto error_exit;
}
@ -371,7 +230,7 @@ error_close:
if (fd < 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
status = dc_socket_syserror(errcode);
goto error_exit;
}
@ -380,7 +239,7 @@ error_close:
if (devices == NULL) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
status = dc_socket_syserror(errcode);
goto error_close;
}
@ -391,7 +250,7 @@ error_close:
if (ndevices < 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
status = dc_socket_syserror(errcode);
goto error_free;
}
@ -427,7 +286,7 @@ dc_status_t
dc_bluetooth_connect (dc_iostream_t *abstract, dc_bluetooth_address_t address, unsigned int port)
{
#ifdef BLUETOOTH
dc_bluetooth_t *device = (dc_bluetooth_t *) abstract;
dc_socket_t *device = (dc_socket_t *) abstract;
if (!ISINSTANCE (abstract))
return DC_STATUS_INVALIDARGS;
@ -447,147 +306,8 @@ dc_bluetooth_connect (dc_iostream_t *abstract, dc_bluetooth_address_t address, u
dc_address_set (&sa.rc_bdaddr, address);
#endif
if (connect (device->fd, (struct sockaddr *) &sa, sizeof (sa)) != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
return syserror(errcode);
}
return DC_STATUS_SUCCESS;
return dc_socket_connect (&device->base, (struct sockaddr *) &sa, sizeof (sa));
#else
return DC_STATUS_UNSUPPORTED;
#endif
}
#ifdef BLUETOOTH
static dc_status_t
dc_bluetooth_get_available (dc_iostream_t *abstract, size_t *value)
{
dc_bluetooth_t *device = (dc_bluetooth_t *) abstract;
#ifdef _WIN32
unsigned long bytes = 0;
#else
int bytes = 0;
#endif
if (S_IOCTL (device->fd, FIONREAD, &bytes) != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
return syserror(errcode);
}
if (value)
*value = bytes;
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_bluetooth_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_bluetooth_t *device = (dc_bluetooth_t *) abstract;
size_t nbytes = 0;
while (nbytes < size) {
fd_set fds;
FD_ZERO (&fds);
FD_SET (device->fd, &fds);
struct timeval tvt;
if (device->timeout > 0) {
tvt.tv_sec = (device->timeout / 1000);
tvt.tv_usec = (device->timeout % 1000) * 1000;
} else if (device->timeout == 0) {
timerclear (&tvt);
}
int rc = select (device->fd + 1, &fds, NULL, NULL, device->timeout >= 0 ? &tvt : NULL);
if (rc < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR)
continue; // Retry.
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
goto out;
} else if (rc == 0) {
break; // Timeout.
}
s_ssize_t n = recv (device->fd, (char*) data + nbytes, size - nbytes, 0);
if (n < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR || errcode == S_EAGAIN)
continue; // Retry.
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
goto out;
} else if (n == 0) {
break; // EOF reached.
}
nbytes += n;
}
if (nbytes != size) {
status = DC_STATUS_TIMEOUT;
}
out:
if (actual)
*actual = nbytes;
return status;
}
static dc_status_t
dc_bluetooth_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_bluetooth_t *device = (dc_bluetooth_t *) abstract;
size_t nbytes = 0;
while (nbytes < size) {
fd_set fds;
FD_ZERO (&fds);
FD_SET (device->fd, &fds);
int rc = select (device->fd + 1, NULL, &fds, NULL, NULL);
if (rc < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR)
continue; // Retry.
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
goto out;
} else if (rc == 0) {
break; // Timeout.
}
s_ssize_t n = send (device->fd, (const char *) data + nbytes, size - nbytes, 0);
if (n < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR || errcode == S_EAGAIN)
continue; // Retry.
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
goto out;
} else if (n == 0) {
break; // EOF.
}
nbytes += n;
}
if (nbytes != size) {
status = DC_STATUS_TIMEOUT;
}
out:
if (actual)
*actual = nbytes;
return status;
}
#endif

View File

@ -25,28 +25,21 @@
#include <stdlib.h> // malloc, free
#include <stdio.h> // snprintf
#include <string.h>
#include "socket.h"
#ifdef _WIN32
#define NOGDI
#include <winsock2.h>
#include <windows.h>
#ifdef HAVE_AF_IRDA_H
#define IRDA
#include <af_irda.h>
#endif
#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
#ifdef HAVE_LINUX_IRDA_H
#define IRDA
#include <linux/types.h> // irda
#include <linux/irda.h> // irda
#include <linux/types.h>
#include <linux/irda.h>
#endif
#include <sys/select.h> // select
#include <sys/ioctl.h> // ioctl
#include <sys/time.h>
#endif
#include "irda.h"
@ -57,88 +50,27 @@
#include "array.h"
#include "platform.h"
#ifdef _WIN32
typedef int s_ssize_t;
typedef DWORD s_errcode_t;
#define S_ERRNO WSAGetLastError ()
#define S_EINTR WSAEINTR
#define S_EAGAIN WSAEWOULDBLOCK
#define S_ENOMEM WSA_NOT_ENOUGH_MEMORY
#define S_EINVAL WSAEINVAL
#define S_EACCES WSAEACCES
#define S_EAFNOSUPPORT WSAEAFNOSUPPORT
#define S_INVALID INVALID_SOCKET
#define S_IOCTL ioctlsocket
#define S_CLOSE closesocket
#else
typedef ssize_t s_ssize_t;
typedef int s_errcode_t;
#define S_ERRNO errno
#define S_EINTR EINTR
#define S_EAGAIN EAGAIN
#define S_ENOMEM ENOMEM
#define S_EINVAL EINVAL
#define S_EACCES EACCES
#define S_EAFNOSUPPORT EAFNOSUPPORT
#define S_INVALID -1
#define S_IOCTL ioctl
#define S_CLOSE close
#endif
#define ISINSTANCE(device) dc_iostream_isinstance((device), &dc_irda_vtable)
#ifdef IRDA
static dc_status_t dc_irda_set_timeout (dc_iostream_t *iostream, int timeout);
static dc_status_t dc_irda_get_available (dc_iostream_t *iostream, size_t *value);
static dc_status_t dc_irda_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual);
static dc_status_t dc_irda_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual);
static dc_status_t dc_irda_close (dc_iostream_t *iostream);
typedef struct dc_irda_t {
dc_iostream_t base;
#ifdef _WIN32
SOCKET fd;
#else
int fd;
#endif
int timeout;
} dc_irda_t;
static const dc_iostream_vtable_t dc_irda_vtable = {
sizeof(dc_irda_t),
dc_irda_set_timeout, /* set_timeout */
sizeof(dc_socket_t),
dc_socket_set_timeout, /* set_timeout */
NULL, /* set_latency */
NULL, /* set_halfduplex */
NULL, /* set_break */
NULL, /* set_dtr */
NULL, /* set_rts */
NULL, /* get_lines */
dc_irda_get_available, /* get_received */
dc_socket_get_available, /* get_received */
NULL, /* configure */
dc_irda_read, /* read */
dc_irda_write, /* write */
dc_socket_read, /* read */
dc_socket_write, /* write */
NULL, /* flush */
NULL, /* purge */
NULL, /* sleep */
dc_irda_close, /* close */
dc_socket_close, /* close */
};
static dc_status_t
syserror(s_errcode_t errcode)
{
switch (errcode) {
case S_EINVAL:
return DC_STATUS_INVALIDARGS;
case S_ENOMEM:
return DC_STATUS_NOMEMORY;
case S_EACCES:
return DC_STATUS_NOACCESS;
case S_EAFNOSUPPORT:
return DC_STATUS_UNSUPPORTED;
default:
return DC_STATUS_IO;
}
}
#endif
dc_status_t
@ -146,61 +78,29 @@ dc_irda_open (dc_iostream_t **out, dc_context_t *context)
{
#ifdef IRDA
dc_status_t status = DC_STATUS_SUCCESS;
dc_irda_t *device = NULL;
dc_socket_t *device = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
device = (dc_irda_t *) dc_iostream_allocate (context, &dc_irda_vtable);
device = (dc_socket_t *) dc_iostream_allocate (context, &dc_irda_vtable);
if (device == NULL) {
SYSERROR (context, S_ENOMEM);
return DC_STATUS_NOMEMORY;
}
// Default to blocking reads.
device->timeout = -1;
#ifdef _WIN32
// Initialize the winsock dll.
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD (2, 2);
int rc = WSAStartup (wVersionRequested, &wsaData);
if (rc != 0) {
SYSERROR (context, rc);
status = DC_STATUS_UNSUPPORTED;
goto error_free;
}
// Confirm that the winsock dll supports version 2.2.
// Note that if the dll supports versions greater than 2.2 in addition to
// 2.2, it will still return 2.2 since that is the version we requested.
if (LOBYTE (wsaData.wVersion) != 2 ||
HIBYTE (wsaData.wVersion) != 2) {
ERROR (context, "Incorrect winsock version.");
status = DC_STATUS_UNSUPPORTED;
goto error_wsacleanup;
}
#endif
// Open the socket.
device->fd = socket (AF_IRDA, SOCK_STREAM, 0);
if (device->fd == S_INVALID) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (context, errcode);
status = syserror(errcode);
goto error_wsacleanup;
status = dc_socket_open (&device->base, AF_IRDA, SOCK_STREAM, 0);
if (status != DC_STATUS_SUCCESS) {
goto error_free;
}
*out = (dc_iostream_t *) device;
return DC_STATUS_SUCCESS;
error_wsacleanup:
#ifdef _WIN32
WSACleanup ();
error_free:
#endif
dc_iostream_deallocate ((dc_iostream_t *) device);
return status;
#else
@ -208,46 +108,6 @@ error_free:
#endif
}
#ifdef IRDA
static dc_status_t
dc_irda_close (dc_iostream_t *abstract)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_irda_t *device = (dc_irda_t *) abstract;
// Terminate all send and receive operations.
shutdown (device->fd, 0);
// Close the socket.
if (S_CLOSE (device->fd) != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
dc_status_set_error(&status, syserror(errcode));
}
#ifdef _WIN32
// Terminate the winsock dll.
if (WSACleanup () != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
dc_status_set_error(&status, syserror(errcode));
}
#endif
return status;
}
static dc_status_t
dc_irda_set_timeout (dc_iostream_t *abstract, int timeout)
{
dc_irda_t *device = (dc_irda_t *) abstract;
device->timeout = timeout;
return DC_STATUS_SUCCESS;
}
#endif
#define DISCOVER_MAX_DEVICES 16 // Maximum number of devices.
#define DISCOVER_MAX_RETRIES 4 // Maximum number of retries.
@ -263,7 +123,7 @@ dc_status_t
dc_irda_discover (dc_iostream_t *abstract, dc_irda_callback_t callback, void *userdata)
{
#ifdef IRDA
dc_irda_t *device = (dc_irda_t *) abstract;
dc_socket_t *device = (dc_socket_t *) abstract;
if (!ISINSTANCE (abstract))
return DC_STATUS_INVALIDARGS;
@ -294,7 +154,7 @@ dc_irda_discover (dc_iostream_t *abstract, dc_irda_callback_t callback, void *us
s_errcode_t errcode = S_ERRNO;
if (errcode != S_EAGAIN) {
SYSERROR (abstract->context, errcode);
return syserror(errcode);
return dc_socket_syserror(errcode);
}
}
@ -347,7 +207,7 @@ dc_status_t
dc_irda_connect_name (dc_iostream_t *abstract, unsigned int address, const char *name)
{
#ifdef IRDA
dc_irda_t *device = (dc_irda_t *) abstract;
dc_socket_t *device = (dc_socket_t *) abstract;
if (!ISINSTANCE (abstract))
return DC_STATUS_INVALIDARGS;
@ -375,13 +235,7 @@ dc_irda_connect_name (dc_iostream_t *abstract, unsigned int address, const char
memset (peer.sir_name, 0x00, 25);
#endif
if (connect (device->fd, (struct sockaddr *) &peer, sizeof (peer)) != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
return syserror(errcode);
}
return DC_STATUS_SUCCESS;
return dc_socket_connect (&device->base, (struct sockaddr *) &peer, sizeof (peer));
#else
return DC_STATUS_UNSUPPORTED;
#endif
@ -391,7 +245,7 @@ dc_status_t
dc_irda_connect_lsap (dc_iostream_t *abstract, unsigned int address, unsigned int lsap)
{
#ifdef IRDA
dc_irda_t *device = (dc_irda_t *) abstract;
dc_socket_t *device = (dc_socket_t *) abstract;
if (!ISINSTANCE (abstract))
return DC_STATUS_INVALIDARGS;
@ -414,147 +268,8 @@ dc_irda_connect_lsap (dc_iostream_t *abstract, unsigned int address, unsigned in
memset (peer.sir_name, 0x00, 25);
#endif
if (connect (device->fd, (struct sockaddr *) &peer, sizeof (peer)) != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
return syserror(errcode);
}
return DC_STATUS_SUCCESS;
return dc_socket_connect (&device->base, (struct sockaddr *) &peer, sizeof (peer));
#else
return DC_STATUS_UNSUPPORTED;
#endif
}
#ifdef IRDA
static dc_status_t
dc_irda_get_available (dc_iostream_t *abstract, size_t *value)
{
dc_irda_t *device = (dc_irda_t *) abstract;
#ifdef _WIN32
unsigned long bytes = 0;
#else
int bytes = 0;
#endif
if (S_IOCTL (device->fd, FIONREAD, &bytes) != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
return syserror(errcode);
}
if (value)
*value = bytes;
return DC_STATUS_SUCCESS;
}
static dc_status_t
dc_irda_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_irda_t *device = (dc_irda_t *) abstract;
size_t nbytes = 0;
while (nbytes < size) {
fd_set fds;
FD_ZERO (&fds);
FD_SET (device->fd, &fds);
struct timeval tvt;
if (device->timeout > 0) {
tvt.tv_sec = (device->timeout / 1000);
tvt.tv_usec = (device->timeout % 1000) * 1000;
} else if (device->timeout == 0) {
timerclear (&tvt);
}
int rc = select (device->fd + 1, &fds, NULL, NULL, device->timeout >= 0 ? &tvt : NULL);
if (rc < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR)
continue; // Retry.
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
goto out;
} else if (rc == 0) {
break; // Timeout.
}
s_ssize_t n = recv (device->fd, (char*) data + nbytes, size - nbytes, 0);
if (n < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR || errcode == S_EAGAIN)
continue; // Retry.
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
goto out;
} else if (n == 0) {
break; // EOF reached.
}
nbytes += n;
}
if (nbytes != size) {
status = DC_STATUS_TIMEOUT;
}
out:
if (actual)
*actual = nbytes;
return status;
}
static dc_status_t
dc_irda_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_irda_t *device = (dc_irda_t *) abstract;
size_t nbytes = 0;
while (nbytes < size) {
fd_set fds;
FD_ZERO (&fds);
FD_SET (device->fd, &fds);
int rc = select (device->fd + 1, NULL, &fds, NULL, NULL);
if (rc < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR)
continue; // Retry.
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
goto out;
} else if (rc == 0) {
break; // Timeout.
}
s_ssize_t n = send (device->fd, (const char *) data + nbytes, size - nbytes, 0);
if (n < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR || errcode == S_EAGAIN)
continue; // Retry.
SYSERROR (abstract->context, errcode);
status = syserror(errcode);
goto out;
} else if (n == 0) {
break; // EOF.
}
nbytes += n;
}
if (nbytes != size) {
status = DC_STATUS_TIMEOUT;
}
out:
if (actual)
*actual = nbytes;
return status;
}
#endif

295
src/socket.c Normal file
View File

@ -0,0 +1,295 @@
/*
* libdivecomputer
*
* Copyright (C) 2017 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 "socket.h"
#include "common-private.h"
#include "context-private.h"
dc_status_t
dc_socket_syserror (s_errcode_t errcode)
{
switch (errcode) {
case S_EINVAL:
return DC_STATUS_INVALIDARGS;
case S_ENOMEM:
return DC_STATUS_NOMEMORY;
case S_EACCES:
return DC_STATUS_NOACCESS;
case S_EAFNOSUPPORT:
return DC_STATUS_UNSUPPORTED;
default:
return DC_STATUS_IO;
}
}
dc_status_t
dc_socket_init (dc_context_t *context)
{
#ifdef _WIN32
// Initialize the winsock dll.
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD (2, 2);
int rc = WSAStartup (wVersionRequested, &wsaData);
if (rc != 0) {
SYSERROR (context, rc);
return DC_STATUS_UNSUPPORTED;
}
// Confirm that the winsock dll supports version 2.2.
// Note that if the dll supports versions greater than 2.2 in addition to
// 2.2, it will still return 2.2 since that is the version we requested.
if (LOBYTE (wsaData.wVersion) != 2 ||
HIBYTE (wsaData.wVersion) != 2) {
ERROR (context, "Incorrect winsock version.");
return DC_STATUS_UNSUPPORTED;
}
#endif
return DC_STATUS_SUCCESS;
}
dc_status_t
dc_socket_exit (dc_context_t *context)
{
#ifdef _WIN32
// Terminate the winsock dll.
if (WSACleanup () != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (context, errcode);
return dc_socket_syserror(errcode);
}
#endif
return DC_STATUS_SUCCESS;
}
dc_status_t
dc_socket_open (dc_iostream_t *abstract, int family, int type, int protocol)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_socket_t *device = (dc_socket_t *) abstract;
// Default to blocking reads.
device->timeout = -1;
// Initialize the socket library.
status = dc_socket_init (abstract->context);
if (status != DC_STATUS_SUCCESS) {
return status;
}
// Open the socket.
device->fd = socket (family, type, protocol);
if (device->fd == S_INVALID) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
status = dc_socket_syserror(errcode);
goto error;
}
return DC_STATUS_SUCCESS;
error:
dc_socket_exit (abstract->context);
return status;
}
dc_status_t
dc_socket_close (dc_iostream_t *abstract)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_socket_t *socket = (dc_socket_t *) abstract;
dc_status_t rc = DC_STATUS_SUCCESS;
// Terminate all send and receive operations.
shutdown (socket->fd, 0);
// Close the socket.
if (S_CLOSE (socket->fd) != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
dc_status_set_error(&status, dc_socket_syserror(errcode));
}
// Terminate the socket library.
rc = dc_socket_exit (abstract->context);
if (rc != DC_STATUS_SUCCESS) {
dc_status_set_error(&status, rc);
}
return status;
}
dc_status_t
dc_socket_connect (dc_iostream_t *abstract, const struct sockaddr *addr, s_socklen_t addrlen)
{
dc_socket_t *socket = (dc_socket_t *) abstract;
if (connect (socket->fd, addr, addrlen) != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
return dc_socket_syserror(errcode);
}
return DC_STATUS_SUCCESS;
}
dc_status_t
dc_socket_set_timeout (dc_iostream_t *abstract, int timeout)
{
dc_socket_t *socket = (dc_socket_t *) abstract;
socket->timeout = timeout;
return DC_STATUS_SUCCESS;
}
dc_status_t
dc_socket_get_available (dc_iostream_t *abstract, size_t *value)
{
dc_socket_t *socket = (dc_socket_t *) abstract;
#ifdef _WIN32
unsigned long bytes = 0;
#else
int bytes = 0;
#endif
if (S_IOCTL (socket->fd, FIONREAD, &bytes) != 0) {
s_errcode_t errcode = S_ERRNO;
SYSERROR (abstract->context, errcode);
return dc_socket_syserror(errcode);
}
if (value)
*value = bytes;
return DC_STATUS_SUCCESS;
}
dc_status_t
dc_socket_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_socket_t *socket = (dc_socket_t *) abstract;
size_t nbytes = 0;
while (nbytes < size) {
fd_set fds;
FD_ZERO (&fds);
FD_SET (socket->fd, &fds);
struct timeval tvt;
if (socket->timeout > 0) {
tvt.tv_sec = (socket->timeout / 1000);
tvt.tv_usec = (socket->timeout % 1000) * 1000;
} else if (socket->timeout == 0) {
timerclear (&tvt);
}
int rc = select (socket->fd + 1, &fds, NULL, NULL, socket->timeout >= 0 ? &tvt : NULL);
if (rc < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR)
continue; // Retry.
SYSERROR (abstract->context, errcode);
status = dc_socket_syserror(errcode);
goto out;
} else if (rc == 0) {
break; // Timeout.
}
s_ssize_t n = recv (socket->fd, (char *) data + nbytes, size - nbytes, 0);
if (n < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR || errcode == S_EAGAIN)
continue; // Retry.
SYSERROR (abstract->context, errcode);
status = dc_socket_syserror(errcode);
goto out;
} else if (n == 0) {
break; // EOF reached.
}
nbytes += n;
}
if (nbytes != size) {
status = DC_STATUS_TIMEOUT;
}
out:
if (actual)
*actual = nbytes;
return status;
}
dc_status_t
dc_socket_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_socket_t *socket = (dc_socket_t *) abstract;
size_t nbytes = 0;
while (nbytes < size) {
fd_set fds;
FD_ZERO (&fds);
FD_SET (socket->fd, &fds);
int rc = select (socket->fd + 1, NULL, &fds, NULL, NULL);
if (rc < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR)
continue; // Retry.
SYSERROR (abstract->context, errcode);
status = dc_socket_syserror(errcode);
goto out;
} else if (rc == 0) {
break; // Timeout.
}
s_ssize_t n = send (socket->fd, (const char *) data + nbytes, size - nbytes, 0);
if (n < 0) {
s_errcode_t errcode = S_ERRNO;
if (errcode == S_EINTR || errcode == S_EAGAIN)
continue; // Retry.
SYSERROR (abstract->context, errcode);
status = dc_socket_syserror(errcode);
goto out;
} else if (n == 0) {
break; // EOF.
}
nbytes += n;
}
if (nbytes != size) {
status = DC_STATUS_TIMEOUT;
}
out:
if (actual)
*actual = nbytes;
return status;
}

119
src/socket.h Normal file
View File

@ -0,0 +1,119 @@
/*
* libdivecomputer
*
* Copyright (C) 2017 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
*/
#ifndef DC_SOCKET_H
#define DC_SOCKET_H
#ifdef _WIN32
#define NOGDI
#include <winsock2.h>
#include <windows.h>
#else
#include <errno.h> // errno
#include <unistd.h> // close
#include <sys/types.h> // socket, getsockopt
#include <sys/socket.h> // socket, getsockopt
#include <sys/select.h> // select
#include <sys/ioctl.h> // ioctl
#include <sys/time.h>
#endif
#include <libdivecomputer/common.h>
#include <libdivecomputer/context.h>
#include "iostream-private.h"
#ifdef _WIN32
typedef SOCKET s_socket_t;
typedef int s_ssize_t;
typedef DWORD s_errcode_t;
typedef int s_socklen_t;
#define S_ERRNO WSAGetLastError ()
#define S_EINTR WSAEINTR
#define S_EAGAIN WSAEWOULDBLOCK
#define S_ENOMEM WSA_NOT_ENOUGH_MEMORY
#define S_EINVAL WSAEINVAL
#define S_EACCES WSAEACCES
#define S_EAFNOSUPPORT WSAEAFNOSUPPORT
#define S_INVALID INVALID_SOCKET
#define S_IOCTL ioctlsocket
#define S_CLOSE closesocket
#else
typedef int s_socket_t;
typedef ssize_t s_ssize_t;
typedef int s_errcode_t;
typedef socklen_t s_socklen_t;
#define S_ERRNO errno
#define S_EINTR EINTR
#define S_EAGAIN EAGAIN
#define S_ENOMEM ENOMEM
#define S_EINVAL EINVAL
#define S_EACCES EACCES
#define S_EAFNOSUPPORT EAFNOSUPPORT
#define S_INVALID -1
#define S_IOCTL ioctl
#define S_CLOSE close
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct dc_socket_t {
dc_iostream_t base;
s_socket_t fd;
int timeout;
} dc_socket_t;
dc_status_t
dc_socket_syserror (s_errcode_t errcode);
dc_status_t
dc_socket_init (dc_context_t *context);
dc_status_t
dc_socket_exit (dc_context_t *context);
dc_status_t
dc_socket_open (dc_iostream_t *iostream, int family, int type, int protocol);
dc_status_t
dc_socket_close (dc_iostream_t *iostream);
dc_status_t
dc_socket_connect (dc_iostream_t *iostream, const struct sockaddr *addr, s_socklen_t addrlen);
dc_status_t
dc_socket_set_timeout (dc_iostream_t *iostream, int timeout);
dc_status_t
dc_socket_get_available (dc_iostream_t *iostream, size_t *value);
dc_status_t
dc_socket_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual);
dc_status_t
dc_socket_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* DC_SOCKET_H */