mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2026-08-31 12:17:28 +08:00
termios: PR2153: New low-level device API
Add a new low-level device API to Termios that passes the TTY structure to the low-level device functions. This greatly simplifies the low-level device drivers since they are no longer forced to derive their private data from the minor number. It makes it possible to use the TTY low-level lock in the device driver low-level functions which is necessary for proper SMP support. For example to set the attributes it is often necessary to perform a read-modify-write operation on a control register used also by interrupt routines. A compatibility layer is provided to support device drivers using the old callback functions so it is not necessary to modify existing device drivers.
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#include <rtems.h>
|
||||
#include <rtems/libio.h>
|
||||
#include <rtems/assoc.h>
|
||||
#include <rtems/chain.h>
|
||||
#include <stdint.h>
|
||||
#include <termios.h>
|
||||
|
||||
@@ -52,11 +53,138 @@ struct rtems_termios_rawbuf {
|
||||
volatile unsigned int Size;
|
||||
rtems_id Semaphore;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
TERMIOS_POLLED,
|
||||
TERMIOS_IRQ_DRIVEN,
|
||||
TERMIOS_TASK_DRIVEN
|
||||
} rtems_termios_device_mode;
|
||||
|
||||
struct rtems_termios_tty;
|
||||
|
||||
/**
|
||||
* @brief Termios device handler.
|
||||
*
|
||||
* @see rtems_termios_device_install().
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief First open of this device.
|
||||
*
|
||||
* @param[in] tty The Termios control.
|
||||
* @param[in] args The open/close arguments. This is parameter provided to
|
||||
* support legacy drivers. It must not be used by new drivers.
|
||||
*
|
||||
* @retval true Successful operation.
|
||||
* @retval false Cannot open device.
|
||||
*
|
||||
* @see rtems_termios_get_device_context().
|
||||
*/
|
||||
bool (*first_open)(
|
||||
struct rtems_termios_tty *tty,
|
||||
rtems_libio_open_close_args_t *args
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Last close of this device.
|
||||
*
|
||||
* @param[in] tty The Termios control.
|
||||
* @param[in] args The open/close arguments. This is parameter provided to
|
||||
* support legacy drivers. It must not be used by new drivers.
|
||||
*
|
||||
* @see rtems_termios_get_device_context().
|
||||
*/
|
||||
void (*last_close)(
|
||||
struct rtems_termios_tty *tty,
|
||||
rtems_libio_open_close_args_t *args
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Polled read.
|
||||
*
|
||||
* In case mode is TERMIOS_IRQ_DRIVEN or TERMIOS_TASK_DRIVEN, then data is
|
||||
* received via rtems_termios_enqueue_raw_characters().
|
||||
*
|
||||
* @param[in] tty The Termios control.
|
||||
*
|
||||
* @retval char The received data encoded as unsigned char.
|
||||
* @retval -1 No data currently available.
|
||||
*
|
||||
* @see rtems_termios_get_device_context().
|
||||
*/
|
||||
int (*poll_read)(struct rtems_termios_tty *tty);
|
||||
|
||||
/**
|
||||
* @brief Polled write in case mode is TERMIOS_POLLED or write support
|
||||
* otherwise.
|
||||
*
|
||||
* @param[in] tty The Termios control.
|
||||
* @param[in] buf The output buffer.
|
||||
* @param[in] len The output buffer length in characters.
|
||||
*
|
||||
* @see rtems_termios_get_device_context().
|
||||
*/
|
||||
void (*write)(struct rtems_termios_tty *tty, const char *buf, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Set attributes after a Termios settings change.
|
||||
*
|
||||
* @param[in] tty The Termios control.
|
||||
* @param[in] term The new Termios attributes.
|
||||
*
|
||||
* @retval true Successful operation.
|
||||
* @retval false Invalid attributes.
|
||||
*
|
||||
* @see rtems_termios_get_device_context().
|
||||
*/
|
||||
bool (*set_attributes)(
|
||||
struct rtems_termios_tty *tty,
|
||||
const struct termios *term
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Indicate to stop remote transmitter.
|
||||
*
|
||||
* @param[in] tty The Termios control.
|
||||
*
|
||||
* @see rtems_termios_get_device_context().
|
||||
*/
|
||||
void (*stop_remote_tx)(struct rtems_termios_tty *tty);
|
||||
|
||||
/**
|
||||
* @brief Indicate to start remote transmitter.
|
||||
*
|
||||
* @param[in] tty The Termios control.
|
||||
*
|
||||
* @see rtems_termios_get_device_context().
|
||||
*/
|
||||
void (*start_remote_tx)(struct rtems_termios_tty *tty);
|
||||
|
||||
/**
|
||||
* @brief Termios device mode.
|
||||
*/
|
||||
rtems_termios_device_mode mode;
|
||||
} rtems_termios_device_handler;
|
||||
|
||||
/**
|
||||
* @brief Termios device node for installed devices.
|
||||
*
|
||||
* @see rtems_termios_device_install().
|
||||
*/
|
||||
typedef struct rtems_termios_device_node {
|
||||
rtems_chain_node node;
|
||||
rtems_device_major_number major;
|
||||
rtems_device_minor_number minor;
|
||||
const rtems_termios_device_handler *handler;
|
||||
void *context;
|
||||
struct rtems_termios_tty *tty;
|
||||
} rtems_termios_device_node;
|
||||
|
||||
/*
|
||||
* Variables associated with each termios instance.
|
||||
* One structure for each hardware I/O device.
|
||||
*/
|
||||
struct rtems_termios_tty {
|
||||
typedef struct rtems_termios_tty {
|
||||
/*
|
||||
* Linked-list of active TERMIOS devices
|
||||
*/
|
||||
@@ -119,6 +247,12 @@ struct rtems_termios_tty {
|
||||
* Callbacks to device-specific routines
|
||||
*/
|
||||
rtems_termios_callbacks device;
|
||||
|
||||
/**
|
||||
* @brief The device handler.
|
||||
*/
|
||||
rtems_termios_device_handler handler;
|
||||
|
||||
volatile unsigned int flow_ctrl;
|
||||
unsigned int lowwater,highwater;
|
||||
|
||||
@@ -142,7 +276,101 @@ struct rtems_termios_tty {
|
||||
int tty_rcvwakeup;
|
||||
|
||||
rtems_interrupt_lock interrupt_lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Corresponding device node.
|
||||
*/
|
||||
rtems_termios_device_node *device_node;
|
||||
|
||||
/**
|
||||
* @brief Context for device driver.
|
||||
*
|
||||
* @see rtems_termios_get_device_context().
|
||||
*/
|
||||
void *device_context;
|
||||
} rtems_termios_tty;
|
||||
|
||||
/**
|
||||
* @brief Installes a Termios device.
|
||||
*
|
||||
* @param[in] device_file If not @c NULL, then a device file for the specified
|
||||
* major and minor number will be created.
|
||||
* @param[in] major The device major number of the corresponding device driver.
|
||||
* @param[in] minor The device minor number of the corresponding device driver.
|
||||
* @param[in] handler The device handler. It must be persistent throughout the
|
||||
* installed time of the device.
|
||||
* @param[in] context The device context. It must be persistent throughout the
|
||||
* installed time of the device.
|
||||
*
|
||||
* @retval RTEMS_SUCCESSFUL Successful operation.
|
||||
* @retval RTEMS_NO_MEMORY Not enough memory to create a device node.
|
||||
* @retval RTEMS_UNSATISFIED Creation of the device file failed.
|
||||
* @retval RTEMS_RESOURCE_IN_USE There exists a device node for this major and
|
||||
* minor number pair.
|
||||
* @retval RTEMS_INCORRECT_STATE Termios is not initialized.
|
||||
*
|
||||
* @see rtems_termios_device_remove(), rtems_termios_device_open(),
|
||||
* rtems_termios_device_close() and rtems_termios_get_device_context().
|
||||
*/
|
||||
rtems_status_code rtems_termios_device_install(
|
||||
const char *device_file,
|
||||
rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
const rtems_termios_device_handler *handler,
|
||||
void *context
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Removes a Termios device.
|
||||
*
|
||||
* @param[in] device_file If not @c NULL, then the device file to remove.
|
||||
* @param[in] major The device major number of the corresponding device driver.
|
||||
* @param[in] minor The device minor number of the corresponding device driver.
|
||||
*
|
||||
* @retval RTEMS_SUCCESSFUL Successful operation.
|
||||
* @retval RTEMS_INVALID_ID There is no device installed with this major and
|
||||
* minor number pair.
|
||||
* @retval RTEMS_RESOURCE_IN_USE This device is currently in use.
|
||||
* @retval RTEMS_UNSATISFIED Removal of the device file failed.
|
||||
* @retval RTEMS_INCORRECT_STATE Termios is not initialized.
|
||||
*
|
||||
* @see rtems_termios_device_install().
|
||||
*/
|
||||
rtems_status_code rtems_termios_device_remove(
|
||||
const char *device_file,
|
||||
rtems_device_major_number major,
|
||||
rtems_device_minor_number minor
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Opens an installed Termios device.
|
||||
*
|
||||
* @see rtems_termios_device_install().
|
||||
*/
|
||||
rtems_status_code rtems_termios_device_open(
|
||||
rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
void *arg
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Closes an installed Termios device.
|
||||
*
|
||||
* @retval RTEMS_SUCCESSFUL Successful operation.
|
||||
*
|
||||
* @see rtems_termios_device_install().
|
||||
*/
|
||||
rtems_status_code rtems_termios_device_close(void *arg);
|
||||
|
||||
/**
|
||||
* @brief Returns the device context of an installed Termios device.
|
||||
*/
|
||||
RTEMS_INLINE_ROUTINE void *rtems_termios_get_device_context(
|
||||
const rtems_termios_tty *tty
|
||||
)
|
||||
{
|
||||
return tty->device_context;
|
||||
}
|
||||
|
||||
struct rtems_termios_linesw {
|
||||
int (*l_open) (struct rtems_termios_tty *tp);
|
||||
@@ -155,14 +383,6 @@ struct rtems_termios_linesw {
|
||||
int (*l_modem)(struct rtems_termios_tty *tp,int flags);
|
||||
};
|
||||
|
||||
/*
|
||||
* FIXME: this should move to libio.h!
|
||||
* values for rtems_termios_callbacks.outputUsesInterrupts
|
||||
*/
|
||||
#define TERMIOS_POLLED 0
|
||||
#define TERMIOS_IRQ_DRIVEN 1
|
||||
#define TERMIOS_TASK_DRIVEN 2
|
||||
|
||||
/*
|
||||
* FIXME: this should move to termios.h!
|
||||
*/
|
||||
|
||||
+401
-104
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,8 @@
|
||||
|
||||
#include "tmacros.h"
|
||||
#include <termios.h>
|
||||
#include <rtems/libcsupport.h>
|
||||
#include <rtems/malloc.h>
|
||||
#include <rtems/termiostypes.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
@@ -515,6 +517,94 @@ static void test_termios_cfmakeraw(void)
|
||||
rtems_test_assert( term.c_cflag & CS8 );
|
||||
}
|
||||
|
||||
static void test_early_device_install_remove(
|
||||
rtems_device_major_number major,
|
||||
rtems_device_minor_number minor,
|
||||
void *arg
|
||||
)
|
||||
{
|
||||
rtems_resource_snapshot snapshot;
|
||||
rtems_status_code sc;
|
||||
|
||||
rtems_resource_snapshot_take( &snapshot );
|
||||
|
||||
sc = rtems_termios_device_install( "/", 0, 0, NULL, NULL );
|
||||
rtems_test_assert( sc == RTEMS_INCORRECT_STATE );
|
||||
|
||||
sc = rtems_termios_device_remove( "/", 0, 0 );
|
||||
rtems_test_assert( sc == RTEMS_INCORRECT_STATE );
|
||||
|
||||
rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
|
||||
}
|
||||
|
||||
static void test_device_install_remove(void)
|
||||
{
|
||||
static const rtems_termios_device_handler handler;
|
||||
static const rtems_device_major_number major = 123456789;
|
||||
static const rtems_device_minor_number minor = 0xdeadbeef;
|
||||
static const char dev[] = "/foobar";
|
||||
|
||||
rtems_resource_snapshot snapshot;
|
||||
rtems_status_code sc;
|
||||
void *greedy;
|
||||
rtems_libio_t iop;
|
||||
rtems_libio_open_close_args_t args;
|
||||
|
||||
memset( &iop, 0, sizeof( iop ) );
|
||||
memset( &args, 0, sizeof( args ) );
|
||||
args.iop = &iop;
|
||||
|
||||
rtems_resource_snapshot_take( &snapshot );
|
||||
|
||||
greedy = rtems_heap_greedy_allocate( NULL, 0 );
|
||||
|
||||
sc = rtems_termios_device_install( "/", major, minor, &handler, NULL );
|
||||
rtems_test_assert( sc == RTEMS_NO_MEMORY );
|
||||
|
||||
rtems_heap_greedy_free( greedy );
|
||||
|
||||
rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
|
||||
|
||||
sc = rtems_termios_device_install( NULL, major, minor, &handler, NULL );
|
||||
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
|
||||
|
||||
sc = rtems_termios_device_install( NULL, major, minor, &handler, NULL );
|
||||
rtems_test_assert( sc == RTEMS_RESOURCE_IN_USE );
|
||||
|
||||
sc = rtems_termios_device_remove( NULL, major, minor );
|
||||
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
|
||||
|
||||
rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
|
||||
|
||||
sc = rtems_termios_device_install( "/", major, minor, &handler, NULL );
|
||||
rtems_test_assert( sc == RTEMS_UNSATISFIED );
|
||||
|
||||
rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
|
||||
|
||||
sc = rtems_termios_device_remove( NULL, major, minor );
|
||||
rtems_test_assert( sc == RTEMS_INVALID_ID );
|
||||
|
||||
sc = rtems_termios_device_install( &dev[0], major, minor, &handler, NULL );
|
||||
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
|
||||
|
||||
sc = rtems_termios_device_remove( "/barfoo", major, minor );
|
||||
rtems_test_assert( sc == RTEMS_UNSATISFIED );
|
||||
|
||||
sc = rtems_termios_device_open( major, minor, &args );
|
||||
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
|
||||
|
||||
sc = rtems_termios_device_remove( &dev[0], major, minor );
|
||||
rtems_test_assert( sc == RTEMS_RESOURCE_IN_USE );
|
||||
|
||||
sc = rtems_termios_device_close( &args );
|
||||
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
|
||||
|
||||
sc = rtems_termios_device_remove( &dev[0], major, minor );
|
||||
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
|
||||
|
||||
rtems_test_assert( rtems_resource_snapshot_check( &snapshot ) );
|
||||
}
|
||||
|
||||
static rtems_task Init(
|
||||
rtems_task_argument ignored
|
||||
)
|
||||
@@ -668,17 +758,22 @@ static rtems_task Init(
|
||||
}
|
||||
puts( "" );
|
||||
|
||||
test_device_install_remove();
|
||||
|
||||
TEST_END();
|
||||
rtems_test_exit(0);
|
||||
}
|
||||
|
||||
/* configuration information */
|
||||
|
||||
#define CONFIGURE_APPLICATION_PREREQUISITE_DRIVERS \
|
||||
{ .initialization_entry = test_early_device_install_remove }
|
||||
|
||||
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
|
||||
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
|
||||
|
||||
/* include an extra slot for registering the termios one dynamically */
|
||||
#define CONFIGURE_MAXIMUM_DRIVERS 3
|
||||
#define CONFIGURE_MAXIMUM_DRIVERS 4
|
||||
|
||||
/* one for the console and one for the test port */
|
||||
#define CONFIGURE_NUMBER_OF_TERMIOS_PORTS 3
|
||||
|
||||
@@ -24,8 +24,13 @@ directives:
|
||||
cfsetspeed
|
||||
cfsetispeed
|
||||
cfsetospeed
|
||||
rtems_termios_device_install
|
||||
rtems_termios_device_remove
|
||||
rtems_termios_device_open
|
||||
rtems_termios_device_close
|
||||
|
||||
concepts:
|
||||
|
||||
+ Exercise termios ioctl for all baud, character size, parity and
|
||||
bits per character options.
|
||||
+ Ensure that Termios device install/remove works.
|
||||
|
||||
Reference in New Issue
Block a user