wiki:TBR/UserManual/DriverManager

Version 2 (modified by Hellstrom, on 02/16/09 at 21:34:06) (diff)

DriverManager

Driver Manager

This document describes the Driver Manager patch for RTEMS-4.10. The LEON2 and the LEON3 BSP have been used to test the Driver Manager, the two Hardware platforms are different when it comes to Plug and Play. The LEON2 systems traditionally use a hardcoded register location, whereas in LEON3 systems Plug & Play is used to find all cores such a Memory controller, Timer, IRQ Controller, Ethernet MAC.

Purpose

The purpose of the Driver Manager is mainly,

  • more easily reuse driver code
  • enable drivers to be written independent of how certain sevices are implemented.
    • interrupt handling (registeration, masking, unmasking, acknowledging)
    • memory space translation
  • Unify the way drivers are configured. The driver registration string is replaced.
  • build images suitable for multiple targets with different hardware present.
  • Hardware topology awareness.
  • Easier to access which drivers are loaded and what hardware is present.
  • An initial step towards an architecture independent PCI layer.

Overview

The Driver Manager fills some of the purposes the "The Linux Device Model" fills.

It is a small and simple manager that connects a driver to a device and a device to driver resources. The driver is presented an API where it is possible to get the bus frequency, manage interrupt, get base register addresses, get configuration for a certain hardware device, etc.

Once a device has been registered by a bus driver the manager tries to find a suitable driver by comparing the information about the hardware (provided by the bus driver) to the driver hardware support information (provided by the driver). When a driver is found that can handle the device the device and driver are "united". The device is put into a list later processed by the driver manager.

All drivers are registered before starting scanning for devices.

The driver manager uses the concepts bus, device, driver and driver resources. A bus represents a device with child devices. A device represents a certain hardware. The topmost device is a bus device, the root bus device. All devices is connected to the root bus device.

The root bus device is the first device created during driver manager initialization. It is assigned a driver, the root bus driver, manually. The root bus driver must be configured by the user or BSP prior to driver manager initialization. The root bus device is initialized and reports the available hardware on the bus., once the root bus device is done initializing the driver manager proceeds to initialize the found devices in a two step process.

Once a device has been registered by a bus driver the manager tries to find a suitable driver by comparing the information about the hardware (provided by the bus driver) to the driver hardware support information (provided by the driver). When a driver is found that can handle the device the device and driver are "united". The device is put into a list later processed by the driver manager (init1 stage). When the root bus device driver is done initializing the driver manager proceeds to initialize the found devices in a two step process.

All devices are initialized in a two step process, called init1 and init2. Each driver provides two function pointers init1 and init2 which are called in order. All device's init1 is first called, when completed the driver manager enter stage two where all devices are called once more, this time using the init2 function. The initialization process is separated in two stages to make it possible for drivers to rely on other drivers being initialized. The order in which the devices are initialized on a bus can not be known (the same order as they are registered in which often depends on how the hardware is found in the Plug and Play information).

All drivers that export some functionality to other drivers, for example memory controller drivers, must initialize their API in stage1 in order to guarantee the service for other drivers when they enter stage 2.

All devices and bus devices should be found and registered in stage1. If not, they are called "hotplug" devices by the driver manager. Hotplugging has currently no special support in the driver manager.

Below are some concepts described that are used by the manager.

Bus Driver

The Driver Manager needs to be informed about hardware devices present in the system, that is what the bus driver does. The bus driver reads a hardcoded set up or Plug & Play information and registers devices to the manager. The bus driver must provide some services in order for the drivers to the found devices to function properly. The services are implemented by functions that are pointed to by a operations struct making it possible for the manager driver interface to call the functions.

The bus driver must be able to unite a hardware device with a driver.

/*! Bus information. Describes a bus. */
struct rtems_drvmgr_bus_info {
	int				bus_type;	/*!< Type of bus */
	struct rtems_drvmgr_bus_info	*next;		/*!< Next Bus */
	struct rtems_drvmgr_dev_info	*dev;		/*!< Bus device, the hardware... */
	void				*priv;		/*!< Private data structure used by BUS driver */
	struct rtems_drvmgr_dev_info	*children;	/*!< Hardware devices on this bus */
	struct rtems_drvmgr_bus_ops	*ops;		/*!< Bus operations supported by this bus driver */
	int				dev_cnt;	/*!< Number of devices this bus has */
	struct bus_res_node		*reslist;	/*!< Bus resources, head of a linked list of resources. */
	struct bus_mmap_entry		*mmaps;		/*!< Memory Map Translation, array of address spaces */
};

Root bus driver

The driver manager needs to know what driver should handle the CPU bus. The root bus driver is registered separately and before the driver manager is initialized.

