blob: 05ca0628d6ae3e65c13f98ec84e2cfcff5cd17e7 [file] [log] [blame]
/****************************************************************************
(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/version.h>
//#include <linux/config.h>
#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>
#ifdef CONFIG_DEVFS_FS
#include <linux/major.h>
#include <linux/devfs_fs_kernel.h>
#endif
#include "Epl.h"
#include "EplApiLinux.h"
//#include "kernel/EplPdokCal.h"
#include "proc_fs.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
// remove ("make invisible") obsolete symbols for kernel versions 2.6
// and higher
#define MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#define EXPORT_NO_SYMBOLS
#else
#error "This driver needs a 2.6.x kernel or higher"
#endif
/***************************************************************************/
/* */
/* */
/* 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 PUBLIC TgtDbgSignalTracePoint(BYTE 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
//---------------------------------------------------------------------------
#ifdef CONFIG_DEVFS_FS
// driver major number
static int nDrvMajorNumber_g;
#else
// device number (major and minor)
static dev_t nDevNum_g;
static struct cdev *pEpl_cdev_g;
#endif
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 PUBLIC EplLinCbEvent(tEplApiEventType EventType_p, // IN: event type (enum)
tEplApiEventArg * pEventArg_p, // IN: event argument (union)
void GENERIC * pUserArg_p);
tEplKernel PUBLIC 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
//---------------------------------------------------------------------------
EXPORT_NO_SYMBOLS;
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;
#ifdef CONFIG_DEVFS_FS
int nMinorNumber;
#endif
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);
#ifdef CONFIG_DEVFS_FS
// register character device handler
TRACE2("EPL: Installing Driver '%s', Version %s...\n",
EPLLIN_DRV_NAME, EPL_PRODUCT_VERSION);
TRACE0("EPL: (using dynamic major number assignment)\n");
nDrvMajorNumber_g =
register_chrdev(0, EPLLIN_DRV_NAME, &EplLinFileOps_g);
if (nDrvMajorNumber_g != 0) {
TRACE2
("EPL: Driver '%s' installed successful, assigned MajorNumber=%d\n",
EPLLIN_DRV_NAME, nDrvMajorNumber_g);
} else {
TRACE1
("EPL: ERROR: Driver '%s' is unable to get a free MajorNumber!\n",
EPLLIN_DRV_NAME);
iRet = -EIO;
goto Exit;
}
// create device node in DEVFS
nMinorNumber = 0;
TRACE1("EPL: Creating device node '/dev/%s'...\n", EPLLIN_DEV_NAME);
iErr =
devfs_mk_cdev(MKDEV(nDrvMajorNumber_g, nMinorNumber),
S_IFCHR | S_IRUGO | S_IWUGO, EPLLIN_DEV_NAME);
if (iErr == 0) {
TRACE1("EPL: Device node '/dev/%s' created successful.\n",
EPLLIN_DEV_NAME);
} else {
TRACE1("EPL: ERROR: unable to create device node '/dev/%s'\n",
EPLLIN_DEV_NAME);
iRet = -EIO;
goto Exit;
}
#else
// 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;
}
#endif
// 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");
#ifdef CONFIG_DEVFS_FS
// remove device node from DEVFS
devfs_remove(EPLLIN_DEV_NAME);
TRACE1("EPL: Device node '/dev/%s' removed.\n", EPLLIN_DEV_NAME);
// unregister character device handler
unregister_chrdev(nDrvMajorNumber_g, EPLLIN_DRV_NAME);
#else
// remove cdev structure
cdev_del(pEpl_cdev_g);
// unregister character device handler
unregister_chrdev_region(nDevNum_g, 1);
#endif
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");
MOD_INC_USE_COUNT;
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;
MOD_DEC_USE_COUNT;
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 PUBLIC EplLinCbEvent(tEplApiEventType EventType_p, // IN: event type (enum)
tEplApiEventArg * pEventArg_p, // IN: event argument (union)
void GENERIC * 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 PUBLIC 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