From 0359a57fdc71dd227f81c8fb024108935c527445 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 19 Apr 2019 20:46:47 +0200 Subject: [PATCH] Add an ioctl function to the I/O interface 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. --- include/libdivecomputer/custom.h | 1 + include/libdivecomputer/ioctl.h | 73 ++++++++++++++++++++++++++++++ include/libdivecomputer/iostream.h | 13 ++++++ src/bluetooth.c | 1 + src/custom.c | 13 ++++++ src/iostream-private.h | 2 + src/iostream.c | 36 +++++++++++++++ src/irda.c | 1 + src/libdivecomputer.symbols | 1 + src/serial_posix.c | 8 ++++ src/serial_win32.c | 8 ++++ src/socket.c | 6 +++ src/socket.h | 3 ++ src/usbhid.c | 8 ++++ 14 files changed, 174 insertions(+) create mode 100644 include/libdivecomputer/ioctl.h diff --git a/include/libdivecomputer/custom.h b/include/libdivecomputer/custom.h index eca3b1c..abaf0e7 100644 --- a/include/libdivecomputer/custom.h +++ b/include/libdivecomputer/custom.h @@ -42,6 +42,7 @@ typedef struct dc_custom_cbs_t { dc_status_t (*poll) (void *userdata, int timeout); dc_status_t (*read) (void *userdata, void *data, size_t size, size_t *actual); dc_status_t (*write) (void *userdata, const void *data, size_t size, size_t *actual); + dc_status_t (*ioctl) (void *userdata, unsigned int request, void *data, size_t size); dc_status_t (*flush) (void *userdata); dc_status_t (*purge) (void *userdata, dc_direction_t direction); dc_status_t (*sleep) (void *userdata, unsigned int milliseconds); diff --git a/include/libdivecomputer/ioctl.h b/include/libdivecomputer/ioctl.h new file mode 100644 index 0000000..dd7bc90 --- /dev/null +++ b/include/libdivecomputer/ioctl.h @@ -0,0 +1,73 @@ +/* + * libdivecomputer + * + * Copyright (C) 2019 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_IOCTL_H +#define DC_IOCTL_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Ioctl direction bits. + * + * Note: WRITE means the application is writing and the driver is + * reading. READ means the application is reading and the driver is + * writing. + */ +#define DC_IOCTL_DIR_NONE 0u +#define DC_IOCTL_DIR_READ 1u +#define DC_IOCTL_DIR_WRITE 2u + +/* + * Ioctl variable size bits. + */ +#define DC_IOCTL_SIZE_VARIABLE 0 + +/* + * Helper macro to encode ioctl numbers. + */ +#define DC_IOCTL_BASE(dir,type,nr,size) \ + (((dir) << 30) | \ + ((size) << 16) | \ + ((type) << 8) | \ + ((nr) << 0)) + +/* + * Macros to encode ioctl numbers. + */ +#define DC_IOCTL_IO(type,nr) DC_IOCTL_BASE(DC_IOCTL_DIR_NONE, (type), (nr), 0) +#define DC_IOCTL_IOR(type,nr,size) DC_IOCTL_BASE(DC_IOCTL_DIR_READ, (type), (nr), (size)) +#define DC_IOCTL_IOW(type,nr,size) DC_IOCTL_BASE(DC_IOCTL_DIR_WRITE, (type), (nr), (size)) +#define DC_IOCTL_IORW(type,nr,size) DC_IOCTL_BASE(DC_IOCTL_DIR_READ | DC_IOCTL_DIR_WRITE, (type), (nr), (size)) + +/* + * Macros to decode ioctl numbers. + */ +#define DC_IOCTL_DIR(request) (((request) >> 30) & 0x0003) +#define DC_IOCTL_SIZE(request) (((request) >> 16) & 0x3FFF) +#define DC_IOCTL_TYPE(request) (((request) >> 8) & 0x00FF) +#define DC_IOCTL_NR(request) (((request) >> 0) & 0x00FF) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_IOCTL_H */ diff --git a/include/libdivecomputer/iostream.h b/include/libdivecomputer/iostream.h index fb0de9a..8715496 100644 --- a/include/libdivecomputer/iostream.h +++ b/include/libdivecomputer/iostream.h @@ -269,6 +269,19 @@ dc_iostream_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actu dc_status_t dc_iostream_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual); +/** + * Perform an I/O stream specific request. + * + * @param[in] iostream A valid I/O stream. + * @param[in] request The request to perform. + * @param[in,out] data The request specific data. + * @param[in] size The size of the request specific data. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_iostream_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size); + /** * Flush the internal output buffer and wait until the data has been * transmitted. diff --git a/src/bluetooth.c b/src/bluetooth.c index 649d006..23e2006 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -109,6 +109,7 @@ static const dc_iostream_vtable_t dc_bluetooth_vtable = { dc_socket_poll, /* poll */ dc_socket_read, /* read */ dc_socket_write, /* write */ + dc_socket_ioctl, /* ioctl */ NULL, /* flush */ NULL, /* purge */ dc_socket_sleep, /* sleep */ diff --git a/src/custom.c b/src/custom.c index 2bdd37c..4a4ead4 100644 --- a/src/custom.c +++ b/src/custom.c @@ -38,6 +38,7 @@ static dc_status_t dc_custom_configure (dc_iostream_t *abstract, unsigned int ba static dc_status_t dc_custom_poll (dc_iostream_t *abstract, int timeout); static dc_status_t dc_custom_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual); static dc_status_t dc_custom_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual); +static dc_status_t dc_custom_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size); static dc_status_t dc_custom_flush (dc_iostream_t *abstract); static dc_status_t dc_custom_purge (dc_iostream_t *abstract, dc_direction_t direction); static dc_status_t dc_custom_sleep (dc_iostream_t *abstract, unsigned int milliseconds); @@ -64,6 +65,7 @@ static const dc_iostream_vtable_t dc_custom_vtable = { dc_custom_poll, /* poll */ dc_custom_read, /* read */ dc_custom_write, /* write */ + dc_custom_ioctl, /* ioctl */ dc_custom_flush, /* flush */ dc_custom_purge, /* purge */ dc_custom_sleep, /* sleep */ @@ -216,6 +218,17 @@ dc_custom_write (dc_iostream_t *abstract, const void *data, size_t size, size_t return custom->callbacks.write (custom->userdata, data, size, actual); } +static dc_status_t +dc_custom_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + dc_custom_t *custom = (dc_custom_t *) abstract; + + if (custom->callbacks.ioctl == NULL) + return DC_STATUS_SUCCESS; + + return custom->callbacks.ioctl (custom->userdata, request, data, size); +} + static dc_status_t dc_custom_flush (dc_iostream_t *abstract) { diff --git a/src/iostream-private.h b/src/iostream-private.h index 4ce8529..cdf1b16 100644 --- a/src/iostream-private.h +++ b/src/iostream-private.h @@ -63,6 +63,8 @@ struct dc_iostream_vtable_t { dc_status_t (*write) (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual); + dc_status_t (*ioctl) (dc_iostream_t *iostream, unsigned int request, void *data, size_t size); + dc_status_t (*flush) (dc_iostream_t *iostream); dc_status_t (*purge) (dc_iostream_t *iostream, dc_direction_t direction); diff --git a/src/iostream.c b/src/iostream.c index 677f744..fbfeec9 100644 --- a/src/iostream.c +++ b/src/iostream.c @@ -23,6 +23,8 @@ #include #include +#include + #include "iostream-private.h" #include "context-private.h" #include "platform.h" @@ -236,6 +238,40 @@ out: return status; } +dc_status_t +dc_iostream_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + + if (iostream == NULL || iostream->vtable->ioctl == NULL) + return DC_STATUS_SUCCESS; + + // The size should match the size encoded in the ioctl request, + // unless it's a variable size request. + if (size != DC_IOCTL_SIZE(request) && + !(DC_IOCTL_DIR(request) != DC_IOCTL_DIR_NONE && DC_IOCTL_SIZE(request) == 0)) { + ERROR (iostream->context, "Invalid size for ioctl request 0x%08x (" DC_PRINTF_SIZE ").", request, size); + return DC_STATUS_INVALIDARGS; + } + + INFO (iostream->context, "Ioctl: request=0x%08x (dir=%u, type=%u, nr=%u, size=%u)", + request, + DC_IOCTL_DIR(request), DC_IOCTL_TYPE(request), + DC_IOCTL_NR(request), DC_IOCTL_SIZE(request)); + + if (DC_IOCTL_DIR(request) & DC_IOCTL_DIR_WRITE) { + HEXDUMP (iostream->context, DC_LOGLEVEL_INFO, "Ioctl write", (unsigned char *) data, size); + } + + status = iostream->vtable->ioctl (iostream, request, data, size); + + if (DC_IOCTL_DIR(request) & DC_IOCTL_DIR_READ) { + HEXDUMP (iostream->context, DC_LOGLEVEL_INFO, "Ioctl read", (unsigned char *) data, size); + } + + return status; +} + dc_status_t dc_iostream_flush (dc_iostream_t *iostream) { diff --git a/src/irda.c b/src/irda.c index 5ffc162..66e1409 100644 --- a/src/irda.c +++ b/src/irda.c @@ -101,6 +101,7 @@ static const dc_iostream_vtable_t dc_irda_vtable = { dc_socket_poll, /* poll */ dc_socket_read, /* read */ dc_socket_write, /* write */ + dc_socket_ioctl, /* ioctl */ NULL, /* flush */ NULL, /* purge */ dc_socket_sleep, /* sleep */ diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 4195d66..613e360 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -46,6 +46,7 @@ dc_iostream_configure dc_iostream_poll dc_iostream_read dc_iostream_write +dc_iostream_ioctl dc_iostream_flush dc_iostream_purge dc_iostream_sleep diff --git a/src/serial_posix.c b/src/serial_posix.c index 08cfdc4..4b478aa 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -77,6 +77,7 @@ static dc_status_t dc_serial_configure (dc_iostream_t *iostream, unsigned int ba static dc_status_t dc_serial_poll (dc_iostream_t *iostream, int timeout); static dc_status_t dc_serial_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual); static dc_status_t dc_serial_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual); +static dc_status_t dc_serial_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size); static dc_status_t dc_serial_flush (dc_iostream_t *iostream); static dc_status_t dc_serial_purge (dc_iostream_t *iostream, dc_direction_t direction); static dc_status_t dc_serial_sleep (dc_iostream_t *iostream, unsigned int milliseconds); @@ -127,6 +128,7 @@ static const dc_iostream_vtable_t dc_serial_vtable = { dc_serial_poll, /* poll */ dc_serial_read, /* read */ dc_serial_write, /* write */ + dc_serial_ioctl, /* ioctl */ dc_serial_flush, /* flush */ dc_serial_purge, /* purge */ dc_serial_sleep, /* sleep */ @@ -849,6 +851,12 @@ out: return status; } +static dc_status_t +dc_serial_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + return DC_STATUS_UNSUPPORTED; +} + static dc_status_t dc_serial_purge (dc_iostream_t *abstract, dc_direction_t direction) { diff --git a/src/serial_win32.c b/src/serial_win32.c index 0f8bd18..d21247a 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -46,6 +46,7 @@ static dc_status_t dc_serial_configure (dc_iostream_t *iostream, unsigned int ba static dc_status_t dc_serial_poll (dc_iostream_t *iostream, int timeout); static dc_status_t dc_serial_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual); static dc_status_t dc_serial_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual); +static dc_status_t dc_serial_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size); static dc_status_t dc_serial_flush (dc_iostream_t *iostream); static dc_status_t dc_serial_purge (dc_iostream_t *iostream, dc_direction_t direction); static dc_status_t dc_serial_sleep (dc_iostream_t *iostream, unsigned int milliseconds); @@ -102,6 +103,7 @@ static const dc_iostream_vtable_t dc_serial_vtable = { dc_serial_poll, /* poll */ dc_serial_read, /* read */ dc_serial_write, /* write */ + dc_serial_ioctl, /* ioctl */ dc_serial_flush, /* flush */ dc_serial_purge, /* purge */ dc_serial_sleep, /* sleep */ @@ -682,6 +684,12 @@ out: return status; } +static dc_status_t +dc_serial_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + return DC_STATUS_UNSUPPORTED; +} + static dc_status_t dc_serial_purge (dc_iostream_t *abstract, dc_direction_t direction) { diff --git a/src/socket.c b/src/socket.c index 3bb8718..e9412ab 100644 --- a/src/socket.c +++ b/src/socket.c @@ -330,6 +330,12 @@ out: return status; } +dc_status_t +dc_socket_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + return DC_STATUS_UNSUPPORTED; +} + dc_status_t dc_socket_sleep (dc_iostream_t *abstract, unsigned int timeout) { diff --git a/src/socket.h b/src/socket.h index 455b945..e2efa14 100644 --- a/src/socket.h +++ b/src/socket.h @@ -117,6 +117,9 @@ 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); +dc_status_t +dc_socket_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size); + dc_status_t dc_socket_sleep (dc_iostream_t *abstract, unsigned int timeout); diff --git a/src/usbhid.c b/src/usbhid.c index 6fa395a..f56d115 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -97,6 +97,7 @@ static dc_status_t dc_usbhid_set_timeout (dc_iostream_t *iostream, int timeout); static dc_status_t dc_usbhid_poll (dc_iostream_t *iostream, int timeout); static dc_status_t dc_usbhid_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual); static dc_status_t dc_usbhid_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual); +static dc_status_t dc_usbhid_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size); static dc_status_t dc_usbhid_close (dc_iostream_t *iostream); typedef struct dc_usbhid_iterator_t { @@ -148,6 +149,7 @@ static const dc_iostream_vtable_t dc_usbhid_vtable = { dc_usbhid_poll, /* poll */ dc_usbhid_read, /* read */ dc_usbhid_write, /* write */ + dc_usbhid_ioctl, /* ioctl */ NULL, /* flush */ NULL, /* purge */ NULL, /* sleep */ @@ -781,4 +783,10 @@ out: return status; } + +static dc_status_t +dc_usbhid_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + return DC_STATUS_UNSUPPORTED; +} #endif