| /**************************************************************************** |
| |
| (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 |