[PATCH 009/111] DRVMGR: added driver manager to cpukit/libdrvmgr

Joel Sherrill joel.sherrill at oarcorp.com
Thu Mar 5 22:29:54 UTC 2015



On 3/5/2015 3:44 PM, Gedare Bloom wrote:
> Continued...
>
> A high-level question I have is whether we want to consider this
> drvmgr as an externally-supported library, or if it is to be part of
> RTEMS proper. It makes sense to treat it as third-party code if there
> are other OSs that gaisler uses the same API for, but if this is
> specifically designed for RTEMS then we may want to take "ownership"
> of it in RTEMS Project and it makes sense to make the interface
> changes (namespace, interface design) that I suggest. If it is to be
> treated as third party code, then the interface changes do not
> necessarily need to be made, but we do need to ensure there is a
> visible "upstream" to contribute back to. More suggestions below.
I am going to answer this from a very selfish standpoint. The SPARC user
base is important to RTEMS.  The fact that Gaisler distributes tool binaries
with an RTEMS version has never really caused us problems beyond
recognizing the version when someone asks a question on the mailing list.

If we treat this as third party code, then Gaisler will be unable to merge
some number of drivers and other improvements to the LEON and NGMP
BSPs. This is going to further drive the deviation between community
RTEMS and Gaisler distributed RTEMS. The community distributed one
will be harder to use and folks will be more likely to pick the Gaisler one
due to more drivers.

IMO we need to find a way to merge this code. Any capabilities that can be
BSP independent, we need to find a way to make that happen.

In talks with Daniel, this backlog has occurred not due to unwillingness to
merge but due to overload, personnel, and personal issues.  The patch set
has unfortunately grown in size since the first attempt to get through their
patch backlog.

