7c14e54458
devfs is long dead and burried, don't check for it, as it doesn't make any sense to do so. Cc: Daniel Krueger <daniel.krueger@systec-electronic.com> Cc: Ronald Sieber <Ronald.Sieber@systec-electronic.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
1174 lines
34 KiB
C
1174 lines
34 KiB
C
/****************************************************************************
|
|
|
|
(c) SYSTEC electronic GmbH, D-07973 Greiz, August-Bebel-Str. 29
|
|
www.systec-electronic.com
|
|
|
|
Project: openPOWERLINK
|
|
|
|
Description: Linux kernel module as wrapper of EPL API layer,
|
|
i.e. counterpart to a Linux application
|
|
|
|
License:
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
3. Neither the name of SYSTEC electronic GmbH nor the names of its
|
|
contributors may be used to endorse or promote products derived
|
|
from this software without prior written permission. For written
|
|
permission, please contact info@systec-electronic.com.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
Severability Clause:
|
|
|
|
If a provision of this License is or becomes illegal, invalid or
|
|
unenforceable in any jurisdiction, that shall not affect:
|
|
1. the validity or enforceability in that jurisdiction of any other
|
|
provision of this License; or
|
|
2. the validity or enforceability in other jurisdictions of that or
|
|
any other provision of this License.
|
|
|
|
-------------------------------------------------------------------------
|
|
|
|
$RCSfile: EplApiLinuxKernel.c,v $
|
|
|
|
$Author: D.Krueger $
|
|
|
|
$Revision: 1.9 $ $Date: 2008/11/21 09:00:38 $
|
|
|
|
$State: Exp $
|
|
|
|
Build Environment:
|
|
GNU-Compiler for m68k
|
|
|
|
-------------------------------------------------------------------------
|
|
|
|
Revision History:
|
|
|
|
2006/10/11 d.k.: Initial Version
|
|
2008/04/10 m.u.: Changed to new char driver init
|
|
|
|
****************************************************************************/
|
|
|
|
// kernel modul and driver
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/types.h>
|
|
|
|
//#include <linux/module.h>
|
|
//#include <linux/kernel.h>
|
|
//#include <linux/init.h>
|
|
//#include <linux/errno.h>
|
|
|
|
// scheduling
|
|
#include <linux/sched.h>
|
|
|
|
// memory access
|
|
#include <asm/uaccess.h>
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include "Epl.h"
|
|
#include "EplApiLinux.h"
|
|
//#include "kernel/EplPdokCal.h"
|
|
#include "proc_fs.h"
|
|
|
|
|
|
/***************************************************************************/
|
|
/* */
|
|
/* */
|
|
/* G L O B A L D E F I N I T I O N S */
|
|
/* */
|
|
/* */
|
|
/***************************************************************************/
|
|
|
|
// Metainformation
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
#ifdef MODULE_AUTHOR
|
|
MODULE_AUTHOR("Daniel.Krueger@SYSTEC-electronic.com");
|
|
MODULE_DESCRIPTION("EPL API driver");
|
|
#endif
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Configuration
|
|
//---------------------------------------------------------------------------
|
|
|
|
#define EPLLIN_DRV_NAME "systec_epl" // used for <register_chrdev>
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Constant definitions
|
|
//---------------------------------------------------------------------------
|
|
|
|
// TracePoint support for realtime-debugging
|
|
#ifdef _DBG_TRACE_POINTS_
|
|
void TgtDbgSignalTracePoint(u8 bTracePointNumber_p);
|
|
#define TGT_DBG_SIGNAL_TRACE_POINT(p) TgtDbgSignalTracePoint(p)
|
|
#else
|
|
#define TGT_DBG_SIGNAL_TRACE_POINT(p)
|
|
#endif
|
|
|
|
#define EVENT_STATE_INIT 0
|
|
#define EVENT_STATE_IOCTL 1 // ioctl entered and ready to receive EPL event
|
|
#define EVENT_STATE_READY 2 // EPL event can be forwarded to user application
|
|
#define EVENT_STATE_TERM 3 // terminate processing
|
|
|
|
#define EPL_STATE_NOTOPEN 0
|
|
#define EPL_STATE_NOTINIT 1
|
|
#define EPL_STATE_RUNNING 2
|
|
#define EPL_STATE_SHUTDOWN 3
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Global variables
|
|
//---------------------------------------------------------------------------
|
|
|
|
// device number (major and minor)
|
|
static dev_t nDevNum_g;
|
|
static struct cdev *pEpl_cdev_g;
|
|
|
|
static volatile unsigned int uiEplState_g = EPL_STATE_NOTOPEN;
|
|
|
|
static struct semaphore SemaphoreCbEvent_g; // semaphore for EplLinCbEvent
|
|
static wait_queue_head_t WaitQueueCbEvent_g; // wait queue EplLinCbEvent
|
|
static wait_queue_head_t WaitQueueProcess_g; // wait queue for EplApiProcess (user process)
|
|
static wait_queue_head_t WaitQueueRelease_g; // wait queue for EplLinRelease
|
|
static atomic_t AtomicEventState_g = ATOMIC_INIT(EVENT_STATE_INIT);
|
|
static tEplApiEventType EventType_g; // event type (enum)
|
|
static tEplApiEventArg *pEventArg_g; // event argument (union)
|
|
static tEplKernel RetCbEvent_g; // return code from event callback function
|
|
static wait_queue_head_t WaitQueueCbSync_g; // wait queue EplLinCbSync
|
|
static wait_queue_head_t WaitQueuePI_In_g; // wait queue for EplApiProcessImageExchangeIn (user process)
|
|
static atomic_t AtomicSyncState_g = ATOMIC_INIT(EVENT_STATE_INIT);
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Local types
|
|
//---------------------------------------------------------------------------
|
|
|
|
typedef struct {
|
|
void *m_pUserArg;
|
|
void *m_pData;
|
|
|
|
} tEplLinSdoBufHeader;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Local variables
|
|
//---------------------------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Prototypes of internal functions
|
|
//---------------------------------------------------------------------------
|
|
|
|
tEplKernel EplLinCbEvent(tEplApiEventType EventType_p, // IN: event type (enum)
|
|
tEplApiEventArg *pEventArg_p, // IN: event argument (union)
|
|
void *pUserArg_p);
|
|
|
|
tEplKernel EplLinCbSync(void);
|
|
|
|
static int __init EplLinInit(void);
|
|
static void __exit EplLinExit(void);
|
|
|
|
static int EplLinOpen(struct inode *pDeviceFile_p, struct file *pInstance_p);
|
|
static int EplLinRelease(struct inode *pDeviceFile_p, struct file *pInstance_p);
|
|
static ssize_t EplLinRead(struct file *pInstance_p, char *pDstBuff_p,
|
|
size_t BuffSize_p, loff_t * pFileOffs_p);
|
|
static ssize_t EplLinWrite(struct file *pInstance_p, const char *pSrcBuff_p,
|
|
size_t BuffSize_p, loff_t * pFileOffs_p);
|
|
static int EplLinIoctl(struct inode *pDeviceFile_p, struct file *pInstance_p,
|
|
unsigned int uiIoctlCmd_p, unsigned long ulArg_p);
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Kernel Module specific Data Structures
|
|
//---------------------------------------------------------------------------
|
|
|
|
module_init(EplLinInit);
|
|
module_exit(EplLinExit);
|
|
|
|
static struct file_operations EplLinFileOps_g = {
|
|
.owner = THIS_MODULE,
|
|
.open = EplLinOpen,
|
|
.release = EplLinRelease,
|
|
.read = EplLinRead,
|
|
.write = EplLinWrite,
|
|
.ioctl = EplLinIoctl,
|
|
|
|
};
|
|
|
|
//=========================================================================//
|
|
// //
|
|
// P U B L I C F U N C T I O N S //
|
|
// //
|
|
//=========================================================================//
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Initailize Driver
|
|
//---------------------------------------------------------------------------
|
|
// -> insmod driver
|
|
//---------------------------------------------------------------------------
|
|
|
|
static int __init EplLinInit(void)
|
|
{
|
|
|
|
tEplKernel EplRet;
|
|
int iErr;
|
|
int iRet;
|
|
|
|
TRACE0("EPL: + EplLinInit...\n");
|
|
TRACE2("EPL: Driver build: %s / %s\n", __DATE__, __TIME__);
|
|
|
|
iRet = 0;
|
|
|
|
// initialize global variables
|
|
atomic_set(&AtomicEventState_g, EVENT_STATE_INIT);
|
|
sema_init(&SemaphoreCbEvent_g, 1);
|
|
init_waitqueue_head(&WaitQueueCbEvent_g);
|
|
init_waitqueue_head(&WaitQueueProcess_g);
|
|
init_waitqueue_head(&WaitQueueRelease_g);
|
|
|
|
// register character device handler
|
|
// only one Minor required
|
|
TRACE2("EPL: Installing Driver '%s', Version %s...\n",
|
|
EPLLIN_DRV_NAME, EPL_PRODUCT_VERSION);
|
|
iRet = alloc_chrdev_region(&nDevNum_g, 0, 1, EPLLIN_DRV_NAME);
|
|
if (iRet == 0) {
|
|
TRACE2
|
|
("EPL: Driver '%s' installed successful, assigned MajorNumber=%d\n",
|
|
EPLLIN_DRV_NAME, MAJOR(nDevNum_g));
|
|
} else {
|
|
TRACE1
|
|
("EPL: ERROR: Driver '%s' is unable to get a free MajorNumber!\n",
|
|
EPLLIN_DRV_NAME);
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
// register cdev structure
|
|
pEpl_cdev_g = cdev_alloc();
|
|
pEpl_cdev_g->ops = &EplLinFileOps_g;
|
|
pEpl_cdev_g->owner = THIS_MODULE;
|
|
iErr = cdev_add(pEpl_cdev_g, nDevNum_g, 1);
|
|
if (iErr) {
|
|
TRACE2("EPL: ERROR %d: Driver '%s' could not be added!\n",
|
|
iErr, EPLLIN_DRV_NAME);
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
// create device node in PROCFS
|
|
EplRet = EplLinProcInit();
|
|
if (EplRet != kEplSuccessful) {
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
TRACE1("EPL: - EplLinInit (iRet=%d)\n", iRet);
|
|
return (iRet);
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Remove Driver
|
|
//---------------------------------------------------------------------------
|
|
// -> rmmod driver
|
|
//---------------------------------------------------------------------------
|
|
|
|
static void __exit EplLinExit(void)
|
|
{
|
|
|
|
tEplKernel EplRet;
|
|
|
|
// delete instance for all modules
|
|
// EplRet = EplApiShutdown();
|
|
// printk("EplApiShutdown(): 0x%X\n", EplRet);
|
|
|
|
// deinitialize proc fs
|
|
EplRet = EplLinProcFree();
|
|
printk("EplLinProcFree(): 0x%X\n", EplRet);
|
|
|
|
TRACE0("EPL: + EplLinExit...\n");
|
|
|
|
// remove cdev structure
|
|
cdev_del(pEpl_cdev_g);
|
|
|
|
// unregister character device handler
|
|
unregister_chrdev_region(nDevNum_g, 1);
|
|
|
|
TRACE1("EPL: Driver '%s' removed.\n", EPLLIN_DRV_NAME);
|
|
|
|
TRACE0("EPL: - EplLinExit\n");
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Open Driver
|
|
//---------------------------------------------------------------------------
|
|
// -> open("/dev/driver", O_RDWR)...
|
|
//---------------------------------------------------------------------------
|
|
|
|
static int EplLinOpen(struct inode *pDeviceFile_p, // information about the device to open
|
|
struct file *pInstance_p) // information about driver instance
|
|
{
|
|
|
|
int iRet;
|
|
|
|
TRACE0("EPL: + EplLinOpen...\n");
|
|
|
|
if (uiEplState_g != EPL_STATE_NOTOPEN) { // stack already initialized
|
|
iRet = -EALREADY;
|
|
} else {
|
|
atomic_set(&AtomicEventState_g, EVENT_STATE_INIT);
|
|
sema_init(&SemaphoreCbEvent_g, 1);
|
|
init_waitqueue_head(&WaitQueueCbEvent_g);
|
|
init_waitqueue_head(&WaitQueueProcess_g);
|
|
init_waitqueue_head(&WaitQueueRelease_g);
|
|
atomic_set(&AtomicSyncState_g, EVENT_STATE_INIT);
|
|
init_waitqueue_head(&WaitQueueCbSync_g);
|
|
init_waitqueue_head(&WaitQueuePI_In_g);
|
|
|
|
uiEplState_g = EPL_STATE_NOTINIT;
|
|
iRet = 0;
|
|
}
|
|
|
|
TRACE1("EPL: - EplLinOpen (iRet=%d)\n", iRet);
|
|
return (iRet);
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Close Driver
|
|
//---------------------------------------------------------------------------
|
|
// -> close(device)...
|
|
//---------------------------------------------------------------------------
|
|
|
|
static int EplLinRelease(struct inode *pDeviceFile_p, // information about the device to open
|
|
struct file *pInstance_p) // information about driver instance
|
|
{
|
|
|
|
tEplKernel EplRet = kEplSuccessful;
|
|
int iRet;
|
|
|
|
TRACE0("EPL: + EplLinRelease...\n");
|
|
|
|
if (uiEplState_g != EPL_STATE_NOTINIT) {
|
|
// pass control to sync kernel thread, but signal termination
|
|
atomic_set(&AtomicSyncState_g, EVENT_STATE_TERM);
|
|
wake_up_interruptible(&WaitQueueCbSync_g);
|
|
wake_up_interruptible(&WaitQueuePI_In_g);
|
|
|
|
// pass control to event queue kernel thread
|
|
atomic_set(&AtomicEventState_g, EVENT_STATE_TERM);
|
|
wake_up_interruptible(&WaitQueueCbEvent_g);
|
|
|
|
if (uiEplState_g == EPL_STATE_RUNNING) { // post NmtEventSwitchOff
|
|
EplRet = EplApiExecNmtCommand(kEplNmtEventSwitchOff);
|
|
|
|
}
|
|
|
|
if (EplRet == kEplSuccessful) {
|
|
TRACE0("EPL: waiting for NMT_GS_OFF\n");
|
|
wait_event_interruptible(WaitQueueRelease_g,
|
|
(uiEplState_g ==
|
|
EPL_STATE_SHUTDOWN));
|
|
} else { // post NmtEventSwitchOff failed
|
|
TRACE0("EPL: event post failed\n");
|
|
}
|
|
|
|
// $$$ d.k.: What if waiting was interrupted by signal?
|
|
|
|
TRACE0("EPL: call EplApiShutdown()\n");
|
|
// EPL stack can be safely shut down
|
|
// delete instance for all EPL modules
|
|
EplRet = EplApiShutdown();
|
|
printk("EplApiShutdown(): 0x%X\n", EplRet);
|
|
}
|
|
|
|
uiEplState_g = EPL_STATE_NOTOPEN;
|
|
iRet = 0;
|
|
|
|
TRACE1("EPL: - EplLinRelease (iRet=%d)\n", iRet);
|
|
return (iRet);
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Read Data from Driver
|
|
//---------------------------------------------------------------------------
|
|
// -> read(...)
|
|
//---------------------------------------------------------------------------
|
|
|
|
static ssize_t EplLinRead(struct file *pInstance_p, // information about driver instance
|
|
char *pDstBuff_p, // address of buffer to fill with data
|
|
size_t BuffSize_p, // length of the buffer
|
|
loff_t * pFileOffs_p) // offset in the file
|
|
{
|
|
|
|
int iRet;
|
|
|
|
TRACE0("EPL: + EplLinRead...\n");
|
|
|
|
TRACE0("EPL: Sorry, this operation isn't supported.\n");
|
|
iRet = -EINVAL;
|
|
|
|
TRACE1("EPL: - EplLinRead (iRet=%d)\n", iRet);
|
|
return (iRet);
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Write Data to Driver
|
|
//---------------------------------------------------------------------------
|
|
// -> write(...)
|
|
//---------------------------------------------------------------------------
|
|
|
|
static ssize_t EplLinWrite(struct file *pInstance_p, // information about driver instance
|
|
const char *pSrcBuff_p, // address of buffer to get data from
|
|
size_t BuffSize_p, // length of the buffer
|
|
loff_t * pFileOffs_p) // offset in the file
|
|
{
|
|
|
|
int iRet;
|
|
|
|
TRACE0("EPL: + EplLinWrite...\n");
|
|
|
|
TRACE0("EPL: Sorry, this operation isn't supported.\n");
|
|
iRet = -EINVAL;
|
|
|
|
TRACE1("EPL: - EplLinWrite (iRet=%d)\n", iRet);
|
|
return (iRet);
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Generic Access to Driver
|
|
//---------------------------------------------------------------------------
|
|
// -> ioctl(...)
|
|
//---------------------------------------------------------------------------
|
|
|
|
static int EplLinIoctl(struct inode *pDeviceFile_p, // information about the device to open
|
|
struct file *pInstance_p, // information about driver instance
|
|
unsigned int uiIoctlCmd_p, // Ioctl command to execute
|
|
unsigned long ulArg_p) // Ioctl command specific argument/parameter
|
|
{
|
|
|
|
tEplKernel EplRet;
|
|
int iErr;
|
|
int iRet;
|
|
|
|
// TRACE1("EPL: + EplLinIoctl (uiIoctlCmd_p=%d)...\n", uiIoctlCmd_p);
|
|
|
|
iRet = -EINVAL;
|
|
|
|
switch (uiIoctlCmd_p) {
|
|
// ----------------------------------------------------------
|
|
case EPLLIN_CMD_INITIALIZE:
|
|
{
|
|
tEplApiInitParam EplApiInitParam;
|
|
|
|
iErr =
|
|
copy_from_user(&EplApiInitParam,
|
|
(const void *)ulArg_p,
|
|
sizeof(EplApiInitParam));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
EplApiInitParam.m_pfnCbEvent = EplLinCbEvent;
|
|
EplApiInitParam.m_pfnCbSync = EplLinCbSync;
|
|
|
|
EplRet = EplApiInitialize(&EplApiInitParam);
|
|
|
|
uiEplState_g = EPL_STATE_RUNNING;
|
|
|
|
iRet = (int)EplRet;
|
|
break;
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
case EPLLIN_CMD_SHUTDOWN:
|
|
{ // shutdown the threads
|
|
|
|
// pass control to sync kernel thread, but signal termination
|
|
atomic_set(&AtomicSyncState_g, EVENT_STATE_TERM);
|
|
wake_up_interruptible(&WaitQueueCbSync_g);
|
|
wake_up_interruptible(&WaitQueuePI_In_g);
|
|
|
|
// pass control to event queue kernel thread
|
|
atomic_set(&AtomicEventState_g, EVENT_STATE_TERM);
|
|
wake_up_interruptible(&WaitQueueCbEvent_g);
|
|
|
|
if (uiEplState_g == EPL_STATE_RUNNING) { // post NmtEventSwitchOff
|
|
EplRet =
|
|
EplApiExecNmtCommand(kEplNmtEventSwitchOff);
|
|
|
|
}
|
|
|
|
iRet = 0;
|
|
break;
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
case EPLLIN_CMD_READ_LOCAL_OBJECT:
|
|
{
|
|
tEplLinLocalObject LocalObject;
|
|
void *pData;
|
|
|
|
iErr =
|
|
copy_from_user(&LocalObject, (const void *)ulArg_p,
|
|
sizeof(LocalObject));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
if ((LocalObject.m_pData == NULL)
|
|
|| (LocalObject.m_uiSize == 0)) {
|
|
iRet = (int)kEplApiInvalidParam;
|
|
goto Exit;
|
|
}
|
|
|
|
pData = vmalloc(LocalObject.m_uiSize);
|
|
if (pData == NULL) { // no memory available
|
|
iRet = -ENOMEM;
|
|
goto Exit;
|
|
}
|
|
|
|
EplRet =
|
|
EplApiReadLocalObject(LocalObject.m_uiIndex,
|
|
LocalObject.m_uiSubindex,
|
|
pData, &LocalObject.m_uiSize);
|
|
|
|
if (EplRet == kEplSuccessful) {
|
|
iErr =
|
|
copy_to_user(LocalObject.m_pData, pData,
|
|
LocalObject.m_uiSize);
|
|
|
|
vfree(pData);
|
|
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
// return actual size (LocalObject.m_uiSize)
|
|
iErr = put_user(LocalObject.m_uiSize,
|
|
(unsigned int *)(ulArg_p +
|
|
(unsigned long)
|
|
&LocalObject.
|
|
m_uiSize -
|
|
(unsigned long)
|
|
&LocalObject));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
} else {
|
|
vfree(pData);
|
|
}
|
|
|
|
iRet = (int)EplRet;
|
|
break;
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
case EPLLIN_CMD_WRITE_LOCAL_OBJECT:
|
|
{
|
|
tEplLinLocalObject LocalObject;
|
|
void *pData;
|
|
|
|
iErr =
|
|
copy_from_user(&LocalObject, (const void *)ulArg_p,
|
|
sizeof(LocalObject));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
if ((LocalObject.m_pData == NULL)
|
|
|| (LocalObject.m_uiSize == 0)) {
|
|
iRet = (int)kEplApiInvalidParam;
|
|
goto Exit;
|
|
}
|
|
|
|
pData = vmalloc(LocalObject.m_uiSize);
|
|
if (pData == NULL) { // no memory available
|
|
iRet = -ENOMEM;
|
|
goto Exit;
|
|
}
|
|
iErr =
|
|
copy_from_user(pData, LocalObject.m_pData,
|
|
LocalObject.m_uiSize);
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
EplRet =
|
|
EplApiWriteLocalObject(LocalObject.m_uiIndex,
|
|
LocalObject.m_uiSubindex,
|
|
pData, LocalObject.m_uiSize);
|
|
|
|
vfree(pData);
|
|
|
|
iRet = (int)EplRet;
|
|
break;
|
|
}
|
|
|
|
case EPLLIN_CMD_READ_OBJECT:
|
|
{
|
|
tEplLinSdoObject SdoObject;
|
|
void *pData;
|
|
tEplLinSdoBufHeader *pBufHeader;
|
|
tEplSdoComConHdl *pSdoComConHdl;
|
|
|
|
iErr =
|
|
copy_from_user(&SdoObject, (const void *)ulArg_p,
|
|
sizeof(SdoObject));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
if ((SdoObject.m_le_pData == NULL)
|
|
|| (SdoObject.m_uiSize == 0)) {
|
|
iRet = (int)kEplApiInvalidParam;
|
|
goto Exit;
|
|
}
|
|
|
|
pBufHeader =
|
|
(tEplLinSdoBufHeader *)
|
|
vmalloc(sizeof(tEplLinSdoBufHeader) +
|
|
SdoObject.m_uiSize);
|
|
if (pBufHeader == NULL) { // no memory available
|
|
iRet = -ENOMEM;
|
|
goto Exit;
|
|
}
|
|
// initiate temporary buffer
|
|
pBufHeader->m_pUserArg = SdoObject.m_pUserArg; // original user argument pointer
|
|
pBufHeader->m_pData = SdoObject.m_le_pData; // original data pointer from app
|
|
pData = pBufHeader + sizeof(tEplLinSdoBufHeader);
|
|
|
|
if (SdoObject.m_fValidSdoComConHdl != FALSE) {
|
|
pSdoComConHdl = &SdoObject.m_SdoComConHdl;
|
|
} else {
|
|
pSdoComConHdl = NULL;
|
|
}
|
|
|
|
EplRet =
|
|
EplApiReadObject(pSdoComConHdl,
|
|
SdoObject.m_uiNodeId,
|
|
SdoObject.m_uiIndex,
|
|
SdoObject.m_uiSubindex, pData,
|
|
&SdoObject.m_uiSize,
|
|
SdoObject.m_SdoType, pBufHeader);
|
|
|
|
// return actual SDO handle (SdoObject.m_SdoComConHdl)
|
|
iErr = put_user(SdoObject.m_SdoComConHdl,
|
|
(unsigned int *)(ulArg_p +
|
|
(unsigned long)
|
|
&SdoObject.
|
|
m_SdoComConHdl -
|
|
(unsigned long)
|
|
&SdoObject));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
if (EplRet == kEplSuccessful) {
|
|
iErr =
|
|
copy_to_user(SdoObject.m_le_pData, pData,
|
|
SdoObject.m_uiSize);
|
|
|
|
vfree(pBufHeader);
|
|
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
// return actual size (SdoObject.m_uiSize)
|
|
iErr = put_user(SdoObject.m_uiSize,
|
|
(unsigned int *)(ulArg_p +
|
|
(unsigned long)
|
|
&SdoObject.
|
|
m_uiSize -
|
|
(unsigned long)
|
|
&SdoObject));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
} else if (EplRet != kEplApiTaskDeferred) { // error ocurred
|
|
vfree(pBufHeader);
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
iRet = (int)EplRet;
|
|
break;
|
|
}
|
|
|
|
case EPLLIN_CMD_WRITE_OBJECT:
|
|
{
|
|
tEplLinSdoObject SdoObject;
|
|
void *pData;
|
|
tEplLinSdoBufHeader *pBufHeader;
|
|
tEplSdoComConHdl *pSdoComConHdl;
|
|
|
|
iErr =
|
|
copy_from_user(&SdoObject, (const void *)ulArg_p,
|
|
sizeof(SdoObject));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
if ((SdoObject.m_le_pData == NULL)
|
|
|| (SdoObject.m_uiSize == 0)) {
|
|
iRet = (int)kEplApiInvalidParam;
|
|
goto Exit;
|
|
}
|
|
|
|
pBufHeader =
|
|
(tEplLinSdoBufHeader *)
|
|
vmalloc(sizeof(tEplLinSdoBufHeader) +
|
|
SdoObject.m_uiSize);
|
|
if (pBufHeader == NULL) { // no memory available
|
|
iRet = -ENOMEM;
|
|
goto Exit;
|
|
}
|
|
// initiate temporary buffer
|
|
pBufHeader->m_pUserArg = SdoObject.m_pUserArg; // original user argument pointer
|
|
pBufHeader->m_pData = SdoObject.m_le_pData; // original data pointer from app
|
|
pData = pBufHeader + sizeof(tEplLinSdoBufHeader);
|
|
|
|
iErr =
|
|
copy_from_user(pData, SdoObject.m_le_pData,
|
|
SdoObject.m_uiSize);
|
|
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
if (SdoObject.m_fValidSdoComConHdl != FALSE) {
|
|
pSdoComConHdl = &SdoObject.m_SdoComConHdl;
|
|
} else {
|
|
pSdoComConHdl = NULL;
|
|
}
|
|
|
|
EplRet =
|
|
EplApiWriteObject(pSdoComConHdl,
|
|
SdoObject.m_uiNodeId,
|
|
SdoObject.m_uiIndex,
|
|
SdoObject.m_uiSubindex, pData,
|
|
SdoObject.m_uiSize,
|
|
SdoObject.m_SdoType, pBufHeader);
|
|
|
|
// return actual SDO handle (SdoObject.m_SdoComConHdl)
|
|
iErr = put_user(SdoObject.m_SdoComConHdl,
|
|
(unsigned int *)(ulArg_p +
|
|
(unsigned long)
|
|
&SdoObject.
|
|
m_SdoComConHdl -
|
|
(unsigned long)
|
|
&SdoObject));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
if (EplRet != kEplApiTaskDeferred) { // succeeded or error ocurred, but task not deferred
|
|
vfree(pBufHeader);
|
|
}
|
|
|
|
iRet = (int)EplRet;
|
|
break;
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
case EPLLIN_CMD_FREE_SDO_CHANNEL:
|
|
{
|
|
// forward SDO handle to EPL stack
|
|
EplRet =
|
|
EplApiFreeSdoChannel((tEplSdoComConHdl) ulArg_p);
|
|
|
|
iRet = (int)EplRet;
|
|
break;
|
|
}
|
|
|
|
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
|
|
// ----------------------------------------------------------
|
|
case EPLLIN_CMD_MN_TRIGGER_STATE_CHANGE:
|
|
{
|
|
tEplLinNodeCmdObject NodeCmdObject;
|
|
|
|
iErr =
|
|
copy_from_user(&NodeCmdObject,
|
|
(const void *)ulArg_p,
|
|
sizeof(NodeCmdObject));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
EplRet =
|
|
EplApiMnTriggerStateChange(NodeCmdObject.m_uiNodeId,
|
|
NodeCmdObject.
|
|
m_NodeCommand);
|
|
iRet = (int)EplRet;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
// ----------------------------------------------------------
|
|
case EPLLIN_CMD_GET_EVENT:
|
|
{
|
|
tEplLinEvent Event;
|
|
|
|
// save event structure
|
|
iErr =
|
|
copy_from_user(&Event, (const void *)ulArg_p,
|
|
sizeof(Event));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
// save return code from application's event callback function
|
|
RetCbEvent_g = Event.m_RetCbEvent;
|
|
|
|
if (RetCbEvent_g == kEplShutdown) {
|
|
// pass control to event queue kernel thread, but signal termination
|
|
atomic_set(&AtomicEventState_g,
|
|
EVENT_STATE_TERM);
|
|
wake_up_interruptible(&WaitQueueCbEvent_g);
|
|
// exit with error -> EplApiProcess() will leave the infinite loop
|
|
iRet = 1;
|
|
goto Exit;
|
|
}
|
|
// pass control to event queue kernel thread
|
|
atomic_set(&AtomicEventState_g, EVENT_STATE_IOCTL);
|
|
wake_up_interruptible(&WaitQueueCbEvent_g);
|
|
|
|
// fall asleep itself in own wait queue
|
|
iErr = wait_event_interruptible(WaitQueueProcess_g,
|
|
(atomic_read
|
|
(&AtomicEventState_g)
|
|
== EVENT_STATE_READY)
|
|
||
|
|
(atomic_read
|
|
(&AtomicEventState_g)
|
|
== EVENT_STATE_TERM));
|
|
if (iErr != 0) { // waiting was interrupted by signal
|
|
// pass control to event queue kernel thread, but signal termination
|
|
atomic_set(&AtomicEventState_g,
|
|
EVENT_STATE_TERM);
|
|
wake_up_interruptible(&WaitQueueCbEvent_g);
|
|
// exit with this error -> EplApiProcess() will leave the infinite loop
|
|
iRet = iErr;
|
|
goto Exit;
|
|
} else if (atomic_read(&AtomicEventState_g) == EVENT_STATE_TERM) { // termination in progress
|
|
// pass control to event queue kernel thread, but signal termination
|
|
wake_up_interruptible(&WaitQueueCbEvent_g);
|
|
// exit with this error -> EplApiProcess() will leave the infinite loop
|
|
iRet = 1;
|
|
goto Exit;
|
|
}
|
|
// copy event to user space
|
|
iErr =
|
|
copy_to_user(Event.m_pEventType, &EventType_g,
|
|
sizeof(EventType_g));
|
|
if (iErr != 0) { // not all data could be copied
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
// $$$ d.k. perform SDO event processing
|
|
if (EventType_g == kEplApiEventSdo) {
|
|
void *pData;
|
|
tEplLinSdoBufHeader *pBufHeader;
|
|
|
|
pBufHeader =
|
|
(tEplLinSdoBufHeader *) pEventArg_g->m_Sdo.
|
|
m_pUserArg;
|
|
pData =
|
|
pBufHeader + sizeof(tEplLinSdoBufHeader);
|
|
|
|
if (pEventArg_g->m_Sdo.m_SdoAccessType ==
|
|
kEplSdoAccessTypeRead) {
|
|
// copy read data to user space
|
|
iErr =
|
|
copy_to_user(pBufHeader->m_pData,
|
|
pData,
|
|
pEventArg_g->m_Sdo.
|
|
m_uiTransferredByte);
|
|
if (iErr != 0) { // not all data could be copied
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
}
|
|
pEventArg_g->m_Sdo.m_pUserArg =
|
|
pBufHeader->m_pUserArg;
|
|
vfree(pBufHeader);
|
|
}
|
|
|
|
iErr =
|
|
copy_to_user(Event.m_pEventArg, pEventArg_g,
|
|
min(sizeof(tEplApiEventArg),
|
|
Event.m_uiEventArgSize));
|
|
if (iErr != 0) { // not all data could be copied
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
// return to EplApiProcess(), which will call the application's event callback function
|
|
iRet = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
case EPLLIN_CMD_PI_SETUP:
|
|
{
|
|
EplRet = EplApiProcessImageSetup();
|
|
iRet = (int)EplRet;
|
|
|
|
break;
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
case EPLLIN_CMD_PI_IN:
|
|
{
|
|
tEplApiProcessImage ProcessImageIn;
|
|
|
|
// save process image structure
|
|
iErr =
|
|
copy_from_user(&ProcessImageIn,
|
|
(const void *)ulArg_p,
|
|
sizeof(ProcessImageIn));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
// pass control to event queue kernel thread
|
|
atomic_set(&AtomicSyncState_g, EVENT_STATE_IOCTL);
|
|
|
|
// fall asleep itself in own wait queue
|
|
iErr = wait_event_interruptible(WaitQueuePI_In_g,
|
|
(atomic_read
|
|
(&AtomicSyncState_g) ==
|
|
EVENT_STATE_READY)
|
|
||
|
|
(atomic_read
|
|
(&AtomicSyncState_g) ==
|
|
EVENT_STATE_TERM));
|
|
if (iErr != 0) { // waiting was interrupted by signal
|
|
// pass control to sync kernel thread, but signal termination
|
|
atomic_set(&AtomicSyncState_g,
|
|
EVENT_STATE_TERM);
|
|
wake_up_interruptible(&WaitQueueCbSync_g);
|
|
// exit with this error -> application will leave the infinite loop
|
|
iRet = iErr;
|
|
goto Exit;
|
|
} else if (atomic_read(&AtomicSyncState_g) == EVENT_STATE_TERM) { // termination in progress
|
|
// pass control to sync kernel thread, but signal termination
|
|
wake_up_interruptible(&WaitQueueCbSync_g);
|
|
// exit with this error -> application will leave the infinite loop
|
|
iRet = 1;
|
|
goto Exit;
|
|
}
|
|
// exchange process image
|
|
EplRet = EplApiProcessImageExchangeIn(&ProcessImageIn);
|
|
|
|
// return to EplApiProcessImageExchangeIn()
|
|
iRet = (int)EplRet;
|
|
|
|
break;
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
case EPLLIN_CMD_PI_OUT:
|
|
{
|
|
tEplApiProcessImage ProcessImageOut;
|
|
|
|
// save process image structure
|
|
iErr =
|
|
copy_from_user(&ProcessImageOut,
|
|
(const void *)ulArg_p,
|
|
sizeof(ProcessImageOut));
|
|
if (iErr != 0) {
|
|
iRet = -EIO;
|
|
goto Exit;
|
|
}
|
|
|
|
if (atomic_read(&AtomicSyncState_g) !=
|
|
EVENT_STATE_READY) {
|
|
iRet = (int)kEplInvalidOperation;
|
|
goto Exit;
|
|
}
|
|
// exchange process image
|
|
EplRet =
|
|
EplApiProcessImageExchangeOut(&ProcessImageOut);
|
|
|
|
// pass control to sync kernel thread
|
|
atomic_set(&AtomicSyncState_g, EVENT_STATE_TERM);
|
|
wake_up_interruptible(&WaitQueueCbSync_g);
|
|
|
|
// return to EplApiProcessImageExchangeout()
|
|
iRet = (int)EplRet;
|
|
|
|
break;
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
case EPLLIN_CMD_NMT_COMMAND:
|
|
{
|
|
// forward NMT command to EPL stack
|
|
EplRet = EplApiExecNmtCommand((tEplNmtEvent) ulArg_p);
|
|
|
|
iRet = (int)EplRet;
|
|
|
|
break;
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
// TRACE1("EPL: - EplLinIoctl (iRet=%d)\n", iRet);
|
|
return (iRet);
|
|
|
|
}
|
|
|
|
//=========================================================================//
|
|
// //
|
|
// P R I V A T E F U N C T I O N S //
|
|
// //
|
|
//=========================================================================//
|
|
|
|
tEplKernel EplLinCbEvent(tEplApiEventType EventType_p, // IN: event type (enum)
|
|
tEplApiEventArg *pEventArg_p, // IN: event argument (union)
|
|
void *pUserArg_p)
|
|
{
|
|
tEplKernel EplRet = kEplSuccessful;
|
|
int iErr;
|
|
|
|
// block any further call to this function, i.e. enter critical section
|
|
iErr = down_interruptible(&SemaphoreCbEvent_g);
|
|
if (iErr != 0) { // waiting was interrupted by signal
|
|
EplRet = kEplShutdown;
|
|
goto Exit;
|
|
}
|
|
// wait for EplApiProcess() to call ioctl
|
|
// normally it should be waiting already for us to pass a new event
|
|
iErr = wait_event_interruptible(WaitQueueCbEvent_g,
|
|
(atomic_read(&AtomicEventState_g) ==
|
|
EVENT_STATE_IOCTL)
|
|
|| (atomic_read(&AtomicEventState_g) ==
|
|
EVENT_STATE_TERM));
|
|
if ((iErr != 0) || (atomic_read(&AtomicEventState_g) == EVENT_STATE_TERM)) { // waiting was interrupted by signal
|
|
EplRet = kEplShutdown;
|
|
goto LeaveCriticalSection;
|
|
}
|
|
// save event information for ioctl
|
|
EventType_g = EventType_p;
|
|
pEventArg_g = pEventArg_p;
|
|
|
|
// pass control to application's event callback function, i.e. EplApiProcess()
|
|
atomic_set(&AtomicEventState_g, EVENT_STATE_READY);
|
|
wake_up_interruptible(&WaitQueueProcess_g);
|
|
|
|
// now, the application's event callback function processes the event
|
|
|
|
// wait for completion of application's event callback function, i.e. EplApiProcess() calls ioctl again
|
|
iErr = wait_event_interruptible(WaitQueueCbEvent_g,
|
|
(atomic_read(&AtomicEventState_g) ==
|
|
EVENT_STATE_IOCTL)
|
|
|| (atomic_read(&AtomicEventState_g) ==
|
|
EVENT_STATE_TERM));
|
|
if ((iErr != 0) || (atomic_read(&AtomicEventState_g) == EVENT_STATE_TERM)) { // waiting was interrupted by signal
|
|
EplRet = kEplShutdown;
|
|
goto LeaveCriticalSection;
|
|
}
|
|
// read return code from application's event callback function
|
|
EplRet = RetCbEvent_g;
|
|
|
|
LeaveCriticalSection:
|
|
up(&SemaphoreCbEvent_g);
|
|
|
|
Exit:
|
|
// check if NMT_GS_OFF is reached
|
|
if (EventType_p == kEplApiEventNmtStateChange) {
|
|
if (pEventArg_p->m_NmtStateChange.m_NewNmtState == kEplNmtGsOff) { // NMT state machine was shut down
|
|
TRACE0("EPL: EplLinCbEvent(NMT_GS_OFF)\n");
|
|
uiEplState_g = EPL_STATE_SHUTDOWN;
|
|
atomic_set(&AtomicEventState_g, EVENT_STATE_TERM);
|
|
wake_up(&WaitQueueRelease_g);
|
|
} else { // NMT state machine is running
|
|
uiEplState_g = EPL_STATE_RUNNING;
|
|
}
|
|
}
|
|
|
|
return EplRet;
|
|
}
|
|
|
|
tEplKernel EplLinCbSync(void)
|
|
{
|
|
tEplKernel EplRet = kEplSuccessful;
|
|
int iErr;
|
|
|
|
// check if user process waits for sync
|
|
if (atomic_read(&AtomicSyncState_g) == EVENT_STATE_IOCTL) {
|
|
// pass control to application, i.e. EplApiProcessImageExchangeIn()
|
|
atomic_set(&AtomicSyncState_g, EVENT_STATE_READY);
|
|
wake_up_interruptible(&WaitQueuePI_In_g);
|
|
|
|
// now, the application processes the sync event
|
|
|
|
// wait for call of EplApiProcessImageExchangeOut()
|
|
iErr = wait_event_interruptible(WaitQueueCbSync_g,
|
|
(atomic_read(&AtomicSyncState_g)
|
|
== EVENT_STATE_IOCTL)
|
|
||
|
|
(atomic_read(&AtomicSyncState_g)
|
|
== EVENT_STATE_TERM));
|
|
if ((iErr != 0) || (atomic_read(&AtomicEventState_g) == EVENT_STATE_IOCTL)) { // waiting was interrupted by signal or application called wrong function
|
|
EplRet = kEplShutdown;
|
|
}
|
|
} else { // application is currently not waiting for sync
|
|
// continue without interruption
|
|
// TPDO are set valid by caller (i.e. EplEventkProcess())
|
|
}
|
|
|
|
TGT_DBG_SIGNAL_TRACE_POINT(1);
|
|
|
|
return EplRet;
|
|
}
|
|
|
|
// EOF
|