| /* |
| <:copyright-BRCM:2016:DUAL/GPL:standard |
| |
| Broadcom Proprietary and Confidential.(c) 2016 Broadcom |
| All Rights Reserved |
| |
| Unless you and Broadcom execute a separate written software license |
| agreement governing use of this software, this software is licensed |
| to you under the terms of the GNU General Public License version 2 |
| (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, |
| with the following added to such license: |
| |
| As a special exception, the copyright holders of this software give |
| you permission to link this software with independent modules, and |
| to copy and distribute the resulting executable under terms of your |
| choice, provided that you also meet, for each linked independent |
| module, the terms and conditions of the license of that module. |
| An independent module is a module which is not derived from this |
| software. The special exception does not apply to any modifications |
| of the software. |
| |
| Not withstanding the above, under no circumstances may you combine |
| this software in any way with any other Broadcom software provided |
| under a license other than the GPL, without Broadcom's express prior |
| written consent. |
| |
| :> |
| */ |
| |
| #include "bcmos_system.h" |
| #ifdef BCMOS_MSG_QUEUE_DOMAIN_SOCKET |
| #include <sys/un.h> |
| #endif |
| #include <sys/resource.h> |
| |
| /* task control blocks */ |
| extern STAILQ_HEAD(task_list, bcmos_task) task_list; |
| |
| /* global OS lock */ |
| extern bcmos_mutex bcmos_res_lock; |
| |
| /* |
| * Init |
| */ |
| |
| #define TIMER_SIG SIGRTMIN |
| |
| /* Initialize system library */ |
| bcmos_errno bcmos_sys_init(void) |
| { |
| return BCM_ERR_OK; |
| } |
| |
| /* Clean-up system library */ |
| void bcmos_sys_exit(void) |
| { |
| } |
| |
| /* |
| * Task management |
| */ |
| |
| /* |
| * Default task handler |
| */ |
| |
| /* Create a new task */ |
| bcmos_errno bcmos_task_create(bcmos_task *task, const bcmos_task_parm *parm) |
| { |
| F_bcmos_task_handler handler; |
| pthread_attr_t pthread_attr; |
| struct sched_param pthread_sched_param = {}; |
| int pthread_prio; |
| uint32_t stack_size; |
| void *data; |
| int rc; |
| |
| if (!task || !parm) |
| BCMOS_TRACE_RETURN(BCM_ERR_PARM, "task %p, parm %p\n", task, parm); |
| memset(task, 0, sizeof(*task)); |
| if (parm->handler) |
| { |
| /* "traditional task */ |
| handler = parm->handler; |
| data = (void *)parm->data; |
| } |
| else |
| { |
| /* "integrated" task */ |
| handler = bcmos_dft_task_handler; |
| data = task; |
| |
| /* Initialize and lock mutex to wait on */ |
| rc = bcmos_sem_create(&task->active_sem, 0, task->parm.flags, parm->name); |
| if (rc) |
| { |
| BCMOS_TRACE_ERR("Task %s: can't create active_sem. Error %s (%d)\n", |
| parm->name, bcmos_strerror(rc), rc); |
| return rc; |
| } |
| } |
| |
| task->parm = *parm; |
| /* Copy name to make sure that it is not released - in case it was on the stack */ |
| if (task->parm.name) |
| { |
| strncpy(task->name, task->parm.name, sizeof(task->name) - 1); |
| task->parm.name = task->name; |
| } |
| bcmos_fastlock_init(&task->active_lock, 0); |
| bcmos_mutex_lock(&bcmos_res_lock); |
| STAILQ_INSERT_TAIL(&task_list, task, list); |
| bcmos_mutex_unlock(&bcmos_res_lock); |
| task->magic = BCMOS_TASK_MAGIC; |
| /* pthread priorities are 1..32, 32 being the highest */ |
| pthread_prio = 32 - (int)parm->priority; |
| if (pthread_prio <= 0) |
| pthread_prio = 1; |
| stack_size = PTHREAD_STACK_MIN; |
| if (parm->stack_size && parm->stack_size > PTHREAD_STACK_MIN) |
| stack_size = parm->stack_size; |
| rc = pthread_attr_init(&pthread_attr); |
| pthread_sched_param.sched_priority = pthread_prio; |
| rc = rc ? rc : pthread_attr_setinheritsched(&pthread_attr, PTHREAD_EXPLICIT_SCHED); |
| rc = rc ? rc : pthread_attr_setschedpolicy(&pthread_attr, SCHED_RR); |
| rc = rc ? rc : pthread_attr_setschedparam(&pthread_attr, &pthread_sched_param); |
| rc = rc ? rc : pthread_attr_setstacksize(&pthread_attr, stack_size); |
| rc = rc ? rc : pthread_create(&task->sys_task.t, &pthread_attr, (void *(*)(void *))handler, data); |
| pthread_attr_destroy(&pthread_attr); |
| if (rc == EPERM) |
| { |
| BCMOS_TRACE_INFO("Thread %s: priority %d is ignored. Start as root to honor priorities\n", |
| parm->name, (int)parm->priority); |
| rc = pthread_create(&task->sys_task.t, NULL, (void *(*)(void *))handler, data); |
| } |
| if (rc) |
| { |
| bcmos_mutex_lock(&bcmos_res_lock); |
| STAILQ_REMOVE(&task_list, task, bcmos_task, list); |
| bcmos_mutex_unlock(&bcmos_res_lock); |
| task->magic = 0; |
| if (!parm->handler) |
| { |
| bcmos_sem_destroy(&task->active_sem); |
| } |
| BCMOS_TRACE_RETURN(BCM_ERR_SYSCALL_ERR, "%s (%d)\n", strerror(rc), rc); |
| } |
| return BCM_ERR_OK; |
| } |
| |
| /* Destroy task */ |
| bcmos_errno bcmos_task_destroy(bcmos_task *task) |
| { |
| void *res; |
| int rc_cancel; |
| int rc; |
| |
| if (task->magic != BCMOS_TASK_MAGIC) |
| { |
| return BCM_ERR_PARM; |
| } |
| task->destroy_request = BCMOS_TRUE; |
| task->magic = BCMOS_TASK_MAGIC_DESTROYED; |
| bcmos_mutex_lock(&bcmos_res_lock); |
| STAILQ_REMOVE(&task_list, task, bcmos_task, list); |
| bcmos_mutex_unlock(&bcmos_res_lock); |
| /* The task may be waiting on semaphore. Kick it */ |
| if (!task->parm.handler) |
| { |
| bcmos_sem_post(&task->active_sem); |
| } |
| rc_cancel = pthread_cancel(task->sys_task.t); |
| rc = pthread_join(task->sys_task.t, &res); |
| return (rc || ((!rc_cancel) && (res != PTHREAD_CANCELED))) ? BCM_ERR_SYSCALL_ERR : 0; |
| } |
| |
| /** Get current task |
| * \returns task handle or NULL if not in task context |
| */ |
| bcmos_task *bcmos_task_current(void) |
| { |
| pthread_t pt = pthread_self(); |
| bcmos_task *t, *tmp; |
| |
| STAILQ_FOREACH_SAFE(t, &task_list, list, tmp) |
| { |
| if (pthread_equal(pt, t->sys_task.t)) |
| break; |
| } |
| return t; |
| } |
| |
| /* |
| * Recursive mutex |
| */ |
| |
| /* Create recursive mutex */ |
| bcmos_errno bcmos_mutex_create(bcmos_mutex *mutex, uint32_t flags, const char *name) |
| { |
| int err; |
| err = pthread_mutexattr_init(&mutex->attr); |
| err = err ? err : pthread_mutexattr_settype(&mutex->attr, PTHREAD_MUTEX_RECURSIVE_NP); |
| err = err ? err : pthread_mutex_init(&mutex->m, &mutex->attr); |
| if (err) |
| BCMOS_TRACE_RETURN(BCM_ERR_SYSCALL_ERR, "errno=%s\n", strerror(err)); |
| return BCM_ERR_OK; |
| } |
| |
| /** Destroy mutex |
| * \param[in] mutex Mutex control block |
| */ |
| void bcmos_mutex_destroy(bcmos_mutex *mutex) |
| { |
| pthread_mutex_destroy(&mutex->m); |
| pthread_mutexattr_destroy(&mutex->attr); |
| } |
| |
| /* calculate absolute time equal to the current time + timeout */ |
| static inline void _bcmos_get_abs_time(struct timespec *ts, uint32_t timeout) |
| { |
| int rc; |
| rc = clock_gettime(CLOCK_REALTIME, ts); |
| BUG_ON(rc); |
| ts->tv_sec += timeout / 1000000; |
| ts->tv_nsec += (timeout % 1000000) * 1000; |
| if (ts->tv_nsec > 1000000000) |
| { |
| ts->tv_sec += ts->tv_nsec / 1000000000; |
| ts->tv_nsec %= 1000000000; |
| } |
| } |
| |
| /** Lock mutex |
| */ |
| void bcmos_mutex_lock(bcmos_mutex *mutex) |
| { |
| int rc; |
| rc = pthread_mutex_lock(&mutex->m); |
| BUG_ON(rc); |
| return; |
| } |
| |
| /** Release mutex |
| * \param[in] mutex Mutex control block |
| */ |
| void bcmos_mutex_unlock(bcmos_mutex *mutex) |
| { |
| int rc; |
| rc = pthread_mutex_unlock(&mutex->m); |
| if (rc) |
| BCMOS_TRACE_ERR("pthread_mutex_unlock()->%d\n", rc); |
| BUG_ON(rc); |
| } |
| |
| /* |
| * Semaphores |
| * Most of semaphore functions are inline |
| */ |
| |
| /* Create semaphore */ |
| bcmos_errno bcmos_sem_create(bcmos_sem *sem, uint32_t count, uint32_t flags, const char *name) |
| { |
| sem_init(&sem->s, 0, count); |
| return BCM_ERR_OK; |
| } |
| |
| /* Destroy semaphore */ |
| void bcmos_sem_destroy(bcmos_sem *sem) |
| { |
| sem_destroy(&sem->s); |
| } |
| |
| /* Decrement semaphore counter. Wait if the counter is 0 */ |
| bcmos_errno bcmos_sem_wait(bcmos_sem *sem, uint32_t timeout) |
| { |
| int rc; |
| struct timespec ts; |
| |
| /* Init end time if necessary */ |
| if (timeout && timeout != BCMOS_WAIT_FOREVER) |
| { |
| _bcmos_get_abs_time(&ts, timeout); |
| } |
| |
| do |
| { |
| if (timeout == BCMOS_WAIT_FOREVER) |
| { |
| rc = sem_wait(&sem->s); |
| } |
| else if (timeout) |
| { |
| rc = sem_timedwait(&sem->s, &ts); |
| } |
| else |
| { |
| rc = sem_trywait(&sem->s); |
| } |
| } while (rc && errno == EINTR); |
| |
| if (rc) |
| { |
| bcmos_errno err; |
| |
| rc = errno; |
| if (rc == ETIMEDOUT) |
| { |
| err = BCM_ERR_TIMEOUT; |
| } |
| else |
| { |
| err = BCM_ERR_INTERNAL; |
| BCMOS_TRACE_ERR("sem_wait()->%d\n", rc); |
| } |
| return err; |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| |
| /* |
| * Timers |
| */ |
| |
| /** Get current timestamp |
| * \returns the current system timestamp (us) |
| */ |
| uint32_t bcmos_timestamp(void) |
| { |
| struct timespec tp; |
| clock_gettime(CLOCK_MONOTONIC, &tp); |
| return (tp.tv_sec * 1000000 + tp.tv_nsec / 1000); |
| } |
| |
| /** Get current timestamp - 64 bit |
| * \returns the current system timestamp (us) |
| */ |
| uint64_t bcmos_timestamp64(void) |
| { |
| struct timespec tp; |
| clock_gettime(CLOCK_MONOTONIC, &tp); |
| return ((uint64_t)tp.tv_sec * 1000000LL + (uint64_t)tp.tv_nsec / 1000LL); |
| } |
| |
| /* |
| * Timer handlers |
| */ |
| |
| |
| |
| /* For posix we must create timer task, because there is no way |
| * to enforce protection between a signal handler and a thread |
| */ |
| static bcmos_task _bcmos_timer_task; |
| static sem_t _bcmos_timer_lock; |
| |
| static int _bcmos_tmr_task_handler(long data) |
| { |
| bcmos_sys_timer *timer = (bcmos_sys_timer *)data; |
| bcmos_task *this_task = bcmos_task_current(); |
| |
| while(!this_task->destroy_request) |
| { |
| sem_wait(&_bcmos_timer_lock); |
| timer->handler(timer->data); |
| } |
| this_task->destroyed = BCMOS_TRUE; |
| |
| return 0; |
| } |
| |
| /* timer signal handler */ |
| static void timer_sig_handler(int sig, siginfo_t *si, void *uc) |
| { |
| BUG_ON(si->si_code != SI_TIMER); |
| sem_post(&_bcmos_timer_lock); |
| } |
| |
| /* Create timer */ |
| bcmos_errno bcmos_sys_timer_create(bcmos_sys_timer *timer, bcmos_sys_timer_handler handler, void *data) |
| { |
| static bcmos_task_parm tmr_task_parm = { |
| .name = "tmr_task", |
| .priority = BCMOS_TASK_PRIORITY_0, |
| .handler = _bcmos_tmr_task_handler |
| }; |
| bcmos_errno rc; |
| |
| struct sigaction sa; |
| struct sigevent sev = {}; |
| |
| if (!timer || !handler) |
| BCMOS_TRACE_RETURN(BCM_ERR_PARM, "timer %p, handler %p\n", timer, handler); |
| |
| timer->handler = handler; |
| timer->data = data; |
| |
| sem_init(&_bcmos_timer_lock, 0, 0); |
| tmr_task_parm.data = (long)timer; |
| rc = bcmos_task_create(&_bcmos_timer_task, &tmr_task_parm); |
| if (rc) |
| BCMOS_TRACE_RETURN(rc, "Can't create timer task\n"); |
| |
| sa.sa_flags = SA_SIGINFO; |
| sa.sa_sigaction = timer_sig_handler; |
| sigemptyset(&sa.sa_mask); |
| if (sigaction(TIMER_SIG, &sa, NULL) == -1) |
| perror("sigaction"); |
| /* Prevent timer signal from interrupting system calls */ |
| if (siginterrupt(TIMER_SIG, 0) == -1) |
| perror("siginterrupt"); |
| |
| /* Create librt timer */ |
| sev.sigev_notify = SIGEV_SIGNAL; |
| sev.sigev_signo = TIMER_SIG; |
| sev.sigev_value.sival_ptr = timer; |
| if (timer_create(CLOCK_REALTIME, &sev, &timer->t) == -1) |
| BCMOS_TRACE_RETURN(BCM_ERR_SYSCALL_ERR, "errno %s\n", strerror(errno)); |
| |
| return BCM_ERR_OK; |
| } |
| |
| /* Destroy timer */ |
| void bcmos_sys_timer_destroy(bcmos_sys_timer *timer) |
| { |
| timer_delete(timer->t); |
| sem_destroy(&_bcmos_timer_lock); |
| bcmos_task_destroy(&_bcmos_timer_task); |
| } |
| |
| /* (Re)start timer */ |
| void bcmos_sys_timer_start(bcmos_sys_timer *timer, uint32_t delay) |
| { |
| struct itimerspec its; |
| |
| its.it_value.tv_sec = delay / 1000000; |
| its.it_value.tv_nsec = (delay % 1000000) * 1000; |
| its.it_interval.tv_sec = 0; |
| its.it_interval.tv_nsec = 0; |
| if (timer_settime(timer->t, 0, &its, NULL) == -1) |
| { |
| BCMOS_TRACE_ERR("timer_settime errno %s\n", strerror(errno)); |
| BUG(); |
| } |
| } |
| |
| /* Stop timer if running */ |
| void bcmos_sys_timer_stop(bcmos_sys_timer *timer) |
| { |
| struct itimerspec its; |
| |
| BUG_ON(!timer); |
| memset(&its, 0, sizeof(its)); |
| timer_settime(timer->t, 0, &its, NULL); |
| } |
| |
| |
| /* Suspend current task for a time */ |
| void bcmos_usleep(uint32_t us) |
| { |
| uint32_t ts = bcmos_timestamp(); |
| uint32_t tse = ts + us; |
| int32_t d = (us > 1000000) ? 1000000 : us; |
| |
| do |
| { |
| usleep(d); |
| d = tse - bcmos_timestamp(); |
| if (d > 1000000) d = 1000000; |
| } while (d > 0); |
| } |
| |
| /* |
| * Memory management |
| */ |
| |
| /* Allocate memory from the main heap */ |
| void *bcmos_alloc(uint32_t size) |
| { |
| return malloc(size); |
| } |
| |
| /* Release memory to the main pool */ |
| void bcmos_free(void *ptr) |
| { |
| free(ptr); |
| } |
| |
| /* |
| * Byte memory pool |
| */ |
| |
| /* Memory block header */ |
| typedef struct bcmos_byte_memblk |
| { |
| bcmos_byte_pool *pool; /** pool that owns the block */ |
| uint32_t size; /** block size (bytes) including bcmos_byte_memblk header */ |
| #ifdef BCMOS_MEM_DEBUG |
| uint32_t magic; /** magic number */ |
| #define BCMOS_MEM_MAGIC_ALLOC (('m'<<24) | ('b' << 16) | ('l' << 8) | 'k') |
| #define BCMOS_MEM_MAGIC_FREE (('m'<<24) | ('b' << 16) | ('l' << 8) | '~') |
| #endif |
| } bcmos_byte_memblk; |
| |
| /* Create byte memory pool */ |
| bcmos_errno bcmos_byte_pool_create(bcmos_byte_pool *pool, const bcmos_byte_pool_parm *parm) |
| { |
| if (!pool || !parm) |
| { |
| BCMOS_TRACE_RETURN(BCM_ERR_PARM, "pool %p, parm %p\n", pool, parm); |
| } |
| |
| BCM_MEMZERO_STRUCT(pool); |
| pool->parm = *parm; |
| if (!pool->parm.size) |
| { |
| BCMOS_TRACE_RETURN(BCM_ERR_PARM, "size %u\n", parm->size); |
| } |
| #ifdef BCMOS_MEM_DEBUG |
| pool->magic = BCMOS_BYTE_POOL_VALID; |
| #endif |
| return BCM_ERR_OK; |
| } |
| |
| /* Destroy memory pool */ |
| bcmos_errno bcmos_byte_pool_destroy(bcmos_byte_pool *pool) |
| { |
| if (pool->allocated) |
| { |
| BCMOS_TRACE_RETURN(BCM_ERR_STATE, "%u bytes of memory are still allocated from the pool %s\n", |
| pool->allocated, pool->parm.name); |
| } |
| #ifdef BCMOS_MEM_DEBUG |
| pool->magic = BCMOS_BYTE_POOL_DELETED; |
| #endif |
| return BCM_ERR_OK; |
| } |
| |
| /* Allocate memory from memory pool */ |
| void *bcmos_byte_pool_alloc(bcmos_byte_pool *pool, uint32_t size) |
| { |
| bcmos_byte_memblk *blk; |
| uint32_t byte_size; |
| void *ptr; |
| |
| #ifdef BCMOS_MEM_DEBUG |
| BUG_ON(pool->magic != BCMOS_BYTE_POOL_VALID); |
| #endif |
| |
| if (size + pool->allocated > pool->parm.size) |
| return NULL; |
| |
| byte_size = size + sizeof(bcmos_byte_memblk); |
| #ifdef BCMOS_MEM_DEBUG |
| byte_size += sizeof(uint32_t); /* block suffix */ |
| #endif |
| /* ToDo: Maintain LL of allocated blocks */ |
| blk = (bcmos_byte_memblk *)malloc(byte_size); |
| if (!blk) |
| return NULL; |
| ptr = (void *)(blk + 1); |
| blk->size = byte_size; |
| pool->allocated += byte_size; |
| blk->pool = pool; |
| #ifdef BCMOS_MEM_DEBUG |
| blk->magic = BCMOS_MEM_MAGIC_ALLOC; |
| *(uint32_t *)((long)blk + byte_size - sizeof(uint32_t)) = BCMOS_MEM_MAGIC_ALLOC; |
| #endif |
| |
| return ptr; |
| } |
| |
| /* Release memory allocated using bcmos_byte_pool_alloc() */ |
| void bcmos_byte_pool_free(void *ptr) |
| { |
| bcmos_byte_memblk *blk; |
| bcmos_byte_pool *pool; |
| |
| BUG_ON(!ptr); |
| blk = (bcmos_byte_memblk *)((long)ptr - sizeof(bcmos_byte_memblk)); |
| pool = blk->pool; |
| #ifdef BCMOS_MEM_DEBUG |
| BUG_ON(pool->magic != BCMOS_BYTE_POOL_VALID); |
| BUG_ON(blk->magic != BCMOS_MEM_MAGIC_ALLOC); |
| BUG_ON(*(uint32_t *)((long)blk + blk->size - sizeof(uint32_t)) != BCMOS_MEM_MAGIC_ALLOC); |
| blk->magic = BCMOS_MEM_MAGIC_FREE; |
| #endif |
| pool->allocated -= blk->size; |
| free(blk); |
| } |
| |
| void _bcmos_backtrace(void) |
| { |
| void *array[32]; |
| size_t size; |
| char **strings; |
| size_t i; |
| |
| size = backtrace(array, sizeof(array)/sizeof(array[0])); |
| strings = backtrace_symbols(array, size); |
| |
| printf("Obtained %zd stack frames.\n", size); |
| |
| for (i = 0; i < size; i++) |
| printf("%s\n", strings[i]); |
| |
| free(strings); |
| } |
| |
| /* stub for int enable */ |
| void bcmos_int_enable(int irq) |
| { |
| } |
| |
| void bcmos_int_disable(int irq) |
| { |
| } |
| |
| /* stub for int connect */ |
| int bcmos_int_connect(int irq, int cpu, int flags, |
| int (*isr)(int irq, void *priv), const char *name, void *priv) |
| { |
| return 0; |
| } |
| |
| /* Functions common for domain socket and UDP socket - based message queues */ |
| #if defined(BCMOS_MSG_QUEUE_DOMAIN_SOCKET) || defined(BCMOS_MSG_QUEUE_UDP_SOCKET) |
| |
| /** "send" to socket */ |
| static bcmos_errno _bcmos_socket_send(bcmos_msg_queue *queue, uint8_t *buf, uint32_t buf_length) |
| { |
| int rc; |
| /* Message queue without remote endpoint address doesn't support transmit */ |
| if (!queue->ep_extra_data_size) |
| return BCM_ERR_NOT_SUPPORTED; |
| rc = sendto(queue->ep, buf, buf_length, 0, |
| (struct sockaddr *)queue->ep_extra_data, queue->ep_extra_data_size); |
| return (rc == buf_length) ? BCM_ERR_OK : BCM_ERR_COMM_FAIL; |
| } |
| |
| /** "recv" from socket */ |
| static bcmos_errno _bcmos_socket_recv(bcmos_msg_queue *queue, uint32_t timeout, uint8_t **buf, uint32_t *buf_length) |
| { |
| int rc; |
| int wait = 0; |
| |
| /* Message queue without local endpoint address doesn't support receive */ |
| if (!queue->q.parm.local_ep_address) |
| return BCM_ERR_NOT_SUPPORTED; |
| |
| if (timeout && timeout != BCMOS_WAIT_FOREVER) |
| { |
| fd_set read_fds; |
| struct timeval tv; |
| |
| FD_ZERO(&read_fds); |
| FD_SET(queue->ep, &read_fds); |
| tv.tv_sec = timeout / 1000000; |
| tv.tv_usec = (timeout % 1000000) * 1000; |
| rc = select(queue->ep + 1, &read_fds, NULL, NULL, &tv); |
| if (rc < 0) |
| { |
| return BCM_ERR_COMM_FAIL; |
| } |
| if (!rc || !FD_ISSET(queue->ep, &read_fds)) |
| return BCM_ERR_TIMEOUT; |
| wait = MSG_DONTWAIT; |
| } |
| |
| rc = recv(queue->ep, queue->recv_buf, queue->q.parm.max_mtu, wait); |
| if (rc < 0) |
| { |
| return BCM_ERR_COMM_FAIL; |
| } |
| if (rc == 0) |
| return BCM_ERR_NOENT; |
| |
| *buf = queue->recv_buf; |
| *buf_length = rc; |
| return BCM_ERR_OK; |
| } |
| |
| |
| static bcmos_errno _bcmos_socket_close(bcmos_msg_queue *queue) |
| { |
| /* Close domain socket */ |
| if (queue->ep > 0) |
| { |
| close(queue->ep); |
| } |
| if (queue->ep_extra_data) |
| { |
| bcmos_free(queue->ep_extra_data); |
| } |
| return BCM_ERR_OK; |
| } |
| |
| |
| /* Pack message for over-the-socket transmission. |
| * This function is good for case when both queue ends are on the same CPU |
| * and there is no need to do any translation. |
| */ |
| static bcmos_errno _bcmos_transparent_pack(bcmos_msg_queue *queue, bcmos_msg *msg, uint8_t **buf, uint32_t *buf_length) |
| { |
| uint32_t size = BCMOS_MSG_HDR_SIZE + msg->size; |
| |
| if (size > queue->q.parm.max_mtu) |
| { |
| BCMOS_TRACE_RETURN(BCM_ERR_OVERFLOW, "Attempt to send message longer than configured max_mtu %u > %u\n", |
| size, queue->q.parm.max_mtu); |
| } |
| bcmos_msg_hdr_pack(msg, queue->send_buf, msg->size); |
| if (msg->size) |
| { |
| BUG_ON(msg->data == NULL); |
| memcpy(queue->send_buf + BCMOS_MSG_HDR_SIZE, msg->data, msg->size); |
| } |
| *buf = queue->send_buf; |
| *buf_length = size; |
| return BCM_ERR_OK; |
| } |
| |
| /** "unpack" message. In case of domain socket both queue ends are in the same CPU. |
| * Message is sent as-is |
| */ |
| static bcmos_errno _bcmos_transparent_unpack(bcmos_msg_queue *queue, uint8_t *buf, uint32_t buf_length, bcmos_msg **msg) |
| { |
| bcmos_msg *m; |
| |
| if (buf_length < BCMOS_MSG_HDR_SIZE) |
| { |
| BCMOS_TRACE_RETURN(BCM_ERR_INTERNAL, "Received message is too short (%u)\n", buf_length); |
| } |
| |
| /* Adjust buf_length to account for difference in packed and unpacked message header sizes */ |
| buf_length -= BCMOS_MSG_HDR_SIZE; |
| buf_length += sizeof(bcmos_msg); |
| |
| m = bcmos_alloc(buf_length); |
| if (!m) |
| { |
| BCMOS_TRACE_RETURN(BCM_ERR_NOMEM, "Received message discarded: %u bytes\n", buf_length); |
| } |
| |
| bcmos_msg_hdr_unpack(buf, m); |
| m->release = NULL; |
| m->data_release = NULL; |
| if (m->size != buf_length - sizeof(bcmos_msg)) |
| { |
| BCMOS_TRACE_ERR("Received message is insane. Expected data length %u, got %u\n", |
| m->size, buf_length - sizeof(bcmos_msg)); |
| bcmos_free(m); |
| return BCM_ERR_INTERNAL; |
| } |
| if (m->size) |
| { |
| m->data = m->start = (void *)(m + 1); |
| memcpy(m->data, &buf[BCMOS_MSG_HDR_SIZE], m->size); |
| } |
| else |
| { |
| m->data = m->start = NULL; |
| } |
| *msg = m; |
| |
| return BCM_ERR_OK; |
| } |
| |
| |
| #endif |
| |
| /* Domain-socket-based inter-process communication */ |
| #ifdef BCMOS_MSG_QUEUE_DOMAIN_SOCKET |
| |
| bcmos_errno bcmos_sys_msg_queue_domain_socket_open(bcmos_msg_queue *queue) |
| { |
| struct sockaddr_un sa; |
| |
| /* Open domain socket */ |
| queue->ep = socket(AF_UNIX, SOCK_DGRAM, 0); |
| if (queue->ep < 0) |
| { |
| BCMOS_TRACE_RETURN(BCM_ERR_PARM, "Can't create domain socket. error %s\n", strerror(errno)); |
| } |
| |
| memset(&sa, 0, sizeof(sa)); |
| sa.sun_family = AF_UNIX; |
| |
| /* If local_ep_address is set - the queue supports receive */ |
| if (queue->q.parm.local_ep_address) |
| { |
| strncpy(sa.sun_path, queue->q.parm.local_ep_address, sizeof(sa.sun_path) - 1); |
| /* In linux path can start from 0 character */ |
| if (!sa.sun_path[0]) |
| { |
| strncpy(&sa.sun_path[1], &queue->q.parm.local_ep_address[1], sizeof(sa.sun_path) - 1); |
| } |
| if (bind(queue->ep, (struct sockaddr *)&sa, sizeof(sa)) < 0) |
| { |
| int err = errno; |
| close(queue->ep); |
| BCMOS_TRACE_RETURN(BCM_ERR_PARM, "Can't bind domain socket to %s. error %s\n", |
| queue->q.parm.local_ep_address, strerror(err)); |
| } |
| } |
| |
| /* If remote_ep_address is set - the queue supports transmit */ |
| if (queue->q.parm.remote_ep_address) |
| { |
| queue->ep_extra_data = bcmos_calloc(sizeof(struct sockaddr_un)); |
| if (!queue->ep_extra_data) |
| { |
| close(queue->ep); |
| return BCM_ERR_NOMEM; |
| } |
| strncpy(sa.sun_path, queue->q.parm.remote_ep_address, sizeof(sa.sun_path) - 1); |
| /* In linux path can start from 0 character */ |
| if (!sa.sun_path[0]) |
| { |
| strncpy(&sa.sun_path[1], &queue->q.parm.remote_ep_address[1], sizeof(sa.sun_path) - 1); |
| } |
| memcpy(queue->ep_extra_data, &sa, sizeof(sa)); |
| queue->ep_extra_data_size = sizeof(sa); |
| } |
| |
| /* Set callbacks */ |
| queue->q.parm.close = _bcmos_socket_close; |
| queue->q.parm.send = _bcmos_socket_send; |
| queue->q.parm.recv = _bcmos_socket_recv; |
| if (!queue->q.parm.pack) |
| queue->q.parm.pack = _bcmos_transparent_pack; |
| if (!queue->q.parm.unpack) |
| queue->q.parm.unpack = _bcmos_transparent_unpack; |
| |
| return BCM_ERR_OK; |
| } |
| |
| #endif |
| |
| /* UDP-socket-based inter-process communication */ |
| #ifdef BCMOS_MSG_QUEUE_UDP_SOCKET |
| |
| static bcmos_errno _bcmos_parse_ip_port(const char *s, struct sockaddr_in *sa) |
| { |
| uint32_t ip; |
| int n; |
| uint32_t ip1, ip2, ip3, ip4, port; |
| |
| n = sscanf(s, "%u.%u.%u.%u:%u", &ip1, &ip2, &ip3, &ip4, &port); |
| if (n != 5 || ip1 > 0xff || ip2 > 0xff || ip3 > 0xff || ip4 > 0xff || port > 0xffff) |
| { |
| BCMOS_TRACE_RETURN(BCM_ERR_PARM, "Can't parse %s. Must be ip_address:port\n", s); |
| } |
| ip = (ip1 << 24) | (ip2 << 16) | (ip3 << 8) | ip4; |
| sa->sin_port = htons(port); |
| sa->sin_addr.s_addr = htonl(ip); |
| |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_errno bcmos_sys_msg_queue_udp_socket_open(bcmos_msg_queue *queue) |
| { |
| struct sockaddr_in sa; |
| bcmos_errno rc; |
| |
| /* Open UDP socket */ |
| queue->ep = socket(AF_INET, SOCK_DGRAM, 0); |
| if (queue->ep < 0) |
| { |
| BCMOS_TRACE_RETURN(BCM_ERR_PARM, "Can't create UDP socket. error %s\n", strerror(errno)); |
| } |
| |
| memset(&sa, 0, sizeof(sa)); |
| sa.sin_family = AF_INET; |
| |
| /* If local_ep_address is set - the queue supports receive */ |
| if (queue->q.parm.local_ep_address) |
| { |
| rc = _bcmos_parse_ip_port(queue->q.parm.local_ep_address, &sa); |
| if (rc) |
| return rc; |
| if (!sa.sin_addr.s_addr) |
| sa.sin_addr.s_addr = INADDR_ANY; |
| if (bind(queue->ep, (struct sockaddr *)&sa, sizeof(sa)) < 0) |
| { |
| int err = errno; |
| close(queue->ep); |
| BCMOS_TRACE_RETURN(BCM_ERR_PARM, "Can't bind UDP socket to %s. error %s\n", |
| queue->q.parm.local_ep_address, strerror(err)); |
| } |
| } |
| |
| /* If remote_ep_address is set - the queue supports transmit */ |
| if (queue->q.parm.remote_ep_address) |
| { |
| rc = _bcmos_parse_ip_port(queue->q.parm.remote_ep_address, &sa); |
| if (rc) |
| return rc; |
| queue->ep_extra_data = bcmos_calloc(sizeof(sa)); |
| if (!queue->ep_extra_data) |
| { |
| close(queue->ep); |
| return BCM_ERR_NOMEM; |
| } |
| memcpy(queue->ep_extra_data, &sa, sizeof(sa)); |
| queue->ep_extra_data_size = sizeof(sa); |
| } |
| |
| /* Set callbacks */ |
| queue->q.parm.close = _bcmos_socket_close; |
| queue->q.parm.send = _bcmos_socket_send; |
| queue->q.parm.recv = _bcmos_socket_recv; |
| if (!queue->q.parm.pack) |
| queue->q.parm.pack = _bcmos_transparent_pack; |
| if (!queue->q.parm.unpack) |
| queue->q.parm.unpack = _bcmos_transparent_unpack; |
| |
| return BCM_ERR_OK; |
| } |
| #endif |