/**************************************************************************** (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 //#include #include #include #include #include //#include //#include //#include //#include // scheduling #include // memory access #include #include #ifdef CONFIG_DEVFS_FS #include #include #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 //--------------------------------------------------------------------------- // 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