I can't speak for Gaisler but my understanding is that they would prefer to
be largely in sync with the community version and that is my desire as well.
They want to treat us as upstream like we do gcc, binutils, etc. Sometimes
we need a patch but it gets merged appropriately upstream as soon as
possible. When a new upstream release occurs with our fixes, we switch.
>> +/*! Remove a device, and all its children devices if device is a bus device. The
>> + *  device driver will be requested to remove the device and once gone from bus,
>> + *  device and driver list the device is put into a inactive list for debugging
>> + *  (this is optional by using remove argument).
>> + *
>> + * Removing the Root Bus Device is not supported.
>> + *
>> + * \param remove If non-zero the device will be deallocated, and not put into
>> + *               the inacitve list.
>> + */
>> +extern int drvmgr_dev_unregister(struct drvmgr_dev *dev);
>> +
>> +/*! Register a bus */
>> +extern int drvmgr_bus_register(struct drvmgr_bus *bus);
>> +
>> +/*! Unregister a bus */
>> +extern int drvmgr_bus_unregister(struct drvmgr_bus *bus);
>> +
>> +/*! Unregister all child devices of a bus.
>> + *
>> + * This function is called from the bus driver, from a "safe" state where
>> + * devices will not be added or removed on this particular bus at this time
>> + */
>> +extern int drvmgr_children_unregister(struct drvmgr_bus *bus);
>> +
>> +/* Separate a device from the driver it has been united with */
>> +extern int drvmgr_dev_drv_separate(struct drvmgr_dev *dev);
>> +
>> +/*! Allocate a device structure, if no memory available
>> + *  rtems_error_fatal_occurred is called.
>> + * The 'extra' argment tells how many bytes extra space is to be allocated after
>> + * the device structure, this is typically used for "businfo" structures. The extra
>> + * space is always aligned to a 4-byte boundary.
>> + */
>> +extern int drvmgr_alloc_dev(struct drvmgr_dev **pdev, int extra);
>> +
>> +/*! Allocate a bus structure, if no memory available rtems_error_fatal_occurred
>> + * is called.
>> + * The 'extra' argment tells how many bytes extra space is to be allocated after
>> + * the device structure, this is typically used for "businfo" structures. The
>> + * extra space is always aligned to a 4-byte boundary.
>> + */
>> +extern int drvmgr_alloc_bus(struct drvmgr_bus **pbus, int extra);
>> +
>> +/*** DRIVER RESOURCE FUNCTIONS ***/
>> +
>> +/*! Add resources to a bus, typically used by a bus driver.
>> + *
>> + * \param bus   The Bus to add the resources to.
>> + * \param res   An array with Driver resources, all together are called bus
>> + *              resources.
>> + */
>> +extern void drvmgr_bus_res_add(struct drvmgr_bus *bus,
>> +                                       struct drvmgr_bus_res *bres);
>> +
>> +/*! Find all the resource keys for a device among all driver resources on a
>> + *  bus. Typically used by a device driver to get configuration options.
>> + *
>> + * \param dev   Device to find resources for
>> + * \param key   Location where the pointer to the driver resource array (drvmgr_drv_res->keys) is stored.
>> + */
>> +extern int drvmgr_keys_get(struct drvmgr_dev *dev, struct drvmgr_key **keys);
>> +
>> +/*! Return the one key that matches key name from a driver keys array. The keys
>> + *  can be obtained using drvmgr_keys_get().
>> + *
>> + * \param keys       An array of keys ended with KEY_EMPTY to search among.
>> + * \param key_name   Name of key to search for among the keys.
>> + */
>> +extern struct drvmgr_key *drvmgr_key_get(struct drvmgr_key *keys, char *key_name);
> These two functions are too similarly named. Anyway, I guess this is
> just a string-based key-value search mechanism? Perhaps this whole
> approach would be better served by refactoring out the key-value
> search from the rest of the drvmgr... could be a future extension I
> suppose, but it would drastically simplify all this stuff.
>
>> +
>> +/*! Extract key value from the key in the keys array matching name and type.
>> + *
>> + *  This function calls drvmgr_keys_get to get the key requested (from key
>> + *  name), then determines if the type is correct. A pointer to the key value
>> + *  is returned.
>> + *
>> + *  \param keys       An array of keys ended with KEY_EMPTY to search among.
>> + *  \param key_name   Name of key to search for among the keys.
>> + *  \param key_type   Data Type of value. INTEGER, ADDRESS, STRING.
>> + *  \return           Returns NULL if no value found matching Key Name and Key
>> + *                    Type.
>> + */
>> +extern union drvmgr_key_value *drvmgr_key_val_get(
>> +       struct drvmgr_key *keys,
>> +       char *key_name,
>> +       int key_type);
>> +
>> +/*! Get key value from the bus resources matching [device, key name, key type]
>> + *  if no matching key is found NULL is returned.
>> + *
>> + * This is typically used by device drivers to find a particular device
>> + * resource.
>> + *
>> + * \param dev         The device to search resource for.
>> + * \param key_name    The key name to search for
>> + * \param key_type    The key type expected.
>> + * \return            Returns NULL if no value found matching Key Name and
>> + *                    Key Type was found for device.
>> + */
>> +extern union drvmgr_key_value *drvmgr_dev_key_get(
>> +       struct drvmgr_dev *dev,
>> +       char *key_name,
>> +       int key_type);
>> +
>> +/*** DRIVER INTERACE USED TO REQUEST INFORMATION/SERVICES FROM BUS DRIVER ***/
>> +
>> +/*! Get parent bus */
>> +static inline struct drvmgr_bus *drvmgr_get_parent(struct drvmgr_dev *dev)
>> +{
>> +       if (dev)
>> +               return dev->parent;
>> +       else
>> +               return NULL;
>> +}
>> +
>> +/*! Get Driver of device */
>> +static inline struct drvmgr_drv *drvmgr_get_drv(struct drvmgr_dev *dev)
>> +{
>> +       if (dev)
>> +               return dev->drv;
>> +       else
>> +               return NULL;
>> +}
>> +
>> +/*! Calls func() for every device found in the device tree, regardless of
>> + * device state or if a driver is assigned. With the options argument the user
>> + * can decide to do either a depth-first or a breadth-first search.
>> + *
>> + * If the function func() returns a non-zero value then for_each_dev will
>> + * return imediatly with the same return value as func() returned.
>> + *
>> + * \param func       Function called on each device
>> + * \param arg        Custom function argument
>> + * \param options    Search Options, see DRVMGR_FED_*
>> + *
>> + */
>> +#define DRVMGR_FED_BF 1                /* Breadth-first search */
>> +#define DRVMGR_FED_DF 0                /* Depth first search */
>> +extern int drvmgr_for_each_dev(
>> +       int (*func)(struct drvmgr_dev *dev, void *arg),
>> +       void *arg,
>> +       int options);
>> +
>> +/*! 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.
>> + */
>> +extern int drvmgr_get_dev(
>> +       struct drvmgr_drv *drv,
>> +       int minor,
>> +       struct drvmgr_dev **pdev);
>> +
> I commented on this before, but I guess this should be
> drvmgr_get_device() and could return the pdev directly or NULL if not
> found.
>
>> +/*! Get Bus frequency in Hertz. Frequency is stored into address of freq_hz.
>> + *
>> + * \param dev        The Device to get Bus frequency for.
>> + * \param options    Bus-type specific options
>> + * \param freq_hz    Location where Bus Frequency will be stored.
>> + */
>> +extern int drvmgr_freq_get(
>> +       struct drvmgr_dev *dev,
>> +       int options,
>> +       unsigned int *freq_hz);
>> +
> Would it be ok to just return freq_hz? 0 would be an error? or are
> there multiple error cases?
>
>> +/*! Return 0 if dev is not located on the root bus, 1 if on root bus */
>> +extern int drvmgr_on_rootbus(struct drvmgr_dev *dev);
>> +
>> +/*! Get device name prefix, this name can be used to register a unique name in
>> + *  the bus->error 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.
>> + */
>> +extern int drvmgr_get_dev_prefix(struct drvmgr_dev *dev, char *dev_prefix);
>> +
>> +/*! Register a shared interrupt handler. Since this service is shared among
>> + *  interrupt drivers/handlers the handler[arg] must be installed before the
>> + *  interrupt can be cleared or disabled. The handler is by default disabled
>> + *  after registration.
>> + *
>> + *  \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.
>> + *                    A negative index is interpreted as a absolute bus IRQ
>> + *                    number.
>> + *  \param isr        Interrupt Service Routine.
>> + *  \param arg        Optional ISR argument.
>> + */
>> +extern int drvmgr_interrupt_register(
>> +       struct drvmgr_dev *dev,
>> +       int index,
>> +       const char *info,
>> +       drvmgr_isr isr,
>> +       void *arg);
>> +
>> +/*! Unregister an interrupt handler. This also disables the interrupt before
>> + *  unregistering the 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.
>> + *                    A negative index is interpreted as a absolute bus IRQ
>> + *                    number.
>> + *  \param isr        Interrupt Service Routine, previously registered.
>> + *  \param arg        Optional ISR argument, previously registered.
>> + */
>> +extern int drvmgr_interrupt_unregister(
>> +       struct drvmgr_dev *dev,
>> +       int index,
>> +       drvmgr_isr isr,
>> +       void *arg);
>> +
>> +/*! 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.
>> + *                    A negative index is interpreted as a absolute bus IRQ number.
>> + *  \param isr        Interrupt Service Routine, previously registered.
>> + *  \param arg        Optional ISR argument, previously registered.
>> + */
>> +extern int drvmgr_interrupt_clear(
>> +       struct drvmgr_dev *dev,
>> +       int index);
>> +
>> +/*! Force unmasking/enableing an interrupt on the interrupt controller, this is not normally used,
>> + *  if used the caller has masked/disabled the interrupt just before.
>> + *
>> + *  \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.
>> + *                    A negative index is interpreted as a absolute bus IRQ number.
>> + *  \param isr        Interrupt Service Routine, previously registered.
>> + *  \param arg        Optional ISR argument, previously registered.
>> + */
>> +extern int drvmgr_interrupt_unmask(
>> +       struct drvmgr_dev *dev,
>> +       int index);
>> +
>> +/*! Force masking/disable an interrupt on the interrupt controller, this is not normally performed
>> + *  since this will stop all other (shared) ISRs to be disabled until _unmask() is called.
>> + *
>> + *  \param dev        Device to mask 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.
>> + *                    A negative index is interpreted as a absolute bus IRQ number.
>> + */
>> +extern int drvmgr_interrupt_mask(
>> +       struct drvmgr_dev *dev,
>> +       int index);
>> +
>> +/*! drvmgr_translate() translation options */
>> +enum drvmgr_tr_opts {
> drvmgr_translate_option or drvmgr_translate_type maybe better names?
>
>> +       /* Translate CPU RAM Address (input) to DMA unit accessible address
>> +        * (output), this is an upstreams translation in reverse order.
>> +        *
>> +        * Typical Usage:
>> +        * It is common to translate a CPU accessible RAM address to an
>> +        * address that DMA units can access over bridges.
>> +        */
>> +       CPUMEM_TO_DMA = 0x0,
>> +
>> +       /* Translate DMA Unit Accessible address mapped to CPU RAM (input) to
>> +        * CPU accessible address (output). This is an upstreams translation.
>> +        *
>> +        * Typical Usage (not often used):
>> +        * The DMA unit descriptors contain pointers to DMA buffers located at
>> +        * CPU RAM addresses that the DMA unit can access, the CPU processes
>> +        * the descriptors and want to access the data but a translation back
>> +        * to CPU address is required.
>> +        */
>> +       CPUMEM_FROM_DMA = 0x1,
>> +
>> +       /* Translate DMA Memory Address (input) to CPU accessible address
>> +        * (output), this is a downstreams translation in reverse order.
>> +        *
>> +        * Typical Usage:
>> +        * A PCI network card puts packets into its memory not doing DMA over
>> +        * PCI, in order for the CPU to access them the PCI address must be
>> +        * translated.
>> +        */
>> +       DMAMEM_TO_CPU = 0x2,
>> +
>> +       /* Translate CPU accessible address (input) mapped to DMA Memory Address
>> +        * to DMA Unit accessible address (output). This is a downstreams
>> +        * translation.
>> +        */
>> +       DMAMEM_FROM_CPU = 0x3,
>> +};
>> +#define DRVMGR_TR_REVERSE 0x1  /* do reverse translation direction order */
>> +#define DRVMGR_TR_PATH 0x2     /* 0x0=down-stream 0x2=up-stream address path */
>> +
>> +/*! Translate an address on one bus to an address on another bus.
>> + *
>> + *  The device determines source or destination bus, the root bus is always
>> + *  the other bus. It is assumed that the CPU is located on the root bus or
>> + *  that it can access it without address translation (mapped 1:1). The CPU
>> + *  is thus assumed to be located on level 0 top most in the bus hierarchy.
>> + *
>> + *  If no map is present in the bus driver src_address is translated 1:1
>> + *  (just copied).
>> + *
>> + *  Addresses are typically converted up-streams from the DMA unit towards the
>> + *  CPU (DMAMEM_TO_CPU) or down-streams towards DMA hardware from the CPU
>> + *  (CPUMEM_TO_DMA) over one or multiple bridges depending on bus architecture.
>> + *  See 'enum drvmgr_tr_opts' for other translation direction options.
>> + *  For example:
>> + *  Two common operations is to translate a CPU accessible RAM address to an
>> + *  address that DMA units can access (dev=DMA-unit, CPUMEM_TO_DMA,
>> + *  src_address=CPU-RAM-ADR) and to translate an address of a PCI resource for
>> + *  example RAM mapped into a PCI BAR to an CPU accessible address
>> + *  (dev=PCI-device, DMAMEM_TO_CPU, src_address=PCI-BAR-ADR).
>> + *
>> + *  Source address is translated and the result is put into *dst_address, if
>> + *  the address is not accessible on the other bus -1 is returned.
>> + *
>> + *  \param dev             Device to translate addresses for
>> + *  \param options         Tanslation direction options, see enum drvmgr_tr_opts
>> + *  \param src_address     Address to translate
>> + *  \param dst_address     Location where translated address is stored
>> + *
>> + *  Returns 0 if unable to translate. The remaining length from the given
>> + *  address of the map is returned on success, for example if a map starts
>> + *  at 0x40000000 of size 0x100000 the result will be 0x40000 if the address
>> + *  was translated into 0x400C0000.
>> + *  If dev is on root-bus no translation is performed 0xffffffff is returned
>> + *  and src_address is stored in *dst_address.
>> + */
> What happens if the remaining length is 0?
>
>> +extern unsigned int drvmgr_translate(
>> +       struct drvmgr_dev *dev,
>> +       unsigned int options,
> Why not use the enum type here?
>
>> +       void *src_address,
>> +       void **dst_address);
>> +
> I think I'd rather have the dst_address returned, and set the
> remaining length as a side-effect if that is useful. This way the
> usage is more like
> dest = drvmgr_translate(my_dev, my_opts, src, &length);
>
>> +/* Translate addresses between buses, used internally to implement
>> + * drvmgr_translate. Function is not limited to translate from/to root bus
>> + * where CPU is resident, however buses must be on a straight path relative
>> + * to each other (parent of parent of parent and so on).
>> + *
>> + * \param from         src_address is given for this bus
>> + * \param to           src_address is translated to this bus
>> + * \param reverse      Selects translation method, if map entries are used in
>> + *                     the reverse order (map_up->to is used as map_up->from)
>> + * \param src_address  Address to be translated
>> + * \param dst_address  Translated address is stored here on success (return=0)
>> + *
>> + *  Returns 0 if unable to translate. The remaining length from the given
>> + *  address of the map is returned on success and the result is stored into
>> + *  *dst_address. For example if a map starts at 0x40000000 of size 0x100000
>> + *  the result will be 0x40000 if the address was translated into 0x400C0000.
>> + *  If dev is on root-bus no translation is performed 0xffffffff is returned.
>> + *  and src_address is stored in *dst_address.
>> + */
>> +extern unsigned int drvmgr_translate_bus(
>> +       struct drvmgr_bus *from,
>> +       struct drvmgr_bus *to,
>> +       int reverse,
>> +       void *src_address,
>> +       void **dst_address);
>> +
>> +/* Calls drvmgr_translate() to translate an address range and checks the result,
>> + * a printout is generated if the check fails. All parameters are passed on to
>> + * drvmgr_translate() except for size, see paramters of drvmgr_translate().
>> + *
>> + * If size=0 only the starting address is not checked.
>> + *
>> + * If mapping failes a non-zero result is returned.
>> + */
>> +extern int drvmgr_translate_check(
>> +       struct drvmgr_dev *dev,
>> +       unsigned int options,
>> +       void *src_address,
>> +       void **dst_address,
>> +       unsigned int size);
>> +
> There seems to be a lot of functions to accomplish the basic job of
> translating the address. Are all these really needed? For "internal"
> functions you may consider hiding them from the public-facing API of
> the drvmgr.
>
>> +/*! Get function pointer from Device Driver or Bus Driver.
>> + *
>> + *  Returns 0 if function is available
>> + */
>> +extern int drvmgr_func_get(void *obj, int funcid, void **func);
>> +
> Again, it might be better to return the pointer. I don't exactly know
> what this function pointer is though. I guess a pointer to the given
> funcid?
>
>> +/*! Lookup function and call it directly with the four optional arguments */
>> +extern int drvmgr_func_call(void *obj, int funcid, void *a, void *b, void *c, void *d);
>> +
>> +/* Builds a Function ID.
>> + *
>> + * Used to request optional functions by a bus or device driver
>> + */
>> +#define DRVMGR_FUNCID(major, minor) ((((major) & 0xfff) << 20) | ((minor) & 0xfffff))
>> +#define DRVMGR_FUNCID_NONE 0
>> +#define DRVMGR_FUNCID_END DRVMGR_FUNCID(DRVMGR_FUNCID_NONE, 0)
>> +
>> +/* Major Function ID. Most significant 12-bits. */
>> +enum {
>> +       FUNCID_NONE             = 0x000,
>> +       FUNCID_RW               = 0x001, /* Read/Write functions */
>> +};
>> +
>> +/* Select Sub-Function Read/Write function by ID */
>> +#define RW_SIZE_1   0x00001    /* Access Size */
>> +#define RW_SIZE_2   0x00002
>> +#define RW_SIZE_4   0x00004
>> +#define RW_SIZE_8   0x00008
>> +#define RW_SIZE_ANY 0x00000
>> +#define RW_SIZE(id) ((unsigned int)(id) & 0xf)
>> +
>> +#define RW_DIR_ANY  0x00000   /* Access Direction */
>> +#define RW_READ     0x00000   /* Read */
>> +#define RW_WRITE    0x00010   /* Write */
>> +#define RW_SET      0x00020   /* Write with same value (memset) */
>> +#define RW_DIR(id)  (((unsigned int)(id) >> 4) & 0x3)
>> +
>> +#define RW_RAW      0x00000  /* Raw access - no swapping (machine default) */
>> +#define RW_LITTLE   0x00040  /* Little Endian */
>> +#define RW_BIG      0x00080  /* Big Endian */
>> +#define RW_ENDIAN(id) (((unsigned int)(id) >> 6) & 0x3)
>> +
>> +#define RW_TYPE_ANY 0x00000  /* Access type */
>> +#define RW_REG      0x00100
>> +#define RW_MEM      0x00200
>> +#define RW_MEMREG   0x00300
>> +#define RW_CFG      0x00400
>> +#define RW_TYPE(id) (((unsigned int)(id) >> 8) & 0xf)
>> +
>> +#define RW_ARG      0x01000 /* Optional Argument */
>> +#define RW_ERR      0x02000 /* Optional Error Handler */
>> +
>> +/* Build a Read/Write function ID */
>> +#define DRVMGR_RWFUNC(minor) DRVMGR_FUNCID(FUNCID_RW, minor)
>> +
>> +/* Argument to Read/Write functions, the "void *arg" pointer is returned by
>> + * RW_ARG. If NULL is returned no argument is needed.
>> + */
>> +struct drvmgr_rw_arg {
>> +       void *arg;
>> +       struct drvmgr_dev *dev;
>> +};
>> +
>> +/* Standard Read/Write function types */
>> +typedef uint8_t (*drvmgr_r8)(uint8_t *srcadr);
>> +typedef uint16_t (*drvmgr_r16)(uint16_t *srcadr);
>> +typedef uint32_t (*drvmgr_r32)(uint32_t *srcadr);
>> +typedef uint64_t (*drvmgr_r64)(uint64_t *srcadr);
>> +typedef void (*drvmgr_w8)(uint8_t *dstadr, uint8_t data);
>> +typedef void (*drvmgr_w16)(uint16_t *dstadr, uint16_t data);
>> +typedef void (*drvmgr_w32)(uint32_t *dstadr, uint32_t data);
>> +typedef void (*drvmgr_w64)(uint64_t *dstadr, uint64_t data);
>> +/* READ/COPY a memory area located on bus into CPU memory.
>> + * From 'src' (remote) to the destination 'dest' (local), n=number of bytes
>> + */
>> +typedef int (*drvmgr_rmem)(void *dest, const void *src, int n);
>> +/* WRITE/COPY a user buffer located in CPU memory to a location on the bus.
>> + * From 'src' (local) to the destination 'dest' (remote), n=number of bytes
>> + */
>> +typedef int (*drvmgr_wmem)(void *dest, const void *src, int n);
>> +/* Set a memory area to the byte value given in c, see LIBC memset(). Memset is
>> + * implemented by calling wmem() multiple times with a "large" buffer.
>> + */
>> +typedef int (*drvmgr_memset)(void *dstadr, int c, size_t n);
>> +
>> +/* Read/Write function types with additional argument */
>> +typedef uint8_t (*drvmgr_r8_arg)(uint8_t *srcadr, void *a);
>> +typedef uint16_t (*drvmgr_r16_arg)(uint16_t *srcadr, void *a);
>> +typedef uint32_t (*drvmgr_r32_arg)(uint32_t *srcadr, void *a);
>> +typedef uint64_t (*drvmgr_r64_arg)(uint64_t *srcadr, void *a);
>> +typedef void (*drvmgr_w8_arg)(uint8_t *dstadr, uint8_t data, void *a);
>> +typedef void (*drvmgr_w16_arg)(uint16_t *dstadr, uint16_t data, void *a);
>> +typedef void (*drvmgr_w32_arg)(uint32_t *dstadr, uint32_t data, void *a);
>> +typedef void (*drvmgr_w64_arg)(uint64_t *dstadr, uint64_t data, void *a);
>> +typedef int (*drvmgr_rmem_arg)(void *dest, const void *src, int n, void *a);
>> +typedef int (*drvmgr_wmem_arg)(void *dest, const void *src, int n, void *a);
>> +typedef int (*drvmgr_memset_arg)(void *dstadr, int c, size_t n, void *a);
>> +
> Is there an advantage to having two versions one with and one without
> an arg? I'm just feeling like this code is overly bloated by
> duplication and I want to get a feel for how necessary it all is.
>
>> +/* Report an error to the parent bus of the device */
>> +typedef void (*drvmgr_rw_err)(struct drvmgr_rw_arg *a, struct drvmgr_bus *bus,
>> +                               int funcid, void *adr);
>> +
>> +/* Helper function for buses that implement the memset() over wmem() */
>> +extern void drvmgr_rw_memset(
>> +       void *dstadr,
>> +       int c,
>> +       size_t n,
>> +       void *a,
>> +       drvmgr_wmem_arg wmem
>> +       );
>> +
>> +/*** PRINT INFORMATION ABOUT DRIVER MANAGER ***/
>> +
>> +/*! Calls func() for every device found matching the search requirements of
>> + * set_mask and clr_mask. Each bit set in set_mask must be set in the
>> + * device state bit mask (dev->state), and Each bit in the clr_mask must
>> + * be cleared in the device state bit mask (dev->state). There are three
>> + * special cases:
>> + *
>> + * 1. If state_set_mask and state_clr_mask are zero the state bits are
>> + *    ignored and all cores are treated as a match.
>> + *
>> + * 2. If state_set_mask is zero the function func will not be called due to
>> + *    a bit being set in the state mask.
>> + *
>> + * 3. If state_clr_mask is zero the function func will not be called due to
>> + *    a bit being cleared in the state mask.
>> + *
>> + * If the function func() returns a non-zero value then for_each_dev will
>> + * return imediatly with the same return value as func() returned.
>> + *
>> + * \param devlist            The list to iterate though searching for devices.
>> + * \param state_set_mask     Defines the bits that must be set in dev->state
>> + * \param state_clr_mask     Defines the bits that must be cleared in dev->state
>> + * \param func               Function called on each
>> + *
>> + */
>> +extern int drvmgr_for_each_listdev(
>> +       struct drvmgr_list *devlist,
>> +       unsigned int state_set_mask,
>> +       unsigned int state_clr_mask,
>> +       int (*func)(struct drvmgr_dev *dev, void *arg),
>> +       void *arg);
>> +
>> +/* Print all devices */
>> +#define PRINT_DEVS_FAILED      0x01    /* Failed during initialization */
>> +#define PRINT_DEVS_ASSIGNED    0x02    /* Driver assigned */
>> +#define PRINT_DEVS_UNASSIGNED  0x04    /* Driver not assigned */
>> +#define PRINT_DEVS_IGNORED     0x08    /* Device ignored on user's request */
>> +#define PRINT_DEVS_ALL         (PRINT_DEVS_FAILED | \
>> +                               PRINT_DEVS_ASSIGNED | \
>> +                               PRINT_DEVS_UNASSIGNED |\
>> +                               PRINT_DEVS_IGNORED)
>> +
>> +/*! Print number of devices, buses and drivers */
>> +extern void drvmgr_summary(void);
>> +
>> +/*! Print devices with certain condictions met according to 'options' */
>> +extern void drvmgr_print_devs(unsigned int options);
>> +
>> +/*! Print device/bus topology */
>> +extern void drvmgr_print_topo(void);
>> +
>> +/*! Print the memory usage
>> + * Only accounts for data structures. Not for the text size.
>> + */
>> +extern void drvmgr_print_mem(void);
>> +
>> +#define OPTION_DEV_GENINFO   0x00000001
>> +#define OPTION_DEV_BUSINFO   0x00000002
>> +#define OPTION_DEV_DRVINFO   0x00000004
>> +#define OPTION_DRV_DEVS      0x00000100
>> +#define OPTION_BUS_DEVS      0x00010000
>> +#define OPTION_RECURSIVE     0x01000000
>> +#define OPTION_INFO_ALL      0xffffffff
>> +
>> +/*! Print information about a driver manager object (device, driver, bus) */
>> +extern void drvmgr_info(void *id, unsigned int options);
>> +
>> +/*! Get information about a device */
>> +extern void drvmgr_info_dev(struct drvmgr_dev *dev, unsigned int options);
>> +
>> +/*! Get information about a bus */
>> +extern void drvmgr_info_bus(struct drvmgr_bus *bus, unsigned int options);
>> +
>> +/*! Get information about a driver */
>> +extern void drvmgr_info_drv(struct drvmgr_drv *drv, unsigned int options);
>> +
>> +/*! Get information about all devices on a bus */
>> +extern void drvmgr_info_devs_on_bus(struct drvmgr_bus *bus, unsigned int options);
>> +
>> +/*! Get information about all devices in the system (on all buses) */
>> +extern void drvmgr_info_devs(unsigned int options);
>> +
>> +/*! Get information about all drivers in the system */
>> +extern void drvmgr_info_drvs(unsigned int options);
>> +
>> +/*! Get information about all buses in the system */
>> +extern void drvmgr_info_buses(unsigned int options);
>> +
>> +/*! Get Driver by Driver ID */
>> +extern struct drvmgr_drv *drvmgr_drv_by_id(uint64_t id);
>> +
>> +/*! Get Driver by Driver Name */
>> +extern struct drvmgr_drv *drvmgr_drv_by_name(const char *name);
>> +
>> +/*! Get Device by Device Name */
>> +extern struct drvmgr_dev *drvmgr_dev_by_name(const char *name);
>> +
>> +#ifdef __cplusplus
>> +}
>> +#endif
>> +
>> +#endif
> This was a pretty massive header file. It might be worth splitting it
> up internally.
>
>> diff --git a/cpukit/libdrvmgr/drvmgr_by_id.c b/cpukit/libdrvmgr/drvmgr_by_id.c
>> new file mode 100644
>> index 0000000..7a0ea42
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_by_id.c
>> @@ -0,0 +1,33 @@
>> +/* Find driver by driver-ID
>> + *
>> + * COPYRIGHT (c) 2011.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <drvmgr/drvmgr.h>
>> +#include "drvmgr_internal.h"
>> +
>> +/* Get driver from driver name */
>> +struct drvmgr_drv *drvmgr_drv_by_id(uint64_t id)
>> +{
>> +       struct rtems_driver_manager *mgr = &drv_mgr;
> Is it also true for an smp system there can only be one drv_mgr?
> Should the drvmgr functions be modified to take the
> rtems_driver_manager* as first argument?
>
>> +       struct drvmgr_drv *drv = NULL;
>> +
>> +       /* NOTE: No locking is needed here since Driver list is supposed to be
>> +        *       initialized once during startup, we treat it as a static
>> +        *       read-only list
>> +        */
>> +
>> +       drv = DRV_LIST_HEAD(&mgr->drivers);
>> +       while (drv) {
>> +               if (drv->drv_id == id)
>> +                       break;
>> +               drv = drv->next;
>> +       }
>> +
>> +       return drv;
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_by_name.c b/cpukit/libdrvmgr/drvmgr_by_name.c
>> new file mode 100644
>> index 0000000..2ae527a
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_by_name.c
>> @@ -0,0 +1,37 @@
>> +/* Find driver by driver-name
>> + *
>> + * COPYRIGHT (c) 2011.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <string.h>
>> +#include <drvmgr/drvmgr.h>
>> +#include "drvmgr_internal.h"
>> +
>> +/* Get driver from driver name */
>> +struct drvmgr_drv *drvmgr_drv_by_name(const char *name)
>> +{
>> +       struct rtems_driver_manager *mgr = &drv_mgr;
>> +       struct drvmgr_drv *drv = NULL;
>> +
>> +       if (!name)
>> +               return NULL;
>> +
>> +       /* NOTE: No locking is needed here since Driver list is supposed to be
>> +        *       initialized once during startup, we treat it as a static
>> +        *       read-only list
>> +        */
> Maybe put he note on the drviers field in the definition of
> rtems_driver_manager, along with any other assumptions about
> synchronization.
>
>> +
>> +       drv = DRV_LIST_HEAD(&mgr->drivers);
>> +       while (drv) {
>> +               if (drv->name && (strcmp(drv->name, name) == 0))
>> +                       break;
>> +               drv = drv->next;
>> +       }
>> +
>> +       return drv;
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_confdefs.h b/cpukit/libdrvmgr/drvmgr_confdefs.h
>> new file mode 100644
>> index 0000000..23affe9
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_confdefs.h
>> @@ -0,0 +1,86 @@
>> +/* Driver Manager Configuration file.
>> + *
>> + * COPYRIGHT (c) 2009.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +/*
>> + *  The configuration consist of an array with function pointers that
>> + *  register one or more drivers that will be used by the Driver Manger.
>> + *
>> + *  The Functions are called in the order they are declared.
>> + *
>> + */
>> +
>> +#ifndef _DRIVER_MANAGER_CONFDEFS_H_
>> +#define _DRIVER_MANAGER_CONFDEFS_H_
>> +
>> +#include "drvmgr.h"
>> +
>> +#ifdef __cplusplus
>> +extern "C" {
>> +#endif
>> +
>> +extern struct drvmgr_drv_reg_func drvmgr_drivers[];
>> +
>> +#ifdef CONFIGURE_INIT
>> +
>> +#if 0 /* EXAMPLE: GPTIMER driver definition */
>> +#define DRIVER_AMBAPP_GAISLER_GPTIMER_REG {gptimer_register_drv}
>> +extern void gptimer_register_drv(void);
>> +#endif
> I guess these #if 0 pieces will go away when there are real drivers available?
>
>> +
>> +/* CONFIGURE DRIVER MANAGER */
>> +struct drvmgr_drv_reg_func drvmgr_drivers[] = {
>> +#if 0 /* EXAMPLE: GPTIMER Driver registration */
>> +#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GPTIMER
>> +       DRIVER_AMBAPP_GAISLER_GPTIMER_REG,
>> +#endif
>> +#endif
>> +
>> +/* Macros for adding custom drivers without needing to recompile
>> + * kernel.
>> + */
>> +#ifdef CONFIGURE_DRIVER_CUSTOM1
>> +       DRIVER_CUSTOM1_REG,
>> +#endif
>> +#ifdef CONFIGURE_DRIVER_CUSTOM2
>> +       DRIVER_CUSTOM2_REG,
>> +#endif
>> +#ifdef CONFIGURE_DRIVER_CUSTOM3
>> +       DRIVER_CUSTOM3_REG,
>> +#endif
>> +#ifdef CONFIGURE_DRIVER_CUSTOM4
>> +       DRIVER_CUSTOM4_REG,
>> +#endif
>> +#ifdef CONFIGURE_DRIVER_CUSTOM5
>> +       DRIVER_CUSTOM5_REG,
>> +#endif
>> +#ifdef CONFIGURE_DRIVER_CUSTOM6
>> +       DRIVER_CUSTOM6_REG,
>> +#endif
>> +#ifdef CONFIGURE_DRIVER_CUSTOM7
>> +       DRIVER_CUSTOM7_REG,
>> +#endif
>> +#ifdef CONFIGURE_DRIVER_CUSTOM8
>> +       DRIVER_CUSTOM8_REG,
>> +#endif
>> +#ifdef CONFIGURE_DRIVER_CUSTOM9
>> +       DRIVER_CUSTOM9_REG,
>> +#endif
> out of curiousity, couldn't you just use
> #ifdef DRIVER_CUSTOM1_REG
>         DRIVER_CUSTOM1_REG
> #endif
> and so on?
>
>> +
>> +       /* End array with NULL */
>> +       {NULL}
>> +};
>> +
>> +#endif /* CONFIGURE_INIT */
>> +
>> +#ifdef __cplusplus
>> +}
>> +#endif
>> +
>> +#endif /* _DRIVER_MANAGER_CONFDEFS_H_ */
>> diff --git a/cpukit/libdrvmgr/drvmgr_dev_by_name.c b/cpukit/libdrvmgr/drvmgr_dev_by_name.c
>> new file mode 100644
>> index 0000000..f5a99ee
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_dev_by_name.c
>> @@ -0,0 +1,34 @@
>> +/* Find device by device name
>> + *
>> + * COPYRIGHT (c) 2011.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + *
>> + */
>> +
>> +#include <string.h>
>> +#include <drvmgr/drvmgr.h>
>> +#include "drvmgr_internal.h"
>> +
>> +static int dev_name_compare(struct drvmgr_dev *dev, void *arg)
>> +{
>> +       const char *name = arg;
>> +
>> +       if (dev->name && (strcmp(dev->name, name) == 0))
>> +               return (int)dev;
> This is not portable.
>
>> +       else
>> +               return 0;
>> +}
>> +
>> +/* Get device by device name or bus name */
>> +struct drvmgr_dev *drvmgr_dev_by_name(const char *name)
>> +{
>> +       if (!name)
>> +               return NULL;
>> +
>> +       return (struct drvmgr_dev *)
>> +               drvmgr_for_each_dev(dev_name_compare, (void *)name, 0);
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_drvinf.c b/cpukit/libdrvmgr/drvmgr_drvinf.c
>> new file mode 100644
>> index 0000000..9f7c7a5
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_drvinf.c
>> @@ -0,0 +1,148 @@
>> +/* Driver Manager Driver Interface Implementation.
>> + *
>> + * COPYRIGHT (c) 2009.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + *
>> + */
>> +
>> +/*
>> + * This is the part the device driver API, the functions rely on that the
>> + * parent bus driver has implemented the neccessary operations correctly.
>> + *
>> + */
>> +
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +
>> +#include <drvmgr/drvmgr.h>
>> +#include "drvmgr_internal.h"
>> +
>> +/* Get device pointer from knowing the Driver and the Driver minor
>> + * that was assigned to it
>> + */
>> +int drvmgr_get_dev(
>> +       struct drvmgr_drv *drv,
>> +       int minor,
>> +       struct drvmgr_dev **pdev)
>> +{
>> +       struct drvmgr_dev *dev;
>> +       if (!drv)
>> +               return -1;
>> +
>> +       DRVMGR_LOCK_READ();
>> +       dev = drv->dev;
>> +       while (dev) {
>> +               if (dev->minor_drv == minor)
>> +                       break;
>> +               dev = dev->next_in_drv;
>> +       }
>> +       DRVMGR_UNLOCK();
>> +       if (!dev)
>> +               return -1;
>> +       if (pdev)
>> +               *pdev = dev;
>> +       return 0;
> if (!pdev) this returns success without setting the return. Though
> this problem goes away if the function is changed to return dev
> instead of an int.
>
>> +}
>> +
>> +/* Get Bus frequency in HZ from bus driver */
>> +int drvmgr_freq_get(
>> +       struct drvmgr_dev *dev,
>> +       int options,
>> +       unsigned int *freq_hz)
>> +{
>> +       if (!dev || !dev->parent || !dev->parent->ops->freq_get)
>> +               return -1;
>> +
>> +       return dev->parent->ops->freq_get(dev, options, freq_hz);
>> +}
>> +
>> +/* Get driver prefix */
>> +int drvmgr_get_dev_prefix(struct drvmgr_dev *dev, char *dev_prefix)
>> +{
>> +       struct drvmgr_bus_params params;
>> +       if (!dev || !dev->parent || !dev->parent->ops->get_params)
>> +               return -1;
>> +
>> +       dev->parent->ops->get_params(dev, &params);
>> +       if (!params.dev_prefix)
>> +               return -1;
>> +       if (dev_prefix)
>> +               strcpy(dev_prefix, params.dev_prefix);
> Could return params.dev_prefix.
>
>> +       return 0;
>> +}
>> +
>> +/* Register an interrupt */
>> +int drvmgr_interrupt_register(
>> +       struct drvmgr_dev *dev,
>> +       int index,
>> +       const char *info,
>> +       drvmgr_isr isr,
>> +       void *arg)
>> +{
>> +       if (!dev || !dev->parent || !dev->parent->ops->int_register)
>> +               return -1;
>> +
>> +       if (!isr)
>> +               return -1;
>> +
>> +       return dev->parent->ops->int_register(dev, index, info, isr, arg);
>> +}
>> +
>> +/* Unregister an interrupt */
>> +int drvmgr_interrupt_unregister(
>> +       struct drvmgr_dev *dev,
>> +       int index,
>> +       drvmgr_isr isr,
>> +       void *arg)
>> +{
>> +       if (!dev || !dev->parent || !dev->parent->ops->int_unregister)
>> +               return -1;
>> +
>> +       if (!isr)
>> +               return -1;
>> +
>> +       return dev->parent->ops->int_unregister(dev, index, isr, arg);
>> +}
>> +
>> +int drvmgr_interrupt_clear(
>> +       struct drvmgr_dev *dev,
>> +       int index)
>> +{
>> +       if (!dev || !dev->parent || !dev->parent->ops->int_clear)
>> +               return -1;
>> +
>> +       return dev->parent->ops->int_clear(dev, index);
>> +}
>> +
>> +int drvmgr_interrupt_unmask(
>> +       struct drvmgr_dev *dev,
>> +       int index)
>> +{
>> +       if (!dev || !dev->parent || !dev->parent->ops->int_unmask)
>> +               return -1;
>> +
>> +       return dev->parent->ops->int_unmask(dev, index);
>> +}
>> +
>> +int drvmgr_interrupt_mask(
>> +       struct drvmgr_dev *dev,
>> +       int index)
>> +{
>> +       if (!dev || !dev->parent || !dev->parent->ops->int_mask)
>> +               return -1;
>> +
>> +       return dev->parent->ops->int_mask(dev, index);
>> +}
>> +
>> +int drvmgr_on_rootbus(struct drvmgr_dev *dev)
>> +{
>> +       if (dev->parent && dev->parent->dev && dev->parent->dev->parent)
>> +               return 0;
>> +       else
>> +               return 1;
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_for_each_dev.c b/cpukit/libdrvmgr/drvmgr_for_each_dev.c
>> new file mode 100644
>> index 0000000..a3e63ec
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_for_each_dev.c
>> @@ -0,0 +1,104 @@
>> +/* Iterate over device tree topology, breadth or depth-first
>> + *
>> + * COPYRIGHT (c) 2009.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <string.h>
>> +#include <drvmgr/drvmgr.h>
>> +#include <drvmgr/drvmgr_list.h>
>> +#include "drvmgr_internal.h"
>> +
>> +/* Traverse device tree breadth-first. Supports up to 31 buses */
> This should more clearly state that it will stop when a visitor
> function returns a non-zero value. Certainly it should be documented
> for the primary function drvmgr_for_each_dev()./
>
>> +static int drvmgr_for_each_dev_breadth(
>> +       int (*func)(struct drvmgr_dev *dev, void *arg),
>> +       void *arg
>> +       )
>> +{
>> +       int ret = 0, i, pos;
>> +       struct drvmgr_bus *bus, *buses[32];
> 32?
>
>> +       struct drvmgr_dev *dev;
>> +
>> +       pos = 0;
>> +       memset(&buses[0], 0, sizeof(buses));
>> +       buses[pos++] = drv_mgr.root_dev.bus;    /* Get root bus */
>> +
>> +       for (i = 0, bus = buses[0]; buses[i]; i++, bus = buses[i]) {
>> +               dev = bus->children;
>> +               while (dev) {
>> +                       ret = func(dev, arg);
>> +                       if (ret != 0)
>> +                               break;
>> +                       if (dev->bus && pos < 31)
>> +                               buses[pos++] = dev->bus;
>> +
>> +                       dev = dev->next_in_bus;
>> +               }
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +/* Traverse device tree depth-first. */
>> +static int drvmgr_for_each_dev_depth(
>> +       int (*func)(struct drvmgr_dev *dev, void *arg),
>> +       void *arg
>> +       )
>> +{
>> +       int ret = 0;
>> +       struct drvmgr_dev *dev;
>> +
>> +       /* Get first device */
>> +       dev = drv_mgr.root_dev.bus->children;
>> +
>> +       while (dev) {
>> +               ret = func(dev, arg);
>> +               if (ret != 0)
>> +                       break;
>> +               if (dev->bus && dev->bus->children) {
>> +                       dev = dev->bus->children;
>> +               } else {
>> +next_dev:
>> +                       if (dev->next_in_bus == NULL) {
>> +                               /* Step up one level... back to parent bus */
>> +                               dev = dev->parent->dev;
>> +                               if (dev == &drv_mgr.root_dev)
>> +                                       break;
>> +                               goto next_dev;
> Avoid this goto if you can.
>
>> +                       } else {
>> +                               dev = dev->next_in_bus;
>> +                       }
>> +               }
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +/* Traverse device tree depth-first or breadth-first */
>> +int drvmgr_for_each_dev(
>> +       int (*func)(struct drvmgr_dev *dev, void *arg),
>> +       void *arg,
>> +       int options
>> +       )
>> +{
>> +       int ret;
>> +
>> +       DRVMGR_LOCK_READ();
>> +
>> +       /* Get Root Device */
>> +       if (drv_mgr.root_dev.bus->children != NULL) {
>> +               if (options & DRVMGR_FED_BF)
>> +                       ret = drvmgr_for_each_dev_breadth(func, arg);
>> +               else
>> +                       ret = drvmgr_for_each_dev_depth(func, arg);
>> +       } else
>> +               ret = 0;
>> +
>> +       DRVMGR_UNLOCK();
>> +
>> +       return ret;
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_for_each_list_dev.c b/cpukit/libdrvmgr/drvmgr_for_each_list_dev.c
>> new file mode 100644
>> index 0000000..9e1b95b
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_for_each_list_dev.c
>> @@ -0,0 +1,44 @@
>> +/* Iterate over one list of devices used internally by driver manager
>> + *
>> + * COPYRIGHT (c) 2009.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <drvmgr/drvmgr.h>
>> +#include <drvmgr/drvmgr_list.h>
>> +#include "drvmgr_internal.h"
>> +
>> +int drvmgr_for_each_listdev(
>> +       struct drvmgr_list *devlist,
>> +       unsigned int state_set_mask,
>> +       unsigned int state_clr_mask,
>> +       int (*func)(struct drvmgr_dev *dev, void *arg),
>> +       void *arg
>> +       )
>> +{
>> +       struct drvmgr_dev *dev;
>> +       int ret = 0;
>> +
>> +       DRVMGR_LOCK_READ();
>> +
>> +       /* Get First Device */
>> +       dev = DEV_LIST_HEAD(devlist);
>> +       while (dev) {
>> +               if (((state_set_mask != 0) && ((dev->state & state_set_mask) == state_set_mask)) ||
>> +                   ((state_clr_mask != 0) && ((dev->state & state_clr_mask) == 0)) ||
>> +                   ((state_set_mask == 0) && (state_clr_mask == 0))) {
>> +                       ret = func(dev, arg);
>> +                       if (ret != 0)
>> +                               break;
>> +               }
>> +               dev = dev->next;
>> +       }
>> +
>> +       DRVMGR_UNLOCK();
>> +
>> +       return ret;
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_func.c b/cpukit/libdrvmgr/drvmgr_func.c
>> new file mode 100644
>> index 0000000..e02a444
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_func.c
>> @@ -0,0 +1,42 @@
>> +/* Driver Manager optional dynamic function interface
>> + *
>> + * COPYRIGHT (c) 2011.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <drvmgr/drvmgr.h>
>> +
>> +/* Get Function from Function ID */
>> +int drvmgr_func_get(void *obj, int funcid, void **func)
>> +{
> This function could return f or NULL;
>
>> +       int objtype;
>> +       struct drvmgr_func *f;
>> +
>> +       if (!obj)
>> +               return DRVMGR_FAIL;
>> +       objtype = *(int *)obj;
>> +
>> +       if (objtype == DRVMGR_OBJ_BUS)
>> +               f = ((struct drvmgr_bus *)obj)->funcs;
>> +       else if (objtype == DRVMGR_OBJ_DRV)
>> +               f = ((struct drvmgr_drv *)obj)->funcs;
>> +       else
>> +               return DRVMGR_FAIL;
>> +
>> +       if (f == NULL)
>> +               return DRVMGR_FAIL;
>> +
>> +       while (f->funcid != DRVMGR_FUNCID_NONE) {
>> +               if (f->funcid == funcid) {
>> +                       *func = f->func;
>> +                       return DRVMGR_OK;
>> +               }
>> +               f++;
>> +       }
>> +
>> +       return DRVMGR_FAIL;
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_func_call.c b/cpukit/libdrvmgr/drvmgr_func_call.c
>> new file mode 100644
>> index 0000000..75836c3
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_func_call.c
>> @@ -0,0 +1,21 @@
>> +/* Driver Manager optional dynamic function interface
>> + *
>> + * COPYRIGHT (c) 2011.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <drvmgr/drvmgr.h>
>> +
>> +/* Lookup function from function ID and call it using given arguments */
>> +int drvmgr_func_call(void *obj, int funcid, void *a, void *b, void *c, void *d)
>> +{
>> +       int (*func)(void *arg1, void *arg2, void *arg3, void *arg4) = NULL;
>> +
>> +       if (drvmgr_func_get(obj, funcid, (void *)&func) != DRVMGR_OK)
>> +               return DRVMGR_FAIL;
>> +       return func(a, b, c, d);
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_init.c b/cpukit/libdrvmgr/drvmgr_init.c
>> new file mode 100644
>> index 0000000..b8a738f
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_init.c
>> @@ -0,0 +1,26 @@
>> +/* Driver Manager Initialization
>> + *
>> + * COPYRIGHT (c) 2009.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <drvmgr/drvmgr.h>
>> +
>> +/* Init driver manager - all in one go. Typically called from Init task when
>> + * user wants to initilize driver manager after startup, otherwise not used.
>> + */
>> +int drvmgr_init(void)
>> +{
>> +       int level;
>> +
>> +       _DRV_Manager_initialization();
>> +
>> +       for (level = 1; level <= DRVMGR_LEVEL_MAX; level++)
>> +               _DRV_Manager_init_level(level);
>> +
>> +       return 0;
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_internal.h b/cpukit/libdrvmgr/drvmgr_internal.h
>> new file mode 100644
>> index 0000000..b459455
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_internal.h
>> @@ -0,0 +1,70 @@
>> +/* Private driver manager declarations
>> + *
>> + * COPYRIGHT (c) 2009.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +/*  Structure hold all information the driver manager needs to know of. Used
>> + *  internally by Driver Manager routines.
>> + */
>> +struct rtems_driver_manager {
>> +       int     level;
>> +       int     initializing_objs;
>> +
>> +       /* Device tree Lock */
>> +       rtems_id                lock;
>> +
>> +       /* The first device - The root device and it's driver */
>> +       struct drvmgr_drv       *root_drv;
>> +       struct drvmgr_dev       root_dev;
>> +
>> +       /*!< Linked list of all registered drivers */
>> +       struct drvmgr_list      drivers;
>> +
>> +       /* Buses that reached a certain initialization level.
>> +        * Lists by Level:
>> +        *  N=0         - Not intialized, just registered
>> +        *  N=1..MAX-1  - Reached init level N
>> +        *  N=MAX       - Successfully initialized bus
>> +        */
>> +       struct drvmgr_list      buses[DRVMGR_LEVEL_MAX+1];
>> +       /* Buses failed to initialize or has been removed by not freed */
>> +       struct drvmgr_list      buses_inactive;
>> +
>> +       /* Devices that reached a certain initialization level.
>> +        * Lists by Level:
>> +        *  N=0         - Not intialized, just registered
>> +        *  N=1..MAX-1  - Reached init level N
>> +        *  N=MAX       - Successfully initialized device
>> +        */
>> +       struct drvmgr_list      devices[DRVMGR_LEVEL_MAX+1];
>> +       /*!< Devices failed to initialize, removed, ignored, no driver */
>> +       struct drvmgr_list      devices_inactive;
>> +};
>> +
>> +extern struct rtems_driver_manager drv_mgr;
>> +
>> +extern void _DRV_Manager_Lock(void);
>> +extern void _DRV_Manager_Unlock(void);
>> +extern int _DRV_Manager_Init_Lock(void);
>> +
>> +/* The best solution is to implement the locking with a RW lock, however there
>> + * is no such API available. Care must be taken so that dead-lock isn't created
>> + * for example in recursive functions.
>> + */
>> +#if defined(DRVMGR_USE_LOCKS) && (DRVMGR_USE_LOCKS == 1)
>> + #define DRVMGR_LOCK_INIT() _DRV_Manager_Init_Lock()
>> + #define DRVMGR_LOCK_WRITE() _DRV_Manager_Lock()
>> + #define DRVMGR_LOCK_READ() _DRV_Manager_Lock()
>> + #define DRVMGR_UNLOCK() _DRV_Manager_Unlock()
>> +#else
>> + /* no locking */
>> + #define DRVMGR_LOCK_INIT()
>> + #define DRVMGR_LOCK_WRITE()
>> + #define DRVMGR_LOCK_READ()
>> + #define DRVMGR_UNLOCK()
>> +#endif
>> diff --git a/cpukit/libdrvmgr/drvmgr_list.c b/cpukit/libdrvmgr/drvmgr_list.c
>> new file mode 100644
>> index 0000000..dc1665f
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_list.c
>> @@ -0,0 +1,67 @@
>> +/* Driver Manager List Interface Implementation.
>> + *
>> + * COPYRIGHT (c) 2009.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <stdlib.h>
>> +#include <drvmgr/drvmgr_list.h>
>> +
>> +/* LIST interface */
>> +
>> +void drvmgr_list_init(struct drvmgr_list *list, int offset)
>> +{
>> +       list->head = list->tail = NULL;
>> +       list->ofs = offset;
>> +}
>> +
>> +void drvmgr_list_empty(struct drvmgr_list *list)
>> +{
>> +       list->head = list->tail = NULL;
>> +}
>> +
>> +void drvmgr_list_add_head(struct drvmgr_list *list, void *entry)
>> +{
>> +       LIST_FIELD(list, entry) = list->head;
>> +       if (list->head == NULL)
>> +               list->tail = entry;
>> +       list->head = entry;
>> +}
>> +
>> +void drvmgr_list_add_tail(struct drvmgr_list *list, void *entry)
>> +{
>> +       if (list->tail == NULL)
>> +               list->head = entry;
>> +       else
>> +               LIST_FIELD(list, list->tail) = entry;
>> +       LIST_FIELD(list, entry) = NULL;
>> +       list->tail = entry;
>> +}
>> +
>> +void drvmgr_list_remove_head(struct drvmgr_list *list)
>> +{
>> +       list->head = LIST_FIELD(list, list->head);
>> +       if (list->head == NULL)
>> +               list->tail = NULL;
>> +}
>> +
>> +void drvmgr_list_remove(struct drvmgr_list *list, void *entry)
>> +{
>> +       void **prevptr = &list->head;
>> +       void *curr, *prev;
>> +
>> +       prev = NULL;
>> +       curr = list->head;
>> +       while (curr != entry) {
>> +               prev = curr;
>> +               prevptr = &LIST_FIELD(list, curr);
>> +               curr = LIST_FIELD(list, curr);
>> +       }
>> +       *prevptr = LIST_FIELD(list, entry);
>> +       if (list->tail == entry)
>> +               list->tail = prev;
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_list.h b/cpukit/libdrvmgr/drvmgr_list.h
>> new file mode 100644
>> index 0000000..6f4b98e
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_list.h
>> @@ -0,0 +1,79 @@
>> +/* Linked list help functions used by driver manager.
>> + *
>> + * COPYRIGHT (c) 2009.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +/*
>> + * Help functions for the Driver Manager. Implements a singly linked list
>> + * with head and tail pointers for fast insertions/deletions to head and
>> + * tail in list.
>> + */
>> +
> Is there a good reason not to use either RTEMS Chains or sys/queue.h?
> Does singly-linked save much space / cost much time?
>
>> +#ifndef _DRVIVER_MANAGER_LIST_H_
>> +#define _DRVIVER_MANAGER_LIST_H_
> s/DRVIVER/DRIVER
>
>> +
>> +#ifdef __cplusplus
>> +extern "C" {
>> +#endif
>> +
>> +/*! List description, Singly link list with head and tail pointers. */
>> +struct drvmgr_list {
>> +       void    *head;  /*!< First entry in queue */
>> +       void    *tail;  /*!< Last entry in queue */
>> +       int     ofs;    /*!< Offset into head and tail to find next field */
>> +};
>> +
>> +/* Static initialization of list */
>> +#define LIST_INITIALIZER(type, field) {NULL, NULL, offsetof(type, field)}
>> +
>> +/* Return the first element in list */
>> +#define LIST_HEAD(list, type) ((type *)(list)->head)
>> +
>> +/* Return the last element in list */
>> +#define LIST_TAIL(list, type) ((type *)(list)->tail)
>> +
>> +/* Get the next pointer of an entry */
>> +#define LIST_FIELD(list, entry) (*(void **)((char *)(entry) + (list)->ofs))
>> +
>> +/* Return the next emlement in list */
>> +#define LIST_NEXT(list, entry, type) ((type *)(LIST_FIELD(list, entry)))
>> +
>> +/* Iterate through all entries in list */
>> +#define LIST_FOR_EACH(list, entry, type) \
>> +       for (entry = LIST_HEAD(list, type); \
>> +            entry; \
>> +            entry = LIST_NEXT(list, entry, type))
>> +
>> +/*! Initialize a list during runtime
>> + *
>> + * \param list    The list to initialize
>> + * \param offset  The number of bytes into the entry structure the next pointer
>> + *                is found
>> + */
>> +extern void drvmgr_list_init(struct drvmgr_list *list, int offset);
>> +
>> +/*! Clear list */
>> +extern void drvmgr_list_empty(struct drvmgr_list *list);
>> +
>> +/*! Add entry to front of list */
>> +extern void drvmgr_list_add_head(struct drvmgr_list *list, void *entry);
>> +
>> +/*! Add entry to end of list */
>> +extern void drvmgr_list_add_tail(struct drvmgr_list *list, void *entry);
>> +
>> +/*! Remove entry from front of list */
>> +extern void drvmgr_list_remove_head(struct drvmgr_list *list);
>> +
>> +/*! Remove entry from anywhere in list */
>> +extern void drvmgr_list_remove(struct drvmgr_list *list, void *entry);
>> +
>> +#ifdef __cplusplus
>> +}
>> +#endif
>> +
>> +#endif
>> diff --git a/cpukit/libdrvmgr/drvmgr_lock.c b/cpukit/libdrvmgr/drvmgr_lock.c
>> new file mode 100644
>> index 0000000..d6601d3
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_lock.c
>> @@ -0,0 +1,38 @@
>> +/* Driver Manager Internal locking implementation
>> + *
>> + * COPYRIGHT (c) 2009.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <rtems.h>
>> +#include <drvmgr/drvmgr.h>
>> +#include "drvmgr_internal.h"
>> +
>> +void _DRV_Manager_Lock(void)
>> +{
>> +       rtems_semaphore_obtain(drv_mgr.lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
>> +}
>> +
>> +void _DRV_Manager_Unlock(void)
>> +{
>> +       rtems_semaphore_release(drv_mgr.lock);
>> +}
>> +
>> +int _DRV_Manager_Init_Lock(void)
>> +{
>> +       int rc;
>> +
>> +       rc = rtems_semaphore_create(
>> +               rtems_build_name('D', 'R', 'V', 'M'),
>> +               1,
>> +               RTEMS_DEFAULT_ATTRIBUTES,
>> +               0,
>> +               &drv_mgr.lock);
>> +       if (rc != RTEMS_SUCCESSFUL)
>> +               return -1;
>> +       return 0;
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_print.c b/cpukit/libdrvmgr/drvmgr_print.c
>> new file mode 100644
>> index 0000000..c36abfe
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_print.c
>> @@ -0,0 +1,457 @@
>> +/* Driver Manager Information printing Interface Implementation
>> + *
>> + * COPYRIGHT (c) 2009.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +/*
>> + *  These functions print stuff about the driver manager, what devices were
>> + *  found and were united with a driver, the Bus topology, memory taken, etc.
>> + *
>> + */
>> +
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +
>> +#include <drvmgr/drvmgr.h>
>> +#include "drvmgr_internal.h"
>> +
>> +typedef void (*fun_ptr)(void);
>> +
>> +static int print_dev_found(struct drvmgr_dev *dev, void *arg)
>> +{
>> +       char **pparg = arg;
>> +
>> +       if (pparg && *pparg) {
>> +               printf(*pparg);
>> +               *pparg = NULL;
>> +       }
>> +
>> +       printf(" DEV %p  %s on bus %p\n", dev,
>> +               dev->name ? dev->name : "NO_NAME", dev->parent);
>> +
>> +       return 0; /* Continue to next device */
>> +}
>> +
>> +void drvmgr_print_devs(unsigned int options)
>> +{
>> +       struct rtems_driver_manager *mgr = &drv_mgr;
>> +       char *parg;
>> +
>> +       /* Print Drivers */
>> +       if (options & PRINT_DEVS_ASSIGNED) {
>> +               parg = " --- DEVICES ASSIGNED TO DRIVER ---\n";
>> +               drvmgr_for_each_listdev(&mgr->devices[DRVMGR_LEVEL_MAX],
>> +                               DEV_STATE_UNITED, 0, print_dev_found, &parg);
>> +               if (parg != NULL)
>> +                       printf("\n NO DEVICES WERE ASSIGNED A DRIVER\n");
>> +       }
>> +
>> +       if (options & PRINT_DEVS_UNASSIGNED) {
>> +               parg = "\n --- DEVICES WITHOUT DRIVER ---\n";
>> +               drvmgr_for_each_listdev(&mgr->devices_inactive, 0,
>> +                       DEV_STATE_UNITED, print_dev_found, &parg);
>> +               if (parg != NULL)
>> +                       printf("\n NO DEVICES WERE WITHOUT DRIVER\n");
>> +       }
>> +
>> +       if (options & PRINT_DEVS_FAILED) {
>> +               parg = "\n --- DEVICES FAILED TO INITIALIZE ---\n";
>> +               drvmgr_for_each_listdev(&mgr->devices_inactive,
>> +                       DEV_STATE_INIT_FAILED, 0, print_dev_found, &parg);
>> +               if (parg != NULL)
>> +                       printf("\n NO DEVICES FAILED TO INITIALIZE\n");
>> +       }
>> +
>> +       if (options & PRINT_DEVS_IGNORED) {
>> +               parg = "\n --- DEVICES IGNORED ---\n";
>> +               drvmgr_for_each_listdev(&mgr->devices_inactive,
>> +                       DEV_STATE_IGNORED, 0, print_dev_found, &parg);
>> +               if (parg != NULL)
>> +                       printf("\n NO DEVICES WERE IGNORED\n");
>> +       }
>> +
>> +       printf("\n\n");
>> +}
>> +
>> +static int drvmgr_topo_func(struct drvmgr_dev *dev, void *arg)
>> +{
>> +       char prefix[32];
>> +       int depth = dev->parent->depth;
>> +
>> +       if (depth > 30)
>> +               return 0; /* depth more than 30 not supported */
>> +       memset(prefix, ' ', depth + 1);
>> +       prefix[depth + 1] = '\0';
>> +
>> +       printf(" %s|-> DEV  %p  %s\n", prefix, dev,
>> +               dev->name ? dev->name :  "NO_NAME");
>> +       return 0;
>> +}
>> +
>> +void drvmgr_print_topo(void)
>> +{
>> +       /* Print Bus topology */
>> +       printf(" --- BUS TOPOLOGY ---\n");
>> +       drvmgr_for_each_dev(drvmgr_topo_func, NULL, DRVMGR_FED_DF);
>> +       printf("\n\n");
>> +}
>> +
>> +/* Print the memory usage */
>> +void drvmgr_print_mem(void)
>> +{
>> +       struct rtems_driver_manager *mgr = &drv_mgr;
>> +       struct drvmgr_bus *bus;
>> +       struct drvmgr_dev *dev;
>> +       struct drvmgr_drv *drv;
>> +
>> +       struct drvmgr_bus_res *node;
>> +       struct drvmgr_drv_res *res;
>> +       struct drvmgr_key *key;
>> +
>> +       unsigned int busmem = 0;
>> +       unsigned int devmem = 0;
>> +       unsigned int drvmem = 0;
>> +       unsigned int resmem = 0;
>> +       unsigned int devprivmem = 0;
>> +
>> +       DRVMGR_LOCK_READ();
>> +
>> +       bus = BUS_LIST_HEAD(&mgr->buses[DRVMGR_LEVEL_MAX]);
>> +       while (bus) {
>> +               busmem += sizeof(struct drvmgr_bus);
>> +
>> +               /* Get size of resources on this bus */
>> +               node = bus->reslist;
>> +               while (node) {
>> +                       resmem += sizeof(struct drvmgr_bus_res);
>> +
>> +                       res = node->resource;
>> +                       while (res->keys) {
>> +                               resmem += sizeof(struct drvmgr_drv_res);
>> +
>> +                               key = res->keys;
>> +                               while (key->key_type != KEY_TYPE_NONE) {
>> +                                       resmem += sizeof
>> +                                               (struct drvmgr_key);
>> +                                       key++;
>> +                               }
>> +                               resmem += sizeof(struct drvmgr_key);
>> +                               res++;
>> +                       }
>> +
>> +                       node = node->next;
>> +               }
>> +
>> +               bus = bus->next;
>> +       }
>> +
>> +       drv = DRV_LIST_HEAD(&mgr->drivers);
>> +       while (drv) {
>> +               drvmem += sizeof(struct drvmgr_drv);
>> +               drv = drv->next;
>> +       }
>> +
>> +       dev = DEV_LIST_HEAD(&mgr->devices[DRVMGR_LEVEL_MAX]);
>> +       while (dev) {
>> +               devmem += sizeof(struct drvmgr_dev);
>> +               if (dev->drv && dev->drv->dev_priv_size > 0)
>> +                       devprivmem += dev->drv->dev_priv_size;
>> +               dev = dev->next;
>> +       }
>> +
>> +       DRVMGR_UNLOCK();
>> +
>> +       printf(" --- MEMORY USAGE ---\n");
>> +       printf(" BUS:          %d bytes\n", busmem);
>> +       printf(" DRV:          %d bytes\n", drvmem);
>> +       printf(" DEV:          %d bytes\n", devmem);
>> +       printf(" DEV private:  %d bytes\n", devprivmem);
>> +       printf(" RES:          %d bytes\n", resmem);
>> +       printf(" TOTAL:        %d bytes\n",
>> +                       busmem + drvmem + devmem + devprivmem + resmem);
>> +       printf("\n\n");
>> +}
>> +
>> +/* Print the memory usage */
>> +void drvmgr_summary(void)
>> +{
>> +       struct rtems_driver_manager *mgr = &drv_mgr;
>> +       struct drvmgr_bus *bus;
>> +       struct drvmgr_dev *dev;
>> +       struct drvmgr_drv *drv;
>> +       int i, buscnt = 0, devcnt = 0, drvcnt = 0;
>> +
>> +       printf(" --- SUMMARY ---\n");
>> +
>> +       drv = DRV_LIST_HEAD(&mgr->drivers);
>> +       while (drv) {
>> +               drvcnt++;
>> +               drv = drv->next;
>> +       }
>> +       printf(" NUMBER OF DRIVERS:               %d\n", drvcnt);
>> +
>> +       DRVMGR_LOCK_READ();
>> +
>> +       for (i = 0; i <= DRVMGR_LEVEL_MAX; i++) {
>> +               buscnt = 0;
>> +               bus = BUS_LIST_HEAD(&mgr->buses[i]);
>> +               while (bus) {
>> +                       buscnt++;
>> +                       bus = bus->next;
>> +               }
>> +               if (buscnt > 0) {
>> +                       printf(" NUMBER OF BUSES IN LEVEL[%d]:     %d\n",
>> +                               i, buscnt);
>> +               }
>> +       }
>> +
>> +       for (i = 0; i <= DRVMGR_LEVEL_MAX; i++) {
>> +               devcnt = 0;
>> +               dev = DEV_LIST_HEAD(&mgr->devices[i]);
>> +               while (dev) {
>> +                       devcnt++;
>> +                       dev = dev->next;
>> +               }

>> +               if (devcnt > 0) {
>> +                       printf(" NUMBER OF DEVS IN LEVEL[%d]:      %d\n",
>> +                               i, devcnt);
>> +               }
>> +       }
>> +
>> +       DRVMGR_UNLOCK();
>> +
>> +       printf("\n\n");
>> +}
>> +
>> +static void print_info(void *p, char *str)
>> +{
>> +       printf("  ");
>> +       puts(str);
>> +}
>> +
>> +void drvmgr_info_dev(struct drvmgr_dev *dev, unsigned int options)
>> +{
>> +       if (!dev)
>> +               return;
>> +
>> +       printf(" -- DEVICE %p --\n", dev);
>> +       if (options & OPTION_DEV_GENINFO) {
>> +               printf("  PARENT BUS:  %p\n", dev->parent);
>> +               printf("  NAME:        %s\n", dev->name ? dev->name : "NO_NAME");
>> +               printf("  STATE:       0x%08x\n", dev->state);
>> +               if (dev->bus)
>> +                       printf("  BRIDGE TO:   %p\n", dev->bus);
>> +               printf("  INIT LEVEL:  %d\n", dev->level);
>> +               printf("  ERROR:       %d\n", dev->error);
>> +               printf("  MINOR BUS:   %d\n", dev->minor_bus);
>> +               if (dev->drv) {
>> +                       printf("  MINOR DRV:   %d\n", dev->minor_drv);
>> +                       printf("  DRIVER:      %p (%s)\n", dev->drv,
>> +                               dev->drv->name ? dev->drv->name : "NO_NAME");
>> +                       printf("  PRIVATE:     %p\n", dev->priv);
>> +               }
>> +       }
>> +
>> +       if (options & OPTION_DEV_BUSINFO) {
>> +               printf("  --- DEVICE INFO FROM BUS DRIVER ---\n");
>> +               if (!dev->parent)
>> +                       printf("  !! device has no parent bus !!\n");
>> +               else if (dev->parent->ops->info_dev)
>> +                       dev->parent->ops->info_dev(dev, print_info, NULL);
>> +               else
>> +                       printf("  Bus doesn't implement info_dev func\n");
>> +       }
>> +
>> +       if (options & OPTION_DEV_DRVINFO) {
>> +               if (dev->drv) {
>> +                       printf("  --- DEVICE INFO FROM DEVICE DRIVER ---\n");
>> +                       if (dev->drv->ops->info)
>> +                               dev->drv->ops->info(dev, print_info, NULL, 0, 0);
>> +                       else
>> +                               printf("  Driver doesn't implement info func\n");
>> +               }
>> +       }
>> +}
>> +
>> +static void drvmgr_info_bus_map(struct drvmgr_map_entry *map)
>> +{
>> +       if (map == NULL)
>> +               printf("    Addresses mapped 1:1\n");
>> +       else if (map == DRVMGR_TRANSLATE_NO_BRIDGE)
>> +               printf("    No bridge in this direction\n");
>> +       else {
>> +               while (map->size != 0) {
>> +                       printf("    0x%08lx-0x%08lx => 0x%08lx-0x%08lx  %s\n",
>> +                               (unsigned long)map->from_adr,
>> +                               (unsigned long)(map->from_adr + map->size - 1),
>> +                               (unsigned long)map->to_adr,
>> +                               (unsigned long)(map->to_adr + map->size - 1),
>> +                               map->name ? map->name : "no label");
>> +                       map++;
>> +               }
>> +       }
>> +}
>> +
>> +void drvmgr_info_bus(struct drvmgr_bus *bus, unsigned int options)
>> +{
>> +       struct drvmgr_dev *dev;
>> +
>> +       /* Print Driver */
>> +       printf("-- BUS %p --\n", bus);
>> +       printf("  BUS TYPE:    %d\n", bus->bus_type);
>> +       printf("  DEVICE:      %p (%s)\n", bus->dev,
>> +               bus->dev->name ? bus->dev->name : "NO_NAME");
>> +       printf("  OPS:         %p\n", bus->ops);
>> +       printf("  CHILDREN:    %d devices\n", bus->dev_cnt);
>> +       printf("  LEVEL:       %d\n", bus->level);
>> +       printf("  STATE:       0x%08x\n", bus->state);
>> +       printf("  ERROR:       %d\n", bus->error);
>> +
>> +       /* Print address mappings up- (to parent) and down- (from parent to
>> +        * this bus) stream the bridge of this bus
>> +        */
>> +       printf("  DOWN STREAMS BRIDGE MAPPINGS  (from parent to this bus)\n");
>> +       drvmgr_info_bus_map(bus->maps_down);
>> +       printf("  UP STREAMS BRIDGE MAPPINGS    (from this bus to parent)\n");
>> +       drvmgr_info_bus_map(bus->maps_up);
>> +
>> +       /* Print Devices on this bus? */
>> +       if (options & OPTION_BUS_DEVS) {
>> +               printf("  CHILDREN:\n");
>> +               DRVMGR_LOCK_READ();
>> +               dev = bus->children;
>> +               while (dev) {
>> +                       printf("   |- DEV[%02d]: %p  %s\n", dev->minor_bus,
>> +                               dev, dev->name ? dev->name : "NO_NAME");
>> +                       dev = dev->next_in_bus;
>> +               }
>> +               DRVMGR_UNLOCK();
>> +       }
>> +}
>> +
>> +char *drv_ops_names[DRV_OPS_NUM] = {
>> +       "init[1]:",
>> +       "init[2]:",
>> +       "init[3]:",
>> +       "init[4]:",
>> +       "remove: ",
>> +       "info:   "
>> +};
>> +
>> +void drvmgr_info_drv(struct drvmgr_drv *drv, unsigned int options)
>> +{
>> +       struct drvmgr_dev *dev;
>> +       fun_ptr *ppfunc;
>> +       int i;
>> +
>> +       /* Print Driver */
>> +       printf(" -- DRIVER %p --\n", drv);
>> +       printf("  DRIVER ID:   0x%llx\n", drv->drv_id);
>> +       printf("  NAME:        %s\n", drv->name ? drv->name : "NO_NAME");
>> +       printf("  BUS TYPE:    %d\n", drv->bus_type);
>> +       printf("  OPERATIONS:\n");
>> +       for (i = 0, ppfunc = (fun_ptr *)&drv->ops->init[0]; i<DRV_OPS_NUM; i++)
>> +               printf("   %s    %p\n", drv_ops_names[i], ppfunc[i]);
>> +       printf("  NO. DEVICES: %d\n", drv->dev_cnt);
>> +
>> +       /* Print devices united with this driver? */
>> +       if (options & OPTION_DRV_DEVS) {
>> +               DRVMGR_LOCK_READ();
>> +               dev = drv->dev;
>> +               while (dev) {
>> +                       printf("  DEV[%02d]:     %p  %s\n", dev->minor_drv,
>> +                               dev, dev->name ? dev->name : "NO_NAME");
>> +                       dev = dev->next_in_drv;
>> +               }
>> +               DRVMGR_UNLOCK();
>> +       }
>> +}
>> +
>> +void (*info_obj[3])(void *obj, unsigned int) = {
>> +       /* DRVMGR_OBJ_DRV */ (void (*)(void *, unsigned int))drvmgr_info_drv,
>> +       /* DRVMGR_OBJ_BUS */ (void (*)(void *, unsigned int))drvmgr_info_bus,
>> +       /* DRVMGR_OBJ_DEV */ (void (*)(void *, unsigned int))drvmgr_info_dev,
>> +};
>> +
>> +/* Get information about a device/bus/driver */
>> +void drvmgr_info(void *id, unsigned int options)
>> +{
>> +       int obj_type;
>> +       void (*func)(void *, unsigned int);
>> +
>> +       if (!id)
>> +               return;
>> +       obj_type = *(int *)id;
>> +       if ((obj_type < DRVMGR_OBJ_DRV) || (obj_type > DRVMGR_OBJ_DEV))
>> +               return;
>> +       func = info_obj[obj_type - 1];
>> +       func(id, options);
>> +}
>> +
>> +void drvmgr_info_devs_on_bus(struct drvmgr_bus *bus, unsigned int options)
>> +{
>> +       struct drvmgr_dev *dev;
>> +
>> +       /* Print All Devices on Bus */
>> +       printf("\n\n  -= All Devices on BUS %p =-\n\n", bus);
>> +       dev = bus->children;
>> +       while (dev) {
>> +               drvmgr_info_dev(dev, options);
>> +               puts("");
>> +               dev = dev->next_in_bus;
>> +       }
>> +
>> +       if ((options & OPTION_RECURSIVE) == 0)
>> +               return;
>> +
>> +       /* This device provides a bus, print the bus */
>> +       dev = bus->children;
>> +       while (dev) {
>> +               if (dev->bus)
>> +                       drvmgr_info_devs_on_bus(dev->bus, options);
>> +               dev = dev->next_in_bus;
>> +       }
>> +}
>> +
>> +void drvmgr_info_devs(unsigned int options)
>> +{
>> +       struct rtems_driver_manager *mgr = &drv_mgr;
>> +       struct drvmgr_dev *dev;
>> +
>> +       /* Print device information of all devices and their child devices */
>> +       dev = &mgr->root_dev;
>> +       drvmgr_info_devs_on_bus(dev->bus, options);
>> +       printf("\n\n");
>> +}
>> +
>> +void drvmgr_info_drvs(unsigned int options)
>> +{
>> +       struct rtems_driver_manager *mgr = &drv_mgr;
>> +       struct drvmgr_drv *drv;
>> +
>> +       drv = DRV_LIST_HEAD(&mgr->drivers);
>> +       while (drv) {
>> +               drvmgr_info_drv(drv, options);
>> +               puts("\n");
>> +               drv = drv->next;
>> +       }
>> +}
>> +
>> +void drvmgr_info_buses(unsigned int options)
>> +{
>> +       struct rtems_driver_manager *mgr = &drv_mgr;
>> +       struct drvmgr_bus *bus;
>> +
>> +       bus = BUS_LIST_HEAD(&mgr->buses[DRVMGR_LEVEL_MAX]);
>> +       while (bus) {
>> +               drvmgr_info_bus(bus, options);
>> +               puts("\n");
>> +               bus = bus->next;
>> +       }
>> +}
> It might make senes to provide a plugin for printing. I'm not certain
> what is recommended these days. Is it required to have this printf
> code linked in with drvmgr? (Do we care anyways?)
>
>> diff --git a/cpukit/libdrvmgr/drvmgr_res.c b/cpukit/libdrvmgr/drvmgr_res.c
>> new file mode 100644
>> index 0000000..5f8598b
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_res.c
>> @@ -0,0 +1,102 @@
>> +/* Driver Manager Driver Resource Interface Implementation.
>> + *
>> + * COPYRIGHT (c) 2009.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <string.h>
>> +#include <drvmgr/drvmgr.h>
>> +
>> +/* Find all the resource keys for a device among all bus resources */
>> +int drvmgr_keys_get(struct drvmgr_dev *dev, struct drvmgr_key **keys)
>> +{
>> +       struct drvmgr_bus *bus;
>> +       struct drvmgr_bus_res *node;
>> +       struct drvmgr_drv_res *res;
>> +       uint64_t drv_id;
>> +
>> +       bus = dev->parent;
>> +       if (!bus || !dev->drv)
>> +               return -1;
>> +
>> +       drv_id = dev->drv->drv_id;
>> +
>> +       /* Loop all resource arrays */
>> +       node = bus->reslist;
>> +       while (node) {
>> +               /* Find driver ID in resource array */
>> +               res = &node->resource[0];
>> +               while (res->drv_id) {
>> +                       if (res->drv_id == drv_id) {
>> +                               /* Found resource matching driver, now check
>> +                                * that this resource is for this device.
>> +                                */
>> +                               if (dev->minor_bus == res->minor_bus) {
>> +                                       /* Matching driver and core number */
>> +                                       if (keys)
>> +                                               *keys = res->keys;
>> +                                       return 0;
>> +                               }
>> +                       }
>> +                       res++;
>> +               }
>> +               node = node->next;
>> +       }
>> +       if (keys)
>> +               *keys = NULL;
>> +       return 1;
>> +}
>> +
>> +/* Return key that matches key name */
>> +struct drvmgr_key *drvmgr_key_get(
>> +       struct drvmgr_key *keys,
>> +       char *key_name)
>> +{
>> +       struct drvmgr_key *key;
>> +
>> +       if (!keys)
>> +               return NULL;
>> +
>> +       key = keys;
>> +       while (key->key_type != KEY_TYPE_NONE) {
>> +               if (strcmp(key_name, key->key_name) == 0)
>> +                       return key;
>> +               key++;
>> +       }
>> +       return NULL;
>> +}
>> +
>> +union drvmgr_key_value *drvmgr_key_val_get(
>> +       struct drvmgr_key *keys,
>> +       char *key_name,
>> +       int key_type)
>> +{
>> +       struct drvmgr_key *key_match;
>> +
>> +       key_match = drvmgr_key_get(keys, key_name);
>> +       if (key_match) {
>> +               /* Found key, put pointer to value into */
>> +               if ((key_type == -1) || (key_match->key_type == key_type))
>> +                       return &key_match->key_value;
>> +       }
>> +       return NULL;
>> +}
>> +
>> +union drvmgr_key_value *drvmgr_dev_key_get(
>> +       struct drvmgr_dev *dev,
>> +       char *key_name,
>> +       int key_type)
>> +{
>> +       struct drvmgr_key *keys = NULL;
>> +
>> +       /* Find first entry in key array for the device */
>> +       if (drvmgr_keys_get(dev, &keys))
>> +               return NULL;
>> +
>> +       /* Find a specific key among the device keys */
>> +       return drvmgr_key_val_get(keys, key_name, key_type);
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_rw.c b/cpukit/libdrvmgr/drvmgr_rw.c
>> new file mode 100644
>> index 0000000..6530cdd
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_rw.c
>> @@ -0,0 +1,52 @@
>> +/* Driver Manager Read/Write Interface Implementation.
>> + *
>> + * COPYRIGHT (c) 2009.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <string.h>
>> +#include <drvmgr/drvmgr.h>
>> +
>> +/* Set a range of memory in 128 byte chunks.
>> + * This call will take 128 bytes for buffer on stack
>> + */
>> +void drvmgr_rw_memset(
>> +       void *dstadr,
>> +       int c,
>> +       size_t n,
>> +       void *a,
>> +       drvmgr_wmem_arg wmem
>> +       )
>> +{
>> +       unsigned long long buf[16+1]; /* Extra bytes after data are reserved
>> +                                      * for optimizations by write_mem */
>> +       int txlen;
> could be size_t txlen.
>
>> +       char *adr;
>> +
>> +       if (n <= 0)
>> +               return;
>> +
>> +       if (n > sizeof(unsigned long long)*16)
>> +               txlen = sizeof(unsigned long long)*16;
>> +       else
>> +               txlen = n;
>> +
>> +       memset(buf, c, txlen);
>> +
>> +       adr = dstadr;
>> +       do {
>> +               wmem(adr, (const void *)&buf[0], txlen, a);
>> +               adr += txlen;
>> +               n -= txlen;
>> +
>> +               /* next length to transmitt */
>> +               if (n > 16*sizeof(unsigned long long))
>> +                       txlen = 16*sizeof(unsigned long long);
>> +               else
>> +                       txlen = n;
>> +       } while (n > 0);
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_translate.c b/cpukit/libdrvmgr/drvmgr_translate.c
>> new file mode 100644
>> index 0000000..7febf77
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_translate.c
>> @@ -0,0 +1,149 @@
>> +/* Driver Manager Driver Translate Interface Implementation
>> + *
>> + * COPYRIGHT (c) 2010.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +/*
>> + * Used by device drivers. The functions rely on that the parent bus driver
>> + * has implemented the neccessary operations correctly.
>> + *
>> + * The translate functions are used to translate addresses between buses
>> + * for DMA cores located on a "remote" bus, or for memory-mapped obtaining
>> + * an address that can be used to access an remote bus.
>> + *
>> + * For example, PCI I/O might be memory-mapped at the PCI Host bridge,
>> + * say address 0xfff10000-0xfff1ffff is mapped to the PCI I/O address
>> + * of 0x00000000-0x0000ffff. The PCI Host bridge driver may then set up
>> + * a map so that a driver that get PCI address 0x100 can translate that
>> + * into 0xfff10100.
>> + */
>> +
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +
>> +#include <drvmgr/drvmgr.h>
>> +#include "drvmgr_internal.h"
>> +
>> +unsigned int drvmgr_translate_bus(
>> +       struct drvmgr_bus *from,
>> +       struct drvmgr_bus *to,
>> +       int reverse,
>> +       void *src_address,
>> +       void **dst_address)
>> +{
>> +       struct drvmgr_bus *path[16];
>> +       int dir, levels, i;
>> +       void *dst, *from_adr, *to_adr;
>> +       struct drvmgr_map_entry *map;
>> +       struct drvmgr_bus *bus;
>> +       unsigned int sz;
>> +       struct drvmgr_bus *bus_bot, *bus_top;
>> +
>> +       dst = src_address;
>> +       sz = 0xffffffff;
>> +
>> +       if (from == to) /* no need translating addresses when on same bus */
>> +               goto out;
> prefer to return directly.
>
>> +
>> +       /* Always find translation path from remote bus towards root bus. All
>> +        * buses have root bus has parent at some level
>> +        */
>> +       if (from->depth > to->depth) {
>> +               bus_bot = from;
>> +               bus_top = to;
>> +               dir = 0;
>> +       } else {
>> +               bus_bot = to;
>> +               bus_top = from;
>> +               dir = 1;
>> +       }
>> +       levels = bus_bot->depth - bus_top->depth;
>> +       if (levels >= 16)
>> +               return 0; /* Does not support such a big depth */
>> +       i = 0;
>> +       while ((bus_bot != NULL) && bus_bot != bus_top) {
>> +               if (dir)
>> +                       path[(levels - 1) - i] = bus_bot;
>> +               else
>> +                       path[i] = bus_bot;
>> +               i++;
>> +               bus_bot = bus_bot->dev->parent;
>> +       }
>> +       if (bus_bot == NULL)
>> +               return 0; /* from -> to is not linearly connected */
>> +
>> +       for (i = 0; i < levels; i++) {
>> +               bus = path[i];
>> +
>> +               if ((dir && reverse) || (!dir && !reverse))
>> +                       map = bus->maps_up;
>> +               else
>> +                       map = bus->maps_down;
>> +
>> +               if (map == NULL)
>> +                       continue; /* No translation needed - 1:1 mapping */
>> +
>> +               if (map == DRVMGR_TRANSLATE_NO_BRIDGE) {
>> +                       sz = 0;
>> +                       break; /* No bridge interface in this direction */
>> +               }
>> +
>> +               while (map->size != 0) {
>> +                       if (reverse) {
>> +                               /* Opposite direction */
>> +                               from_adr = map->to_adr;
>> +                               to_adr = map->from_adr;
>> +                       } else {
>> +                               from_adr = map->from_adr;
>> +                               to_adr = map->to_adr;
>> +                       }
>> +
>> +                       if ((dst >= from_adr) &&
>> +                           (dst <= (from_adr + (map->size - 1)))) {
>> +                               if (((from_adr + (map->size - 1)) - dst) < sz)
>> +                                       sz = (from_adr + (map->size - 1)) - dst;
>> +                               dst = (dst - from_adr) + to_adr;
>> +                               break;
>> +                       }
>> +                       map++;
>> +               }
>> +               /* quit if no matching translation information */
>> +               if (map->size == 0) {
>> +                       sz = 0;
>> +                       break;
>> +               }
>> +       }
>> +
>> +out:
>> +       if (dst_address)
>> +               *dst_address = dst;
>> +
>> +       return sz;
>> +}
>> +
>> +unsigned int drvmgr_translate(
>> +       struct drvmgr_dev *dev,
>> +       unsigned int options,
>> +       void *src_address,
>> +       void **dst_address)
>> +{
>> +       struct drvmgr_bus *to, *from;
>> +       int rev = 0;
>> +
>> +       rev = (~options) & 1;
>> +       if ((options == CPUMEM_TO_DMA) || (options == DMAMEM_FROM_CPU)) {
>> +               from = drv_mgr.root_dev.bus;
>> +               to = dev->parent;
>> +       } else { /* CPUMEM_FROM_DMA || DMAMEM_TO_CPU */
>> +               from = dev->parent;
>> +               to = drv_mgr.root_dev.bus;
>> +       }
> If there is no distinguishing between downstream/upstream translations
> why bother to have different options for them? why not just CPU_TO_DMA
> and DMA_TO_CPU?
>
>> +
>> +       return drvmgr_translate_bus(from, to, rev, src_address, dst_address);
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_translate_check.c b/cpukit/libdrvmgr/drvmgr_translate_check.c
>> new file mode 100644
>> index 0000000..c882b88
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_translate_check.c
>> @@ -0,0 +1,35 @@
>> +/* Driver Manager Driver Translate Interface Implementation
>> + *
>> + * COPYRIGHT (c) 2010.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <drvmgr/drvmgr.h>
>> +
>> +/* Calls drvmgr_translate() to translate an address range and check the result,
>> + * a printout is generated if the check fails. See paramters of
>> + * drvmgr_translate().
>> + * If size=0 only the starting address is not checked.
>> + */
>> +int drvmgr_translate_check(
>> +       struct drvmgr_dev *dev,
>> +       unsigned int options,
>> +       void *src_address,
>> +       void **dst_address,
>> +       unsigned int size)
>> +{
>> +       unsigned int max;
>> +
>> +       max = drvmgr_translate(dev, options, src_address, dst_address);
>> +       if (max == 0 || (max < size && (size != 0))) {
>> +               printk(" ### dev %p (%s) failed mapping %p\n",
>> +                       dev, dev->name ? dev->name : "unnamed", src_address);
>> +               return -1;
>> +       }
>> +
>> +       return 0;
>> +}
>> diff --git a/cpukit/libdrvmgr/drvmgr_unregister.c b/cpukit/libdrvmgr/drvmgr_unregister.c
>> new file mode 100644
>> index 0000000..ef5260a
>> --- /dev/null
>> +++ b/cpukit/libdrvmgr/drvmgr_unregister.c
>> @@ -0,0 +1,186 @@
>> +/* Driver Manager Device Unregister (removal) implementation
>> + *
>> + * COPYRIGHT (c) 2011.
>> + * Cobham Gaisler AB.
>> + *
>> + * The license and distribution terms for this file may be
>> + * found in the file LICENSE in this distribution or at
>> + * http://www.rtems.com/license/LICENSE.
>> + */
>> +
>> +#include <stdlib.h>
>> +
>> +#include <drvmgr/drvmgr.h>
>> +#include <drvmgr/drvmgr_list.h>
>> +#include "drvmgr_internal.h"
>> +
>> +/* Unregister all children on a bus.
>> + *
>> + * This function is called from the bus driver, from a "safe" state where
>> + * devices will not be added or removed on this particular bus at this time
>> + */
>> +int drvmgr_children_unregister(struct drvmgr_bus *bus)
>> +{
>> +       int err;
>> +
>> +       while (bus->children != NULL) {
>> +               err = drvmgr_dev_unregister(bus->children);
>> +               if (err != DRVMGR_OK) {
>> +                       /* An error occured */
>> +                       bus->children->error = err;
>> +                       return err;
>> +               }
>> +       }
>> +
>> +       return DRVMGR_OK;
>> +}
>> +
>> +/* Unregister a BUS and all it's devices.
>> + *
>> + * It is up to the bus driver to remove all it's devices, either manually
>> + * one by one calling drvmgr_dev_unregister(), or by letting the driver
>> + * manager unregister all children by calling drvmgr_children_unregister().
>> + */
>> +int drvmgr_bus_unregister(struct drvmgr_bus *bus)
>> +{
>> +       struct rtems_driver_manager *mgr = &drv_mgr;
>> +       struct drvmgr_list *list;
>> +
>> +       if (bus->ops->remove == NULL)
>> +               return DRVMGR_ENOSYS;
>> +
>> +       /* Call Bus driver to clean things up, it must remove all children */
>> +       bus->error = bus->ops->remove(bus);
>> +       if (bus->error != DRVMGR_OK)
>> +               return bus->error;
>> +       /* Check that bus driver has done its job and removed all children */
>> +       if (bus->children != NULL)
>> +               return DRVMGR_FAIL;
>> +       /* Remove References to bus */
>> +       bus->dev->bus = NULL;
>> +
>> +       DRVMGR_LOCK_WRITE();
>> +
>> +       /* Remove bus from bus-list */
>> +       if (bus->state & BUS_STATE_LIST_INACTIVE)
>> +               list = &mgr->buses_inactive;
>> +       else
>> +               list = &mgr->buses[bus->level];
>> +       drvmgr_list_remove(list, bus);
>> +
>> +       DRVMGR_UNLOCK();
>> +
>> +       /* All references to this bus has been removed at this point */
>> +       free(bus);
>> +
>> +       return DRVMGR_OK;
>> +}
>> +
>> +/* Separate Driver and Device from each other */
>> +int drvmgr_dev_drv_separate(struct drvmgr_dev *dev)
> The typical antonym of unite is divide. The alliteration of
> drvmgr_dev_drv_divide is interesting.
>
>> +{
>> +       struct rtems_driver_manager *mgr = &drv_mgr;
>> +       struct drvmgr_dev *subdev, **pprev;
>> +       int rc;
>> +
>> +       /* Remove children if this device exports a bus of devices. All
>> +        * children must be removed first as they depend upon the bus
>> +        * services this bridge provide.
>> +        */
>> +       if (dev->bus) {
>> +               rc = drvmgr_bus_unregister(dev->bus);
>> +               if (rc != DRVMGR_OK)
>> +                       return rc;
>> +       }
>> +
>> +       if (dev->drv == NULL)
>> +               return DRVMGR_OK;
>> +
>> +       /* Remove device by letting assigned driver take care of hardware
>> +        * issues
>> +        */
>> +       if (!dev->drv->ops->remove) {
>> +               /* No remove function is considered severe when someone
>> +                * is trying to remove the device
>> +                */
>> +               return DRVMGR_ENOSYS;
>> +       }
>> +       dev->error = dev->drv->ops->remove(dev);
>> +       if (dev->error != DRVMGR_OK)
>> +               return DRVMGR_FAIL;
>> +
>> +       DRVMGR_LOCK_WRITE();
>> +
>> +       /* Delete device from driver's device list */
>> +       pprev = &dev->drv->dev;
>> +       subdev = dev->drv->dev;
>> +       while (subdev != dev) {
>> +               pprev = &subdev->next_in_drv;
>> +               subdev = subdev->next_in_drv;
>> +       }
>> +       *pprev = subdev->next_in_drv;
>> +       dev->drv->dev_cnt--;
>> +
>> +       /* Move device to inactive list */
>> +       drvmgr_list_remove(&mgr->devices[dev->level], dev);
>> +       dev->level = 0;
>> +       dev->state &= ~(DEV_STATE_UNITED|DEV_STATE_INIT_DONE);
>> +       dev->state |= DEV_STATE_LIST_INACTIVE;
>> +       drvmgr_list_add_tail(&mgr->devices_inactive, dev);
>> +
>> +       DRVMGR_UNLOCK();
>> +
>> +       /* Free Device Driver Private memory if allocated previously by
>> +        * Driver manager.
>> +        */
>> +       if (dev->drv->dev_priv_size && dev->priv) {
>> +               free(dev->priv);
>> +               dev->priv = NULL;
>> +       }
>> +       dev->drv = NULL;
>> +
>> +       return DRVMGR_OK;
>> +}
>> +
>> +/* Unregister device,
>> + *  - let assigned driver handle deletion
>> + *  - remove from device list
>> + *  - remove from driver list
>> + *  - remove from bus list
>> + */
>> +int drvmgr_dev_unregister(struct drvmgr_dev *dev)
>> +{
>> +       struct rtems_driver_manager *mgr = &drv_mgr;
>> +       struct drvmgr_dev *subdev, **pprev;
>> +       int err;
>> +
>> +       /* Separate device from driver, if the device is united with a driver.
>> +        *
>> +        * If this device is a bridge all child buses/devices are also removed.
>> +        */
>> +       err = drvmgr_dev_drv_separate(dev);
>> +       if (err != DRVMGR_OK)
>> +               return err;
>> +
>> +       DRVMGR_LOCK_WRITE();
>> +
>> +       /* Remove it from inactive list */
>> +       drvmgr_list_remove(&mgr->devices_inactive, dev);
>> +
>> +       /* Remove device from parent bus list (no check if dev not in list) */
>> +       pprev = &dev->parent->children;
>> +       subdev = dev->parent->children;
>> +       while (subdev != dev) {
>> +               pprev = &subdev->next_in_bus;
>> +               subdev = subdev->next_in_bus;
>> +       }
>> +       *pprev = subdev->next_in_bus;
>> +       dev->parent->dev_cnt--;
>> +
>> +       DRVMGR_UNLOCK();
>> +
>> +       /* All references to this device has been removed at this point */
>> +       free(dev);
>> +
>> +       return DRVMGR_OK;
>> +}
>> diff --git a/cpukit/preinstall.am b/cpukit/preinstall.am
>> index b125b2f..806e4bc 100644
>> --- a/cpukit/preinstall.am
>> +++ b/cpukit/preinstall.am
>> @@ -514,3 +514,20 @@ $(PROJECT_INCLUDE)/rtems/fsmount.h: libmisc/fsmount/fsmount.h $(PROJECT_INCLUDE)
>>         $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/fsmount.h
>>  PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/fsmount.h
>>
>> +$(PROJECT_INCLUDE)/drvmgr/$(dirstamp):
>> +       @$(MKDIR_P) $(PROJECT_INCLUDE)/drvmgr
>> +       @: > $(PROJECT_INCLUDE)/drvmgr/$(dirstamp)
>> +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/drvmgr/$(dirstamp)
>> +
>> +$(PROJECT_INCLUDE)/drvmgr/drvmgr.h: libdrvmgr/drvmgr.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp)
>> +       $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/drvmgr.h
>> +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/drvmgr.h
>> +
>> +$(PROJECT_INCLUDE)/drvmgr/drvmgr_confdefs.h: libdrvmgr/drvmgr_confdefs.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp)
>> +       $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/drvmgr_confdefs.h
>> +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/drvmgr_confdefs.h
>> +
>> +$(PROJECT_INCLUDE)/drvmgr/drvmgr_list.h: libdrvmgr/drvmgr_list.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp)
>> +       $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/drvmgr_list.h
>> +PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/drvmgr_list.h
>> +
>> diff --git a/cpukit/sapi/include/confdefs.h b/cpukit/sapi/include/confdefs.h
>> index cce961c..1bb630b 100644
>> --- a/cpukit/sapi/include/confdefs.h
>> +++ b/cpukit/sapi/include/confdefs.h
>> @@ -144,6 +144,15 @@ const rtems_libio_helper rtems_fs_init_helper =
>>   */
>>  #define CONFIGURE_LIBIO_POSIX_KEYS 1
>>
>> +/**
>> + *  Driver Manager Configuration
>> + */
>> +#ifdef RTEMS_DRVMGR_STARTUP
>> +  #define CONFIGURE_DRVMGR_SEMAPHORES 1
>> +#else
>> +  #define CONFIGURE_DRVMGR_SEMAPHORES 0
>> +#endif
>> +
>>  #ifdef CONFIGURE_INIT
>>    rtems_libio_t rtems_libio_iops[CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS];
>>
>> @@ -1863,7 +1872,7 @@ const rtems_libio_helper rtems_fs_init_helper =
>>      (CONFIGURE_MAXIMUM_SEMAPHORES + CONFIGURE_LIBIO_SEMAPHORES + \
>>        CONFIGURE_TERMIOS_SEMAPHORES + CONFIGURE_LIBBLOCK_SEMAPHORES + \
>>        CONFIGURE_SEMAPHORES_FOR_FILE_SYSTEMS + \
>> -      CONFIGURE_NETWORKING_SEMAPHORES)
>> +      CONFIGURE_NETWORKING_SEMAPHORES + CONFIGURE_DRVMGR_SEMAPHORES)
>>
>>    #if !defined(RTEMS_SMP) || \
>>      !defined(CONFIGURE_MAXIMUM_MRSP_SEMAPHORES)
>> diff --git a/cpukit/sapi/src/exinit.c b/cpukit/sapi/src/exinit.c
>> index 235ba77..ba71ec4 100644
>> --- a/cpukit/sapi/src/exinit.c
>> +++ b/cpukit/sapi/src/exinit.c
>> @@ -56,6 +56,10 @@
>>  #include <rtems/rtems/rtemsapi.h>
>>  #include <rtems/posix/posixapi.h>
>>
>> +#ifdef RTEMS_DRVMGR_STARTUP
>> +  #include <drvmgr/drvmgr.h>
>> +#endif
>> +
>>  Objects_Information *_Internal_Objects[ OBJECTS_INTERNAL_CLASSES_LAST + 1 ];
>>
>>  void rtems_initialize_data_structures(void)
>> @@ -161,6 +165,9 @@ void rtems_initialize_data_structures(void)
>>
>>  void rtems_initialize_before_drivers(void)
>>  {
>> +  #ifdef RTEMS_DRVMGR_STARTUP
>> +  _DRV_Manager_initialization();
>> +  #endif
>>
>>    #if defined(RTEMS_MULTIPROCESSING)
>>      _MPCI_Create_server();
>> @@ -182,8 +189,64 @@ void rtems_initialize_device_drivers(void)
>>     *  NOTE:  The MPCI may be build upon a device driver.
>>     */
>>
>> +  #ifdef RTEMS_DRVMGR_STARTUP
>> +  /* BSPs has already registered their "root bus" driver in the
>> +   * bsp_predriver hook or so.
>> +   *
>> +   * Init Drivers to Level 1, constraints:
>> +   *   - Interrupts and system clock timer does not work.
>> +   *   - malloc() work, however other memory services may not
>> +   *     have been initialized yet.
>> +   *   - initializes most basic stuff
>> +   *
>> +   * Typical setup in Level 1:
>> +   *   - Find most devices in system, do PCI scan and configuration.
>> +   *   - Reset hardware if needed.
>> +   *   - Install IRQ driver
>> +   *   - Install Timer driver
>> +   *   - Install console driver and debug printk()
>> +   *   - Install extra memory.
>> +   */
>> +  _DRV_Manager_init_level(1);
>> +  bsp_driver_level_hook(1);
>> +  #endif
>> +
>> +  /* Initialize I/O drivers.
>> +   *
>> +   * Driver Manager note:
>> +   * All drivers may not be registered yet. Drivers will dynamically
>> +   * be initialized when registered in level 2,3 and 4.
>> +   */
>>    _IO_Initialize_all_drivers();
>>
>> +  #ifdef RTEMS_DRVMGR_STARTUP
>> +  /* Init Drivers to Level 2, constraints:
>> +   *  - Interrupts can be registered and enabled.
>> +   *  - System Clock is running
>> +   *  - Console may be used.
>> +   *
>> +   * This is typically where drivers are initialized
>> +   * for the first time.
>> +   */
>> +  _DRV_Manager_init_level(2);
>> +  bsp_driver_level_hook(2);
>> +
>> +  /* Init Drivers to Level 3
>> +   *
>> +   * This is typically where normal drivers are initialized
>> +   * for the second time, they may depend on other drivers
>> +   * API inited in level 2
>> +   */
>> +  _DRV_Manager_init_level(3);
>> +  bsp_driver_level_hook(3);
>> +
>> +  /* Init Drivers to Level 4,
>> +   * Init drivers that depend on services initialized in Level 3
>> +   */
>> +  _DRV_Manager_init_level(4);
>> +  bsp_driver_level_hook(4);
>> +  #endif
>> +
>>    #if defined(RTEMS_MULTIPROCESSING)
>>      if ( _System_state_Is_multiprocessing ) {
>>        _MPCI_Initialization();
>> diff --git a/cpukit/wrapup/Makefile.am b/cpukit/wrapup/Makefile.am
>> index c61ab05..7ae7d8a 100644
>> --- a/cpukit/wrapup/Makefile.am
>> +++ b/cpukit/wrapup/Makefile.am
>> @@ -62,10 +62,9 @@ TMP_LIBS += ../libmisc/libutf8proc.a
>>  endif
>>
>>  TMP_LIBS += ../libmisc/libuuid.a
>> -
>>  TMP_LIBS += ../libi2c/libi2c.a
>> -
>>  TMP_LIBS += ../libpci/libpci.a
>> +TMP_LIBS += ../libdrvmgr/libdrvmgr.a
>>
>>  if LIBNETWORKING
>>  TMP_LIBS += ../libnetworking/libnetworking.a
>> --
>> 1.7.0.4
>>
>> _______________________________________________
>> devel mailing list
>> devel at rtems.org
>> http://lists.rtems.org/mailman/listinfo/devel
> _______________________________________________
> devel mailing list
> devel at rtems.org
> http://lists.rtems.org/mailman/listinfo/devel

-- 
Joel Sherrill, Ph.D.             Director of Research & Development
joel.sherrill at OARcorp.com        On-Line Applications Research
Ask me about RTEMS: a free RTOS  Huntsville AL 35805
Support Available                (256) 722-9985




More information about the devel mailing list