Device

Represents a hardware normally found by the bus driver. A device is created and registered by the bus driver, once registered the driver manager finds the driver that supports the hardware, then a driver is united with the device. Each device structure has a private pointer that the driver use to access the information needed by the driver.

/* States of a device */
#define DEV_STATE_INIT1_DONE	0x00000001	/* Init 1 Stage has been done, not neccessarily successful */
#define DEV_STATE_INIT2_DONE	0x00000002	/* Init 2 Stage has been done, not neccessarily successful */
#define DEV_STATE_INIT1_FAILED	0x00000010	/* Init 1 Stage Failed */
#define DEV_STATE_INIT2_FAILED	0x00000020	/* Init 2 Stage Failed */
#define DEV_STATE_UNITED	0x00000100	/* Device United with Device Driver */
#define DEV_STATE_DELETED	0x00000200	/* Device has been deleted (unregistered) */

/*! Device information */
struct rtems_drvmgr_dev_info {
	struct rtems_drvmgr_dev_info	*next;		/*!< Next device */
	struct rtems_drvmgr_dev_info	*next_in_bus;	/*!< Next device on the same bus */
	struct rtems_drvmgr_dev_info	*next_in_drv;	/*!< Next device using the same driver */

	struct rtems_drvmgr_drv_info	*drv;		/*!< The driver owning this device */
	struct rtems_drvmgr_bus_info	*parent;	/*!< Bus that this device resides on */
	short				minor_drv;	/*!< Device number on driver (often minor in filesystem) */
	short				minor_bus;	/*!< Device number on bus (for device separation) */
	unsigned int			state;		/*!< State of this device, see DEV_STATE_* */
	char				*name;		/*!< Name of Device Hardware */
	void				*priv;		/*!< Pointer to driver private device structure */
	void				*businfo;	/*!< Host bus specific information */
	struct rtems_drvmgr_bus_info	*bus;		/*!< Pointer to bus, set only if this is a bus */
};

Driver

Driver for a hardware device. It uses the driver manager services provided, which in turn rely on the bus driver. The driver holds information to identify a hardware device, it tells the driver manager what kind of bus is supported and perhaps the Plug & Play Vendor and Device ID used to identify certain hardware.

Drivers are called twice to initialize a hardware device. Init1 and Init2 stages. A delete function may optionally be defined if the driver can handle removing of hardware.

/*! Driver operations, function pointers. */
struct rtems_drvmgr_drv_ops {
	rtems_status_code	(*init1)(struct rtems_drvmgr_dev_info *);	/*! Function doing Init Stage 1 of a hardware device */
	rtems_status_code	(*init2)(struct rtems_drvmgr_dev_info *);	/*! Function doing Init Stage 2 of a hardware device */
	rtems_status_code	(*delete)(struct rtems_drvmgr_dev_info *);	/*! Function called when device instance is to be removed */
};

/*! Information about a driver used during registration */
struct rtems_drvmgr_drv_info {
	struct rtems_drvmgr_drv_info	*next;		/*!< Next Driver */
	struct rtems_drvmgr_dev_info	*dev;		/*!< Devices using this driver */

	unsigned long long		drv_id;		/*!< Unique Driver ID */
	char				*name;		/*!< Name of Driver */
	int				bus_type;	/*!< Type of Bus this driver supports */
	struct rtems_drvmgr_drv_ops	*ops;		/*!< Driver operations */
	unsigned int			dev_cnt;	/*!< Number of devices in dev */
};

Driver Resource

A driver resource is a resource used by a driver for a certain device instance. The resource may be integer with value 65 called "numberTxDescriptors". The resources are grouped together in arrays targeting one device instance. The resources are assigned to one or multiple buses making it possible for the drivers to find whose hardware device is situated on that very bus. Multiple hardware devices of the same type is separated by their bus unit number which is always the same between starts. The number usually comes from the order the device if found in the plug and play information. Below is a typical bus resource definition grlib_drv_resource configuring two GRSPW device drivers. The second GRSPW core will have double the amount of descriptors than the first.

/*** Resource definitions ***
 * 
 * Overview of structures:
 *  All bus resources entries (bus_res_node) are linked together for a bus (bus_info->reslist).
 *  One bus resource entry has a pointer to an array of driver resources (drv_res). One driver 
 *  resouces is made out of an array of keys (drv_res_key). All keys belongs to the same driver 
 *  and harwdare device. Each key has a Name, Type ID and Data interpreted differently 
 *  depending on the Type ID (union key_value).
 *
 */

