diff options
| author | Meco Man <[email protected]> | 2021-10-17 17:19:12 -0400 |
|---|---|---|
| committer | Meco Man <[email protected]> | 2021-10-17 17:19:12 -0400 |
| commit | b513d37cf2fc33c2dd20021316533feaac0bef23 (patch) | |
| tree | f14e24d145a40620dbb8fd5a5d807b972ec4a1d5 /components/libc/posix | |
| parent | 9fe546b0d86b17c64e57447913c8a9f3a9c40534 (diff) | |
[libc][posix] create 'posix' folder and move related files into it
Diffstat (limited to 'components/libc/posix')
30 files changed, 4519 insertions, 0 deletions
diff --git a/components/libc/posix/SConscript b/components/libc/posix/SConscript new file mode 100644 index 0000000000..4c815c49b8 --- /dev/null +++ b/components/libc/posix/SConscript @@ -0,0 +1,15 @@ +# RT-Thread building script for bridge + +import os +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') diff --git a/components/libc/posix/aio/SConscript b/components/libc/posix/aio/SConscript new file mode 100644 index 0000000000..f10462e9c4 --- /dev/null +++ b/components/libc/posix/aio/SConscript @@ -0,0 +1,11 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('libc', src, depend = ['RT_USING_POSIX', 'RT_USING_POSIX_AIO'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/libc/posix/aio/posix_aio.c b/components/libc/posix/aio/posix_aio.c new file mode 100644 index 0000000000..66cced8687 --- /dev/null +++ b/components/libc/posix/aio/posix_aio.c @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017/12/30 Bernard The first version. + */ + +#include <stdint.h> +#include <rthw.h> +#include <dfs_posix.h> +#include "posix_aio.h" + +struct rt_workqueue* aio_queue = NULL; + +/** + * The aio_cancel() function shall attempt to cancel one or more asynchronous I/O + * requests currently outstanding against file descriptor fildes. The aiocbp + * argument points to the asynchronous I/O control block for a particular request + * to be canceled. If aiocbp is NULL, then all outstanding cancelable asynchronous + * I/O requests against fildes shall be canceled. + * + * Normal asynchronous notification shall occur for asynchronous I/O operations + * that are successfully canceled. If there are requests that cannot be canceled, + * then the normal asynchronous completion process shall take place for those + * requests when they are completed. + * + * For requested operations that are successfully canceled, the associated error + * status shall be set to [ECANCELED] and the return status shall be -1. For + * requested operations that are not successfully canceled, the aiocbp shall not + * be modified by aio_cancel(). + * + * If aiocbp is not NULL, then if fildes does not have the same value as the file + * descriptor with which the asynchronous operation was initiated, unspecified results occur. + * + * Which operations are cancelable is implementation-defined. + */ +int aio_cancel(int fd, struct aiocb *cb) +{ + rt_err_t ret; + + if (!cb) return -EINVAL; + if (cb->aio_fildes != fd) return -EINVAL; + + ret = rt_workqueue_cancel_work_sync(aio_queue, &(cb->aio_work)); + if (ret == RT_EOK) + { + errno = -ECANCELED; + return -1; + } + + return 0; +} + +/** + * The aio_error() function shall return the error status associated with the + * aiocb structure referenced by the aiocbp argument. The error status for an + * asynchronous I/O operation is the errno value that would be set by the corresponding + * read(), write(), + */ +int aio_error (const struct aiocb *cb) +{ + if (cb) + { + return cb->aio_result; + } + + return -EINVAL; +} + +/** + * The aio_fsync() function shall asynchronously perform a file synchronization + * operation, as specified by the op argument, for I/O operations associated with + * the file indicated by the file descriptor aio_fildes member of the aiocb + * structure referenced by the aiocbp argument and queued at the time of the + * call to aio_fsync(). The function call shall return when the synchronization + * request has been initiated or queued to the file or device (even when the data + * cannot be synchronized immediately). + * + * option: If op is O_DSYNC, all currently queued I/O operations shall be completed + * as if by a call to fdatasync(); that is, as defined for synchronized I/O data + * integrity completion. + * + * option: If op is O_SYNC, all currently queued I/O operations shall be completed + * as if by a call to fsync(); that is, as defined for synchronized I/O file integrity + * completion. If the aio_fsync() function fails, or if the operation queued by + * aio_fsync() fails, then outstanding I/O operations are not guaranteed to have + * been completed. + * + * If aio_fsync() succeeds, then it is only the I/O that was queued at the time + * of the call to aio_fsync() that is guaranteed to be forced to the relevant + * completion state. The completion of subsequent I/O on the file descriptor is + * not guaranteed to be completed in a synchronized fashion. + * + * The aiocbp argument refers to an asynchronous I/O control block. The aiocbp + * value may be used as an argument to aio_error() and aio_return() in order to + * determine the error status and return status, respectively, of the asynchronous + * operation while it is proceeding. When the request is queued, the error status + * for the operation is [EINPROGRESS]. When all data has been successfully transferred, + * the error status shall be reset to reflect the success or failure of the operation. + * If the operation does not complete successfully, the error status for the + * operation shall be set to indicate the error. The aio_sigevent member determines + * the asynchronous notification to occur as specified in Signal Generation and + * Delivery when all operations have achieved synchronized I/O completion. All + * other members of the structure referenced by aiocbp are ignored. If the control + * block referenced by aiocbp becomes an illegal address prior to asynchronous + * I/O completion, then the behavior is undefined. + * + * If the aio_fsync() function fails or aiocbp indicates an error condition, + * data is not guaranteed to have been successfully transferred. + */ +static void aio_fync_work(struct rt_work* work, void* work_data) +{ + int result; + rt_ubase_t level; + struct aiocb *cb = (struct aiocb*)work_data; + + RT_ASSERT(cb != RT_NULL); + + result = fsync(cb->aio_fildes); + /* modify result */ + level = rt_hw_interrupt_disable(); + if (result < 0) + cb->aio_result = errno; + else + cb->aio_result = 0; + rt_hw_interrupt_enable(level); + + return ; +} + +int aio_fsync(int op, struct aiocb *cb) +{ + rt_ubase_t level; + if (!cb) return -EINVAL; + + level = rt_hw_interrupt_disable(); + cb->aio_result = -EINPROGRESS; + rt_hw_interrupt_enable(level); + + rt_work_init(&(cb->aio_work), aio_fync_work, cb); + rt_workqueue_dowork(aio_queue, &(cb->aio_work)); + + return 0; +} + +static void aio_read_work(struct rt_work* work, void* work_data) +{ + int len; + rt_ubase_t level; + uint8_t *buf_ptr; + struct aiocb *cb = (struct aiocb*)work_data; + + buf_ptr = (uint8_t*)cb->aio_buf; + + /* seek to offset */ + lseek(cb->aio_fildes, cb->aio_offset, SEEK_SET); + len = read(cb->aio_fildes, &buf_ptr[cb->aio_offset], cb->aio_nbytes); + + /* modify result */ + level = rt_hw_interrupt_disable(); + if (len <= 0) + cb->aio_result = errno; + else + cb->aio_result = len; + rt_hw_interrupt_enable(level); + + return ; +} + +/** + * The aio_read() function shall read aiocbp->aio_nbytes from the file associated + * with aiocbp->aio_fildes into the buffer pointed to by aiocbp->aio_buf. The + * function call shall return when the read request has been initiated or queued + * to the file or device (even when the data cannot be delivered immediately). + * + * If prioritized I/O is supported for this file, then the asynchronous operation + * shall be submitted at a priority equal to a base scheduling priority minus + * aiocbp->aio_reqprio. If Thread Execution Scheduling is not supported, then + * the base scheduling priority is that of the calling process; + * + * otherwise, the base scheduling priority is that of the calling thread. + * + * The aiocbp value may be used as an argument to aio_error() and aio_return() + * in order to determine the error status and return status, respectively, of + * the asynchronous operation while it is proceeding. If an error condition is + * encountered during queuing, the function call shall return without having + * initiated or queued the request. The requested operation takes place at the + * absolute position in the file as given by aio_offset, as if lseek() were called + * immediately prior to the operation with an offset equal to aio_offset and a + * whence equal to SEEK_SET. After a successful call to enqueue an asynchronous + * I/O operation, the value of the file offset for the file is unspecified. + * + * The aio_sigevent member specifies the notification which occurs when the + * request is completed. + * + * The aiocbp->aio_lio_opcode field shall be ignored by aio_read(). + * + * The aiocbp argument points to an aiocb structure. If the buffer pointed to by + * aiocbp->aio_buf or the control block pointed to by aiocbp becomes an illegal + * address prior to asynchronous I/O completion, then the behavior is undefined. + * + * Simultaneous asynchronous operations using the same aiocbp produce undefined + * results. + * + * If synchronized I/O is enabled on the file associated with aiocbp->aio_fildes, + * the behavior of this function shall be according to the definitions of synchronized + * I/O data integrity completion and synchronized I/O file integrity completion. + * + * For any system action that changes the process memory space while an asynchronous + * I/O is outstanding to the address range being changed, the result of that action + * is undefined. + * + * For regular files, no data transfer shall occur past the offset maximum + * established in the open file description associated with aiocbp->aio_fildes. + * + */ +int aio_read(struct aiocb *cb) +{ + rt_ubase_t level; + + if (!cb) return -EINVAL; + if (cb->aio_offset < 0) return -EINVAL; + + level = rt_hw_interrupt_disable(); + cb->aio_result = -EINPROGRESS; + rt_hw_interrupt_enable(level); + + /* en-queue read work */ + rt_work_init(&(cb->aio_work), aio_read_work, cb); + rt_workqueue_dowork(aio_queue, &(cb->aio_work)); + + return 0; +} + +/** + * The aio_return() function shall return the return status associated with the + * aiocb structure referenced by the aiocbp argument. The return status for an + * asynchronous I/O operation is the value that would be returned by the corresponding + * read(), write(), or fsync() function call. If the error status for the operation + * is equal to [EINPROGRESS], then the return status for the operation is undefined. + * The aio_return() function may be called exactly once to retrieve the return + * status of a given asynchronous operation; thereafter, if the same aiocb structure + * is used in a call to aio_return() or aio_error(), an error may be returned. + * When the aiocb structure referred to by aiocbp is used to submit another asynchronous + * operation, then aio_return() may be successfully used to retrieve the return + * status of that operation. + */ +ssize_t aio_return(struct aiocb *cb) +{ + if (cb) + { + if (cb->aio_result < 0) + rt_set_errno(cb->aio_result); + + return cb->aio_result; + } + + return -EINVAL; +} + +/** + * The aio_suspend() function shall suspend the calling thread until at least + * one of the asynchronous I/O operations referenced by the list argument has + * completed, until a signal interrupts the function, or, if timeout is not NULL, + * until the time interval specified by timeout has passed. If any of the aiocb + * structures in the list correspond to completed asynchronous I/O operations + * (that is, the error status for the operation is not equal to [EINPROGRESS]) + * at the time of the call, the function shall return without suspending the + * calling thread. The list argument is an array of pointers to asynchronous I/O + * control blocks. The nent argument indicates the number of elements in the + * array. Each aiocb structure pointed to has been used in initiating an asynchronous + * I/O request via aio_read(), aio_write(), or lio_listio(). This array may + * contain null pointers, which are ignored. If this array contains pointers + * that refer to aiocb structures that have not been used in submitting asynchronous + * I/O, the effect is undefined. + * + * If the time interval indicated in the timespec structure pointed to by timeout + * passes before any of the I/O operations referenced by list are completed, then + * aio_suspend() shall return with an error. + */ +int aio_suspend(const struct aiocb *const list[], int nent, + const struct timespec *timeout) +{ + return -ENOSYS; +} + +static void aio_write_work(struct rt_work* work, void* work_data) +{ + int len, oflags, level; + uint8_t *buf_ptr; + struct aiocb *cb = (struct aiocb*)work_data; + + buf_ptr = (uint8_t*)cb->aio_buf; + + /* whether seek offset */ + oflags = fcntl(cb->aio_fildes, F_GETFL, 0); + if ((oflags & O_APPEND) == 0) + { + lseek(cb->aio_fildes, SEEK_SET, cb->aio_offset); + } + + /* write data */ + len = write(cb->aio_fildes, buf_ptr, cb->aio_nbytes); + + /* modify result */ + level = rt_hw_interrupt_disable(); + if (len <= 0) + cb->aio_result = errno; + else + cb->aio_result = len; + rt_hw_interrupt_enable(level); + + return; +} + +/** + * The aio_write() function shall write aiocbp->aio_nbytes to the file associated + * with aiocbp->aio_fildes from the buffer pointed to by aiocbp->aio_buf. The + * function shall return when the write request has been initiated or, at a minimum, + * queued to the file or device. + * + * The aiocbp argument may be used as an argument to aio_error() and aio_return() + * in order to determine the error status and return status, respectively, of the + * asynchronous operation while it is proceeding. + * + * The aiocbp argument points to an aiocb structure. If the buffer pointed to by + * aiocbp->aio_buf or the control block pointed to by aiocbp becomes an illegal + * address prior to asynchronous I/O completion, then the behavior is undefined. + * + * If O_APPEND is not set for the file descriptor aio_fildes, then the requested + * operation shall take place at the absolute position in the file as given by + * aio_offset, as if lseek() were called immediately prior to the operation with + * an offset equal to aio_offset and a whence equal to SEEK_SET. If O_APPEND is + * set for the file descriptor, or if aio_fildes is associated with a device that + * is incapable of seeking, write operations append to the file in the same order + * as the calls were made, except under circumstances described in Asynchronous + * I/O. After a successful call to enqueue an asynchronous I/O operation, the value + * of the file offset for the file is unspecified. + * + * The aio_sigevent member specifies the notification which occurs when the request + * is completed. + * + * The aiocbp->aio_lio_opcode field shall be ignored by aio_write(). + * + * Simultaneous asynchronous operations using the same aiocbp produce undefined + * results. + * + * If synchronized I/O is enabled on the file associated with aiocbp->aio_fildes, + * the behavior of this function shall be according to the definitions of synchronized + * I/O data integrity completion, and synchronized I/O file integrity completion. + * + * For regular files, no data transfer shall occur past the offset maximum established + * in the open file description associated with aiocbp->aio_fildes. + */ +int aio_write(struct aiocb *cb) +{ + int oflags; + rt_ubase_t level; + + if (!cb || (cb->aio_buf == NULL)) return -EINVAL; + + /* check access mode */ + oflags = fcntl(cb->aio_fildes, F_GETFL, 0); + if ((oflags & O_ACCMODE) != O_WRONLY || + (oflags & O_ACCMODE) != O_RDWR) + return -EINVAL; + + level = rt_hw_interrupt_disable(); + cb->aio_result = -EINPROGRESS; + rt_hw_interrupt_enable(level); + + rt_work_init(&(cb->aio_work), aio_write_work, cb); + rt_workqueue_dowork(aio_queue, &(cb->aio_work)); + + return 0; +} + +/** + * The lio_listio() function shall initiate a list of I/O requests with a single + * function call. + * + * The mode argument takes one of the values LIO_WAIT or LIO_NOWAIT declared in + * <aio.h> and determines whether the function returns when the I/O operations + * have been completed, or as soon as the operations have been queued. If the + * mode argument is LIO_WAIT, the function shall wait until all I/O is complete + * and the sig argument shall be ignored. + * + * If the mode argument is LIO_NOWAIT, the function shall return immediately, and + * asynchronous notification shall occur, according to the sig argument, when all + * the I/O operations complete. If sig is NULL, then no asynchronous notification + * shall occur. If sig is not NULL, asynchronous notification occurs as specified + * in Signal Generation and Delivery when all the requests in list have completed. + * + * The I/O requests enumerated by list are submitted in an unspecified order. + * + * The list argument is an array of pointers to aiocb structures. The array contains + * nent elements. The array may contain NULL elements, which shall be ignored. + * + * If the buffer pointed to by list or the aiocb structures pointed to by the + * elements of the array list become illegal addresses before all asynchronous I/O + * completed and, if necessary, the notification is sent, then the behavior is + * undefined. If the buffers pointed to by the aio_buf member of the aiocb structure + * pointed to by the elements of the array list become illegal addresses prior to + * the asynchronous I/O associated with that aiocb structure being completed, the + * behavior is undefined. + * + * The aio_lio_opcode field of each aiocb structure specifies the operation to be + * performed. The supported operations are LIO_READ, LIO_WRITE, and LIO_NOP; these + * symbols are defined in <aio.h>. The LIO_NOP operation causes the list entry to + * be ignored. If the aio_lio_opcode element is equal to LIO_READ, then an I/O operation + * is submitted as if by a call to aio_read() with the aiocbp equal to the address + * of the aiocb structure. If the aio_lio_opcode element is equal to LIO_WRITE, then + * an I/O operation is submitted as if by a call to aio_write() with the aiocbp equal + * to the address of the aiocb structure. + * + * The aio_fildes member specifies the file descriptor on which the operation is to + * be performed. + * + * The aio_buf member specifies the address of the buffer to or from which the data + * is transferred. + * + * The aio_nbytes member specifies the number of bytes of data to be transferred. + * + * The members of the aiocb structure further describe the I/O operation to be + * performed, in a manner identical to that of the corresponding aiocb structure + * when used by the aio_read() and aio_write() functions. + * + * The nent argument specifies how many elements are members of the list; that is, + * the length of the array. + * + * The behavior of this function is altered according to the definitions of synchronized + * I/O data integrity completion and synchronized I/O file integrity completion if + * synchronized I/O is enabled on the file associated with aio_fildes. + * + * For regular files, no data transfer shall occur past the offset maximum established + * in the open file description associated with aiocbp->aio_fildes. + * + * If sig->sigev_notify is SIGEV_THREAD and sig->sigev_notify_attributes is a + * non-null pointer and the block pointed to by this pointer becomes an illegal + * address prior to all asynchronous I/O being completed, then the behavior is + * undefined. + */ +int lio_listio(int mode, struct aiocb * const list[], int nent, + struct sigevent *sig) +{ + return -ENOSYS; +} + +int aio_system_init(void) +{ + aio_queue = rt_workqueue_create("aio", 2048, RT_THREAD_PRIORITY_MAX/2); + RT_ASSERT(aio_queue != NULL); + + return 0; +} +INIT_COMPONENT_EXPORT(aio_system_init); diff --git a/components/libc/posix/aio/posix_aio.h b/components/libc/posix/aio/posix_aio.h new file mode 100644 index 0000000000..4e25e1d15b --- /dev/null +++ b/components/libc/posix/aio/posix_aio.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017/12/30 Bernard The first version. + */ + +#ifndef POSIX_AIO_H__ +#define POSIX_AIO_H__ + +#include <stdio.h> +#include <sys/signal.h> +#include <rtdevice.h> + +struct aiocb +{ + int aio_fildes; /* File descriptor. */ + off_t aio_offset; /* File offset. */ + + volatile void *aio_buf; /* Location of buffer. */ + size_t aio_nbytes; /* Length of transfer. */ + int aio_reqprio; /* Request priority offset. */ + struct sigevent aio_sigevent; /* Signal number and value. */ + int aio_lio_opcode; /* Operation to be performed. */ + + int aio_result; + struct rt_work aio_work; +}; + +int aio_cancel(int fd, struct aiocb *cb); +int aio_error (const struct aiocb *cb); + +int aio_fsync(int op, struct aiocb *cb); + +int aio_read(struct aiocb *cb); +ssize_t aio_return(struct aiocb *cb); +int aio_suspend(const struct aiocb *const list[], int nent, + const struct timespec *timeout); +int aio_write(struct aiocb *cb); + +int lio_listio(int mode, struct aiocb * const list[], int nent, + struct sigevent *sig); + +#endif diff --git a/components/libc/posix/mmap/SConscript b/components/libc/posix/mmap/SConscript new file mode 100644 index 0000000000..1a36a15710 --- /dev/null +++ b/components/libc/posix/mmap/SConscript @@ -0,0 +1,13 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('libc', src, + depend = ['RT_USING_DFS', 'RT_USING_POSIX_MMAP'], + CPPPATH = CPPPATH) + +Return('group') diff --git a/components/libc/posix/mmap/posix_mmap.c b/components/libc/posix/mmap/posix_mmap.c new file mode 100644 index 0000000000..e68b82e640 --- /dev/null +++ b/components/libc/posix/mmap/posix_mmap.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017/11/30 Bernard The first version. + */ + +#include <stdint.h> +#include <stdio.h> + +#include <rtthread.h> +#include <dfs_posix.h> + +#include <sys/mman.h> + +void *mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset) +{ + uint8_t *mem; + + if (addr) + { + mem = addr; + } + else mem = (uint8_t *)malloc(length); + + if (mem) + { + off_t cur; + size_t read_bytes; + + cur = lseek(fd, 0, SEEK_SET); + + lseek(fd, offset, SEEK_SET); + read_bytes = read(fd, mem, length); + if (read_bytes != length) + { + if (addr == RT_NULL) + { + /* read failed */ + free(mem); + mem = RT_NULL; + } + } + lseek(fd, cur, SEEK_SET); + + return mem; + } + + errno = ENOMEM; + + return MAP_FAILED; +} + +int munmap(void *addr, size_t length) +{ + if (addr) + { + free(addr); + return 0; + } + + return -1; +} diff --git a/components/libc/posix/pthreads/SConscript b/components/libc/posix/pthreads/SConscript new file mode 100644 index 0000000000..767b406d4c --- /dev/null +++ b/components/libc/posix/pthreads/SConscript @@ -0,0 +1,22 @@ +from building import * +from utils import VersionCmp + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] +CPPDEFINES = [] + +# only enable POSIX.1b-1993 Real-time extensions +libc_ver = GetDepend('LIBC_VERSION') +try: + ver = libc_ver.split(' ') + ver = ver[1] + if VersionCmp(ver, "2.5.0") == 1: + CPPDEFINES = ['_POSIX_C_SOURCE=199309L'] +except : + pass + +group = DefineGroup('libc', src, + depend = ['RT_USING_PTHREADS', 'RT_USING_LIBC'], CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES) + +Return('group') diff --git a/components/libc/posix/pthreads/mqueue.c b/components/libc/posix/pthreads/mqueue.c new file mode 100644 index 0000000000..6840164a68 --- /dev/null +++ b/components/libc/posix/pthreads/mqueue.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include <string.h> +#include <sys/signal.h> +#include "mqueue.h" +#include "pthread_internal.h" + +static mqd_t posix_mq_list = RT_NULL; +static struct rt_semaphore posix_mq_lock; +void posix_mq_system_init() +{ + rt_sem_init(&posix_mq_lock, "pmq", 1, RT_IPC_FLAG_FIFO); +} + +rt_inline void posix_mq_insert(mqd_t pmq) +{ + pmq->next = posix_mq_list; + posix_mq_list = pmq; +} + +static void posix_mq_delete(mqd_t pmq) +{ + mqd_t iter; + if (posix_mq_list == pmq) + { + posix_mq_list = pmq->next; + + rt_mq_delete(pmq->mq); + rt_free(pmq); + + return; + } + for (iter = posix_mq_list; iter->next != RT_NULL; iter = iter->next) + { + if (iter->next == pmq) + { + /* delete this mq */ + if (pmq->next != RT_NULL) + iter->next = pmq->next; + else + iter->next = RT_NULL; + + /* delete RT-Thread mqueue */ + rt_mq_delete(pmq->mq); + rt_free(pmq); + + return ; + } + } +} + +static mqd_t posix_mq_find(const char* name) +{ + mqd_t iter; + rt_object_t object; + + for (iter = posix_mq_list; iter != RT_NULL; iter = iter->next) + { + object = (rt_object_t)(iter->mq); + + if (strncmp(object->name, name, RT_NAME_MAX) == 0) + { + return iter; + } + } + + return RT_NULL; +} + +int mq_setattr(mqd_t mqdes, + const struct mq_attr *mqstat, + struct mq_attr *omqstat) +{ + rt_set_errno(-RT_ERROR); + + return -1; +} +RTM_EXPORT(mq_setattr); + +int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat) +{ + if ((mqdes == RT_NULL) || mqstat == RT_NULL) + { + rt_set_errno(EBADF); + + return -1; + } + + mqstat->mq_maxmsg = mqdes->mq->max_msgs; + mqstat->mq_msgsize = mqdes->mq->msg_size; + mqstat->mq_curmsgs = 0; + mqstat->mq_flags = 0; + + return 0; +} +RTM_EXPORT(mq_getattr); + +mqd_t mq_open(const char *name, int oflag, ...) +{ + mqd_t mqdes; + va_list arg; + mode_t mode; + struct mq_attr *attr = RT_NULL; + + /* lock posix mqueue list */ + rt_sem_take(&posix_mq_lock, RT_WAITING_FOREVER); + + mqdes = RT_NULL; + if (oflag & O_CREAT) + { + va_start(arg, oflag); + mode = (mode_t)va_arg(arg, unsigned int); + mode = mode; + attr = (struct mq_attr *)va_arg(arg, struct mq_attr *); + va_end(arg); + + if (oflag & O_EXCL) + { + if (posix_mq_find(name) != RT_NULL) + { + rt_set_errno(EEXIST); + goto __return; + } + } + mqdes = (mqd_t) rt_malloc (sizeof(struct mqdes)); + if (mqdes == RT_NULL) + { + rt_set_errno(ENFILE); + goto __return; + } + + /* create RT-Thread message queue */ + mqdes->mq = rt_mq_create(name, attr->mq_msgsize, attr->mq_maxmsg, RT_IPC_FLAG_FIFO); + if (mqdes->mq == RT_NULL) /* create failed */ + { + rt_set_errno(ENFILE); + goto __return; + } + /* initialize reference count */ + mqdes->refcount = 1; + mqdes->unlinked = 0; + + /* insert mq to posix mq list */ + posix_mq_insert(mqdes); + } + else + { + /* find mqueue */ + mqdes = posix_mq_find(name); + if (mqdes != RT_NULL) + { + mqdes->refcount ++; /* increase reference count */ + } + else + { + rt_set_errno(ENOENT); + goto __return; + } + } + rt_sem_release(&posix_mq_lock); + + return mqdes; + +__return: + /* release lock */ + rt_sem_release(&posix_mq_lock); + + /* release allocated memory */ + if (mqdes != RT_NULL) + { + if (mqdes->mq != RT_NULL) + { + /* delete RT-Thread message queue */ + rt_mq_delete(mqdes->mq); + } + rt_free(mqdes); + } + return RT_NULL; +} +RTM_EXPORT(mq_open); + +ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio) +{ + rt_err_t result; + + if ((mqdes == RT_NULL) || (msg_ptr == RT_NULL)) + { + rt_set_errno(EINVAL); + + return -1; + } + + result = rt_mq_recv(mqdes->mq, msg_ptr, msg_len, RT_WAITING_FOREVER); + if (result == RT_EOK) + return msg_len; + + rt_set_errno(EBADF); + return -1; +} +RTM_EXPORT(mq_receive); + +int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio) +{ + rt_err_t result; + + if ((mqdes == RT_NULL) || (msg_ptr == RT_NULL)) + { + rt_set_errno(EINVAL); + + return -1; + } + + result = rt_mq_send(mqdes->mq, (void*)msg_ptr, msg_len); + if (result == RT_EOK) + return 0; + + rt_set_errno(EBADF); + + return -1; +} +RTM_EXPORT(mq_send); + +ssize_t mq_timedreceive(mqd_t mqdes, + char *msg_ptr, + size_t msg_len, + unsigned *msg_prio, + const struct timespec *abs_timeout) +{ + int tick; + rt_err_t result; + + /* parameters check */ + if ((mqdes == RT_NULL) || (msg_ptr == RT_NULL)) + { + rt_set_errno(EINVAL); + + return -1; + } + + tick = rt_timespec_to_tick(abs_timeout); + + result = rt_mq_recv(mqdes->mq, msg_ptr, msg_len, tick); + if (result == RT_EOK) + return msg_len; + + if (result == -RT_ETIMEOUT) + rt_set_errno(ETIMEDOUT); + else + rt_set_errno(EBADMSG); + + return -1; +} +RTM_EXPORT(mq_timedreceive); + +int mq_timedsend(mqd_t mqdes, + const char *msg_ptr, + size_t msg_len, + unsigned msg_prio, + const struct timespec *abs_timeout) +{ + /* RT-Thread does not support timed send */ + return mq_send(mqdes, msg_ptr, msg_len, msg_prio); +} +RTM_EXPORT(mq_timedsend); + +int mq_notify(mqd_t mqdes, const struct sigevent *notification) +{ + rt_set_errno(-RT_ERROR); + + return -1; +} +RTM_EXPORT(mq_notify); + +int mq_close(mqd_t mqdes) +{ + if (mqdes == RT_NULL) + { + rt_set_errno(EINVAL); + + return -1; + } + + /* lock posix mqueue list */ + rt_sem_take(&posix_mq_lock, RT_WAITING_FOREVER); + mqdes->refcount --; + if (mqdes->refcount == 0) + { + /* delete from posix mqueue list */ + if (mqdes->unlinked) + posix_mq_delete(mqdes); + } + rt_sem_release(&posix_mq_lock); + + return 0; +} +RTM_EXPORT(mq_close); + +int mq_unlink(const char *name) +{ + mqd_t pmq; + + /* lock posix mqueue list */ + rt_sem_take(&posix_mq_lock, RT_WAITING_FOREVER); + pmq = posix_mq_find(name); + if (pmq != RT_NULL) + { + pmq->unlinked = 1; + if (pmq->refcount == 0) + { + /* remove this mqueue */ + posix_mq_delete(pmq); + } + rt_sem_release(&posix_mq_lock); + + return 0; + } + rt_sem_release(&posix_mq_lock); + + /* no this entry */ + rt_set_errno(ENOENT); + + return -1; +} +RTM_EXPORT(mq_unlink); diff --git a/components/libc/posix/pthreads/mqueue.h b/components/libc/posix/pthreads/mqueue.h new file mode 100644 index 0000000000..5152bfad29 --- /dev/null +++ b/components/libc/posix/pthreads/mqueue.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __MQUEUE_H__ +#define __MQUEUE_H__ + +#include <rtthread.h> +#include <pthread.h> + +struct mqdes +{ + /* reference count and unlinked */ + rt_uint16_t refcount; + rt_uint16_t unlinked; + + /* RT-Thread message queue */ + rt_mq_t mq; + /* next posix mqueue */ + struct mqdes* next; +}; +typedef struct mqdes* mqd_t; + +struct mq_attr +{ + long mq_flags; /* Message queue flags. */ + long mq_maxmsg; /* Maximum number of messages. */ + long mq_msgsize; /* Maximum message size. */ + long mq_curmsgs; /* Number of messages currently queued. */ +}; + +int mq_close(mqd_t mqdes); +int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat); +int mq_notify(mqd_t mqdes, const struct sigevent *notification); +mqd_t mq_open(const char *name, int oflag, ...); +ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio); +int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio); +int mq_setattr(mqd_t mqdes, + const struct mq_attr *mqstat, + struct mq_attr *omqstat); +ssize_t mq_timedreceive(mqd_t mqdes, + char *msg_ptr, + size_t msg_len, + unsigned *msg_prio, + const struct timespec *abs_timeout); +int mq_timedsend(mqd_t mqdes, + const char *msg_ptr, + size_t msg_len, + unsigned msg_prio, + const struct timespec *abs_timeout); + +int mq_unlink(const char *name); + +#endif diff --git a/components/libc/posix/pthreads/posix_types.h b/components/libc/posix/pthreads/posix_types.h new file mode 100644 index 0000000000..5838e8b370 --- /dev/null +++ b/components/libc/posix/pthreads/posix_types.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-12-23 Bernard Add the checking for ESHUTDOWN + */ + +#ifndef __POSIX_TYPES_H__ +#define __POSIX_TYPES_H__ + +#include <rtthread.h> + +#include <stddef.h> +#include <stdarg.h> +#include <string.h> + +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#include <sys/errno.h> +#include <fcntl.h> + +#endif + diff --git a/components/libc/posix/pthreads/pthread.c b/components/libc/posix/pthreads/pthread.c new file mode 100644 index 0000000000..b0bf39dff6 --- /dev/null +++ b/components/libc/posix/pthreads/pthread.c @@ -0,0 +1,748 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-01-26 Bernard Fix pthread_detach issue for a none-joinable + * thread. + * 2019-02-07 Bernard Add _pthread_destroy to release pthread resource. + */ + +#include <rthw.h> +#include <pthread.h> +#include <sched.h> +#include <sys/time.h> +#include "pthread_internal.h" + +RT_DEFINE_SPINLOCK(pth_lock); +_pthread_data_t *pth_table[PTHREAD_NUM_MAX] = {NULL}; + +_pthread_data_t *_pthread_get_data(pthread_t thread) +{ + RT_DECLARE_SPINLOCK(pth_lock); + _pthread_data_t *ptd; + + if (thread >= PTHREAD_NUM_MAX) return NULL; + + rt_hw_spin_lock(&pth_lock); + ptd = pth_table[thread]; + rt_hw_spin_unlock(&pth_lock); + + if (ptd && ptd->magic == PTHREAD_MAGIC) return ptd; + + return NULL; +} + +pthread_t _pthread_data_get_pth(_pthread_data_t *ptd) +{ + int index; + RT_DECLARE_SPINLOCK(pth_lock); + + rt_hw_spin_lock(&pth_lock); + for (index = 0; index < PTHREAD_NUM_MAX; index ++) + { + if (pth_table[index] == ptd) break; + } + rt_hw_spin_unlock(&pth_lock); + + return index; +} + +pthread_t _pthread_data_create(void) +{ + int index; + _pthread_data_t *ptd = NULL; + RT_DECLARE_SPINLOCK(pth_lock); + + ptd = (_pthread_data_t*)rt_malloc(sizeof(_pthread_data_t)); + if (!ptd) return PTHREAD_NUM_MAX; + + memset(ptd, 0x0, sizeof(_pthread_data_t)); + ptd->canceled = 0; + ptd->cancelstate = PTHREAD_CANCEL_DISABLE; + ptd->canceltype = PTHREAD_CANCEL_DEFERRED; + ptd->magic = PTHREAD_MAGIC; + + rt_hw_spin_lock(&pth_lock); + for (index = 0; index < PTHREAD_NUM_MAX; index ++) + { + if (pth_table[index] == NULL) + { + pth_table[index] = ptd; + break; + } + } + rt_hw_spin_unlock(&pth_lock); + + /* full of pthreads, clean magic and release ptd */ + if (index == PTHREAD_NUM_MAX) + { + ptd->magic = 0x0; + rt_free(ptd); + } + + return index; +} + +void _pthread_data_destroy(pthread_t pth) +{ + RT_DECLARE_SPINLOCK(pth_lock); + + extern _pthread_key_data_t _thread_keys[PTHREAD_KEY_MAX]; + _pthread_data_t *ptd = _pthread_get_data(pth); + if (ptd) + { + /* destruct thread local key */ + if (ptd->tls != RT_NULL) + { + void *data; + rt_uint32_t index; + for (index = 0; index < PTHREAD_KEY_MAX; index ++) + { + if (_thread_keys[index].is_used) + { + data = ptd->tls[index]; + if (data && _thread_keys[index].destructor) + _thread_keys[index].destructor(data); + } + } + + /* release tls area */ + rt_free(ptd->tls); + ptd->tls = RT_NULL; + } + + /* remove from pthread table */ + rt_hw_spin_lock(&pth_lock); + pth_table[pth] = NULL; + rt_hw_spin_unlock(&pth_lock); + + /* delete joinable semaphore */ + if (ptd->joinable_sem != RT_NULL) + rt_sem_delete(ptd->joinable_sem); + + /* release thread resource */ + if (ptd->attr.stackaddr == RT_NULL) + { + /* release thread allocated stack */ + if (ptd->tid) + { + rt_free(ptd->tid->stack_addr); + } + } + /* clean stack addr pointer */ + if (ptd->tid) + ptd->tid->stack_addr = RT_NULL; + + /* + * if this thread create the local thread data, + * delete it + */ + if (ptd->tls != RT_NULL) rt_free(ptd->tls); + rt_free(ptd->tid); + + /* clean magic */ + ptd->magic = 0x0; + + /* free ptd */ + rt_free(ptd); + } +} + +int pthread_system_init(void) +{ + /* initialize key area */ + pthread_key_system_init(); + /* initialize posix mqueue */ + posix_mq_system_init(); + /* initialize posix semaphore */ + posix_sem_system_init(); + + return 0; +} +INIT_COMPONENT_EXPORT(pthread_system_init); + +static void _pthread_destroy(_pthread_data_t *ptd) +{ + pthread_t pth = _pthread_data_get_pth(ptd); + if (pth != PTHREAD_NUM_MAX) + { + _pthread_data_destroy(pth); + } + + return; +} + +static void _pthread_cleanup(rt_thread_t tid) +{ + _pthread_data_t *ptd; + + /* get pthread data from user data of thread */ + ptd = (_pthread_data_t *)tid->user_data; + RT_ASSERT(ptd != RT_NULL); + + /* clear cleanup function */ + tid->cleanup = RT_NULL; + if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE) + { + rt_sem_release(ptd->joinable_sem); + } + else + { + /* release pthread resource */ + _pthread_destroy(ptd); + } +} + +static void pthread_entry_stub(void *parameter) +{ + void *value; + _pthread_data_t *ptd; + + ptd = (_pthread_data_t *)parameter; + + /* execute pthread entry */ + value = ptd->thread_entry(ptd->thread_parameter); + /* set value */ + ptd->return_value = value; +} + +int pthread_create(pthread_t *pid, + const pthread_attr_t *attr, + void *(*start)(void *), void *parameter) +{ + int ret = 0; + void *stack; + char name[RT_NAME_MAX]; + static rt_uint16_t pthread_number = 0; + + pthread_t pth_id; + _pthread_data_t *ptd; + + /* pid shall be provided */ + RT_ASSERT(pid != RT_NULL); + + /* allocate posix thread data */ + pth_id = _pthread_data_create(); + if (pth_id == PTHREAD_NUM_MAX) + { + ret = ENOMEM; + goto __exit; + } + /* get pthread data */ + ptd = _pthread_get_data(pth_id); + + RT_ASSERT(ptd != RT_NULL); + + if (attr != RT_NULL) + { + ptd->attr = *attr; + } + else + { + /* use default attribute */ + pthread_attr_init(&ptd->attr); + } + + if (ptd->attr.stacksize == 0) + { + ret = EINVAL; + goto __exit; + } + + rt_snprintf(name, sizeof(name), "pth%02d", pthread_number ++); + + /* pthread is a static thread object */ + ptd->tid = (rt_thread_t) rt_malloc(sizeof(struct rt_thread)); + if (ptd->tid == RT_NULL) + { + ret = ENOMEM; + goto __exit; + } + memset(ptd->tid, 0, sizeof(struct rt_thread)); + + if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE) + { + ptd->joinable_sem = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO); + if (ptd->joinable_sem == RT_NULL) + { + ret = ENOMEM; + goto __exit; + } + } + else + { + ptd->joinable_sem = RT_NULL; + } + + /* set parameter */ + ptd->thread_entry = start; + ptd->thread_parameter = parameter; + + /* stack */ + if (ptd->attr.stackaddr == 0) + { + stack = (void *)rt_malloc(ptd->attr.stacksize); + } + else + { + stack = (void *)(ptd->attr.stackaddr); + } + + if (stack == RT_NULL) + { + ret = ENOMEM; + goto __exit; + } + + /* initial this pthread to system */ + if (rt_thread_init(ptd->tid, name, pthread_entry_stub, ptd, + stack, ptd->attr.stacksize, + ptd->attr.schedparam.sched_priority, 20) != RT_EOK) + { + ret = EINVAL; + goto __exit; + } + + /* set pthread id */ + *pid = pth_id; + + /* set pthread cleanup function and ptd data */ + ptd->tid->cleanup = _pthread_cleanup; + ptd->tid->user_data = (rt_ubase_t)ptd; + + /* start thread */ + if (rt_thread_startup(ptd->tid) == RT_EOK) + return 0; + + /* start thread failed */ + rt_thread_detach(ptd->tid); + ret = EINVAL; + +__exit: + if (pth_id != PTHREAD_NUM_MAX) + _pthread_data_destroy(pth_id); + return ret; +} +RTM_EXPORT(pthread_create); + +int pthread_detach(pthread_t thread) +{ + int ret = 0; + _pthread_data_t *ptd = _pthread_get_data(thread); + if (ptd == RT_NULL) + { + /* invalid pthread id */ + ret = EINVAL; + goto __exit; + } + + rt_enter_critical(); + if (ptd->attr.detachstate == PTHREAD_CREATE_DETACHED) + { + /* The implementation has detected that the value specified by thread does not refer + * to a joinable thread. + */ + ret = EINVAL; + goto __exit; + } + + if ((ptd->tid->stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE) + { + /* this defunct pthread is not handled by idle */ + if (rt_sem_trytake(ptd->joinable_sem) != RT_EOK) + { + rt_sem_release(ptd->joinable_sem); + + /* change to detach state */ + ptd->attr.detachstate = PTHREAD_CREATE_DETACHED; + + /* detach joinable semaphore */ + if (ptd->joinable_sem) + { + rt_sem_delete(ptd->joinable_sem); + ptd->joinable_sem = RT_NULL; + } + } + else + { + /* destroy this pthread */ + _pthread_destroy(ptd); + } + + goto __exit; + } + else + { + /* change to detach state */ + ptd->attr.detachstate = PTHREAD_CREATE_DETACHED; + + /* detach joinable semaphore */ + if (ptd->joinable_sem) + { + rt_sem_delete(ptd->joinable_sem); + ptd->joinable_sem = RT_NULL; + } + } + +__exit: + rt_exit_critical(); + return ret; +} +RTM_EXPORT(pthread_detach); + +int pthread_join(pthread_t thread, void **value_ptr) +{ + _pthread_data_t *ptd; + rt_err_t result; + + ptd = _pthread_get_data(thread); + + if (ptd == RT_NULL) + { + return EINVAL; /* invalid pthread id */ + } + + if (ptd && ptd->tid == rt_thread_self()) + { + /* join self */ + return EDEADLK; + } + + if (ptd->attr.detachstate == PTHREAD_CREATE_DETACHED) + { + return EINVAL; /* join on a detached pthread */ + } + + result = rt_sem_take(ptd->joinable_sem, RT_WAITING_FOREVER); + if (result == RT_EOK) + { + /* get return value */ + if (value_ptr != RT_NULL) + *value_ptr = ptd->return_value; + + /* destroy this pthread */ + _pthread_destroy(ptd); + } + else + { + return ESRCH; + } + + return 0; +} +RTM_EXPORT(pthread_join); + +pthread_t pthread_self (void) +{ + rt_thread_t tid; + _pthread_data_t *ptd; + + tid = rt_thread_self(); + if (tid == NULL) return PTHREAD_NUM_MAX; + + /* get pthread data from user data of thread */ + ptd = (_pthread_data_t *)rt_thread_self()->user_data; + RT_ASSERT(ptd != RT_NULL); + + return _pthread_data_get_pth(ptd); +} +RTM_EXPORT(pthread_self); + +void pthread_exit(void *value) +{ + _pthread_data_t *ptd; + _pthread_cleanup_t *cleanup; + extern _pthread_key_data_t _thread_keys[PTHREAD_KEY_MAX]; + + if (rt_thread_self() == NULL) return; + + /* get pthread data from user data of thread */ + ptd = (_pthread_data_t *)rt_thread_self()->user_data; + + rt_enter_critical(); + /* disable cancel */ + ptd->cancelstate = PTHREAD_CANCEL_DISABLE; + /* set return value */ + ptd->return_value = value; + rt_exit_critical(); + + /* invoke pushed cleanup */ + while (ptd->cleanup != RT_NULL) + { + cleanup = ptd->cleanup; + ptd->cleanup = cleanup->next; + + cleanup->cleanup_func(cleanup->parameter); + /* release this cleanup function */ + rt_free(cleanup); + } + + /* destruct thread local key */ + if (ptd->tls != RT_NULL) + { + void *data; + rt_uint32_t index; + + for (index = 0; index < PTHREAD_KEY_MAX; index ++) + { + if (_thread_keys[index].is_used) + { + data = ptd->tls[index]; + if (data && _thread_keys[index].destructor) + _thread_keys[index].destructor(data); + } + } + + /* release tls area */ + rt_free(ptd->tls); + ptd->tls = RT_NULL; + } + + /* detach thread */ + rt_thread_detach(ptd->tid); + /* reschedule thread */ + rt_schedule(); +} +RTM_EXPORT(pthread_exit); + +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) +{ + RT_ASSERT(once_control != RT_NULL); + RT_ASSERT(init_routine != RT_NULL); + + rt_enter_critical(); + if (!(*once_control)) + { + /* call routine once */ + *once_control = 1; + rt_exit_critical(); + + init_routine(); + } + rt_exit_critical(); + + return 0; +} +RTM_EXPORT(pthread_once); + +int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) +{ + return EOPNOTSUPP; +} +RTM_EXPORT(pthread_atfork); + +int pthread_kill(pthread_t thread, int sig) +{ +#ifdef RT_USING_SIGNALS + _pthread_data_t *ptd; + int ret; + + ptd = _pthread_get_data(thread); + if (ptd) + { + ret = rt_thread_kill(ptd->tid, sig); + if (ret == -RT_EINVAL) + { + return EINVAL; + } + + return ret; + } + + return ESRCH; +#else + return ENOSYS; +#endif +} +RTM_EXPORT(pthread_kill); + +#ifdef RT_USING_SIGNALS +int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) +{ + return sigprocmask(how, set, oset); +} +#endif + +void pthread_cleanup_pop(int execute) +{ + _pthread_data_t *ptd; + _pthread_cleanup_t *cleanup; + + if (rt_thread_self() == NULL) return; + + /* get pthread data from user data of thread */ + ptd = (_pthread_data_t *)rt_thread_self()->user_data; + RT_ASSERT(ptd != RT_NULL); + + if (execute) + { + rt_enter_critical(); + cleanup = ptd->cleanup; + if (cleanup) + ptd->cleanup = cleanup->next; + rt_exit_critical(); + + if (cleanup) + { + cleanup->cleanup_func(cleanup->parameter); + + rt_free(cleanup); + } + } +} +RTM_EXPORT(pthread_cleanup_pop); + +void pthread_cleanup_push(void (*routine)(void *), void *arg) +{ + _pthread_data_t *ptd; + _pthread_cleanup_t *cleanup; + + if (rt_thread_self() == NULL) return; + + /* get pthread data from user data of thread */ + ptd = (_pthread_data_t *)rt_thread_self()->user_data; + RT_ASSERT(ptd != RT_NULL); + + cleanup = (_pthread_cleanup_t *)rt_malloc(sizeof(_pthread_cleanup_t)); + if (cleanup != RT_NULL) + { + cleanup->cleanup_func = routine; + cleanup->parameter = arg; + + rt_enter_critical(); + cleanup->next = ptd->cleanup; + ptd->cleanup = cleanup; + rt_exit_critical(); + } +} +RTM_EXPORT(pthread_cleanup_push); + +/* + * According to IEEE Std 1003.1, 2004 Edition , following pthreads + * interface support cancellation point: + * mq_receive() + * mq_send() + * mq_timedreceive() + * mq_timedsend() + * msgrcv() + * msgsnd() + * msync() + * pthread_cond_timedwait() + * pthread_cond_wait() + * pthread_join() + * pthread_testcancel() + * sem_timedwait() + * sem_wait() + * + * A cancellation point may also occur when a thread is + * executing the following functions: + * pthread_rwlock_rdlock() + * pthread_rwlock_timedrdlock() + * pthread_rwlock_timedwrlock() + * pthread_rwlock_wrlock() + * + * The pthread_cancel(), pthread_setcancelstate(), and pthread_setcanceltype() + * functions are defined to be async-cancel safe. + */ + +int pthread_setcancelstate(int state, int *oldstate) +{ + _pthread_data_t *ptd; + + if (rt_thread_self() == NULL) return EINVAL; + + /* get pthread data from user data of thread */ + ptd = (_pthread_data_t *)rt_thread_self()->user_data; + RT_ASSERT(ptd != RT_NULL); + + if ((state == PTHREAD_CANCEL_ENABLE) || (state == PTHREAD_CANCEL_DISABLE)) + { + if (oldstate) + *oldstate = ptd->cancelstate; + ptd->cancelstate = state; + + return 0; + } + + return EINVAL; +} +RTM_EXPORT(pthread_setcancelstate); + +int pthread_setcanceltype(int type, int *oldtype) +{ + _pthread_data_t *ptd; + + if (rt_thread_self() == NULL) return EINVAL; + + /* get pthread data from user data of thread */ + ptd = (_pthread_data_t *)rt_thread_self()->user_data; + RT_ASSERT(ptd != RT_NULL); + + if ((type != PTHREAD_CANCEL_DEFERRED) && (type != PTHREAD_CANCEL_ASYNCHRONOUS)) + return EINVAL; + + if (oldtype) + *oldtype = ptd->canceltype; + ptd->canceltype = type; + + return 0; +} +RTM_EXPORT(pthread_setcanceltype); + +void pthread_testcancel(void) +{ + int cancel = 0; + _pthread_data_t *ptd; + + if (rt_thread_self() == NULL) return; + + /* get pthread data from user data of thread */ + ptd = (_pthread_data_t *)rt_thread_self()->user_data; + RT_ASSERT(ptd != RT_NULL); + + if (ptd->cancelstate == PTHREAD_CANCEL_ENABLE) + cancel = ptd->canceled; + if (cancel) + pthread_exit((void *)PTHREAD_CANCELED); +} +RTM_EXPORT(pthread_testcancel); + +int pthread_cancel(pthread_t thread) +{ + _pthread_data_t *ptd; + + /* get posix thread data */ + ptd = _pthread_get_data(thread); + if (ptd == RT_NULL) + { + return EINVAL; + } + + /* cancel self */ + if (ptd->tid == rt_thread_self()) + return 0; + + /* set canceled */ + if (ptd->cancelstate == PTHREAD_CANCEL_ENABLE) + { + ptd->canceled = 1; + if (ptd->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS) + { + /* + * to detach thread. + * this thread will be removed from scheduler list + * and because there is a cleanup function in the + * thread (pthread_cleanup), it will move to defunct + * thread list and wait for handling in idle thread. + */ + rt_thread_detach(ptd->tid); + } + } + + return 0; +} +RTM_EXPORT(pthread_cancel); + diff --git a/components/libc/posix/pthreads/pthread.h b/components/libc/posix/pthreads/pthread.h new file mode 100644 index 0000000000..177b2539c5 --- /dev/null +++ b/components/libc/posix/pthreads/pthread.h @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-10-26 Bernard the first version + */ + +#ifndef __PTHREAD_H__ +#define __PTHREAD_H__ + +#include <rtthread.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <posix_types.h> +#include <sched.h> + +#define PTHREAD_KEY_MAX 8 + +#define PTHREAD_COND_INITIALIZER {-1, 0} +#define PTHREAD_RWLOCK_INITIALIZER {-1, 0} +#define PTHREAD_MUTEX_INITIALIZER {-1, 0} + +#define PTHREAD_CREATE_JOINABLE 0x00 +#define PTHREAD_CREATE_DETACHED 0x01 + +#define PTHREAD_EXPLICIT_SCHED 0 +#define PTHREAD_INHERIT_SCHED 1 + +typedef long pthread_t; +typedef long pthread_condattr_t; +typedef long pthread_rwlockattr_t; +typedef long pthread_mutexattr_t; +typedef long pthread_barrierattr_t; + +typedef int pthread_key_t; +typedef int pthread_once_t; + +enum +{ + PTHREAD_CANCEL_ASYNCHRONOUS = 0, + PTHREAD_CANCEL_ENABLE, + PTHREAD_CANCEL_DEFERRED, + PTHREAD_CANCEL_DISABLE, + PTHREAD_CANCELED +}; + +enum +{ + PTHREAD_MUTEX_NORMAL = 0, + PTHREAD_MUTEX_RECURSIVE = 1, + PTHREAD_MUTEX_ERRORCHECK = 2, + PTHREAD_MUTEX_ERRORCHECK_NP = PTHREAD_MUTEX_ERRORCHECK, + PTHREAD_MUTEX_RECURSIVE_NP = PTHREAD_MUTEX_RECURSIVE, + PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL +}; + +/* init value for pthread_once_t */ +#define PTHREAD_ONCE_INIT 0 + +enum +{ + PTHREAD_PRIO_INHERIT =0, + PTHREAD_PRIO_NONE, + PTHREAD_PRIO_PROTECT, +}; + +#define PTHREAD_PROCESS_PRIVATE 0 +#define PTHREAD_PROCESS_SHARED 1 + +#define PTHREAD_SCOPE_PROCESS 0 +#define PTHREAD_SCOPE_SYSTEM 1 + +struct sched_param +{ + int sched_priority; +}; + +struct pthread_attr +{ + void* stackaddr; /* stack address of thread */ + int stacksize; /* stack size of thread */ + + int inheritsched; /* Inherit parent prio/policy */ + int schedpolicy; /* scheduler policy */ + struct sched_param schedparam; /* sched parameter */ + + int detachstate; /* detach state */ +}; +typedef struct pthread_attr pthread_attr_t; + +struct pthread_mutex +{ + pthread_mutexattr_t attr; + struct rt_mutex lock; +}; +typedef struct pthread_mutex pthread_mutex_t; + +struct pthread_cond +{ + pthread_condattr_t attr; + struct rt_semaphore sem; +}; +typedef struct pthread_cond pthread_cond_t; + +struct pthread_rwlock +{ + pthread_rwlockattr_t attr; + + pthread_mutex_t rw_mutex; /* basic lock on this struct */ + pthread_cond_t rw_condreaders; /* for reader threads waiting */ + pthread_cond_t rw_condwriters; /* for writer threads waiting */ + + int rw_nwaitreaders; /* the number of reader threads waiting */ + int rw_nwaitwriters; /* the number of writer threads waiting */ + int rw_refcount; /* 0: unlocked, -1: locked by writer, > 0 locked by n readers */ +}; +typedef struct pthread_rwlock pthread_rwlock_t; + +/* spinlock implementation, (ADVANCED REALTIME THREADS)*/ +struct pthread_spinlock +{ + int lock; +}; +typedef struct pthread_spinlock pthread_spinlock_t; + +struct pthread_barrier +{ + int count; + pthread_cond_t cond; + pthread_mutex_t mutex; +}; +typedef struct pthread_barrier pthread_barrier_t; + +/* pthread thread interface */ +int pthread_attr_destroy(pthread_attr_t *attr); +int pthread_attr_init(pthread_attr_t *attr); +int pthread_attr_setdetachstate(pthread_attr_t *attr, int state); +int pthread_attr_getdetachstate(pthread_attr_t const *attr, int *state); +int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); +int pthread_attr_getschedpolicy(pthread_attr_t const *attr, int *policy); +int pthread_attr_setschedparam(pthread_attr_t *attr,struct sched_param const *param); +int pthread_attr_getschedparam(pthread_attr_t const *attr,struct sched_param *param); +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stack_size); +int pthread_attr_getstacksize(pthread_attr_t const *attr, size_t *stack_size); +int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stack_addr); +int pthread_attr_getstackaddr(pthread_attr_t const *attr, void **stack_addr); +int pthread_attr_setstack(pthread_attr_t *attr, + void *stack_base, + size_t stack_size); +int pthread_attr_getstack(pthread_attr_t const *attr, + void **stack_base, + size_t *stack_size); +int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guard_size); +int pthread_attr_getguardsize(pthread_attr_t const *attr, size_t *guard_size); +int pthread_attr_setscope(pthread_attr_t *attr, int scope); +int pthread_attr_getscope(pthread_attr_t const *attr); +int pthread_system_init(void); +int pthread_create (pthread_t *tid, const pthread_attr_t *attr, + void *(*start) (void *), void *arg); + +int pthread_detach (pthread_t thread); +int pthread_join (pthread_t thread, void **value_ptr); + +rt_inline int pthread_equal (pthread_t t1, pthread_t t2) +{ + return t1 == t2; +} + +pthread_t pthread_self (void); + +void pthread_exit (void *value_ptr); +int pthread_once(pthread_once_t * once_control, void (*init_routine) (void)); + +/* pthread cleanup */ +void pthread_cleanup_pop(int execute); +void pthread_cleanup_push(void (*routine)(void*), void *arg); + +/* pthread cancel */ +int pthread_cancel(pthread_t thread); +void pthread_testcancel(void); +int pthread_setcancelstate(int state, int *oldstate); +int pthread_setcanceltype(int type, int *oldtype); + +int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)); +int pthread_kill(pthread_t thread, int sig); + +/* pthread mutex interface */ +int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); +int pthread_mutex_destroy(pthread_mutex_t *mutex); +int pthread_mutex_lock(pthread_mutex_t *mutex); +int pthread_mutex_unlock(pthread_mutex_t *mutex); +int pthread_mutex_trylock(pthread_mutex_t *mutex); + +int pthread_mutexattr_init(pthread_mutexattr_t *attr); +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); +int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type); +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); +int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); +int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared); + +/* pthread condition interface */ +int pthread_condattr_destroy(pthread_condattr_t *attr); +int pthread_condattr_init(pthread_condattr_t *attr); + +/* ADVANCED REALTIME feature in IEEE Std 1003.1, 2004 Edition */ +int pthread_condattr_getclock(const pthread_condattr_t *attr, + clockid_t *clock_id); +int pthread_condattr_setclock(pthread_condattr_t *attr, + clockid_t clock_id); + +int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); +int pthread_cond_destroy(pthread_cond_t *cond); +int pthread_cond_broadcast(pthread_cond_t *cond); +int pthread_cond_signal(pthread_cond_t *cond); + +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); +int pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime); + +/* pthread rwlock interface */ +int pthread_rwlockattr_init (pthread_rwlockattr_t *attr); +int pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr); +int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared); +int pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared); + +int pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr); +int pthread_rwlock_destroy (pthread_rwlock_t *rwlock); + +int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock); +int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock); + +int pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock, const struct timespec *abstime); +int pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, const struct timespec *abstime); + +int pthread_rwlock_unlock (pthread_rwlock_t *rwlock); + +int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock); +int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock); + +/* pthread spinlock interface */ +int pthread_spin_init (pthread_spinlock_t *lock, int pshared); +int pthread_spin_destroy (pthread_spinlock_t *lock); + +int pthread_spin_lock (pthread_spinlock_t * lock); +int pthread_spin_trylock (pthread_spinlock_t * lock); +int pthread_spin_unlock (pthread_spinlock_t * lock); + +/* pthread barrier interface */ +int pthread_barrierattr_destroy(pthread_barrierattr_t *attr); +int pthread_barrierattr_init(pthread_barrierattr_t *attr); +int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, int *pshared); +int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared); + +int pthread_barrier_destroy(pthread_barrier_t *barrier); +int pthread_barrier_init(pthread_barrier_t *barrier, + const pthread_barrierattr_t *attr, + unsigned count); + +int pthread_barrier_wait(pthread_barrier_t *barrier); + +int pthread_setspecific(pthread_key_t key, const void *value); +void *pthread_getspecific(pthread_key_t key); +int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)); +int pthread_key_delete(pthread_key_t key); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/libc/posix/pthreads/pthread_attr.c b/components/libc/posix/pthreads/pthread_attr.c new file mode 100644 index 0000000000..4aedf5a90d --- /dev/null +++ b/components/libc/posix/pthreads/pthread_attr.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-10-26 Bernard the first version + */ + +#include <rtthread.h> +#include "pthread.h" +#include "sched.h" +#include <string.h> + +#define DEFAULT_STACK_SIZE 2048 +#define DEFAULT_PRIORITY (RT_THREAD_PRIORITY_MAX/2 + RT_THREAD_PRIORITY_MAX/4) + +const pthread_attr_t pthread_default_attr = +{ + 0, /* stack base */ + DEFAULT_STACK_SIZE, /* stack size */ + + PTHREAD_INHERIT_SCHED, /* Inherit parent prio/policy */ + SCHED_FIFO, /* scheduler policy */ + { + DEFAULT_PRIORITY, /* scheduler priority */ + }, + PTHREAD_CREATE_JOINABLE, /* detach state */ +}; + +int pthread_attr_init(pthread_attr_t *attr) +{ + RT_ASSERT(attr != RT_NULL); + + *attr = pthread_default_attr; + + return 0; +} +RTM_EXPORT(pthread_attr_init); + +int pthread_attr_destroy(pthread_attr_t *attr) +{ + RT_ASSERT(attr != RT_NULL); + + memset(attr, 0, sizeof(pthread_attr_t)); + + return 0; +} +RTM_EXPORT(pthread_attr_destroy); + +int pthread_attr_setdetachstate(pthread_attr_t *attr, int state) +{ + RT_ASSERT(attr != RT_NULL); + + if (state != PTHREAD_CREATE_JOINABLE && state != PTHREAD_CREATE_DETACHED) + return EINVAL; + + attr->detachstate = state; + + return 0; +} +RTM_EXPORT(pthread_attr_setdetachstate); + +int pthread_attr_getdetachstate(pthread_attr_t const *attr, int *state) +{ + RT_ASSERT(attr != RT_NULL); + + *state = (int)attr->detachstate; + + return 0; +} +RTM_EXPORT(pthread_attr_getdetachstate); + +int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) +{ + RT_ASSERT(attr != RT_NULL); + + attr->schedpolicy = policy; + + return 0; +} +RTM_EXPORT(pthread_attr_setschedpolicy); + +int pthread_attr_getschedpolicy(pthread_attr_t const *attr, int *policy) +{ + RT_ASSERT(attr != RT_NULL); + + *policy = (int)attr->schedpolicy; + + return 0; +} +RTM_EXPORT(pthread_attr_getschedpolicy); + +int pthread_attr_setschedparam(pthread_attr_t *attr, + struct sched_param const *param) +{ + RT_ASSERT(attr != RT_NULL); + RT_ASSERT(param != RT_NULL); + + attr->schedparam.sched_priority = param->sched_priority; + + return 0; +} +RTM_EXPORT(pthread_attr_setschedparam); + +int pthread_attr_getschedparam(pthread_attr_t const *attr, + struct sched_param *param) +{ + RT_ASSERT(attr != RT_NULL); + RT_ASSERT(param != RT_NULL); + + param->sched_priority = attr->schedparam.sched_priority; + + return 0; +} +RTM_EXPORT(pthread_attr_getschedparam); + +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stack_size) +{ + RT_ASSERT(attr != RT_NULL); + + attr->stacksize = stack_size; + + return 0; +} +RTM_EXPORT(pthread_attr_setstacksize); + +int pthread_attr_getstacksize(pthread_attr_t const *attr, size_t *stack_size) +{ + RT_ASSERT(attr != RT_NULL); + + *stack_size = attr->stacksize; + + return 0; +} +RTM_EXPORT(pthread_attr_getstacksize); + +int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stack_addr) +{ + RT_ASSERT(attr != RT_NULL); + + return EOPNOTSUPP; +} +RTM_EXPORT(pthread_attr_setstackaddr); + +int pthread_attr_getstackaddr(pthread_attr_t const *attr, void **stack_addr) +{ + RT_ASSERT(attr != RT_NULL); + + return EOPNOTSUPP; +} +RTM_EXPORT(pthread_attr_getstackaddr); + +int pthread_attr_setstack(pthread_attr_t *attr, + void *stack_base, + size_t stack_size) +{ + RT_ASSERT(attr != RT_NULL); + + attr->stackaddr = stack_base; + attr->stacksize = RT_ALIGN_DOWN(stack_size, RT_ALIGN_SIZE); + + return 0; +} +RTM_EXPORT(pthread_attr_setstack); + +int pthread_attr_getstack(pthread_attr_t const *attr, + void **stack_base, + size_t *stack_size) +{ + RT_ASSERT(attr != RT_NULL); + + *stack_base = attr->stackaddr; + *stack_size = attr->stacksize; + + return 0; +} +RTM_EXPORT(pthread_attr_getstack); + +int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guard_size) +{ + return EOPNOTSUPP; +} + +int pthread_attr_getguardsize(pthread_attr_t const *attr, size_t *guard_size) +{ + return EOPNOTSUPP; +} +RTM_EXPORT(pthread_attr_getguardsize); + +int pthread_attr_setscope(pthread_attr_t *attr, int scope) +{ + if (scope == PTHREAD_SCOPE_SYSTEM) + return 0; + if (scope == PTHREAD_SCOPE_PROCESS) + return EOPNOTSUPP; + + return EINVAL; +} +RTM_EXPORT(pthread_attr_setscope); + +int pthread_attr_getscope(pthread_attr_t const *attr) +{ + return PTHREAD_SCOPE_SYSTEM; +} +RTM_EXPORT(pthread_attr_getscope); diff --git a/components/libc/posix/pthreads/pthread_barrier.c b/components/libc/posix/pthreads/pthread_barrier.c new file mode 100644 index 0000000000..1a5b7fa3f9 --- /dev/null +++ b/components/libc/posix/pthreads/pthread_barrier.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-10-26 Bernard the first version + */ + +#include <pthread.h> + +int pthread_barrierattr_destroy(pthread_barrierattr_t *attr) +{ + if (!attr) + return EINVAL; + + return 0; +} +RTM_EXPORT(pthread_barrierattr_destroy); + +int pthread_barrierattr_init(pthread_barrierattr_t *attr) +{ + if (!attr) + return EINVAL; + *attr = PTHREAD_PROCESS_PRIVATE; + + return 0; +} +RTM_EXPORT(pthread_barrierattr_init); + +int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, + int *pshared) +{ + if (!attr) + return EINVAL; + *pshared = (int)*attr; + + return 0; +} +RTM_EXPORT(pthread_barrierattr_getpshared); + +int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) +{ + if (!attr) + return EINVAL; + if (pshared == PTHREAD_PROCESS_PRIVATE) + attr = PTHREAD_PROCESS_PRIVATE; + + return EINVAL; +} +RTM_EXPORT(pthread_barrierattr_setpshared); + +int pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + rt_err_t result; + + if (!barrier) + return EINVAL; + + result = pthread_cond_destroy(&(barrier->cond)); + + return result; +} +RTM_EXPORT(pthread_barrier_destroy); + +int pthread_barrier_init(pthread_barrier_t *barrier, + const pthread_barrierattr_t *attr, + unsigned count) +{ + if (!barrier) + return EINVAL; + if (attr && (*attr != PTHREAD_PROCESS_PRIVATE)) + return EINVAL; + + barrier->count = count; + pthread_cond_init(&(barrier->cond), NULL); + pthread_mutex_init(&(barrier->mutex), NULL); + + return 0; +} +RTM_EXPORT(pthread_barrier_init); + +int pthread_barrier_wait(pthread_barrier_t *barrier) +{ + rt_err_t result; + if (!barrier) + return EINVAL; + + result = pthread_mutex_lock(&(barrier->mutex)); + if (result != 0) + return EINVAL; + + if (barrier->count == 0) + result = EINVAL; + else + { + barrier->count -= 1; + if (barrier->count == 0) /* broadcast condition */ + pthread_cond_broadcast(&(barrier->cond)); + else + pthread_cond_wait(&(barrier->cond), &(barrier->mutex)); + } + + pthread_mutex_unlock(&(barrier->mutex)); + + return result; +} +RTM_EXPORT(pthread_barrier_wait); + diff --git a/components/libc/posix/pthreads/pthread_cond.c b/components/libc/posix/pthreads/pthread_cond.c new file mode 100644 index 0000000000..9bf49704ed --- /dev/null +++ b/components/libc/posix/pthreads/pthread_cond.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-10-26 Bernard the first version + */ + +#include <pthread.h> +#include "pthread_internal.h" + +int pthread_condattr_destroy(pthread_condattr_t *attr) +{ + if (!attr) + return EINVAL; + + return 0; +} +RTM_EXPORT(pthread_condattr_destroy); + +int pthread_condattr_init(pthread_condattr_t *attr) +{ + if (!attr) + return EINVAL; + *attr = PTHREAD_PROCESS_PRIVATE; + + return 0; +} +RTM_EXPORT(pthread_condattr_init); + +int pthread_condattr_getclock(const pthread_condattr_t *attr, + clockid_t *clock_id) +{ + return 0; +} +RTM_EXPORT(pthread_condattr_getclock); + +int pthread_condattr_setclock(pthread_condattr_t *attr, + clockid_t clock_id) +{ + return 0; +} +RTM_EXPORT(pthread_condattr_setclock); + +int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *pshared) +{ + if (!attr || !pshared) + return EINVAL; + + *pshared = PTHREAD_PROCESS_PRIVATE; + + return 0; +} +RTM_EXPORT(pthread_condattr_getpshared); + +int pthread_condattr_setpshared(pthread_condattr_t*attr, int pshared) +{ + if ((pshared != PTHREAD_PROCESS_PRIVATE) && + (pshared != PTHREAD_PROCESS_SHARED)) + { + return EINVAL; + } + + if (pshared != PTHREAD_PROCESS_PRIVATE) + return ENOSYS; + + return 0; +} +RTM_EXPORT(pthread_condattr_setpshared); + +int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) +{ + rt_err_t result; + char cond_name[RT_NAME_MAX]; + static rt_uint16_t cond_num = 0; + + /* parameter check */ + if (cond == RT_NULL) + return EINVAL; + if ((attr != RT_NULL) && (*attr != PTHREAD_PROCESS_PRIVATE)) + return EINVAL; + + rt_snprintf(cond_name, sizeof(cond_name), "cond%02d", cond_num++); + + if (attr == RT_NULL) /* use default value */ + cond->attr = PTHREAD_PROCESS_PRIVATE; + else + cond->attr = *attr; + + result = rt_sem_init(&cond->sem, cond_name, 0, RT_IPC_FLAG_FIFO); + if (result != RT_EOK) + return EINVAL; + + /* detach the object from system object container */ + rt_object_detach(&(cond->sem.parent.parent)); + cond->sem.parent.parent.type = RT_Object_Class_Semaphore; + + return 0; +} +RTM_EXPORT(pthread_cond_init); + +int pthread_cond_destroy(pthread_cond_t *cond) +{ + rt_err_t result; + if (cond == RT_NULL) + return EINVAL; + if (cond->attr == -1) + return 0; /* which is not initialized */ + + result = rt_sem_trytake(&(cond->sem)); + if (result != RT_EOK) + return EBUSY; + + /* clean condition */ + rt_memset(cond, 0, sizeof(pthread_cond_t)); + cond->attr = -1; + + return 0; +} +RTM_EXPORT(pthread_cond_destroy); + +int pthread_cond_broadcast(pthread_cond_t *cond) +{ + rt_err_t result; + + if (cond == RT_NULL) + return EINVAL; + if (cond->attr == -1) + pthread_cond_init(cond, RT_NULL); + + rt_enter_critical(); + while (1) + { + /* try to take condition semaphore */ + result = rt_sem_trytake(&(cond->sem)); + if (result == -RT_ETIMEOUT) + { + /* it's timeout, release this semaphore */ + rt_sem_release(&(cond->sem)); + } + else if (result == RT_EOK) + { + /* has taken this semaphore, release it */ + rt_sem_release(&(cond->sem)); + break; + } + else + { + rt_exit_critical(); + + return EINVAL; + } + } + rt_exit_critical(); + + return 0; +} +RTM_EXPORT(pthread_cond_broadcast); + +int pthread_cond_signal(pthread_cond_t *cond) +{ + rt_err_t result; + + if (cond == RT_NULL) + return EINVAL; + if (cond->attr == -1) + pthread_cond_init(cond, RT_NULL); + + result = rt_sem_release(&(cond->sem)); + if (result == RT_EOK) + return 0; + + return 0; +} +RTM_EXPORT(pthread_cond_signal); + +rt_err_t _pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mutex, + rt_int32_t timeout) +{ + rt_err_t result; + + if (!cond || !mutex) + return -RT_ERROR; + /* check whether initialized */ + if (cond->attr == -1) + pthread_cond_init(cond, RT_NULL); + + /* The mutex was not owned by the current thread at the time of the call. */ + if (mutex->lock.owner != rt_thread_self()) + return -RT_ERROR; + /* unlock a mutex failed */ + if (pthread_mutex_unlock(mutex) != 0) + return -RT_ERROR; + + result = rt_sem_take(&(cond->sem), timeout); + /* lock mutex again */ + pthread_mutex_lock(mutex); + + return result; +} +RTM_EXPORT(_pthread_cond_timedwait); + +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + rt_err_t result; + + result = _pthread_cond_timedwait(cond, mutex, RT_WAITING_FOREVER); + if (result == RT_EOK) + return 0; + + return EINVAL; +} +RTM_EXPORT(pthread_cond_wait); + +int pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + int timeout; + rt_err_t result; + + timeout = rt_timespec_to_tick(abstime); + result = _pthread_cond_timedwait(cond, mutex, timeout); + if (result == RT_EOK) + return 0; + if (result == -RT_ETIMEOUT) + return ETIMEDOUT; + + return EINVAL; +} +RTM_EXPORT(pthread_cond_timedwait); + diff --git a/components/libc/posix/pthreads/pthread_internal.h b/components/libc/posix/pthreads/pthread_internal.h new file mode 100644 index 0000000000..1fce29f636 --- /dev/null +++ b/components/libc/posix/pthreads/pthread_internal.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-10-26 Bernard the first version + */ + +#ifndef __PTHREAD_INTERNAL_H__ +#define __PTHREAD_INTERNAL_H__ + +#include <rtthread.h> +#include <pthread.h> +#include <sys/time.h> + +struct _pthread_cleanup +{ + void (*cleanup_func)(void *parameter); + void *parameter; + + struct _pthread_cleanup *next; +}; +typedef struct _pthread_cleanup _pthread_cleanup_t; + +struct _pthread_key_data +{ + int is_used; + void (*destructor)(void *parameter); +}; +typedef struct _pthread_key_data _pthread_key_data_t; + +#ifndef PTHREAD_NUM_MAX +#define PTHREAD_NUM_MAX 32 +#endif + +#define PTHREAD_MAGIC 0x70746873 +struct _pthread_data +{ + rt_uint32_t magic; + pthread_attr_t attr; + rt_thread_t tid; + + void* (*thread_entry)(void *parameter); + void *thread_parameter; + + /* return value */ + void *return_value; + + /* semaphore for joinable thread */ + rt_sem_t joinable_sem; + + /* cancel state and type */ + rt_uint8_t cancelstate; + volatile rt_uint8_t canceltype; + volatile rt_uint8_t canceled; + + _pthread_cleanup_t *cleanup; + void** tls; /* thread-local storage area */ +}; +typedef struct _pthread_data _pthread_data_t; + +_pthread_data_t *_pthread_get_data(pthread_t thread); + +void posix_mq_system_init(void); +void posix_sem_system_init(void); +void pthread_key_system_init(void); + +#endif diff --git a/components/libc/posix/pthreads/pthread_mutex.c b/components/libc/posix/pthreads/pthread_mutex.c new file mode 100644 index 0000000000..a681380b11 --- /dev/null +++ b/components/libc/posix/pthreads/pthread_mutex.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-10-26 Bernard the first version + */ + +#include <rtthread.h> +#include "pthread.h" + +#define MUTEXATTR_SHARED_MASK 0x0010 +#define MUTEXATTR_TYPE_MASK 0x000f + +const pthread_mutexattr_t pthread_default_mutexattr = PTHREAD_PROCESS_PRIVATE; + +int pthread_mutexattr_init(pthread_mutexattr_t *attr) +{ + if (attr) + { + *attr = pthread_default_mutexattr; + + return 0; + } + + return EINVAL; +} +RTM_EXPORT(pthread_mutexattr_init); + +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) +{ + if (attr) + { + *attr = -1; + + return 0; + } + + return EINVAL; +} +RTM_EXPORT(pthread_mutexattr_destroy); + +int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) +{ + if (attr && type) + { + int atype = (*attr & MUTEXATTR_TYPE_MASK); + + if (atype >= PTHREAD_MUTEX_NORMAL && atype <= PTHREAD_MUTEX_ERRORCHECK) + { + *type = atype; + + return 0; + } + } + + return EINVAL; +} +RTM_EXPORT(pthread_mutexattr_gettype); + +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) +{ + if (attr && type >= PTHREAD_MUTEX_NORMAL && type <= PTHREAD_MUTEX_ERRORCHECK) + { + *attr = (*attr & ~MUTEXATTR_TYPE_MASK) | type; + + return 0; + } + + return EINVAL; +} +RTM_EXPORT(pthread_mutexattr_settype); + +int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) +{ + if (!attr) + return EINVAL; + + switch (pshared) + { + case PTHREAD_PROCESS_PRIVATE: + *attr &= ~MUTEXATTR_SHARED_MASK; + return 0; + + case PTHREAD_PROCESS_SHARED: + *attr |= MUTEXATTR_SHARED_MASK; + return 0; + } + + return EINVAL; +} +RTM_EXPORT(pthread_mutexattr_setpshared); + +int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared) +{ + if (!attr || !pshared) + return EINVAL; + + *pshared = (*attr & MUTEXATTR_SHARED_MASK) ? PTHREAD_PROCESS_SHARED + : PTHREAD_PROCESS_PRIVATE; + return 0; +} +RTM_EXPORT(pthread_mutexattr_getpshared); + +int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) +{ + rt_err_t result; + char name[RT_NAME_MAX]; + static rt_uint16_t pthread_mutex_number = 0; + + if (!mutex) + return EINVAL; + + /* build mutex name */ + rt_snprintf(name, sizeof(name), "pmtx%02d", pthread_mutex_number ++); + if (attr == RT_NULL) + mutex->attr = pthread_default_mutexattr; + else + mutex->attr = *attr; + + /* init mutex lock */ + result = rt_mutex_init(&(mutex->lock), name, RT_IPC_FLAG_FIFO); + if (result != RT_EOK) + return EINVAL; + + /* detach the object from system object container */ + rt_object_detach(&(mutex->lock.parent.parent)); + mutex->lock.parent.parent.type = RT_Object_Class_Mutex; + + return 0; +} +RTM_EXPORT(pthread_mutex_init); + +int pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + if (!mutex || mutex->attr == -1) + return EINVAL; + + /* it's busy */ + if (mutex->lock.owner != RT_NULL) + return EBUSY; + + rt_memset(mutex, 0, sizeof(pthread_mutex_t)); + mutex->attr = -1; + + return 0; +} +RTM_EXPORT(pthread_mutex_destroy); + +int pthread_mutex_lock(pthread_mutex_t *mutex) +{ + int mtype; + rt_err_t result; + + if (!mutex) + return EINVAL; + + if (mutex->attr == -1) + { + /* init mutex */ + pthread_mutex_init(mutex, RT_NULL); + } + + mtype = mutex->attr & MUTEXATTR_TYPE_MASK; + rt_enter_critical(); + if (mutex->lock.owner == rt_thread_self() && + mtype != PTHREAD_MUTEX_RECURSIVE) + { + rt_exit_critical(); + + return EDEADLK; + } + rt_exit_critical(); + + result = rt_mutex_take(&(mutex->lock), RT_WAITING_FOREVER); + if (result == RT_EOK) + return 0; + + return EINVAL; +} +RTM_EXPORT(pthread_mutex_lock); + +int pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + rt_err_t result; + + if (!mutex) + return EINVAL; + if (mutex->attr == -1) + { + /* init mutex */ + pthread_mutex_init(mutex, RT_NULL); + } + + if (mutex->lock.owner != rt_thread_self()) + { + int mtype; + mtype = mutex->attr & MUTEXATTR_TYPE_MASK; + + /* error check, return EPERM */ + if (mtype == PTHREAD_MUTEX_ERRORCHECK) + return EPERM; + + /* no thread waiting on this mutex */ + if (mutex->lock.owner == RT_NULL) + return 0; + } + + result = rt_mutex_release(&(mutex->lock)); + if (result == RT_EOK) + return 0; + + return EINVAL; +} +RTM_EXPORT(pthread_mutex_unlock); + +int pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + rt_err_t result; + int mtype; + + if (!mutex) + return EINVAL; + if (mutex->attr == -1) + { + /* init mutex */ + pthread_mutex_init(mutex, RT_NULL); + } + + mtype = mutex->attr & MUTEXATTR_TYPE_MASK; + rt_enter_critical(); + if (mutex->lock.owner == rt_thread_self() && + mtype != PTHREAD_MUTEX_RECURSIVE) + { + rt_exit_critical(); + + return EDEADLK; + } + rt_exit_critical(); + + result = rt_mutex_take(&(mutex->lock), 0); + if (result == RT_EOK) return 0; + + return EBUSY; +} +RTM_EXPORT(pthread_mutex_trylock); diff --git a/components/libc/posix/pthreads/pthread_rwlock.c b/components/libc/posix/pthreads/pthread_rwlock.c new file mode 100644 index 0000000000..1c2fe19f5f --- /dev/null +++ b/components/libc/posix/pthreads/pthread_rwlock.c @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-10-26 Bernard the first version + */ + +#include <pthread.h> + +int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) +{ + if (!attr) + return EINVAL; + *attr = PTHREAD_PROCESS_PRIVATE; + + return 0; +} +RTM_EXPORT(pthread_rwlockattr_init); + +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) +{ + if (!attr) + return EINVAL; + + return 0; +} +RTM_EXPORT(pthread_rwlockattr_destroy); + +int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, + int *pshared) +{ + if (!attr || !pshared) + return EINVAL; + + *pshared = PTHREAD_PROCESS_PRIVATE; + + return 0; +} +RTM_EXPORT(pthread_rwlockattr_getpshared); + +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) +{ + if (!attr || pshared != PTHREAD_PROCESS_PRIVATE) + return EINVAL; + + return 0; +} +RTM_EXPORT(pthread_rwlockattr_setpshared); + +int pthread_rwlock_init(pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr) +{ + if (!rwlock) + return EINVAL; + + rwlock->attr = PTHREAD_PROCESS_PRIVATE; + pthread_mutex_init(&(rwlock->rw_mutex), NULL); + pthread_cond_init(&(rwlock->rw_condreaders), NULL); + pthread_cond_init(&(rwlock->rw_condwriters), NULL); + + rwlock->rw_nwaitwriters = 0; + rwlock->rw_nwaitreaders = 0; + rwlock->rw_refcount = 0; + + return 0; +} +RTM_EXPORT(pthread_rwlock_init); + +int pthread_rwlock_destroy (pthread_rwlock_t *rwlock) +{ + int result; + + if (!rwlock) + return EINVAL; + if (rwlock->attr == -1) + return 0; /* rwlock is not initialized */ + + if ( (result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0) + return(result); + + if (rwlock->rw_refcount != 0 || + rwlock->rw_nwaitreaders != 0 || + rwlock->rw_nwaitwriters != 0) + { + result = EBUSY; + + return result; + } + else + { + /* check whether busy */ + result = rt_sem_trytake(&(rwlock->rw_condreaders.sem)); + if (result == RT_EOK) + { + result = rt_sem_trytake(&(rwlock->rw_condwriters.sem)); + if (result == RT_EOK) + { + rt_sem_release(&(rwlock->rw_condreaders.sem)); + rt_sem_release(&(rwlock->rw_condwriters.sem)); + + pthread_cond_destroy(&rwlock->rw_condreaders); + pthread_cond_destroy(&rwlock->rw_condwriters); + } + else + { + rt_sem_release(&(rwlock->rw_condreaders.sem)); + result = EBUSY; + } + } + else + result = EBUSY; + } + + pthread_mutex_unlock(&rwlock->rw_mutex); + if (result == 0) + pthread_mutex_destroy(&rwlock->rw_mutex); + + return result; +} +RTM_EXPORT(pthread_rwlock_destroy); + +int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) +{ + int result; + + if (!rwlock) + return EINVAL; + if (rwlock->attr == -1) + pthread_rwlock_init(rwlock, NULL); + + if ((result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0) + return(result); + + /* give preference to waiting writers */ + while (rwlock->rw_refcount < 0 || rwlock->rw_nwaitwriters > 0) + { + rwlock->rw_nwaitreaders++; + /* rw_mutex will be released when waiting for rw_condreaders */ + result = pthread_cond_wait(&rwlock->rw_condreaders, &rwlock->rw_mutex); + /* rw_mutex should have been taken again when returned from waiting */ + rwlock->rw_nwaitreaders--; + if (result != 0) /* wait error */ + break; + } + + /* another reader has a read lock */ + if (result == 0) + rwlock->rw_refcount++; + + pthread_mutex_unlock(&rwlock->rw_mutex); + + return (result); +} +RTM_EXPORT(pthread_rwlock_rdlock); + +int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) +{ + int result; + + if (!rwlock) + return EINVAL; + if (rwlock->attr == -1) + pthread_rwlock_init(rwlock, NULL); + + if ((result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0) + return(result); + + if (rwlock->rw_refcount < 0 || rwlock->rw_nwaitwriters > 0) + result = EBUSY; /* held by a writer or waiting writers */ + else + rwlock->rw_refcount++; /* increment count of reader locks */ + + pthread_mutex_unlock(&rwlock->rw_mutex); + + return(result); +} +RTM_EXPORT(pthread_rwlock_tryrdlock); + +int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, + const struct timespec *abstime) +{ + int result; + + if (!rwlock) + return EINVAL; + if (rwlock->attr == -1) + pthread_rwlock_init(rwlock, NULL); + + if ( (result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0) + return(result); + + /* give preference to waiting writers */ + while (rwlock->rw_refcount < 0 || rwlock->rw_nwaitwriters > 0) + { + rwlock->rw_nwaitreaders++; + /* rw_mutex will be released when waiting for rw_condreaders */ + result = pthread_cond_timedwait(&rwlock->rw_condreaders, &rwlock->rw_mutex, abstime); + /* rw_mutex should have been taken again when returned from waiting */ + rwlock->rw_nwaitreaders--; + if (result != 0) + break; + } + + /* another reader has a read lock */ + if (result == 0) + rwlock->rw_refcount++; + + pthread_mutex_unlock(&rwlock->rw_mutex); + + return (result); +} +RTM_EXPORT(pthread_rwlock_timedrdlock); + +int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, + const struct timespec *abstime) +{ + int result; + + if (!rwlock) + return EINVAL; + if (rwlock->attr == -1) + pthread_rwlock_init(rwlock, NULL); + + if ((result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0) + return(result); + + while (rwlock->rw_refcount != 0) + { + rwlock->rw_nwaitwriters++; + /* rw_mutex will be released when waiting for rw_condwriters */ + result = pthread_cond_timedwait(&rwlock->rw_condwriters, &rwlock->rw_mutex, abstime); + /* rw_mutex should have been taken again when returned from waiting */ + rwlock->rw_nwaitwriters--; + + if (result != 0) + break; + } + + if (result == 0) + rwlock->rw_refcount = -1; + + pthread_mutex_unlock(&rwlock->rw_mutex); + + return(result); +} +RTM_EXPORT(pthread_rwlock_timedwrlock); + +int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) +{ + int result; + + if (!rwlock) + return EINVAL; + if (rwlock->attr == -1) + pthread_rwlock_init(rwlock, NULL); + + if ((result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0) + return(result); + + if (rwlock->rw_refcount != 0) + result = EBUSY; /* held by either writer or reader(s) */ + else + rwlock->rw_refcount = -1; /* available, indicate a writer has it */ + + pthread_mutex_unlock(&rwlock->rw_mutex); + + return(result); +} +RTM_EXPORT(pthread_rwlock_trywrlock); + +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) +{ + int result; + + if (!rwlock) + return EINVAL; + if (rwlock->attr == -1) + pthread_rwlock_init(rwlock, NULL); + + if ( (result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0) + return(result); + + if (rwlock->rw_refcount > 0) + rwlock->rw_refcount--; /* releasing a reader */ + else if (rwlock->rw_refcount == -1) + rwlock->rw_refcount = 0; /* releasing a writer */ + + /* give preference to waiting writers over waiting readers */ + if (rwlock->rw_nwaitwriters > 0) + { + if (rwlock->rw_refcount == 0) + result = pthread_cond_signal(&rwlock->rw_condwriters); + } + else if (rwlock->rw_nwaitreaders > 0) + { + result = pthread_cond_broadcast(&rwlock->rw_condreaders); + } + + pthread_mutex_unlock(&rwlock->rw_mutex); + + return(result); +} +RTM_EXPORT(pthread_rwlock_unlock); + +int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) +{ + int result; + + if (!rwlock) + return EINVAL; + if (rwlock->attr == -1) + pthread_rwlock_init(rwlock, NULL); + + if ((result = pthread_mutex_lock(&rwlock->rw_mutex)) != 0) + return(result); + + while (rwlock->rw_refcount != 0) + { + rwlock->rw_nwaitwriters++; + /* rw_mutex will be released when waiting for rw_condwriters */ + result = pthread_cond_wait(&rwlock->rw_condwriters, &rwlock->rw_mutex); + /* rw_mutex should have been taken again when returned from waiting */ + rwlock->rw_nwaitwriters--; + + if (result != 0) + break; + } + + if (result == 0) + rwlock->rw_refcount = -1; + + pthread_mutex_unlock(&rwlock->rw_mutex); + + return(result); +} +RTM_EXPORT(pthread_rwlock_wrlock); + diff --git a/components/libc/posix/pthreads/pthread_spin.c b/components/libc/posix/pthreads/pthread_spin.c new file mode 100644 index 0000000000..3f3dc1593c --- /dev/null +++ b/components/libc/posix/pthreads/pthread_spin.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-10-26 Bernard the first version + */ + +#include <pthread.h> + +int pthread_spin_init (pthread_spinlock_t *lock, int pshared) +{ + if (!lock) + return EINVAL; + + lock->lock = 0; + + return 0; +} + +int pthread_spin_destroy (pthread_spinlock_t *lock) +{ + if (!lock) + return EINVAL; + + return 0; +} + +int pthread_spin_lock (pthread_spinlock_t *lock) +{ + if (!lock) + return EINVAL; + + while (!(lock->lock)) + { + lock->lock = 1; + } + + return 0; +} + +int pthread_spin_trylock (pthread_spinlock_t *lock) +{ + if (!lock) + return EINVAL; + + if (!(lock->lock)) + { + lock->lock = 1; + + return 0; + } + + return EBUSY; +} + +int pthread_spin_unlock (pthread_spinlock_t *lock) +{ + if (!lock) + return EINVAL; + if (!(lock->lock)) + return EPERM; + + lock->lock = 0; + + return 0; +} diff --git a/components/libc/posix/pthreads/pthread_tls.c b/components/libc/posix/pthreads/pthread_tls.c new file mode 100644 index 0000000000..d48df7f2dd --- /dev/null +++ b/components/libc/posix/pthreads/pthread_tls.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-10-26 Bernard the first version + */ + +#include <pthread.h> +#include "pthread_internal.h" + +_pthread_key_data_t _thread_keys[PTHREAD_KEY_MAX]; + +void pthread_key_system_init() +{ + rt_memset(&_thread_keys[0], 0, sizeof(_thread_keys)); +} + +void *pthread_getspecific(pthread_key_t key) +{ + struct _pthread_data* ptd; + + if (rt_thread_self() == NULL) return NULL; + + /* get pthread data from user data of thread */ + ptd = (_pthread_data_t *)rt_thread_self()->user_data; + RT_ASSERT(ptd != NULL); + + if (ptd->tls == NULL) + return NULL; + + if ((key < PTHREAD_KEY_MAX) && (_thread_keys[key].is_used)) + return ptd->tls[key]; + + return NULL; +} +RTM_EXPORT(pthread_getspecific); + +int pthread_setspecific(pthread_key_t key, const void *value) +{ + struct _pthread_data* ptd; + + if (rt_thread_self() == NULL) return EINVAL; + + /* get pthread data from user data of thread */ + ptd = (_pthread_data_t *)rt_thread_self()->user_data; + RT_ASSERT(ptd != NULL); + + /* check tls area */ + if (ptd->tls == NULL) + { + ptd->tls = (void**)rt_malloc(sizeof(void*) * PTHREAD_KEY_MAX); + } + + if ((key < PTHREAD_KEY_MAX) && _thread_keys[key].is_used) + { + ptd->tls[key] = (void *)value; + + return 0; + } + + return EINVAL; +} +RTM_EXPORT(pthread_setspecific); + +int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)) +{ + rt_uint32_t index; + + rt_enter_critical(); + for (index = 0; index < PTHREAD_KEY_MAX; index ++) + { + if (_thread_keys[index].is_used == 0) + { + _thread_keys[index].is_used = 1; + _thread_keys[index].destructor = destructor; + + *key = index; + + rt_exit_critical(); + + return 0; + } + } + + rt_exit_critical(); + + return EAGAIN; +} +RTM_EXPORT(pthread_key_create); + +int pthread_key_delete(pthread_key_t key) +{ + if (key >= PTHREAD_KEY_MAX) + return EINVAL; + + rt_enter_critical(); + _thread_keys[key].is_used = 0; + _thread_keys[key].destructor = 0; + rt_exit_critical(); + + return 0; +} +RTM_EXPORT(pthread_key_delete); + diff --git a/components/libc/posix/pthreads/sched.c b/components/libc/posix/pthreads/sched.c new file mode 100644 index 0000000000..5f2a53c01f --- /dev/null +++ b/components/libc/posix/pthreads/sched.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include <sched.h> + +int sched_yield(void) +{ + rt_thread_yield(); + + return 0; +} +RTM_EXPORT(sched_yield); + +int sched_get_priority_min(int policy) +{ + if (policy != SCHED_FIFO && policy != SCHED_RR) + return EINVAL; + + return 0; +} +RTM_EXPORT(sched_get_priority_min); + +int sched_get_priority_max(int policy) +{ + if (policy != SCHED_FIFO && policy != SCHED_RR) + return EINVAL; + + return RT_THREAD_PRIORITY_MAX - 1; +} +RTM_EXPORT(sched_get_priority_max); + +int sched_setscheduler(pid_t pid, int policy) +{ + return EOPNOTSUPP; +} +RTM_EXPORT(sched_setscheduler); diff --git a/components/libc/posix/pthreads/sched.h b/components/libc/posix/pthreads/sched.h new file mode 100644 index 0000000000..07e2c0c6a8 --- /dev/null +++ b/components/libc/posix/pthreads/sched.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __SCHED_H__ +#define __SCHED_H__ + +#include <rtthread.h> +#include <pthread.h> + +/* Thread scheduling policies */ +enum +{ + SCHED_OTHER = 0, + SCHED_FIFO, + SCHED_RR, + SCHED_MIN = SCHED_OTHER, + SCHED_MAX = SCHED_RR +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +int sched_yield(void); +int sched_get_priority_min(int policy); +int sched_get_priority_max(int policy); +int sched_setscheduler(pid_t pid, int policy); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/libc/posix/pthreads/semaphore.c b/components/libc/posix/pthreads/semaphore.c new file mode 100644 index 0000000000..5e59ae850f --- /dev/null +++ b/components/libc/posix/pthreads/semaphore.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-10-26 Bernard the first version + */ + +#include <rtthread.h> +#include <string.h> +#include "semaphore.h" +#include "pthread_internal.h" + +static sem_t *posix_sem_list = RT_NULL; +static struct rt_semaphore posix_sem_lock; +void posix_sem_system_init() +{ + rt_sem_init(&posix_sem_lock, "psem", 1, RT_IPC_FLAG_FIFO); +} + +rt_inline void posix_sem_insert(sem_t *psem) +{ + psem->next = posix_sem_list; + posix_sem_list = psem; +} + +static void posix_sem_delete(sem_t *psem) +{ + sem_t *iter; + if (posix_sem_list == psem) + { + posix_sem_list = psem->next; + + rt_sem_delete(psem->sem); + rt_free(psem); + + return; + } + for (iter = posix_sem_list; iter->next != RT_NULL; iter = iter->next) + { + if (iter->next == psem) + { + /* delete this mq */ + if (psem->next != RT_NULL) + iter->next = psem->next; + else + iter->next = RT_NULL; + + /* delete RT-Thread mqueue */ + rt_sem_delete(psem->sem); + rt_free(psem); + + return ; + } + } +} + +static sem_t *posix_sem_find(const char* name) +{ + sem_t *iter; + rt_object_t object; + + for (iter = posix_sem_list; iter != RT_NULL; iter = iter->next) + { + object = (rt_object_t)iter->sem; + + if (strncmp(object->name, name, RT_NAME_MAX) == 0) + { + return iter; + } + } + + return RT_NULL; +} + +int sem_close(sem_t *sem) +{ + if (sem == RT_NULL) + { + rt_set_errno(EINVAL); + + return -1; + } + + /* lock posix semaphore list */ + rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER); + sem->refcount --; + if (sem->refcount == 0) + { + /* delete from posix semaphore list */ + if (sem->unlinked) + posix_sem_delete(sem); + sem = RT_NULL; + } + rt_sem_release(&posix_sem_lock); + + return 0; +} +RTM_EXPORT(sem_close); + +int sem_destroy(sem_t *sem) +{ + rt_err_t result; + + if ((!sem) || !(sem->unamed)) + { + rt_set_errno(EINVAL); + + return -1; + } + + /* lock posix semaphore list */ + rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER); + result = rt_sem_trytake(sem->sem); + if (result != RT_EOK) + { + rt_sem_release(&posix_sem_lock); + rt_set_errno(EBUSY); + + return -1; + } + + /* destroy an unamed posix semaphore */ + posix_sem_delete(sem); + rt_sem_release(&posix_sem_lock); + + return 0; +} +RTM_EXPORT(sem_destroy); + +int sem_unlink(const char *name) +{ + sem_t *psem; + + /* lock posix semaphore list */ + rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER); + psem = posix_sem_find(name); + if (psem != RT_NULL) + { + psem->unlinked = 1; + if (psem->refcount == 0) + { + /* remove this semaphore */ + posix_sem_delete(psem); + } + rt_sem_release(&posix_sem_lock); + + return 0; + } + rt_sem_release(&posix_sem_lock); + + /* no this entry */ + rt_set_errno(ENOENT); + + return -1; +} +RTM_EXPORT(sem_unlink); + +int sem_getvalue(sem_t *sem, int *sval) +{ + if (!sem || !sval) + { + rt_set_errno(EINVAL); + + return -1; + } + *sval = sem->sem->value; + + return 0; +} +RTM_EXPORT(sem_getvalue); + +int sem_init(sem_t *sem, int pshared, unsigned int value) +{ + char name[RT_NAME_MAX]; + static rt_uint16_t psem_number = 0; + + if (sem == RT_NULL) + { + rt_set_errno(EINVAL); + + return -1; + } + + rt_snprintf(name, sizeof(name), "psem%02d", psem_number++); + sem->sem = rt_sem_create(name, value, RT_IPC_FLAG_FIFO); + if (sem->sem == RT_NULL) + { + rt_set_errno(ENOMEM); + + return -1; + } + + /* initialize posix semaphore */ + sem->refcount = 1; + sem->unlinked = 0; + sem->unamed = 1; + /* lock posix semaphore list */ + rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER); + posix_sem_insert(sem); + rt_sem_release(&posix_sem_lock); + + return 0; +} +RTM_EXPORT(sem_init); + +sem_t *sem_open(const char *name, int oflag, ...) +{ + sem_t* sem; + va_list arg; + mode_t mode; + unsigned int value; + + sem = RT_NULL; + + /* lock posix semaphore list */ + rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER); + if (oflag & O_CREAT) + { + va_start(arg, oflag); + mode = (mode_t) va_arg( arg, unsigned int); mode = mode; + value = va_arg( arg, unsigned int); + va_end(arg); + + if (oflag & O_EXCL) + { + if (posix_sem_find(name) != RT_NULL) + { + rt_set_errno(EEXIST); + goto __return; + } + } + sem = (sem_t*) rt_malloc (sizeof(struct posix_sem)); + if (sem == RT_NULL) + { + rt_set_errno(ENFILE); + goto __return; + } + + /* create RT-Thread semaphore */ + sem->sem = rt_sem_create(name, value, RT_IPC_FLAG_FIFO); + if (sem->sem == RT_NULL) /* create failed */ + { + rt_set_errno(ENFILE); + goto __return; + } + /* initialize reference count */ + sem->refcount = 1; + sem->unlinked = 0; + sem->unamed = 0; + + /* insert semaphore to posix semaphore list */ + posix_sem_insert(sem); + } + else + { + /* find semaphore */ + sem = posix_sem_find(name); + if (sem != RT_NULL) + { + sem->refcount ++; /* increase reference count */ + } + else + { + rt_set_errno(ENOENT); + goto __return; + } + } + rt_sem_release(&posix_sem_lock); + + return sem; + +__return: + /* release lock */ + rt_sem_release(&posix_sem_lock); + + /* release allocated memory */ + if (sem != RT_NULL) + { + /* delete RT-Thread semaphore */ + if (sem->sem != RT_NULL) + rt_sem_delete(sem->sem); + rt_free(sem); + } + + return RT_NULL; +} +RTM_EXPORT(sem_open); + +int sem_post(sem_t *sem) +{ + rt_err_t result; + + if (!sem) + { + rt_set_errno(EINVAL); + + return -1; + } + + result = rt_sem_release(sem->sem); + if (result == RT_EOK) + return 0; + + rt_set_errno(EINVAL); + + return -1; +} +RTM_EXPORT(sem_post); + +int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout) +{ + rt_err_t result; + rt_int32_t tick; + + if (!sem || !abs_timeout) + return EINVAL; + + /* calculate os tick */ + tick = rt_timespec_to_tick(abs_timeout); + + result = rt_sem_take(sem->sem, tick); + if (result == -RT_ETIMEOUT) + { + rt_set_errno(ETIMEDOUT); + + return -1; + } + if (result == RT_EOK) + return 0; + + rt_set_errno(EINTR); + + return -1; +} +RTM_EXPORT(sem_timedwait); + +int sem_trywait(sem_t *sem) +{ + rt_err_t result; + + if (!sem) + { + rt_set_errno(EINVAL); + + return -1; + } + + result = rt_sem_take(sem->sem, 0); + if (result == -RT_ETIMEOUT) + { + rt_set_errno(EAGAIN); + + return -1; + } + if (result == RT_EOK) + return 0; + + rt_set_errno(EINTR); + + return -1; +} +RTM_EXPORT(sem_trywait); + +int sem_wait(sem_t *sem) +{ + rt_err_t result; + + if (!sem) + { + rt_set_errno(EINVAL); + + return -1; + } + + result = rt_sem_take(sem->sem, RT_WAITING_FOREVER); + if (result == RT_EOK) + return 0; + + rt_set_errno(EINTR); + + return -1; +} +RTM_EXPORT(sem_wait); + diff --git a/components/libc/posix/pthreads/semaphore.h b/components/libc/posix/pthreads/semaphore.h new file mode 100644 index 0000000000..872226e385 --- /dev/null +++ b/components/libc/posix/pthreads/semaphore.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-10-26 Bernard the first version + */ + +#ifndef __POSIX_SEMAPHORE_H__ +#define __POSIX_SEMAPHORE_H__ + +#include <rtthread.h> +#include <pthread.h> + +struct posix_sem +{ + /* reference count and unlinked */ + rt_uint16_t refcount; + rt_uint8_t unlinked; + rt_uint8_t unamed; + + /* RT-Thread semaphore */ + rt_sem_t sem; + + /* next posix semaphore */ + struct posix_sem* next; +}; +typedef struct posix_sem sem_t; + +int sem_close(sem_t *sem); +int sem_destroy(sem_t *sem); +int sem_getvalue(sem_t *sem, int *sval); +int sem_init(sem_t *sem, int pshared, unsigned int value); +sem_t *sem_open(const char *name, int oflag, ...); +int sem_post(sem_t *sem); +int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); +int sem_trywait(sem_t *sem); +int sem_unlink(const char *name); +int sem_wait(sem_t *sem); + +#endif diff --git a/components/libc/posix/signal/SConscript b/components/libc/posix/signal/SConscript new file mode 100644 index 0000000000..f07ff58f6e --- /dev/null +++ b/components/libc/posix/signal/SConscript @@ -0,0 +1,13 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('libc', src, + depend = ['RT_USING_SIGNALS', 'RT_USING_PTHREADS', 'RT_USING_LIBC'], + CPPPATH = CPPPATH) + +Return('group') diff --git a/components/libc/posix/signal/posix_signal.c b/components/libc/posix/signal/posix_signal.c new file mode 100644 index 0000000000..ac8d2b1eef --- /dev/null +++ b/components/libc/posix/signal/posix_signal.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017/10/1 Bernard The first version + */ + +#include <rthw.h> +#include <rtthread.h> + +#include <sys/time.h> +#include <sys/errno.h> + +#include "posix_signal.h" + +#define sig_valid(sig_no) (sig_no >= 0 && sig_no < RT_SIG_MAX) + +void (*signal(int sig, void (*func)(int))) (int) +{ + return rt_signal_install(sig, func); +} + +int sigprocmask (int how, const sigset_t *set, sigset_t *oset) +{ + rt_base_t level; + rt_thread_t tid; + + tid = rt_thread_self(); + + level = rt_hw_interrupt_disable(); + if (oset) *oset = tid->sig_mask; + + if (set) + { + switch(how) + { + case SIG_BLOCK: + tid->sig_mask |= *set; + break; + case SIG_UNBLOCK: + tid->sig_mask &= ~*set; + break; + case SIG_SETMASK: + tid->sig_mask = *set; + break; + default: + break; + } + } + rt_hw_interrupt_enable(level); + + return 0; +} + +int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) +{ + rt_sighandler_t old = RT_NULL; + + if (!sig_valid(signum)) return -RT_ERROR; + + if (act) + old = rt_signal_install(signum, act->sa_handler); + else + { + old = rt_signal_install(signum, RT_NULL); + rt_signal_install(signum, old); + } + + if (oldact) + oldact->sa_handler = old; + + return 0; +} + +int sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec *timeout) +{ + int ret = 0; + int tick = RT_WAITING_FOREVER; + + if (timeout) + { + tick = rt_timespec_to_tick(timeout); + } + + ret = rt_signal_wait(set, info, tick); + if (ret == 0) return 0; + + errno = ret; + return -1; +} + +int sigwait(const sigset_t *set, int *sig) +{ + siginfo_t si; + if (sigtimedwait(set, &si, 0) < 0) + return -1; + + *sig = si.si_signo; + return 0; +} + +int sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + return sigtimedwait(set, info, NULL); +} + +int raise(int sig) +{ + rt_thread_kill(rt_thread_self(), sig); + return 0; +} + diff --git a/components/libc/posix/signal/posix_signal.h b/components/libc/posix/signal/posix_signal.h new file mode 100644 index 0000000000..f04b54c5f8 --- /dev/null +++ b/components/libc/posix/signal/posix_signal.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017/10/1 Bernard The first version + */ + +#ifndef POSIX_SIGNAL_H__ +#define POSIX_SIGNAL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/signal.h> + +enum rt_signal_value{ + SIG1 = SIGHUP, + SIG2 = SIGINT, + SIG3 = SIGQUIT, + SIG4 = SIGILL, + SIG5 = SIGTRAP, + SIG6 = SIGABRT, + SIG7 = SIGEMT, + SIG8 = SIGFPE, + SIG9 = SIGKILL, + SIG10 = SIGBUS, + SIG11 = SIGSEGV, + SIG12 = SIGSYS, + SIG13 = SIGPIPE, + SIG14 = SIGALRM, + SIG15 = SIGTERM, + SIG16 = SIGURG, + SIG17 = SIGSTOP, + SIG18 = SIGTSTP, + SIG19 = SIGCONT, + SIG20 = SIGCHLD, + SIG21 = SIGTTIN, + SIG22 = SIGTTOU, + SIG23 = SIGPOLL, + SIG24 = 24, // SIGXCPU, + SIG25 = 25, // SIGXFSZ, + SIG26 = 26, // SIGVTALRM, + SIG27 = 27, // SIGPROF, + SIG28 = SIGWINCH, + SIG29 = 29, // SIGLOST, + SIG30 = SIGUSR1, + SIG31 = SIGUSR2, + SIGRT_MIN = 27, // SIGRTMIN, + SIGRT_MAX = 31, // SIGRTMAX, + SIGMAX = NSIG, +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/libc/posix/termios/SConscript b/components/libc/posix/termios/SConscript new file mode 100644 index 0000000000..381eb5ad8b --- /dev/null +++ b/components/libc/posix/termios/SConscript @@ -0,0 +1,13 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('libc', src, + depend = ['RT_USING_LIBC', 'RT_USING_POSIX', 'RT_USING_POSIX_TERMIOS'], + CPPPATH = CPPPATH) + +Return('group') diff --git a/components/libc/posix/termios/posix_termios.c b/components/libc/posix/termios/posix_termios.c new file mode 100644 index 0000000000..9f33c857e6 --- /dev/null +++ b/components/libc/posix/termios/posix_termios.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017/08/30 Bernard The first version + */ +#include <stdlib.h> +#include <string.h> +#include <dfs_posix.h> +#include <sys/errno.h> +#include <termios.h> + +int tcgetattr(int fd, struct termios *tio) +{ + /* Get the current serial port settings. */ + if (ioctl(fd, TCGETA, tio)) + return -1; + + return 0; +} + +int tcsetattr(int fd, int act, const struct termios *tio) +{ + switch (act) + { + case TCSANOW: + /* make the change immediately */ + return (ioctl(fd, TCSETA, (void*)tio)); + case TCSADRAIN: + /* + * Don't make the change until all currently written data + * has been transmitted. + */ + return (ioctl(fd, TCSETAW, (void*)tio)); + case TCSAFLUSH: + /* Don't make the change until all currently written data + * has been transmitted, at which point any received but + * unread data is also discarded. + */ + return (ioctl(fd, TCSETAF, (void*)tio)); + default: + errno = EINVAL; + return (-1); + } +} + +/** + * this function gets process group ID for session leader for controlling + * terminal + * + * @return always 0 + */ +pid_t tcgetsid(int fd) +{ + return 0; +} + +speed_t cfgetospeed(const struct termios *tio) +{ + return tio->c_cflag & CBAUD; +} + +speed_t cfgetispeed(const struct termios *tio) +{ + return cfgetospeed(tio); +} + +int cfsetospeed(struct termios *tio, speed_t speed) +{ + if (speed & ~CBAUD) + { + errno = EINVAL; + return -1; + } + + tio->c_cflag &= ~CBAUD; + tio->c_cflag |= speed; + return 0; +} + +int cfsetispeed(struct termios *tio, speed_t speed) +{ + return speed ? cfsetospeed(tio, speed) : 0; +} + +int tcsendbreak(int fd, int dur) +{ + /* nonzero duration is implementation-defined, so ignore it */ + return 0; +} + +int tcflush(int fd, int queue) +{ + return ioctl(fd, TCFLSH, (void*)queue); +} + +int tcflow(int fd, int action) +{ + return ioctl(fd, TCXONC, (void*)action); +} + +/** + * this function waits for transmission of output + */ +int tcdrain(int fd) +{ + return 0; +} + +#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) +void cfmakeraw(struct termios *t) +{ + t->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + t->c_oflag &= ~OPOST; + t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + t->c_cflag &= ~(CSIZE|PARENB); + t->c_cflag |= CS8; + t->c_cc[VMIN] = 1; + t->c_cc[VTIME] = 0; +} + +int cfsetspeed(struct termios *tio, speed_t speed) +{ + return cfsetospeed(tio, speed); +} +#endif diff --git a/components/libc/posix/termios/posix_termios.h b/components/libc/posix/termios/posix_termios.h new file mode 100644 index 0000000000..6dc5531955 --- /dev/null +++ b/components/libc/posix/termios/posix_termios.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017/08/30 Bernard The first version + */ +#ifndef TERMIOS_H__ +#define TERMIOS_H__ + +#include <sys/types.h> +#include <sys/ioctl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned int tcflag_t; + +#define NCCS 32 + +struct termios { + tcflag_t c_iflag; + tcflag_t c_oflag; + tcflag_t c_cflag; + tcflag_t c_lflag; + cc_t c_line; + cc_t c_cc[NCCS]; + speed_t __c_ispeed; + speed_t __c_ospeed; +}; + +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IUCLC 0001000 +#define IXON 0002000 +#define IXANY 0004000 +#define IXOFF 0010000 +#define IMAXBEL 0020000 +#define IUTF8 0040000 + +#define OPOST 0000001 +#define OLCUC 0000002 +#define ONLCR 0000004 +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 +#define FFDLY 0100000 +#define FF0 0000000 +#define FF1 0100000 + +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 + +#define B0 0000000 +#define B50 0000001 +#define B75 0000002 +#define B110 0000003 +#define B134 0000004 +#define B150 0000005 +#define B200 0000006 +#define B300 0000007 +#define B600 0000010 +#define B1200 0000011 +#define B1800 0000012 +#define B2400 0000013 +#define B4800 0000014 +#define B9600 0000015 +#define B19200 0000016 +#define B38400 0000017 + +#define B57600 0010001 +#define B115200 0010002 +#define B230400 0010003 +#define B460800 0010004 +#define B500000 0010005 +#define B576000 0010006 +#define B921600 0010007 +#define B1000000 0010010 +#define B1152000 0010011 +#define B1500000 0010012 +#define B2000000 0010013 +#define B2500000 0010014 +#define B3000000 0010015 +#define B3500000 0010016 +#define B4000000 0010017 + +#define CSIZE 0000060 +#define CS5 0000000 +#define CS6 0000020 +#define CS7 0000040 +#define CS8 0000060 +#define CSTOPB 0000100 +#define CREAD 0000200 +#define PARENB 0000400 +#define PARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 + +#define ISIG 0000001 +#define ICANON 0000002 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define IEXTEN 0100000 + +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 + +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +#define EXTA 0000016 +#define EXTB 0000017 +#define CBAUD 0010017 +#define CBAUDEX 0010000 +#define CIBAUD 002003600000 +#define CMSPAR 010000000000 +#define CRTSCTS 020000000000 + +#define XCASE 0000004 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define EXTPROC 0200000 + +#define XTABS 0014000 + +speed_t cfgetospeed (const struct termios *); +speed_t cfgetispeed (const struct termios *); +int cfsetospeed (struct termios *, speed_t); +int cfsetispeed (struct termios *, speed_t); + +int tcgetattr (int, struct termios *); +int tcsetattr (int, int, const struct termios *); + +int tcsendbreak (int, int); +int tcdrain (int); +int tcflush (int, int); +int tcflow (int, int); + +pid_t tcgetsid (int); + +#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) +void cfmakeraw(struct termios *); +int cfsetspeed(struct termios *, speed_t); +#endif + +#ifdef __cplusplus +} +#endif + +#endif |
