Improve the robustness of the IrDA I/O code
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.
This commit is contained in:
parent
24cbff9a9f
commit
f83d156fdb
55
src/irda.c
55
src/irda.c
@ -36,6 +36,7 @@
|
|||||||
#include <linux/irda.h> // irda
|
#include <linux/irda.h> // irda
|
||||||
#include <sys/select.h> // select
|
#include <sys/select.h> // select
|
||||||
#include <sys/ioctl.h> // ioctl
|
#include <sys/ioctl.h> // ioctl
|
||||||
|
#include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "irda.h"
|
#include "irda.h"
|
||||||
@ -47,6 +48,7 @@
|
|||||||
typedef int s_ssize_t;
|
typedef int s_ssize_t;
|
||||||
typedef DWORD s_errcode_t;
|
typedef DWORD s_errcode_t;
|
||||||
#define S_ERRNO WSAGetLastError ()
|
#define S_ERRNO WSAGetLastError ()
|
||||||
|
#define S_EINTR WSAEINTR
|
||||||
#define S_EAGAIN WSAEWOULDBLOCK
|
#define S_EAGAIN WSAEWOULDBLOCK
|
||||||
#define S_ENOMEM WSA_NOT_ENOUGH_MEMORY
|
#define S_ENOMEM WSA_NOT_ENOUGH_MEMORY
|
||||||
#define S_EINVAL WSAEINVAL
|
#define S_EINVAL WSAEINVAL
|
||||||
@ -59,6 +61,7 @@ typedef DWORD s_errcode_t;
|
|||||||
typedef ssize_t s_ssize_t;
|
typedef ssize_t s_ssize_t;
|
||||||
typedef int s_errcode_t;
|
typedef int s_errcode_t;
|
||||||
#define S_ERRNO errno
|
#define S_ERRNO errno
|
||||||
|
#define S_EINTR EINTR
|
||||||
#define S_EAGAIN EAGAIN
|
#define S_EAGAIN EAGAIN
|
||||||
#define S_ENOMEM ENOMEM
|
#define S_ENOMEM ENOMEM
|
||||||
#define S_EINVAL EINVAL
|
#define S_EINVAL EINVAL
|
||||||
@ -408,20 +411,24 @@ dc_irda_read (dc_irda_t *device, void *data, size_t size, size_t *actual)
|
|||||||
goto out_invalidargs;
|
goto out_invalidargs;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
while (nbytes < size) {
|
while (nbytes < size) {
|
||||||
int rc = select (device->fd + 1, &fds, NULL, NULL, (device->timeout >= 0 ? &tv : NULL));
|
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) {
|
if (rc < 0) {
|
||||||
s_errcode_t errcode = S_ERRNO;
|
s_errcode_t errcode = S_ERRNO;
|
||||||
|
if (errcode == S_EINTR)
|
||||||
|
continue; // Retry.
|
||||||
SYSERROR (device->context, errcode);
|
SYSERROR (device->context, errcode);
|
||||||
status = syserror(errcode);
|
status = syserror(errcode);
|
||||||
goto out;
|
goto out;
|
||||||
@ -432,6 +439,8 @@ dc_irda_read (dc_irda_t *device, void *data, size_t size, size_t *actual)
|
|||||||
s_ssize_t n = recv (device->fd, (char*) data + nbytes, size - nbytes, 0);
|
s_ssize_t n = recv (device->fd, (char*) data + nbytes, size - nbytes, 0);
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
s_errcode_t errcode = S_ERRNO;
|
s_errcode_t errcode = S_ERRNO;
|
||||||
|
if (errcode == S_EINTR || errcode == S_EAGAIN)
|
||||||
|
continue; // Retry.
|
||||||
SYSERROR (device->context, errcode);
|
SYSERROR (device->context, errcode);
|
||||||
status = syserror(errcode);
|
status = syserror(errcode);
|
||||||
goto out;
|
goto out;
|
||||||
@ -468,12 +477,32 @@ dc_irda_write (dc_irda_t *device, const void *data, size_t size, size_t *actual)
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (nbytes < size) {
|
while (nbytes < size) {
|
||||||
s_ssize_t n = send (device->fd, (char*) data + nbytes, size - nbytes, 0);
|
fd_set fds;
|
||||||
if (n < 0) {
|
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;
|
s_errcode_t errcode = S_ERRNO;
|
||||||
|
if (errcode == S_EINTR)
|
||||||
|
continue; // Retry.
|
||||||
SYSERROR (device->context, errcode);
|
SYSERROR (device->context, errcode);
|
||||||
status = syserror(errcode);
|
status = syserror(errcode);
|
||||||
goto out;
|
goto out;
|
||||||
|
} else if (rc == 0) {
|
||||||
|
break; // Timeout.
|
||||||
|
}
|
||||||
|
|
||||||
|
s_ssize_t n = send (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 (device->context, errcode);
|
||||||
|
status = syserror(errcode);
|
||||||
|
goto out;
|
||||||
|
} else if (n == 0) {
|
||||||
|
break; // EOF.
|
||||||
}
|
}
|
||||||
|
|
||||||
nbytes += n;
|
nbytes += n;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user