• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2 
3   (c) SYSTEC electronic GmbH, D-07973 Greiz, August-Bebel-Str. 29
4       www.systec-electronic.com
5 
6   Project:      openPOWERLINK
7 
8   Description:  Linux kernel module as wrapper of EPL API layer,
9                 i.e. counterpart to a Linux application
10 
11   License:
12 
13     Redistribution and use in source and binary forms, with or without
14     modification, are permitted provided that the following conditions
15     are met:
16 
17     1. Redistributions of source code must retain the above copyright
18        notice, this list of conditions and the following disclaimer.
19 
20     2. Redistributions in binary form must reproduce the above copyright
21        notice, this list of conditions and the following disclaimer in the
22        documentation and/or other materials provided with the distribution.
23 
24     3. Neither the name of SYSTEC electronic GmbH nor the names of its
25        contributors may be used to endorse or promote products derived
26        from this software without prior written permission. For written
27        permission, please contact info@systec-electronic.com.
28 
29     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
32     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
33     COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
35     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
37     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
39     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40     POSSIBILITY OF SUCH DAMAGE.
41 
42     Severability Clause:
43 
44         If a provision of this License is or becomes illegal, invalid or
45         unenforceable in any jurisdiction, that shall not affect:
46         1. the validity or enforceability in that jurisdiction of any other
47            provision of this License; or
48         2. the validity or enforceability in other jurisdictions of that or
49            any other provision of this License.
50 
51   -------------------------------------------------------------------------
52 
53                 $RCSfile: EplApiLinuxKernel.c,v $
54 
55                 $Author: D.Krueger $
56 
57                 $Revision: 1.9 $  $Date: 2008/11/21 09:00:38 $
58 
59                 $State: Exp $
60 
61                 Build Environment:
62                 GNU-Compiler for m68k
63 
64   -------------------------------------------------------------------------
65 
66   Revision History:
67 
68   2006/10/11 d.k.:  Initial Version
69   2008/04/10 m.u.:  Changed to new char driver init
70 
71 ****************************************************************************/
72 
73 // kernel modul and driver
74 
75 //#include <linux/version.h>
76 //#include <linux/config.h>
77 
78 #include <linux/module.h>
79 #include <linux/fs.h>
80 #include <linux/cdev.h>
81 #include <linux/types.h>
82 
83 //#include <linux/module.h>
84 //#include <linux/kernel.h>
85 //#include <linux/init.h>
86 //#include <linux/errno.h>
87 
88 // scheduling
89 #include <linux/sched.h>
90 
91 // memory access
92 #include <asm/uaccess.h>
93 #include <linux/vmalloc.h>
94 
95 #ifdef CONFIG_DEVFS_FS
96 #include <linux/major.h>
97 #include <linux/devfs_fs_kernel.h>
98 #endif
99 
100 #include "Epl.h"
101 #include "EplApiLinux.h"
102 //#include "kernel/EplPdokCal.h"
103 #include "proc_fs.h"
104 
105 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
106     // remove ("make invisible") obsolete symbols for kernel versions 2.6
107     // and higher
108 #define MOD_INC_USE_COUNT
109 #define MOD_DEC_USE_COUNT
110 #define EXPORT_NO_SYMBOLS
111 #else
112 #error "This driver needs a 2.6.x kernel or higher"
113 #endif
114 
115 /***************************************************************************/
116 /*                                                                         */
117 /*                                                                         */
118 /*          G L O B A L   D E F I N I T I O N S                            */
119 /*                                                                         */
120 /*                                                                         */
121 /***************************************************************************/
122 
123 // Metainformation
124 MODULE_LICENSE("Dual BSD/GPL");
125 #ifdef MODULE_AUTHOR
126 MODULE_AUTHOR("Daniel.Krueger@SYSTEC-electronic.com");
127 MODULE_DESCRIPTION("EPL API driver");
128 #endif
129 
130 //---------------------------------------------------------------------------
131 //  Configuration
132 //---------------------------------------------------------------------------
133 
134 #define EPLLIN_DRV_NAME     "systec_epl"	// used for <register_chrdev>
135 
136 //---------------------------------------------------------------------------
137 //  Constant definitions
138 //---------------------------------------------------------------------------
139 
140 // TracePoint support for realtime-debugging
141 #ifdef _DBG_TRACE_POINTS_
142 void PUBLIC TgtDbgSignalTracePoint(BYTE bTracePointNumber_p);
143 #define TGT_DBG_SIGNAL_TRACE_POINT(p)   TgtDbgSignalTracePoint(p)
144 #else
145 #define TGT_DBG_SIGNAL_TRACE_POINT(p)
146 #endif
147 
148 #define EVENT_STATE_INIT        0
149 #define EVENT_STATE_IOCTL       1	// ioctl entered and ready to receive EPL event
150 #define EVENT_STATE_READY       2	// EPL event can be forwarded to user application
151 #define EVENT_STATE_TERM        3	// terminate processing
152 
153 #define EPL_STATE_NOTOPEN       0
154 #define EPL_STATE_NOTINIT       1
155 #define EPL_STATE_RUNNING       2
156 #define EPL_STATE_SHUTDOWN      3
157 
158 //---------------------------------------------------------------------------
159 //  Global variables
160 //---------------------------------------------------------------------------
161 
162 #ifdef CONFIG_DEVFS_FS
163 
164     // driver major number
165 static int nDrvMajorNumber_g;
166 
167 #else
168 
169     // device number (major and minor)
170 static dev_t nDevNum_g;
171 static struct cdev *pEpl_cdev_g;
172 
173 #endif
174 
175 static volatile unsigned int uiEplState_g = EPL_STATE_NOTOPEN;
176 
177 static struct semaphore SemaphoreCbEvent_g;	// semaphore for EplLinCbEvent
178 static wait_queue_head_t WaitQueueCbEvent_g;	// wait queue EplLinCbEvent
179 static wait_queue_head_t WaitQueueProcess_g;	// wait queue for EplApiProcess (user process)
180 static wait_queue_head_t WaitQueueRelease_g;	// wait queue for EplLinRelease
181 static atomic_t AtomicEventState_g = ATOMIC_INIT(EVENT_STATE_INIT);
182 static tEplApiEventType EventType_g;	// event type (enum)
183 static tEplApiEventArg *pEventArg_g;	// event argument (union)
184 static tEplKernel RetCbEvent_g;	// return code from event callback function
185 static wait_queue_head_t WaitQueueCbSync_g;	// wait queue EplLinCbSync
186 static wait_queue_head_t WaitQueuePI_In_g;	// wait queue for EplApiProcessImageExchangeIn (user process)
187 static atomic_t AtomicSyncState_g = ATOMIC_INIT(EVENT_STATE_INIT);
188 
189 //---------------------------------------------------------------------------
190 //  Local types
191 //---------------------------------------------------------------------------
192 
193 typedef struct {
194 	void *m_pUserArg;
195 	void *m_pData;
196 
197 } tEplLinSdoBufHeader;
198 
199 //---------------------------------------------------------------------------
200 //  Local variables
201 //---------------------------------------------------------------------------
202 
203 //---------------------------------------------------------------------------
204 //  Prototypes of internal functions
205 //---------------------------------------------------------------------------
206 
207 tEplKernel PUBLIC EplLinCbEvent(tEplApiEventType EventType_p,	// IN: event type (enum)
208 				tEplApiEventArg * pEventArg_p,	// IN: event argument (union)
209 				void GENERIC * pUserArg_p);
210 
211 tEplKernel PUBLIC EplLinCbSync(void);
212 
213 static int __init EplLinInit(void);
214 static void __exit EplLinExit(void);
215 
216 static int EplLinOpen(struct inode *pDeviceFile_p, struct file *pInstance_p);
217 static int EplLinRelease(struct inode *pDeviceFile_p, struct file *pInstance_p);
218 static ssize_t EplLinRead(struct file *pInstance_p, char *pDstBuff_p,
219 			  size_t BuffSize_p, loff_t * pFileOffs_p);
220 static ssize_t EplLinWrite(struct file *pInstance_p, const char *pSrcBuff_p,
221 			   size_t BuffSize_p, loff_t * pFileOffs_p);
222 static int EplLinIoctl(struct inode *pDeviceFile_p, struct file *pInstance_p,
223 		       unsigned int uiIoctlCmd_p, unsigned long ulArg_p);
224 
225 //---------------------------------------------------------------------------
226 //  Kernel Module specific Data Structures
227 //---------------------------------------------------------------------------
228 
229 EXPORT_NO_SYMBOLS;
230 
231 module_init(EplLinInit);
232 module_exit(EplLinExit);
233 
234 static struct file_operations EplLinFileOps_g = {
235 	.owner = THIS_MODULE,
236 	.open = EplLinOpen,
237 	.release = EplLinRelease,
238 	.read = EplLinRead,
239 	.write = EplLinWrite,
240 	.ioctl = EplLinIoctl,
241 
242 };
243 
244 //=========================================================================//
245 //                                                                         //
246 //          P U B L I C   F U N C T I O N S                                //
247 //                                                                         //
248 //=========================================================================//
249 
250 //---------------------------------------------------------------------------
251 //  Initailize Driver
252 //---------------------------------------------------------------------------
253 //  -> insmod driver
254 //---------------------------------------------------------------------------
255 
EplLinInit(void)256 static int __init EplLinInit(void)
257 {
258 
259 	tEplKernel EplRet;
260 	int iErr;
261 	int iRet;
262 #ifdef CONFIG_DEVFS_FS
263 	int nMinorNumber;
264 #endif
265 
266 	TRACE0("EPL: + EplLinInit...\n");
267 	TRACE2("EPL:   Driver build: %s / %s\n", __DATE__, __TIME__);
268 
269 	iRet = 0;
270 
271 	// initialize global variables
272 	atomic_set(&AtomicEventState_g, EVENT_STATE_INIT);
273 	sema_init(&SemaphoreCbEvent_g, 1);
274 	init_waitqueue_head(&WaitQueueCbEvent_g);
275 	init_waitqueue_head(&WaitQueueProcess_g);
276 	init_waitqueue_head(&WaitQueueRelease_g);
277 
278 #ifdef CONFIG_DEVFS_FS
279 
280 	// register character device handler
281 	TRACE2("EPL:   Installing Driver '%s', Version %s...\n",
282 	       EPLLIN_DRV_NAME, EPL_PRODUCT_VERSION);
283 	TRACE0("EPL:   (using dynamic major number assignment)\n");
284 	nDrvMajorNumber_g =
285 	    register_chrdev(0, EPLLIN_DRV_NAME, &EplLinFileOps_g);
286 	if (nDrvMajorNumber_g != 0) {
287 		TRACE2
288 		    ("EPL:   Driver '%s' installed successful, assigned MajorNumber=%d\n",
289 		     EPLLIN_DRV_NAME, nDrvMajorNumber_g);
290 	} else {
291 		TRACE1
292 		    ("EPL:   ERROR: Driver '%s' is unable to get a free MajorNumber!\n",
293 		     EPLLIN_DRV_NAME);
294 		iRet = -EIO;
295 		goto Exit;
296 	}
297 
298 	// create device node in DEVFS
299 	nMinorNumber = 0;
300 	TRACE1("EPL:   Creating device node '/dev/%s'...\n", EPLLIN_DEV_NAME);
301 	iErr =
302 	    devfs_mk_cdev(MKDEV(nDrvMajorNumber_g, nMinorNumber),
303 			  S_IFCHR | S_IRUGO | S_IWUGO, EPLLIN_DEV_NAME);
304 	if (iErr == 0) {
305 		TRACE1("EPL:   Device node '/dev/%s' created successful.\n",
306 		       EPLLIN_DEV_NAME);
307 	} else {
308 		TRACE1("EPL:   ERROR: unable to create device node '/dev/%s'\n",
309 		       EPLLIN_DEV_NAME);
310 		iRet = -EIO;
311 		goto Exit;
312 	}
313 
314 #else
315 
316 	// register character device handler
317 	// only one Minor required
318 	TRACE2("EPL:   Installing Driver '%s', Version %s...\n",
319 	       EPLLIN_DRV_NAME, EPL_PRODUCT_VERSION);
320 	iRet = alloc_chrdev_region(&nDevNum_g, 0, 1, EPLLIN_DRV_NAME);
321 	if (iRet == 0) {
322 		TRACE2
323 		    ("EPL:   Driver '%s' installed successful, assigned MajorNumber=%d\n",
324 		     EPLLIN_DRV_NAME, MAJOR(nDevNum_g));
325 	} else {
326 		TRACE1
327 		    ("EPL:   ERROR: Driver '%s' is unable to get a free MajorNumber!\n",
328 		     EPLLIN_DRV_NAME);
329 		iRet = -EIO;
330 		goto Exit;
331 	}
332 
333 	// register cdev structure
334 	pEpl_cdev_g = cdev_alloc();
335 	pEpl_cdev_g->ops = &EplLinFileOps_g;
336 	pEpl_cdev_g->owner = THIS_MODULE;
337 	iErr = cdev_add(pEpl_cdev_g, nDevNum_g, 1);
338 	if (iErr) {
339 		TRACE2("EPL:   ERROR %d: Driver '%s' could not be added!\n",
340 		       iErr, EPLLIN_DRV_NAME);
341 		iRet = -EIO;
342 		goto Exit;
343 	}
344 #endif
345 
346 	// create device node in PROCFS
347 	EplRet = EplLinProcInit();
348 	if (EplRet != kEplSuccessful) {
349 		goto Exit;
350 	}
351 
352       Exit:
353 
354 	TRACE1("EPL: - EplLinInit (iRet=%d)\n", iRet);
355 	return (iRet);
356 
357 }
358 
359 //---------------------------------------------------------------------------
360 //  Remove Driver
361 //---------------------------------------------------------------------------
362 //  -> rmmod driver
363 //---------------------------------------------------------------------------
364 
EplLinExit(void)365 static void __exit EplLinExit(void)
366 {
367 
368 	tEplKernel EplRet;
369 
370 	// delete instance for all modules
371 //    EplRet = EplApiShutdown();
372 //    printk("EplApiShutdown():  0x%X\n", EplRet);
373 
374 	// deinitialize proc fs
375 	EplRet = EplLinProcFree();
376 	printk("EplLinProcFree():        0x%X\n", EplRet);
377 
378 	TRACE0("EPL: + EplLinExit...\n");
379 
380 #ifdef CONFIG_DEVFS_FS
381 
382 	// remove device node from DEVFS
383 	devfs_remove(EPLLIN_DEV_NAME);
384 	TRACE1("EPL:   Device node '/dev/%s' removed.\n", EPLLIN_DEV_NAME);
385 
386 	// unregister character device handler
387 	unregister_chrdev(nDrvMajorNumber_g, EPLLIN_DRV_NAME);
388 
389 #else
390 
391 	// remove cdev structure
392 	cdev_del(pEpl_cdev_g);
393 
394 	// unregister character device handler
395 	unregister_chrdev_region(nDevNum_g, 1);
396 
397 #endif
398 
399 	TRACE1("EPL:   Driver '%s' removed.\n", EPLLIN_DRV_NAME);
400 
401 	TRACE0("EPL: - EplLinExit\n");
402 
403 }
404 
405 //---------------------------------------------------------------------------
406 //  Open Driver
407 //---------------------------------------------------------------------------
408 //  -> open("/dev/driver", O_RDWR)...
409 //---------------------------------------------------------------------------
410 
EplLinOpen(struct inode * pDeviceFile_p,struct file * pInstance_p)411 static int EplLinOpen(struct inode *pDeviceFile_p,	// information about the device to open
412 		      struct file *pInstance_p)	// information about driver instance
413 {
414 
415 	int iRet;
416 
417 	TRACE0("EPL: + EplLinOpen...\n");
418 
419 	MOD_INC_USE_COUNT;
420 
421 	if (uiEplState_g != EPL_STATE_NOTOPEN) {	// stack already initialized
422 		iRet = -EALREADY;
423 	} else {
424 		atomic_set(&AtomicEventState_g, EVENT_STATE_INIT);
425 		sema_init(&SemaphoreCbEvent_g, 1);
426 		init_waitqueue_head(&WaitQueueCbEvent_g);
427 		init_waitqueue_head(&WaitQueueProcess_g);
428 		init_waitqueue_head(&WaitQueueRelease_g);
429 		atomic_set(&AtomicSyncState_g, EVENT_STATE_INIT);
430 		init_waitqueue_head(&WaitQueueCbSync_g);
431 		init_waitqueue_head(&WaitQueuePI_In_g);
432 
433 		uiEplState_g = EPL_STATE_NOTINIT;
434 		iRet = 0;
435 	}
436 
437 	TRACE1("EPL: - EplLinOpen (iRet=%d)\n", iRet);
438 	return (iRet);
439 
440 }
441 
442 //---------------------------------------------------------------------------
443 //  Close Driver
444 //---------------------------------------------------------------------------
445 //  -> close(device)...
446 //---------------------------------------------------------------------------
447 
EplLinRelease(struct inode * pDeviceFile_p,struct file * pInstance_p)448 static int EplLinRelease(struct inode *pDeviceFile_p,	// information about the device to open
449 			 struct file *pInstance_p)	// information about driver instance
450 {
451 
452 	tEplKernel EplRet = kEplSuccessful;
453 	int iRet;
454 
455 	TRACE0("EPL: + EplLinRelease...\n");
456 
457 	if (uiEplState_g != EPL_STATE_NOTINIT) {
458 		// pass control to sync kernel thread, but signal termination
459 		atomic_set(&AtomicSyncState_g, EVENT_STATE_TERM);
460 		wake_up_interruptible(&WaitQueueCbSync_g);
461 		wake_up_interruptible(&WaitQueuePI_In_g);
462 
463 		// pass control to event queue kernel thread
464 		atomic_set(&AtomicEventState_g, EVENT_STATE_TERM);
465 		wake_up_interruptible(&WaitQueueCbEvent_g);
466 
467 		if (uiEplState_g == EPL_STATE_RUNNING) {	// post NmtEventSwitchOff
468 			EplRet = EplApiExecNmtCommand(kEplNmtEventSwitchOff);
469 
470 		}
471 
472 		if (EplRet == kEplSuccessful) {
473 			TRACE0("EPL:   waiting for NMT_GS_OFF\n");
474 			wait_event_interruptible(WaitQueueRelease_g,
475 						 (uiEplState_g ==
476 						  EPL_STATE_SHUTDOWN));
477 		} else {	// post NmtEventSwitchOff failed
478 			TRACE0("EPL:   event post failed\n");
479 		}
480 
481 		// $$$ d.k.: What if waiting was interrupted by signal?
482 
483 		TRACE0("EPL:   call EplApiShutdown()\n");
484 		// EPL stack can be safely shut down
485 		// delete instance for all EPL modules
486 		EplRet = EplApiShutdown();
487 		printk("EplApiShutdown():  0x%X\n", EplRet);
488 	}
489 
490 	uiEplState_g = EPL_STATE_NOTOPEN;
491 	iRet = 0;
492 
493 	MOD_DEC_USE_COUNT;
494 
495 	TRACE1("EPL: - EplLinRelease (iRet=%d)\n", iRet);
496 	return (iRet);
497 
498 }
499 
500 //---------------------------------------------------------------------------
501 //  Read Data from Driver
502 //---------------------------------------------------------------------------
503 //  -> read(...)
504 //---------------------------------------------------------------------------
505 
EplLinRead(struct file * pInstance_p,char * pDstBuff_p,size_t BuffSize_p,loff_t * pFileOffs_p)506 static ssize_t EplLinRead(struct file *pInstance_p,	// information about driver instance
507 			  char *pDstBuff_p,	// address of buffer to fill with data
508 			  size_t BuffSize_p,	// length of the buffer
509 			  loff_t * pFileOffs_p)	// offset in the file
510 {
511 
512 	int iRet;
513 
514 	TRACE0("EPL: + EplLinRead...\n");
515 
516 	TRACE0("EPL:   Sorry, this operation isn't supported.\n");
517 	iRet = -EINVAL;
518 
519 	TRACE1("EPL: - EplLinRead (iRet=%d)\n", iRet);
520 	return (iRet);
521 
522 }
523 
524 //---------------------------------------------------------------------------
525 //  Write Data to Driver
526 //---------------------------------------------------------------------------
527 //  -> write(...)
528 //---------------------------------------------------------------------------
529 
EplLinWrite(struct file * pInstance_p,const char * pSrcBuff_p,size_t BuffSize_p,loff_t * pFileOffs_p)530 static ssize_t EplLinWrite(struct file *pInstance_p,	// information about driver instance
531 			   const char *pSrcBuff_p,	// address of buffer to get data from
532 			   size_t BuffSize_p,	// length of the buffer
533 			   loff_t * pFileOffs_p)	// offset in the file
534 {
535 
536 	int iRet;
537 
538 	TRACE0("EPL: + EplLinWrite...\n");
539 
540 	TRACE0("EPL:   Sorry, this operation isn't supported.\n");
541 	iRet = -EINVAL;
542 
543 	TRACE1("EPL: - EplLinWrite (iRet=%d)\n", iRet);
544 	return (iRet);
545 
546 }
547 
548 //---------------------------------------------------------------------------
549 //  Generic Access to Driver
550 //---------------------------------------------------------------------------
551 //  -> ioctl(...)
552 //---------------------------------------------------------------------------
553 
EplLinIoctl(struct inode * pDeviceFile_p,struct file * pInstance_p,unsigned int uiIoctlCmd_p,unsigned long ulArg_p)554 static int EplLinIoctl(struct inode *pDeviceFile_p,	// information about the device to open
555 		       struct file *pInstance_p,	// information about driver instance
556 		       unsigned int uiIoctlCmd_p,	// Ioctl command to execute
557 		       unsigned long ulArg_p)	// Ioctl command specific argument/parameter
558 {
559 
560 	tEplKernel EplRet;
561 	int iErr;
562 	int iRet;
563 
564 //    TRACE1("EPL: + EplLinIoctl (uiIoctlCmd_p=%d)...\n", uiIoctlCmd_p);
565 
566 	iRet = -EINVAL;
567 
568 	switch (uiIoctlCmd_p) {
569 		// ----------------------------------------------------------
570 	case EPLLIN_CMD_INITIALIZE:
571 		{
572 			tEplApiInitParam EplApiInitParam;
573 
574 			iErr =
575 			    copy_from_user(&EplApiInitParam,
576 					   (const void *)ulArg_p,
577 					   sizeof(EplApiInitParam));
578 			if (iErr != 0) {
579 				iRet = -EIO;
580 				goto Exit;
581 			}
582 
583 			EplApiInitParam.m_pfnCbEvent = EplLinCbEvent;
584 			EplApiInitParam.m_pfnCbSync = EplLinCbSync;
585 
586 			EplRet = EplApiInitialize(&EplApiInitParam);
587 
588 			uiEplState_g = EPL_STATE_RUNNING;
589 
590 			iRet = (int)EplRet;
591 			break;
592 		}
593 
594 		// ----------------------------------------------------------
595 	case EPLLIN_CMD_SHUTDOWN:
596 		{		// shutdown the threads
597 
598 			// pass control to sync kernel thread, but signal termination
599 			atomic_set(&AtomicSyncState_g, EVENT_STATE_TERM);
600 			wake_up_interruptible(&WaitQueueCbSync_g);
601 			wake_up_interruptible(&WaitQueuePI_In_g);
602 
603 			// pass control to event queue kernel thread
604 			atomic_set(&AtomicEventState_g, EVENT_STATE_TERM);
605 			wake_up_interruptible(&WaitQueueCbEvent_g);
606 
607 			if (uiEplState_g == EPL_STATE_RUNNING) {	// post NmtEventSwitchOff
608 				EplRet =
609 				    EplApiExecNmtCommand(kEplNmtEventSwitchOff);
610 
611 			}
612 
613 			iRet = 0;
614 			break;
615 		}
616 
617 		// ----------------------------------------------------------
618 	case EPLLIN_CMD_READ_LOCAL_OBJECT:
619 		{
620 			tEplLinLocalObject LocalObject;
621 			void *pData;
622 
623 			iErr =
624 			    copy_from_user(&LocalObject, (const void *)ulArg_p,
625 					   sizeof(LocalObject));
626 			if (iErr != 0) {
627 				iRet = -EIO;
628 				goto Exit;
629 			}
630 
631 			if ((LocalObject.m_pData == NULL)
632 			    || (LocalObject.m_uiSize == 0)) {
633 				iRet = (int)kEplApiInvalidParam;
634 				goto Exit;
635 			}
636 
637 			pData = vmalloc(LocalObject.m_uiSize);
638 			if (pData == NULL) {	// no memory available
639 				iRet = -ENOMEM;
640 				goto Exit;
641 			}
642 
643 			EplRet =
644 			    EplApiReadLocalObject(LocalObject.m_uiIndex,
645 						  LocalObject.m_uiSubindex,
646 						  pData, &LocalObject.m_uiSize);
647 
648 			if (EplRet == kEplSuccessful) {
649 				iErr =
650 				    copy_to_user(LocalObject.m_pData, pData,
651 						 LocalObject.m_uiSize);
652 
653 				vfree(pData);
654 
655 				if (iErr != 0) {
656 					iRet = -EIO;
657 					goto Exit;
658 				}
659 				// return actual size (LocalObject.m_uiSize)
660 				iErr = put_user(LocalObject.m_uiSize,
661 						(unsigned int *)(ulArg_p +
662 								 (unsigned long)
663 								 &LocalObject.
664 								 m_uiSize -
665 								 (unsigned long)
666 								 &LocalObject));
667 				if (iErr != 0) {
668 					iRet = -EIO;
669 					goto Exit;
670 				}
671 
672 			} else {
673 				vfree(pData);
674 			}
675 
676 			iRet = (int)EplRet;
677 			break;
678 		}
679 
680 		// ----------------------------------------------------------
681 	case EPLLIN_CMD_WRITE_LOCAL_OBJECT:
682 		{
683 			tEplLinLocalObject LocalObject;
684 			void *pData;
685 
686 			iErr =
687 			    copy_from_user(&LocalObject, (const void *)ulArg_p,
688 					   sizeof(LocalObject));
689 			if (iErr != 0) {
690 				iRet = -EIO;
691 				goto Exit;
692 			}
693 
694 			if ((LocalObject.m_pData == NULL)
695 			    || (LocalObject.m_uiSize == 0)) {
696 				iRet = (int)kEplApiInvalidParam;
697 				goto Exit;
698 			}
699 
700 			pData = vmalloc(LocalObject.m_uiSize);
701 			if (pData == NULL) {	// no memory available
702 				iRet = -ENOMEM;
703 				goto Exit;
704 			}
705 			iErr =
706 			    copy_from_user(pData, LocalObject.m_pData,
707 					   LocalObject.m_uiSize);
708 			if (iErr != 0) {
709 				iRet = -EIO;
710 				goto Exit;
711 			}
712 
713 			EplRet =
714 			    EplApiWriteLocalObject(LocalObject.m_uiIndex,
715 						   LocalObject.m_uiSubindex,
716 						   pData, LocalObject.m_uiSize);
717 
718 			vfree(pData);
719 
720 			iRet = (int)EplRet;
721 			break;
722 		}
723 
724 	case EPLLIN_CMD_READ_OBJECT:
725 		{
726 			tEplLinSdoObject SdoObject;
727 			void *pData;
728 			tEplLinSdoBufHeader *pBufHeader;
729 			tEplSdoComConHdl *pSdoComConHdl;
730 
731 			iErr =
732 			    copy_from_user(&SdoObject, (const void *)ulArg_p,
733 					   sizeof(SdoObject));
734 			if (iErr != 0) {
735 				iRet = -EIO;
736 				goto Exit;
737 			}
738 
739 			if ((SdoObject.m_le_pData == NULL)
740 			    || (SdoObject.m_uiSize == 0)) {
741 				iRet = (int)kEplApiInvalidParam;
742 				goto Exit;
743 			}
744 
745 			pBufHeader =
746 			    (tEplLinSdoBufHeader *)
747 			    vmalloc(sizeof(tEplLinSdoBufHeader) +
748 				    SdoObject.m_uiSize);
749 			if (pBufHeader == NULL) {	// no memory available
750 				iRet = -ENOMEM;
751 				goto Exit;
752 			}
753 			// initiate temporary buffer
754 			pBufHeader->m_pUserArg = SdoObject.m_pUserArg;	// original user argument pointer
755 			pBufHeader->m_pData = SdoObject.m_le_pData;	// original data pointer from app
756 			pData = pBufHeader + sizeof(tEplLinSdoBufHeader);
757 
758 			if (SdoObject.m_fValidSdoComConHdl != FALSE) {
759 				pSdoComConHdl = &SdoObject.m_SdoComConHdl;
760 			} else {
761 				pSdoComConHdl = NULL;
762 			}
763 
764 			EplRet =
765 			    EplApiReadObject(pSdoComConHdl,
766 					     SdoObject.m_uiNodeId,
767 					     SdoObject.m_uiIndex,
768 					     SdoObject.m_uiSubindex, pData,
769 					     &SdoObject.m_uiSize,
770 					     SdoObject.m_SdoType, pBufHeader);
771 
772 			// return actual SDO handle (SdoObject.m_SdoComConHdl)
773 			iErr = put_user(SdoObject.m_SdoComConHdl,
774 					(unsigned int *)(ulArg_p +
775 							 (unsigned long)
776 							 &SdoObject.
777 							 m_SdoComConHdl -
778 							 (unsigned long)
779 							 &SdoObject));
780 			if (iErr != 0) {
781 				iRet = -EIO;
782 				goto Exit;
783 			}
784 
785 			if (EplRet == kEplSuccessful) {
786 				iErr =
787 				    copy_to_user(SdoObject.m_le_pData, pData,
788 						 SdoObject.m_uiSize);
789 
790 				vfree(pBufHeader);
791 
792 				if (iErr != 0) {
793 					iRet = -EIO;
794 					goto Exit;
795 				}
796 				// return actual size (SdoObject.m_uiSize)
797 				iErr = put_user(SdoObject.m_uiSize,
798 						(unsigned int *)(ulArg_p +
799 								 (unsigned long)
800 								 &SdoObject.
801 								 m_uiSize -
802 								 (unsigned long)
803 								 &SdoObject));
804 				if (iErr != 0) {
805 					iRet = -EIO;
806 					goto Exit;
807 				}
808 			} else if (EplRet != kEplApiTaskDeferred) {	// error ocurred
809 				vfree(pBufHeader);
810 				if (iErr != 0) {
811 					iRet = -EIO;
812 					goto Exit;
813 				}
814 			}
815 
816 			iRet = (int)EplRet;
817 			break;
818 		}
819 
820 	case EPLLIN_CMD_WRITE_OBJECT:
821 		{
822 			tEplLinSdoObject SdoObject;
823 			void *pData;
824 			tEplLinSdoBufHeader *pBufHeader;
825 			tEplSdoComConHdl *pSdoComConHdl;
826 
827 			iErr =
828 			    copy_from_user(&SdoObject, (const void *)ulArg_p,
829 					   sizeof(SdoObject));
830 			if (iErr != 0) {
831 				iRet = -EIO;
832 				goto Exit;
833 			}
834 
835 			if ((SdoObject.m_le_pData == NULL)
836 			    || (SdoObject.m_uiSize == 0)) {
837 				iRet = (int)kEplApiInvalidParam;
838 				goto Exit;
839 			}
840 
841 			pBufHeader =
842 			    (tEplLinSdoBufHeader *)
843 			    vmalloc(sizeof(tEplLinSdoBufHeader) +
844 				    SdoObject.m_uiSize);
845 			if (pBufHeader == NULL) {	// no memory available
846 				iRet = -ENOMEM;
847 				goto Exit;
848 			}
849 			// initiate temporary buffer
850 			pBufHeader->m_pUserArg = SdoObject.m_pUserArg;	// original user argument pointer
851 			pBufHeader->m_pData = SdoObject.m_le_pData;	// original data pointer from app
852 			pData = pBufHeader + sizeof(tEplLinSdoBufHeader);
853 
854 			iErr =
855 			    copy_from_user(pData, SdoObject.m_le_pData,
856 					   SdoObject.m_uiSize);
857 
858 			if (iErr != 0) {
859 				iRet = -EIO;
860 				goto Exit;
861 			}
862 
863 			if (SdoObject.m_fValidSdoComConHdl != FALSE) {
864 				pSdoComConHdl = &SdoObject.m_SdoComConHdl;
865 			} else {
866 				pSdoComConHdl = NULL;
867 			}
868 
869 			EplRet =
870 			    EplApiWriteObject(pSdoComConHdl,
871 					      SdoObject.m_uiNodeId,
872 					      SdoObject.m_uiIndex,
873 					      SdoObject.m_uiSubindex, pData,
874 					      SdoObject.m_uiSize,
875 					      SdoObject.m_SdoType, pBufHeader);
876 
877 			// return actual SDO handle (SdoObject.m_SdoComConHdl)
878 			iErr = put_user(SdoObject.m_SdoComConHdl,
879 					(unsigned int *)(ulArg_p +
880 							 (unsigned long)
881 							 &SdoObject.
882 							 m_SdoComConHdl -
883 							 (unsigned long)
884 							 &SdoObject));
885 			if (iErr != 0) {
886 				iRet = -EIO;
887 				goto Exit;
888 			}
889 
890 			if (EplRet != kEplApiTaskDeferred) {	// succeeded or error ocurred, but task not deferred
891 				vfree(pBufHeader);
892 			}
893 
894 			iRet = (int)EplRet;
895 			break;
896 		}
897 
898 		// ----------------------------------------------------------
899 	case EPLLIN_CMD_FREE_SDO_CHANNEL:
900 		{
901 			// forward SDO handle to EPL stack
902 			EplRet =
903 			    EplApiFreeSdoChannel((tEplSdoComConHdl) ulArg_p);
904 
905 			iRet = (int)EplRet;
906 			break;
907 		}
908 
909 #if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
910 		// ----------------------------------------------------------
911 	case EPLLIN_CMD_MN_TRIGGER_STATE_CHANGE:
912 		{
913 			tEplLinNodeCmdObject NodeCmdObject;
914 
915 			iErr =
916 			    copy_from_user(&NodeCmdObject,
917 					   (const void *)ulArg_p,
918 					   sizeof(NodeCmdObject));
919 			if (iErr != 0) {
920 				iRet = -EIO;
921 				goto Exit;
922 			}
923 
924 			EplRet =
925 			    EplApiMnTriggerStateChange(NodeCmdObject.m_uiNodeId,
926 						       NodeCmdObject.
927 						       m_NodeCommand);
928 			iRet = (int)EplRet;
929 			break;
930 		}
931 #endif
932 
933 		// ----------------------------------------------------------
934 	case EPLLIN_CMD_GET_EVENT:
935 		{
936 			tEplLinEvent Event;
937 
938 			// save event structure
939 			iErr =
940 			    copy_from_user(&Event, (const void *)ulArg_p,
941 					   sizeof(Event));
942 			if (iErr != 0) {
943 				iRet = -EIO;
944 				goto Exit;
945 			}
946 			// save return code from application's event callback function
947 			RetCbEvent_g = Event.m_RetCbEvent;
948 
949 			if (RetCbEvent_g == kEplShutdown) {
950 				// pass control to event queue kernel thread, but signal termination
951 				atomic_set(&AtomicEventState_g,
952 					   EVENT_STATE_TERM);
953 				wake_up_interruptible(&WaitQueueCbEvent_g);
954 				// exit with error -> EplApiProcess() will leave the infinite loop
955 				iRet = 1;
956 				goto Exit;
957 			}
958 			// pass control to event queue kernel thread
959 			atomic_set(&AtomicEventState_g, EVENT_STATE_IOCTL);
960 			wake_up_interruptible(&WaitQueueCbEvent_g);
961 
962 			// fall asleep itself in own wait queue
963 			iErr = wait_event_interruptible(WaitQueueProcess_g,
964 							(atomic_read
965 							 (&AtomicEventState_g)
966 							 == EVENT_STATE_READY)
967 							||
968 							(atomic_read
969 							 (&AtomicEventState_g)
970 							 == EVENT_STATE_TERM));
971 			if (iErr != 0) {	// waiting was interrupted by signal
972 				// pass control to event queue kernel thread, but signal termination
973 				atomic_set(&AtomicEventState_g,
974 					   EVENT_STATE_TERM);
975 				wake_up_interruptible(&WaitQueueCbEvent_g);
976 				// exit with this error -> EplApiProcess() will leave the infinite loop
977 				iRet = iErr;
978 				goto Exit;
979 			} else if (atomic_read(&AtomicEventState_g) == EVENT_STATE_TERM) {	// termination in progress
980 				// pass control to event queue kernel thread, but signal termination
981 				wake_up_interruptible(&WaitQueueCbEvent_g);
982 				// exit with this error -> EplApiProcess() will leave the infinite loop
983 				iRet = 1;
984 				goto Exit;
985 			}
986 			// copy event to user space
987 			iErr =
988 			    copy_to_user(Event.m_pEventType, &EventType_g,
989 					 sizeof(EventType_g));
990 			if (iErr != 0) {	// not all data could be copied
991 				iRet = -EIO;
992 				goto Exit;
993 			}
994 			// $$$ d.k. perform SDO event processing
995 			if (EventType_g == kEplApiEventSdo) {
996 				void *pData;
997 				tEplLinSdoBufHeader *pBufHeader;
998 
999 				pBufHeader =
1000 				    (tEplLinSdoBufHeader *) pEventArg_g->m_Sdo.
1001 				    m_pUserArg;
1002 				pData =
1003 				    pBufHeader + sizeof(tEplLinSdoBufHeader);
1004 
1005 				if (pEventArg_g->m_Sdo.m_SdoAccessType ==
1006 				    kEplSdoAccessTypeRead) {
1007 					// copy read data to user space
1008 					iErr =
1009 					    copy_to_user(pBufHeader->m_pData,
1010 							 pData,
1011 							 pEventArg_g->m_Sdo.
1012 							 m_uiTransferredByte);
1013 					if (iErr != 0) {	// not all data could be copied
1014 						iRet = -EIO;
1015 						goto Exit;
1016 					}
1017 				}
1018 				pEventArg_g->m_Sdo.m_pUserArg =
1019 				    pBufHeader->m_pUserArg;
1020 				vfree(pBufHeader);
1021 			}
1022 
1023 			iErr =
1024 			    copy_to_user(Event.m_pEventArg, pEventArg_g,
1025 					 min(sizeof(tEplApiEventArg),
1026 					     Event.m_uiEventArgSize));
1027 			if (iErr != 0) {	// not all data could be copied
1028 				iRet = -EIO;
1029 				goto Exit;
1030 			}
1031 			// return to EplApiProcess(), which will call the application's event callback function
1032 			iRet = 0;
1033 
1034 			break;
1035 		}
1036 
1037 		// ----------------------------------------------------------
1038 	case EPLLIN_CMD_PI_SETUP:
1039 		{
1040 			EplRet = EplApiProcessImageSetup();
1041 			iRet = (int)EplRet;
1042 
1043 			break;
1044 		}
1045 
1046 		// ----------------------------------------------------------
1047 	case EPLLIN_CMD_PI_IN:
1048 		{
1049 			tEplApiProcessImage ProcessImageIn;
1050 
1051 			// save process image structure
1052 			iErr =
1053 			    copy_from_user(&ProcessImageIn,
1054 					   (const void *)ulArg_p,
1055 					   sizeof(ProcessImageIn));
1056 			if (iErr != 0) {
1057 				iRet = -EIO;
1058 				goto Exit;
1059 			}
1060 			// pass control to event queue kernel thread
1061 			atomic_set(&AtomicSyncState_g, EVENT_STATE_IOCTL);
1062 
1063 			// fall asleep itself in own wait queue
1064 			iErr = wait_event_interruptible(WaitQueuePI_In_g,
1065 							(atomic_read
1066 							 (&AtomicSyncState_g) ==
1067 							 EVENT_STATE_READY)
1068 							||
1069 							(atomic_read
1070 							 (&AtomicSyncState_g) ==
1071 							 EVENT_STATE_TERM));
1072 			if (iErr != 0) {	// waiting was interrupted by signal
1073 				// pass control to sync kernel thread, but signal termination
1074 				atomic_set(&AtomicSyncState_g,
1075 					   EVENT_STATE_TERM);
1076 				wake_up_interruptible(&WaitQueueCbSync_g);
1077 				// exit with this error -> application will leave the infinite loop
1078 				iRet = iErr;
1079 				goto Exit;
1080 			} else if (atomic_read(&AtomicSyncState_g) == EVENT_STATE_TERM) {	// termination in progress
1081 				// pass control to sync kernel thread, but signal termination
1082 				wake_up_interruptible(&WaitQueueCbSync_g);
1083 				// exit with this error -> application will leave the infinite loop
1084 				iRet = 1;
1085 				goto Exit;
1086 			}
1087 			// exchange process image
1088 			EplRet = EplApiProcessImageExchangeIn(&ProcessImageIn);
1089 
1090 			// return to EplApiProcessImageExchangeIn()
1091 			iRet = (int)EplRet;
1092 
1093 			break;
1094 		}
1095 
1096 		// ----------------------------------------------------------
1097 	case EPLLIN_CMD_PI_OUT:
1098 		{
1099 			tEplApiProcessImage ProcessImageOut;
1100 
1101 			// save process image structure
1102 			iErr =
1103 			    copy_from_user(&ProcessImageOut,
1104 					   (const void *)ulArg_p,
1105 					   sizeof(ProcessImageOut));
1106 			if (iErr != 0) {
1107 				iRet = -EIO;
1108 				goto Exit;
1109 			}
1110 
1111 			if (atomic_read(&AtomicSyncState_g) !=
1112 			    EVENT_STATE_READY) {
1113 				iRet = (int)kEplInvalidOperation;
1114 				goto Exit;
1115 			}
1116 			// exchange process image
1117 			EplRet =
1118 			    EplApiProcessImageExchangeOut(&ProcessImageOut);
1119 
1120 			// pass control to sync kernel thread
1121 			atomic_set(&AtomicSyncState_g, EVENT_STATE_TERM);
1122 			wake_up_interruptible(&WaitQueueCbSync_g);
1123 
1124 			// return to EplApiProcessImageExchangeout()
1125 			iRet = (int)EplRet;
1126 
1127 			break;
1128 		}
1129 
1130 		// ----------------------------------------------------------
1131 	case EPLLIN_CMD_NMT_COMMAND:
1132 		{
1133 			// forward NMT command to EPL stack
1134 			EplRet = EplApiExecNmtCommand((tEplNmtEvent) ulArg_p);
1135 
1136 			iRet = (int)EplRet;
1137 
1138 			break;
1139 		}
1140 
1141 		// ----------------------------------------------------------
1142 	default:
1143 		{
1144 			break;
1145 		}
1146 	}
1147 
1148       Exit:
1149 
1150 //    TRACE1("EPL: - EplLinIoctl (iRet=%d)\n", iRet);
1151 	return (iRet);
1152 
1153 }
1154 
1155 //=========================================================================//
1156 //                                                                         //
1157 //          P R I V A T E   F U N C T I O N S                              //
1158 //                                                                         //
1159 //=========================================================================//
1160 
EplLinCbEvent(tEplApiEventType EventType_p,tEplApiEventArg * pEventArg_p,void GENERIC * pUserArg_p)1161 tEplKernel PUBLIC EplLinCbEvent(tEplApiEventType EventType_p,	// IN: event type (enum)
1162 				tEplApiEventArg * pEventArg_p,	// IN: event argument (union)
1163 				void GENERIC * pUserArg_p)
1164 {
1165 	tEplKernel EplRet = kEplSuccessful;
1166 	int iErr;
1167 
1168 	// block any further call to this function, i.e. enter critical section
1169 	iErr = down_interruptible(&SemaphoreCbEvent_g);
1170 	if (iErr != 0) {	// waiting was interrupted by signal
1171 		EplRet = kEplShutdown;
1172 		goto Exit;
1173 	}
1174 	// wait for EplApiProcess() to call ioctl
1175 	// normally it should be waiting already for us to pass a new event
1176 	iErr = wait_event_interruptible(WaitQueueCbEvent_g,
1177 					(atomic_read(&AtomicEventState_g) ==
1178 					 EVENT_STATE_IOCTL)
1179 					|| (atomic_read(&AtomicEventState_g) ==
1180 					    EVENT_STATE_TERM));
1181 	if ((iErr != 0) || (atomic_read(&AtomicEventState_g) == EVENT_STATE_TERM)) {	// waiting was interrupted by signal
1182 		EplRet = kEplShutdown;
1183 		goto LeaveCriticalSection;
1184 	}
1185 	// save event information for ioctl
1186 	EventType_g = EventType_p;
1187 	pEventArg_g = pEventArg_p;
1188 
1189 	// pass control to application's event callback function, i.e. EplApiProcess()
1190 	atomic_set(&AtomicEventState_g, EVENT_STATE_READY);
1191 	wake_up_interruptible(&WaitQueueProcess_g);
1192 
1193 	// now, the application's event callback function processes the event
1194 
1195 	// wait for completion of application's event callback function, i.e. EplApiProcess() calls ioctl again
1196 	iErr = wait_event_interruptible(WaitQueueCbEvent_g,
1197 					(atomic_read(&AtomicEventState_g) ==
1198 					 EVENT_STATE_IOCTL)
1199 					|| (atomic_read(&AtomicEventState_g) ==
1200 					    EVENT_STATE_TERM));
1201 	if ((iErr != 0) || (atomic_read(&AtomicEventState_g) == EVENT_STATE_TERM)) {	// waiting was interrupted by signal
1202 		EplRet = kEplShutdown;
1203 		goto LeaveCriticalSection;
1204 	}
1205 	// read return code from application's event callback function
1206 	EplRet = RetCbEvent_g;
1207 
1208       LeaveCriticalSection:
1209 	up(&SemaphoreCbEvent_g);
1210 
1211       Exit:
1212 	// check if NMT_GS_OFF is reached
1213 	if (EventType_p == kEplApiEventNmtStateChange) {
1214 		if (pEventArg_p->m_NmtStateChange.m_NewNmtState == kEplNmtGsOff) {	// NMT state machine was shut down
1215 			TRACE0("EPL:   EplLinCbEvent(NMT_GS_OFF)\n");
1216 			uiEplState_g = EPL_STATE_SHUTDOWN;
1217 			atomic_set(&AtomicEventState_g, EVENT_STATE_TERM);
1218 			wake_up(&WaitQueueRelease_g);
1219 		} else {	// NMT state machine is running
1220 			uiEplState_g = EPL_STATE_RUNNING;
1221 		}
1222 	}
1223 
1224 	return EplRet;
1225 }
1226 
EplLinCbSync(void)1227 tEplKernel PUBLIC EplLinCbSync(void)
1228 {
1229 	tEplKernel EplRet = kEplSuccessful;
1230 	int iErr;
1231 
1232 	// check if user process waits for sync
1233 	if (atomic_read(&AtomicSyncState_g) == EVENT_STATE_IOCTL) {
1234 		// pass control to application, i.e. EplApiProcessImageExchangeIn()
1235 		atomic_set(&AtomicSyncState_g, EVENT_STATE_READY);
1236 		wake_up_interruptible(&WaitQueuePI_In_g);
1237 
1238 		// now, the application processes the sync event
1239 
1240 		// wait for call of EplApiProcessImageExchangeOut()
1241 		iErr = wait_event_interruptible(WaitQueueCbSync_g,
1242 						(atomic_read(&AtomicSyncState_g)
1243 						 == EVENT_STATE_IOCTL)
1244 						||
1245 						(atomic_read(&AtomicSyncState_g)
1246 						 == EVENT_STATE_TERM));
1247 		if ((iErr != 0) || (atomic_read(&AtomicEventState_g) == EVENT_STATE_IOCTL)) {	// waiting was interrupted by signal or application called wrong function
1248 			EplRet = kEplShutdown;
1249 		}
1250 	} else {		// application is currently not waiting for sync
1251 		// continue without interruption
1252 		// TPDO are set valid by caller (i.e. EplEventkProcess())
1253 	}
1254 
1255 	TGT_DBG_SIGNAL_TRACE_POINT(1);
1256 
1257 	return EplRet;
1258 }
1259 
1260 // EOF
1261