628 lines
14 KiB
C
628 lines
14 KiB
C
/* device.c - Device Infrastructure Library */
|
|
|
|
/*
|
|
* Copyright (c) 2005-2009, 2011-2013, 2016 Wind River Systems, Inc.
|
|
*
|
|
* The right to copy, distribute, modify or otherwise make use
|
|
* of this software may be licensed only pursuant to the terms
|
|
* of an applicable Wind River license agreement.
|
|
*/
|
|
|
|
/*
|
|
modification history
|
|
--------------------
|
|
01o,18may16,myt fix issue that fsmNameInstall cannot correctly add a XBD driver
|
|
name of which length is equal to MAX_DEVNAME (VXW6-85215)
|
|
01n,08oct13,dee eliminate unused local function
|
|
01m,28jun13,xwu fix WIND00424201 Coverity issue: remain high impact defect of FS
|
|
01l,24may13,xwu Replace mutex with spinlock for performance (WIND00337587)
|
|
01k,27aug12,clx fix ddDelete memory leak issue. (WIND00365416)
|
|
01j,13jan11,m_y Add devGetByName() (WIND00211541)
|
|
01i,15oct09,h_k cleaned up compiler warnings for LP64.
|
|
01h,29aug06,dee WIND00062547 - fix XBD resource limitation caused by queueing
|
|
problem
|
|
01g,01mar06,dee add unsafe devmap and unmap functions for coredump
|
|
01f,11jul05,rfr Mark the struct device when removal is complete
|
|
01e,04jul05,pcm added comments and sections for doc build
|
|
01d,28apr05,rfr Added setting the dv_dev member in devAttach()
|
|
01c,26apr05,rfr Fixed call to strncpy
|
|
01b,20apr05,rfr Added devName() function
|
|
01a,25mar05,rfr Removed prototype eventing system.
|
|
*/
|
|
|
|
/*
|
|
DESCRIPTION
|
|
This library provides the interface for the device infrastructure.
|
|
|
|
INCLUDE FILES: drv/manager/device.h
|
|
*/
|
|
|
|
/* includes */
|
|
|
|
#include <vxWorks.h>
|
|
#include <semLib.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <intLib.h>
|
|
#include <taskLib.h>
|
|
#include <spinLockLib.h>
|
|
#include <stdio.h>
|
|
#include <drv/manager/device.h>
|
|
|
|
/* defines */
|
|
|
|
#define DEVICE_T_ITEM(d) (uint16_t)(((d) & 0xffff0000) >> 16)
|
|
#define DEVICE_T_GEN(d) ((d) & 0xffff)
|
|
|
|
#define DEVICE_T_MAP(index, d) ((index)->ddh_map[DEVICE_T_ITEM(d)])
|
|
#define ITEM_MAP(index, i) ((index)->ddh_map[(i)])
|
|
|
|
/* DEVICE_T_EXISTS() is nonzero if d is a legitimate device_t in index */
|
|
#define DEVICE_T_EXISTS(index, d) ((index) != NULL &&\
|
|
DEVICE_T_GEN(d) != 0 &&\
|
|
DEVICE_T_ITEM(d) < (index)->ddh_size)
|
|
|
|
/* DEVICE_T_VALID() is nonzero if d is a valid mapping in index */
|
|
#define DEVICE_T_VALID(index, d) (DEVICE_T_MAP((index), (d)).ddm_data != NULL &&\
|
|
DEVICE_T_GEN(d) == DEVICE_T_MAP((index), (d)).ddm_generation)
|
|
|
|
#define NULLINDEX 0xffff /* represents end of list for the free list */
|
|
|
|
/* typedefs */
|
|
|
|
/*
|
|
* struct ddMapRec represents individual mappings from device_t to struct device *
|
|
* Each mapping is specified by an index and a generation number
|
|
*/
|
|
|
|
struct ddMapRec
|
|
{
|
|
uint16_t ddm_generation; /* the generation of this mapping */
|
|
uint16_t ddm_next; /* Index of the next free mapping */
|
|
struct device *ddm_data; /* The Item of this mapping */
|
|
struct device *ddm_cache; /* Cached device for marking on removal complete */
|
|
unsigned ddm_refCount; /* The number of active devMap() */
|
|
};
|
|
|
|
/*
|
|
* struct ddHeader represents the entire mapping of device_t to struct device *
|
|
*/
|
|
|
|
struct ddHeader
|
|
{
|
|
uint16_t ddh_size; /* The number of slots */
|
|
uint16_t ddh_freeh; /* The head of the free list */
|
|
uint16_t ddh_freet; /* The tail of the free list */
|
|
spinlockTask_t ddh_lock; /* The lock of the mappings */
|
|
struct ddMapRec ddh_map[1]; /* First element of the mappings */
|
|
};
|
|
|
|
/* locals */
|
|
|
|
LOCAL struct ddHeader *dit_index = NULL;
|
|
|
|
/* forward declarations */
|
|
|
|
LOCAL void ddFinishRemoval(struct ddHeader *index, uint16_t item);
|
|
|
|
/***************************************************************************
|
|
*
|
|
* ddInit -
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL struct ddHeader *ddInit
|
|
(
|
|
uint16_t Size
|
|
)
|
|
{
|
|
uint16_t i, last;
|
|
struct ddHeader *retVal;
|
|
|
|
if (Size == 0)
|
|
{
|
|
errno = EINVAL;
|
|
return (NULL);
|
|
}
|
|
|
|
/* Allocate the index */
|
|
retVal = malloc (sizeof (*retVal) +
|
|
sizeof (struct ddMapRec) * (uint16_t)(Size - 1));
|
|
|
|
if (!retVal)
|
|
return (NULL);
|
|
|
|
SPIN_LOCK_TASK_INIT(&retVal->ddh_lock, 0);
|
|
|
|
last = (uint16_t)(Size - 1);
|
|
|
|
retVal->ddh_size = Size;
|
|
retVal->ddh_freeh = 0;
|
|
retVal->ddh_freet = last;
|
|
|
|
/* Thread the individual mapping records together */
|
|
|
|
for (i = 0; i <= last; i++)
|
|
{
|
|
retVal->ddh_map[i].ddm_generation = 1;
|
|
retVal->ddh_map[i].ddm_data = NULL;
|
|
retVal->ddh_map[i].ddm_refCount = 0;
|
|
|
|
retVal->ddh_map[i].ddm_next = (uint16_t)(i + 1);
|
|
}
|
|
retVal->ddh_map[last].ddm_next = NULLINDEX;
|
|
|
|
return (retVal);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* ddInsert - allocate a device_t slot and put a struct device * in it
|
|
*
|
|
* This function must be called with the index mutex held. It only allocates
|
|
* the device_t and inserts the device. Modifications to the struct device must
|
|
* happen externally.
|
|
*
|
|
* RETURNS: 0 on success or an errno.h value on failure
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
LOCAL int ddInsert
|
|
(
|
|
struct ddHeader *index,
|
|
struct device *dev,
|
|
device_t *result
|
|
)
|
|
{
|
|
uint16_t item;
|
|
|
|
if (index == NULL)
|
|
return (EINVAL);
|
|
|
|
if (dev == NULL)
|
|
return (EINVAL);
|
|
|
|
/* Get a free slot */
|
|
if (index->ddh_freeh == NULLINDEX)
|
|
{
|
|
/* nothing on the free list */
|
|
return (ENOMEM);
|
|
}
|
|
else
|
|
{
|
|
/* Get the first element of the free list */
|
|
item = index->ddh_freeh;
|
|
/* Link the free list to the next element */
|
|
index->ddh_freeh = index->ddh_map[item].ddm_next;
|
|
|
|
/* enforce empty condition on the other side of the list */
|
|
if (index->ddh_freeh == NULLINDEX)
|
|
index->ddh_freet = NULLINDEX;
|
|
}
|
|
|
|
/* item is the index of the new slot */
|
|
index->ddh_map[item].ddm_data = dev;
|
|
index->ddh_map[item].ddm_refCount = 0;
|
|
/* Make next point nowhere */
|
|
index->ddh_map[item].ddm_next = NULLINDEX;
|
|
/* generation stays the same - it is incremented on removal */
|
|
|
|
/* if result is NULL we're gonna lose this mapping */
|
|
if (result)
|
|
{
|
|
/* Roll up the device_t */
|
|
*result = (device_t)((item << 16) |
|
|
index->ddh_map[item].ddm_generation);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* ddRemove - remove a mapping from device_t to struct device *
|
|
*
|
|
* This function must be called with the index mutex held.
|
|
* This function only removes the mapping, it does not modify the contents of the
|
|
* struct device.
|
|
* If this function fails, it will leave the map unchanged.
|
|
*
|
|
* RETURNS: 0 on success or a value from errno.h on failure.
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL int ddRemove
|
|
(
|
|
struct ddHeader *index,
|
|
device_t dev
|
|
)
|
|
{
|
|
uint16_t item;
|
|
|
|
if (!DEVICE_T_EXISTS(index, dev))
|
|
return (EINVAL);
|
|
|
|
if (!DEVICE_T_VALID(index, dev))
|
|
return (ENOENT);
|
|
|
|
/* If we got here, then dev refers to a valid mapping */
|
|
item = DEVICE_T_ITEM(dev);
|
|
|
|
/* Cache the data so we can mark it when we're done */
|
|
ITEM_MAP(index, item).ddm_cache = ITEM_MAP(index, item).ddm_data;
|
|
|
|
/* Set the data to NULL - this effects the unmapping */
|
|
ITEM_MAP(index, item).ddm_data = NULL;
|
|
|
|
/*
|
|
* Now we test to see whether or not we complete the removal here. If the
|
|
* refCount is zero, the device_t is not in use, and we can undo it here.
|
|
* If not, then we must wait and let ddUnmap() do the final removal.
|
|
*/
|
|
|
|
if (ITEM_MAP(index, item).ddm_refCount == 0)
|
|
{
|
|
ddFinishRemoval(index, item);
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* ddFinishRemoval -
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL void ddFinishRemoval
|
|
(
|
|
struct ddHeader *index,
|
|
uint16_t item
|
|
)
|
|
{
|
|
uint16_t tail;
|
|
struct device *dev;
|
|
|
|
/* Get the cached dev out so that we can mark it */
|
|
dev = ITEM_MAP(index, item).ddm_cache;
|
|
|
|
/* We assume that ddh_mutex is held */
|
|
ITEM_MAP(index, item).ddm_generation ++;
|
|
|
|
/* get the tail of the free list */
|
|
tail = index->ddh_freet;
|
|
|
|
/* if its a valid item, link us into the free list */
|
|
if (tail != NULLINDEX)
|
|
{
|
|
ITEM_MAP(index, tail).ddm_next = item;
|
|
index->ddh_freet = item;
|
|
}
|
|
/* otherwise the list is empty - set head and tail */
|
|
else
|
|
{
|
|
index->ddh_freeh = index->ddh_freet = item;
|
|
}
|
|
|
|
/* terminate the list */
|
|
ITEM_MAP(index, item).ddm_next = NULLINDEX;
|
|
|
|
/* mark the device as no longer mapped */
|
|
if (dev)
|
|
dev->dv_dev = NULLDEV;
|
|
|
|
/* We're done */
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* ddMap -
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL struct device *ddMap
|
|
(
|
|
struct ddHeader *index,
|
|
device_t dev
|
|
)
|
|
{
|
|
uint16_t item;
|
|
|
|
if (!DEVICE_T_EXISTS(index, dev))
|
|
{
|
|
errno = EINVAL;
|
|
return (NULL);
|
|
}
|
|
|
|
if (!DEVICE_T_VALID(index, dev))
|
|
{
|
|
errno = ENOENT;
|
|
return (NULL);
|
|
}
|
|
|
|
item = DEVICE_T_ITEM(dev);
|
|
|
|
/* We have a good device_t, increment refCount */
|
|
ITEM_MAP(index, item).ddm_refCount ++;
|
|
|
|
/* We're done */
|
|
return (ITEM_MAP(index, item).ddm_data);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* ddUnmap -
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL int ddUnmap
|
|
(
|
|
struct ddHeader *index,
|
|
struct device *dev
|
|
)
|
|
{
|
|
device_t d;
|
|
uint16_t item;
|
|
|
|
if (dev == NULL)
|
|
return (EINVAL);
|
|
|
|
d = dev->dv_dev;
|
|
|
|
if (!DEVICE_T_EXISTS(index, d))
|
|
return (EINVAL);
|
|
|
|
item = DEVICE_T_ITEM(d);
|
|
|
|
if (ITEM_MAP(index, item).ddm_generation != DEVICE_T_GEN(d))
|
|
return (ENOENT);
|
|
|
|
/* Check the Count */
|
|
if (ITEM_MAP(index, item).ddm_refCount == 0)
|
|
return (EACCES);
|
|
|
|
/* Decrement the count */
|
|
ITEM_MAP(index, item).ddm_refCount--;
|
|
|
|
/* Check to see if we have a pending delete */
|
|
if (!ITEM_MAP(index, item).ddm_refCount && !ITEM_MAP(index, item).ddm_data)
|
|
ddFinishRemoval(index, item);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* devInit - initialize the device manager
|
|
*
|
|
* This routine initializes the device manager for <ndevs> devices.
|
|
*
|
|
* RETURNS: OK upon success, ERROR otherwise
|
|
*
|
|
* ERRNO: EINVAL
|
|
*/
|
|
|
|
STATUS devInit
|
|
(
|
|
uint16_t ndevs /* number of devices */
|
|
)
|
|
{
|
|
/* Initialize the device index */
|
|
dit_index = ddInit (ndevs);
|
|
if (dit_index == NULL)
|
|
return (ERROR);
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* devAttach - attach a device
|
|
*
|
|
* RETURNS: 0 upon success, non-zero otherwise
|
|
*/
|
|
|
|
int devAttach
|
|
(
|
|
struct device *dev,
|
|
const char *name,
|
|
int type,
|
|
device_t *result
|
|
)
|
|
{
|
|
int error = 0;
|
|
|
|
/* Load dev with the appropriate values */
|
|
strncpy (dev->dv_xname, name, MAX_DEVNAME);
|
|
dev->dv_xname[MAX_DEVNAME] = '\0';
|
|
dev->dv_type = type;
|
|
|
|
SPIN_LOCK_TASK_TAKE(&dit_index->ddh_lock);
|
|
error = ddInsert (dit_index, dev, &dev->dv_dev);
|
|
SPIN_LOCK_TASK_GIVE(&dit_index->ddh_lock);
|
|
|
|
if (result != NULL)
|
|
*result = dev->dv_dev;
|
|
|
|
return (error);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* devDetach - detach a device
|
|
*
|
|
* RETURNS: 0 upon success, non-zero otherwise
|
|
*/
|
|
|
|
STATUS devDetach
|
|
(
|
|
struct device *dev /* pointer to device to detach */
|
|
)
|
|
{
|
|
int error;
|
|
|
|
SPIN_LOCK_TASK_TAKE(&dit_index->ddh_lock);
|
|
error = ddRemove (dit_index, dev->dv_dev);
|
|
SPIN_LOCK_TASK_GIVE(&dit_index->ddh_lock);
|
|
return (error);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* devMap - map a device
|
|
*
|
|
* RETURNS: pointer to device upon success, NULL otherwise
|
|
*/
|
|
|
|
struct device *devMap
|
|
(
|
|
device_t dev /* device to map */
|
|
)
|
|
{
|
|
struct device *retVal;
|
|
|
|
SPIN_LOCK_TASK_TAKE(&dit_index->ddh_lock);
|
|
retVal = ddMap (dit_index, dev);
|
|
SPIN_LOCK_TASK_GIVE(&dit_index->ddh_lock);
|
|
|
|
return (retVal);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* devMapUnsafe - map a device unconditionally
|
|
*
|
|
* RETURNS: pointer to device upon success, NULL otherwise
|
|
*/
|
|
|
|
struct device *devMapUnsafe
|
|
(
|
|
device_t dev /* device to map */
|
|
)
|
|
{
|
|
struct device *retVal;
|
|
|
|
retVal = ddMap (dit_index, dev);
|
|
|
|
return (retVal);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* devUnmap - unmap a device
|
|
*
|
|
* RETURNS: 0
|
|
*/
|
|
|
|
int devUnmap
|
|
(
|
|
struct device *dev /* pointer to device to unmap */
|
|
)
|
|
{
|
|
SPIN_LOCK_TASK_TAKE(&dit_index->ddh_lock);
|
|
ddUnmap (dit_index, dev);
|
|
SPIN_LOCK_TASK_GIVE(&dit_index->ddh_lock);
|
|
return (0);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* devUnmapUnsafe - unmap a device unconditionally
|
|
*
|
|
* RETURNS: 0
|
|
*/
|
|
|
|
int devUnmapUnsafe
|
|
(
|
|
struct device *dev /* pointer to device to unmap */
|
|
)
|
|
{
|
|
ddUnmap (dit_index, dev);
|
|
return (0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* devName - name a device
|
|
*
|
|
* RETURNS: OK upon success, ERROR otherwise
|
|
*/
|
|
|
|
STATUS devName
|
|
(
|
|
device_t dev, /* device to name */
|
|
devname_t name /* name to assign */
|
|
)
|
|
{
|
|
struct device *d;
|
|
STATUS retVal;
|
|
|
|
d = devMap (dev);
|
|
if (d != NULL)
|
|
{
|
|
strncpy (name, d->dv_xname, sizeof(devname_t));
|
|
devUnmap (d);
|
|
retVal = OK;
|
|
}
|
|
else
|
|
{
|
|
retVal = ERROR;
|
|
}
|
|
|
|
return (retVal);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* devGetByName - get a device handle by name
|
|
*
|
|
* RETURNS: device handle, NULLDEV otherwise
|
|
*/
|
|
|
|
device_t devGetByName
|
|
(
|
|
char *deviceName
|
|
)
|
|
{
|
|
int i;
|
|
device_t index;
|
|
struct device *pDev = NULL;
|
|
|
|
SPIN_LOCK_TASK_TAKE(&dit_index->ddh_lock);
|
|
|
|
for (i = 0; i < dit_index->ddh_size; i++)
|
|
{
|
|
index = (device_t)((i << 16) | dit_index->ddh_map[i].ddm_generation);
|
|
|
|
if ((pDev = ddMap (dit_index, index)) != NULL)
|
|
{
|
|
if (strcmp(deviceName, pDev->dv_xname) == 0)
|
|
{
|
|
|
|
ddUnmap(dit_index, pDev);
|
|
|
|
SPIN_LOCK_TASK_GIVE(&dit_index->ddh_lock);
|
|
|
|
return index;
|
|
}
|
|
|
|
ddUnmap(dit_index, pDev);
|
|
}
|
|
}
|
|
|
|
SPIN_LOCK_TASK_GIVE(&dit_index->ddh_lock);
|
|
|
|
return NULLDEV;
|
|
}
|
|
|