/** * @ingroup shm * @file * * Interface of the @ref shm "RTAI SHM module". * * @author Paolo Mantegazza * * @note Copyright © 1999-2017 Paolo Mantegazza * @note Copyright © 2019 Alec Ari * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* ACKNOWLEDGMENTS: - The suggestion and the code for mmapping at a user specified address is due to Trevor Woolven (trevw@zentropix.com). */ #ifndef _RTAI_SHM_H #define _RTAI_SHM_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /** @addtogroup shm *@{*/ #define GLOBAL_HEAP_ID 0x9ac6d9e7 // nam2num("RTGLBH"); #define USE_VMALLOC 0 #define USE_GFP_KERNEL 1 #define USE_GFP_ATOMIC 2 #define USE_GFP_DMA 3 /** * Allocate a chunk of memory to be shared inter-intra kernel modules and * Linux processes. * * @internal * * rtai_kalloc is used to allocate shared memory from kernel space. * * @param name is an unsigned long identifier; * * @param size is the amount of required shared memory; * * rtai_kmalloc is a legacy helper macro, the real job is carried out by a * call to rt_shm_alloc() with the same name, size and with vmalloc support. * This function should not be used in newly developed applications. See * rt_shm_alloc for more details. * * @returns a valid address on succes, 0 on failure. * */ #define rtai_kmalloc(name, size) \ rt_shm_alloc(name, size, USE_VMALLOC) // legacy /** * Free a chunk of shared memory being shared inter-intra kernel modules and * Linux processes. * * rtai_kfree is used to free a shared memory chunk from kernel space. * * @param name is the unsigned long identifier used when the memory was * allocated; * * rtai_kfree is a legacy helper macro, the real job is carried out by a * call to rt_shm_free with the same name. This function should not be used * in newly developed applications. See rt_shm_free for more details. * * @returns the size of the succesfully freed memory, 0 on failure. * */ #define rtai_kfree(name) \ rt_shm_free(name) // legacy #if defined(__KERNEL__) #include #include #include #include #define UVIRT_TO_KVA(adr) uvirt_to_kva(pgd_offset_k(adr), (adr)) static inline int remap_page_range(struct vm_area_struct *vma, unsigned long uvaddr, unsigned long paddr, unsigned long size, pgprot_t prot) { return remap_pfn_range(vma, uvaddr, paddr >> PAGE_SHIFT, size, prot); } #include #include static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) { if (!pgd_none(*pgd) && !pgd_bad(*pgd)) { pmd_t *pmd; pmd = pmd_offset(pud_offset((void *)pgd, adr), adr); if (!pmd_none(*pmd)) { pte_t *ptep, pte; ptep = pte_offset_kernel(pmd, adr); pte = *ptep; if (pte_present(pte)) { return (((unsigned long)page_address(pte_page(pte))) | (adr & (PAGE_SIZE - 1))); } } } return 0UL; } static inline unsigned long kvirt_to_pa(unsigned long adr) { return virt_to_phys((void *)uvirt_to_kva(pgd_offset_k(adr), adr)); } int __rtai_shm_init(void); void __rtai_shm_exit(void); void *rt_shm_alloc(unsigned long name, int size, int suprt); #define rt_shm_alloc_adr(adr, name, size) \ rt_shm_alloc(name, size, suprt) RTAI_SYSCALL_MODE int rt_shm_free(unsigned long name); void *rt_heap_open(unsigned long name, int size, int suprt); #define rt_heap_open_adr(adr, name, size, suprt) \ rt_heap_open(name, size, suprt) RTAI_SYSCALL_MODE void *rt_halloc(int size); RTAI_SYSCALL_MODE void rt_hfree(void *addr); RTAI_SYSCALL_MODE void *rt_named_halloc(unsigned long name, int size); RTAI_SYSCALL_MODE void rt_named_hfree(void *addr); void *rt_named_malloc(unsigned long name, int size); void rt_named_free(void *addr); void *rvmalloc(unsigned long size); void rvfree(void *mem, unsigned long size); int rvmmap(void *mem, unsigned long memsize, struct vm_area_struct *vma); void *rkmalloc(int *size, int suprt); void rkfree(void *mem, unsigned long size); int rkmmap(void *mem, unsigned long memsize, struct vm_area_struct *vma); #else /* !__KERNEL__ */ #include #include #include #include #include //#define SHM_USE_LXRT #define RTAI_SHM_DEV "/dev/rtai_shm" RTAI_PROTO (void *, _rt_shm_alloc, (void *start, unsigned long name, int size, int suprt, int isheap)) { int hook; void *adr = NULL; if ((hook = open(RTAI_SHM_DEV, O_RDWR)) <= 0) { return NULL; } else { struct { unsigned long name; long arg, suprt; } arg = { name, size, suprt }; #ifdef SHM_USE_LXRT if ((size = rtai_lxrt(BIDX, SIZARG, SHM_ALLOC, &arg).i[LOW])) { #else if ((size = ioctl(hook, SHM_ALLOC, (unsigned long)(&arg)))) { #endif if ((adr = mmap(start, size, PROT_WRITE | PROT_READ, MAP_SHARED | MAP_LOCKED, hook, 0)) == MAP_FAILED) {; #ifdef SHM_USE_LXRT rtai_lxrt(BIDX, sizeof(name), SHM_FREE, &name); #else ioctl(hook, SHM_FREE, &name); #endif } else if (isheap) { arg.arg = (unsigned long)adr; #ifdef SHM_USE_LXRT rtai_lxrt(BIDX, SIZARG, HEAP_SET, &arg); #else ioctl(hook, HEAP_SET, &arg); #endif } } } close(hook); return adr; } #define rt_shm_alloc(name, size, suprt) \ _rt_shm_alloc(0, name, size, suprt, 0) #define rt_heap_open(name, size, suprt) \ _rt_shm_alloc(0, name, size, suprt, 1) /** * Allocate a chunk of memory to be shared inter-intra kernel modules and * Linux processes. * * @internal * * rtai_malloc is used to allocate shared memory from user space. * * @param name is an unsigned long identifier; * * @param size is the amount of required shared memory; * * rtai_malloc is a legacy helper macro, the real job is carried out by a * call to rt_shm_alloc() with the same name, size and with vmalloc support. * This function should not be used in newly developed applications. See * rt_shm_alloc fro more details. * * @returns a valid address on succes, on failure: 0 if it was unable to * allocate any memory, MAP_FAILED if it was possible to allocate the * required memory but failed to mmap it to user space, in which case the * allocated memory is freed anyhow. * */ #define rtai_malloc(name, size) \ _rt_shm_alloc(0, name, size, USE_VMALLOC, 0) // legacy /** * Allocate a chunk of memory to be shared inter-intra kernel modules and Linux * processes. * * rt_shm_alloc_adr is used to allocate in user space. * * @param start_address is a user desired address where the allocated memory * should be mapped in user space; * * @param name is an unsigned long identifier; * * @param size is the amount of required shared memory. * * @param suprt is the kernel allocation method to be used, it can be: * - USE_VMALLOC, use vmalloc; * - USE_GFP_KERNEL, use kmalloc with GFP_KERNEL; * - USE_GFP_ATOMIC, use kmalloc with GFP_ATOMIC; * - USE_GFP_DMA, use kmalloc with GFP_DMA. * * Since @c name can be a clumsy identifier, services are provided to * convert 6 characters identifiers to unsigned long, and vice versa. * * @see the functions nam2num() and num2nam(). * * It must be remarked that only the very first call does a real allocation, * any subsequent call to allocate with the same name from anywhere will just * increase the usage count and map the area to user space, or return the * related pointer to the already allocated space in kernel space. The function * returns a pointer to the allocated memory, appropriately mapped to the memory * space in use. So if one is really sure that the named shared memory has been * allocated already parameters size and suprt are not used and can be * assigned any value. * * @note If the same process calls rtai_malloc_adr and rtai_malloc() twice in * the same process it get a zero return value on the second call. * * @returns a valid address on succes, on failure: 0 if it was unable to * allocate any memory, MAP_FAILED if it was possible to allocate the * required memory but failed to mmap it to user space, in which case the * allocated memory is freed anyhow. * */ #define rt_shm_alloc_adr(start_address, name, size, suprt) \ _rt_shm_alloc(start_address, name, size, suprt, 0) #define rt_heap_open_adr(start, name, size, suprt) \ _rt_shm_alloc(start, name, size, suprt, 1) /** * Allocate a chunk of memory to be shared inter-intra kernel modules and * Linux processes. * * @internal * * rtai_malloc_adr is used to allocate shared memory from user space. * * @param start_address is the adr were the shared memory should be mapped. * * @param name is an unsigned long identifier; * * @param size is the amount of required shared memory; * * rtai_malloc_adr is a legacy helper macro, the real job is carried out by a * call to rt_shm_alloc_adr() with the same name, size and with vmalloc support. * This function should not be used in newly developed applications. See * rt_shm_alloc_adr for more details. * * @returns a valid address on succes, 0 on failure. * */ #define rtai_malloc_adr(start_address, name, size) \ _rt_shm_alloc(start_address, name, size, USE_VMALLOC, 0) // legacy RTAI_PROTO(int, rt_shm_free, (unsigned long name)) { int hook, size; struct { void *nameadr; } arg = { &name }; if ((hook = open(RTAI_SHM_DEV, O_RDWR)) <= 0) { return 0; } // no SHM_FREE needed, we release it all and munmap will do it through // the vma close operation provided by shm.c #ifdef SHM_USE_LXRT if ((size = rtai_lxrt(BIDX, SIZARG, SHM_SIZE, &arg).i[LOW])) { #else if ((size = ioctl(hook, SHM_SIZE, (unsigned long)&arg))) { #endif if (munmap((void *)name, size)) { size = 0; } } close(hook); return size; } /** * Free a chunk of shared memory being shared inter-intra * kernel modules and Linux processes. * * rtai_free is used to free a shared memory chunk from user space. * * @param name is the unsigned long identifier used when the memory was * allocated; * * @param adr is not used. * * rtai_free is a legacy helper macro, the real job is carried out by a * call to rt_shm_free with the same name. This function should not be used * in newly developed applications. See rt_shm_alloc_adr for more details. * * @returns the size of the succesfully freed memory, 0 on failure. * */ #define rtai_free(name, adr) \ rt_shm_free(name) // legacy RTAI_PROTO(void *, rt_halloc, (int size)) { struct { long size; } arg = { size }; return rtai_lxrt(BIDX, SIZARG, HEAP_ALLOC, &arg).v[LOW]; } RTAI_PROTO(void, rt_hfree, (void *addr)) { struct { void *addr; } arg = { addr }; rtai_lxrt(BIDX, SIZARG, HEAP_FREE, &arg); } RTAI_PROTO(void *, rt_named_halloc, (unsigned long name, int size)) { struct { unsigned long name; long size; } arg = { name, size }; return rtai_lxrt(BIDX, SIZARG, HEAP_NAMED_ALLOC, &arg).v[LOW]; } RTAI_PROTO(void, rt_named_hfree, (void *addr)) { struct { void *addr; } arg = { addr }; rtai_lxrt(BIDX, SIZARG, HEAP_NAMED_FREE, &arg); } RTAI_PROTO(void *, rt_malloc, (int size)) { struct { long size; } arg = { size }; return rtai_lxrt(BIDX, SIZARG, MALLOC, &arg).v[LOW]; } RTAI_PROTO(void, rt_free, (void *addr)) { struct { void *addr; } arg = { addr }; rtai_lxrt(BIDX, SIZARG, FREE, &arg); } RTAI_PROTO(void *, rt_named_malloc, (unsigned long name, int size)) { struct { unsigned long name; long size; } arg = { name, size }; return rtai_lxrt(BIDX, SIZARG, NAMED_MALLOC, &arg).v[LOW]; } RTAI_PROTO(void, rt_named_free, (void *addr)) { struct { void *addr; } arg = { addr }; rtai_lxrt(BIDX, SIZARG, NAMED_FREE, &arg); } #endif /* __KERNEL__ */ /** * Close a real time group heap being shared inter-intra kernel modules and * Linux processes. * * @internal * * rt_heap_close is used to close a previously opened real time group heap. * * @param name is the unsigned long identifier used to identify the heap. * * @param adr is not used. * * Analogously to what done by any allocation function this group real time * heap closing call have just the effect of decrementing a usage count, * unmapping any user space heap being closed, till the last is done, as that * is the one the really closes the group heap, freeing any allocated memory. * * @returns the size of the succesfully freed heap, 0 on failure. * */ #define rt_heap_close(name, adr) rt_shm_free(name) // aliases in use already, different heads different choices #define rt_heap_init rt_heap_open #define rt_heap_create rt_heap_open #define rt_heap_acquire rt_heap_open #define rt_heap_init_adr rt_heap_open_adr #define rt_heap_create_adr rt_heap_open_adr #define rt_heap_acquire_adr rt_heap_open_adr #define rt_heap_delete rt_heap_close #define rt_heap_destroy rt_heap_close #define rt_heap_release rt_heap_close // these have no aliases, and never will /** * Open the global real time heap to be shared inter-intra kernel modules and * Linux processes. * * @internal * * rt_global_heap_open is used to open the global real time heap. * * The global heap is created by the shared memory module and its opening is * needed in user space to map it to the process address space. In kernel * space opening the global heap in a task is not required but should be done * anyhow, both for symmetry and to register its usage. * */ #define rt_global_heap_open() rt_heap_open(GLOBAL_HEAP_ID, 0, 0) /** * Close the global real time heap being shared inter-intra kernel modules and * Linux processes. * * @internal * * rt_global_heap_close is used to close the global real time heap. * * Closing a global heap in user space has just the effect of deregistering * its use and unmapping the related memory from a process address space. * In kernel tasks just the deregistration is performed. * The global real time heap is destroyed just a the rmmoding of the shared * memory module. * */ #define rt_global_heap_close() rt_heap_close(GLOBAL_HEAP_ID, 0) /*@}*/ #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* !_RTAI_SHM_H */