/* Key Data Types */
#define KEY_TYPE_NONE		0
#define KEY_TYPE_INT 		1
#define KEY_TYPE_STRING		2
#define KEY_TYPE_POINTER	3

#define KEY_EMPTY	{NULL, KEY_TYPE_NONE, {0}}
#define RES_EMPTY	{0, 0, NULL}
#define MMAP_EMPTY	{0, 0, 0}

/*! Union of different values */
union key_value {
	unsigned int		i;		/*!< Key data type UNIGNED INTEGER */
	char			*str;		/*!< Key data type STRING */
	void			*ptr;		/*!< Key data type ADDRESS/POINTER */
};

/* One key. Holding information relevant to the driver. */
struct drv_res_key {
	char			*key_name;	/* Name of key */
	int			key_type;	/* How to interpret key_value */
	union key_value		key_value;	/* The value or pointer to the value */
};

/*! Bus resource entry, Driver resources for a certain device instance, containing a number of keys 
 * Where each key hold the data of interest.
 */
struct drv_res {
	unsigned long long	drv_id;		/*!< Identifies the driver this resource is aiming */
	int			minor_bus;	/*!< Indentifies a specfic device */
	struct drv_res_key	*keys;		/*!< First key in key array */
};

/*! Bus resource list node */
struct bus_res_node {
	struct bus_res_node	*next;		/*!< Next resource node in list */
	struct drv_res		*resource;	/*!< Array of resources, one per device instance */
};
/* GRSPW0 resources */
struct drv_res_key grlib_grspw0_res[] =

{
	{"txDesc", KEY_TYPE_INT, {(unsigned int)16}},
	{"rxDesc", KEY_TYPE_INT, {(unsigned int)32}},
	KEY_EMPTY
};
/* GRSPW1 resources */
struct drv_res_key grlib_grspw1_res[] =

{
	{"txDesc", KEY_TYPE_INT, {(unsigned int)32}},
	{"rxDesc", KEY_TYPE_INT, {(unsigned int)64}},
	KEY_EMPTY
};
/* GRLIB Plug & Play bus driver resources */
struct drv_res grlib_drv_resources[] =

{
	{DRIVER_AMBAPP_GAISLER_GRSPW_ID, 0, &grlib_grspw0_res[0]},
	{DRIVER_AMBAPP_GAISLER_GRSPW_ID, 1, &grlib_grspw1_res[0]},
	RES_EMPTY
};

The driver to which the resources belong is identified by a unique driver id. The driver id must be unique for the bus type, in this case AMBAPP (AMBA Plug & Play). The device instances are separated by the bus unit number, second entry in the grlib_drv_resources.

Using

Configuration

The following may be configured,

  • Driver resources per bus.
  • Root bus driver
  • Driver table.

The driver manager is configured by selecting drivers used by the driver manager and by registering a root bus prior to driver manager initialization described in the next section. Drivers may also be configured by using driver resources, see section "Driver Resource".

The driver resources are registered different depending on bus driver.

The root bus device driver is registered by calling root_drv_register, the root bus driver may provide a function doing this, in that case one must call that function instead. For example drv_mgr_grlib_init register the GRLIB-AMBA Plug & Play Bus as the root bus driver and also assigns the driver resources for the root bus.

The drivers are selected by defining the array drv_mgr_drivers, it contains one function pointer per driver that is responsible to register one or more drivers. The drv_mgr_drivers can be set up by defining CONFIGURE_INIT, selecting the appropriate drivers and including drv_mgr/drvmgr_confdefs.h. This approach is similar to configuring a standard RTEMS project using rtems/confdefs.h. Below is an example how to select drivers.

#include <rtems.h>
#include <bsp.h>

#define CONFIGURE_INIT

/* Standard RTEMS set up */
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_MAXIMUM_DRIVERS 32
#include <rtems/confdefs.h>

/* Driver manager set up */
#define CONFIGURE_DRIVER_AMBAPP_GAISLER_GRETH
#define CONFIGURE_DRIVER_AMBAPP_GAISLER_GRSPW
#define CONFIGURE_DRIVER_AMBAPP_GAISLER_GRCAN
#define CONFIGURE_DRIVER_AMBAPP_GAISLER_OCCAN
#define CONFIGURE_DRIVER_AMBAPP_GAISLER_B1553BRM
#define CONFIGURE_DRIVER_AMBAPP_GAISLER_APBUART
#define CONFIGURE_DRIVER_AMBAPP_MCTRL
#define CONFIGURE_DRIVER_AMBAPP_GAISLER_PCIF
#define CONFIGURE_DRIVER_AMBAPP_GAISLER_GRPCI
#define CONFIGURE_DRIVER_PCI_GR_RASTA_IO
#define CONFIGURE_DRIVER_PCI_GR_RASTA_TMTC
#define CONFIGURE_DRIVER_PCI_GR_701
#include <drv_mgr/drv_manager_confdefs.h>

