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.
This commit is contained in:
Jef Driesen 2019-04-19 20:46:47 +02:00
parent be0e32b43b
commit 0359a57fdc
14 changed files with 174 additions and 0 deletions

View File

@ -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);

View File

@ -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 */

View File

@ -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.

View File

@ -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 */

View File

@ -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)
{

View File

@ -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);

View File

@ -23,6 +23,8 @@
#include <stdlib.h>
#include <assert.h>
#include <libdivecomputer/ioctl.h>
#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)
{

View File

@ -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 */

View File

@ -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

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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);

View File

@ -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