Initialization

The driver manager is initialized by the user calling rtems_drvmgr_init() after the root bus driver has been registered. After the driver manager has been initialized all devices have been united with their drivers. The driver are ready for usage.

The initialization cannot currently be done before the I/O Manager intialization, this is one of the reasons it is initialized in the Init() task as below.

/* Initializing Driver Manager */ printf("Initializing manager\n"); if ( rtems_drvmgr_init() ) {

printf("Driver manager Failed to initialize\n"); exit(-1);

}

Driver Interface

The Driver Interface.

/*! Get Device pointer from Driver and Driver minor number 
 *
 * \param drv         Driver the device is united with.
 * \param minor       Driver minor number assigned to device.
 * \param pdev        Location where the Device point will be stored.
 * \return            Zero on success. -1 on failure, when device was not found in driver 
 *                    device list.
 */
int rtems_drvmgr_get_dev(
	struct rtems_drvmgr_drv_info *drv,
	int minor,
	struct rtems_drvmgr_dev_info **pdev);

/*! Get Bus frequency in Hertz. Frequency is stored into address of freq_hz.
 *
 * \param dev        The Device to get Bus frequency for.
 * \param freq_hz    Location where Bus Frequency will be stored.
 */
int rtems_drvmgr_get_freq(struct rtems_drvmgr_dev_info *dev, unsigned int *freq_hz);

/*! Get device name prefix, this name can be used to register a unique name in the 
 *  filesystem or to get an idea where the device is located.
 *
 * \param dev         The Device to get the device Prefix for.
 * \param dev_prefix  Location where the prefix will be stored.
 */
int rtems_drvmgr_get_dev_prefix(struct rtems_drvmgr_dev_info *dev, char *dev_prefix);

/*! Register an interrupt handler.
 *  \param index      Index is used to identify the IRQ number if hardware has multiple IRQ sources. 
 *                    Normally Index is set to 0 to indicated the first and only IRQ source.
 *  \param func       Interrupt Service Routine.
 *  \param arg        Optional ISR argument.
 */
int rtems_drvmgr_interrupt_register(
	struct rtems_drvmgr_dev_info *dev,
	int index,
	void (*func)(int irq, void *arg),
	void *arg);

/*! Enable (unmask) an interrupt source 
 * 
 *  \param dev        Device to enable interrupt for.
 *  \param index      Index is used to identify the IRQ number if hardware has multiple IRQ sources. 
 *                    Normally Index is set to 0 to indicated the first and only IRQ source.
 */
int rtems_drvmgr_interrupt_enable(struct rtems_drvmgr_dev_info *dev, int index);

/*! Disable (mask) an interrupt source 
 *
 *  \param dev        Device to disable interrupt for.
 *  \param index      Index is used to identify the IRQ number if hardware has multiple IRQ sources. 
 *                    Normally Index is set to 0 to indicated the first and only IRQ source.
 */
int rtems_drvmgr_interrupt_disable(struct rtems_drvmgr_dev_info *dev, int index);

/*! Clear (ACK) pending interrupt
 *
 *  \param dev        Device to clear interrupt for.
 *  \param index      Index is used to identify the IRQ number if hardware has multiple IRQ sources. 
 *                    Normally Index is set to 0 to indicated the first and only IRQ source.
 */
int rtems_drvmgr_interrupt_clear(struct rtems_drvmgr_dev_info *dev, int index);

/*! Translate address 
 * 1. From CPU local bus to a remote bus for example a PCI target (from_remote_to_cpu = 0)
 * 2. From remote bus to CPU local bus (from_remote_to_cpu = 1)
 *
 * src_address the address to translate, dst_address is where the translated address is stored.
 *
 * \param dev                  Device to translate addresses for.
 * \param from_remote_to_cpu   Selection tranlation direction.
 * \param src_address          Address to translate
 * \param dst_address          Location where translated address is stored.
 *
 * Returns -1 if unable to translate. If no map is present src_address is translated 1:1 (just copied).
 */
int rtems_drvmgr_mmap_translate(
	struct rtems_drvmgr_dev_info *dev,
	int from_remote_to_cpu,
	void *src_address,
	void **dst_address);

TODO

BSP

Changes to BSPs:

  • Add support to BSP to register root bus
  • Make driver resources on the root bus weak so that the user can override them.

= RTEMS =

  • Make I/O Manager handle regestering I/O Drivers before I/O Manager is initialized.

== Driver Manager ==

  • Initialize the Driver manager before I/O Manager initialization.