📄 fifos.c
字号:
return -ENODEV; } TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_CREATE, minor, size); if (!atomic_cmpxchg(&fifo[minor].opncnt, 0, 1)) { if (size <= PAGE_SIZE*32) { if (!(buf = kmalloc(size, GFP_KERNEL))) { fifo[minor].opncnt = 0; return -ENOMEM; } fifo[minor].malloc_type = 'k'; } else { if (!(buf = vmalloc(size))) { fifo[minor].opncnt = 0; return -ENOMEM; } fifo[minor].malloc_type = 'v'; } fifo[minor].handler = do_nothing; mbx_init(&(fifo[minor].mbx), size, buf); mbx_sem_init(&(fifo[minor].sem), 0); fifo[minor].pol_asyn_pended = 0; fifo[minor].asynq = 0; } else { if (size > fifo[minor].mbx.size) { rtf_resize(minor, size); } atomic_inc((atomic_t *)&fifo[minor].opncnt); } return 0;}/** * @ingroup fifos_ipc * Close a real-time FIFO * * rtf_destroy closes, in kernel space, a real-time fifo previously * created or reopened with rtf_create() or rtf_open_sized(). An internal * mechanism counts how many times a fifo was opened. Opens and closes must be * in pair. rtf_destroy should be called as many times as rtf_create was. * After the last close the fifo is really destroyed. * * No need for any particular function for the same service in user space, * simply use the standard Unix close. * * @return a non-negative value on success. Actually it is the open counter, that * means how many times rtf_destroy should be called yet to destroy the fifo. * * @return a a negative value is returned as described below. * @retval ENODEV if @a fifo is greater than or equal to RTF_NO. * @retval EINVAL if @a fifo refers to a not opened fifo. * * @note The equivalent of rtf_destroy in user space is the standard UNIX * close. */int rtf_destroy(unsigned int minor){ VALID_FIFO; TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_DESTROY, minor, 0); if (atomic_dec_and_test((atomic_t *)&fifo[minor].opncnt)) { if (fifo[minor].malloc_type == 'k') { kfree(fifo[minor].mbx.bufadr); } else { vfree(fifo[minor].mbx.bufadr); } fifo[minor].handler = do_nothing; mbx_delete(&(fifo[minor].mbx)); fifo[minor].pol_asyn_pended = 0; fifo[minor].asynq = 0; fifo[minor].name[0] = 0; } return fifo[minor].opncnt;}/** * @ingroup fifos_ipc * Install a FIFO handler function. * * rtf_create_handler installs a handler which is executed when data is written * to or read from a real-time fifo. * * @param minor is an RT-FIFO that must have previously been created with a call * to rtf_create(). * * @param handler is a pointer on a function wich will be called whenever a * Linux process accesses that fifo. * * rtf_create_handler is often used in conjunction with rtf_get() to process * data acquired asynchronously from a Linux process. The installed handler * calls rtf_get() when data is present. Because the handler is only executed * when there is activity on the fifo, polling is not necessary. * * @retval 0 on success. * @retval EINVAL if @a fifo is greater than or equal to RTF_NO, or handler is * @c NULL. * * @note rtf_create_handler does not check if FIFO referred by @a fifo is open * or not. The next call of rtf_create will uninstall the handler just * "installed". */int rtf_create_handler(unsigned int minor, int (*handler) (unsigned int fifo)){ if (minor >= MAX_FIFOS || !handler) { return -EINVAL; } TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_CREATE_HANDLER, minor, handler); fifo[minor].handler = handler; return 0;}/** * @ingroup fifos_ipc * Write data to FIFO * * rtf_put tries to write a block of data to a real-time fifo previously created * with rtf_create(). * * @param minor is the ID with which the RT-FIFO was created. * @param buf points the block of data to be written. * @param count is the size of the block in bytes. * * This mechanism is available only in kernel space, i.e. either in real-time * tasks or handlers; Linux processes use a write to the corresponding * /dev/fifo\<n\> device to enqueue data to a fifo. Similarly, Linux processes * use read or similar functions to read the data previously written via rtf_put * by a real-time task. * * @return the number of bytes written on succes. Note that this value may * be less than @a count if @a count bytes of free space is not available in the * fifo. * @retval ENODEV if @a fifo is greater than or equal to RTF_NO. * @retval EINVAL if @a fifo refers to a not opened fifo. * * @note The equivalent of rtf_put in user space is the standard UNIX write, * which can be either blocking or nonblocking according to how you opened the * related device. */int rtf_put(unsigned int minor, void *buf, int count){ VALID_FIFO; TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_PUT, minor, count); count -= mbx_send_wp(&(fifo[minor].mbx), buf, count, 0); return count;}int rtf_ovrwr_put(unsigned int minor, void *buf, int count){ VALID_FIFO; return mbx_ovrwr_send(&(fifo[minor].mbx), buf, count, 0);}int rtf_put_if(unsigned int minor, void *buf, int count){ VALID_FIFO; count -= mbx_send_if(&(fifo[minor].mbx), buf, count, 0); return count;}/** * @ingroup fifos_ipc * Read data from FIFO * * rtf_get tries to read a block of data from a real-time fifo previously * created with a call to rtf_create(). * * @param minor is the ID with which the RT-FIFO was created. * @param buf points a buffer provided by the caller. * @param count is the size of @a buf in bytes. * * This mechanism is available only to real-time tasks; Linux processes use a * read from the corresponding fifo device to dequeue data from a fifo. * Similarly, Linux processes use write or similar functions to write the data * to be read via rtf_put() by a real-time task. * * rtf_get is often used in conjunction with rtf_create_handler() to process * data received asynchronously from a Linux process. A handler is installed * via rtf_create_handler(); this handler calls rtf_get to receive any data * present in the RT-FIFO as it becomes available. In this way, polling is not * necessary; the handler is called only when data is present in the fifo. * * @return the size of the received data block on success. Note that this * value may be less than count if count bytes of data is not available in the * fifo. * @retval ENODEV if @a fifo is greater than or equal to RTF_NO. * @retval EINVAL if @a fifo refers to a not opened fifo. * * @note The equivalent of rtf_get in user space is the standard UNIX read, * which can be either blocking or nonblocking according to how you opened the * related device. */int rtf_get(unsigned int minor, void *buf, int count){ VALID_FIFO; TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_GET, minor, count); count -= mbx_receive_wp(&(fifo[minor].mbx), buf, count, 0); return count;}int rtf_evdrp(unsigned int minor, void *msg, int msg_size){ VALID_FIFO; return msg_size - mbx_evdrp(&(fifo[minor].mbx), (char **)(&msg), msg_size, 0);}int rtf_get_if(unsigned int minor, void *buf, int count){ VALID_FIFO; return count - mbx_send_if(&(fifo[minor].mbx), buf, count, 0);}/** * @ingroup fifos_sem * Initialize a binary semaphore * * rtf_sem_init initializes a semaphore identified by the file descriptor or * fifo number @a fd_fifo. * * A fifo semaphore can be used for communication and synchronization between * kernel and user space. * * @param minor is a file descriptor returned by standard UNIX open in user * space while it is directly the chosen fifo number in kernel space. In fact * fifos semaphores must be associated to a fifo for identification purposes. * @param value is the initial value of the semaphore, it must be either 0 or * 1. * * rt_sem_init can be used both in kernel and user space. * * @retval 0 on success. * @retval EINVAL if @a fd_fifo refers to an invalid file descriptor or fifo. */int rtf_sem_init(unsigned int minor, int value){ VALID_FIFO; TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_SEM_INIT, minor, value); mbx_sem_init(&(fifo[minor].sem), value); return 0;}/** * @ingroup fifos_sem * Posting (signaling) a semaphore. * * rtf_sem_post signal an event to a semaphore. The semaphore value is set to * one and the first process, if any, in semaphore's waiting queue is allowed to * run. * * @param minor is a file descriptor returned by standard UNIX open in user * space while it is directly the chosen fifo number in kernel space. In fact * fifos semaphores must be associated to a fifo for identification purposes. * * Since it is not blocking rtf_sem_post can be used both in kernel and user * space. * * @retval 0 on success. * @retval EINVAL if @a fd_fifo refers to an invalid file descriptor or fifo. */int rtf_sem_post(unsigned int minor){ VALID_FIFO; TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_SEM_POST, minor, 0); mbx_sem_signal(&(fifo[minor].sem), 0); return 0;}/** * @ingroup fifos_sem * Take a semaphore, only if the calling task is not blocked. * * rtf_sem_trywait is a version of the semaphore wait operation is similar to * rtf_sem_wait() but it is never blocks the caller. If the semaphore is not * free, rtf_sem_trywait returns immediately and the semaphore value remains * unchanged. * * @param minor is a file descriptor returned by standard UNIX open in user * space while it is directly the chosen fifo number in kernel space. In fact * fifos semaphores must be associated to a fifo for identification purposes. * * Since it is not blocking rtf_sem_trywait can be used both in kernel and user * space. * * @retval 0 on success. * @retval EINVAL if @a fd_fifo refers to an invalid file descriptor or fifo. */int rtf_sem_trywait(unsigned int minor){ VALID_FIFO; TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_SEM_TRY_WAIT, minor, 0); return mbx_sem_wait_if(&(fifo[minor].sem));}/** * @ingroup fifos_sem * Delete a semaphore * * rtf_sem_destroy deletes a semaphore previously created with rtf_sem_init(). * * @param minor is a file descriptor returned by standard UNIX open in user * space while it is directly the chosen fifo number in kernel space. In fact * fifos semaphores must be associated to a fifo for identification purposes. * * Any tasks blocked on this semaphore is returned in error and allowed to run * when semaphore is destroyed. * * rtf_sem_destroy can be used both in kernel and user space. * * @retval 0 on sucess. * @retval EINVAL if @a fd_fifo refers to an invalid file descriptor or fifo. */int rtf_sem_destroy(unsigned int minor){ VALID_FIFO; TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_SEM_DESTROY, minor, 0); return mbx_sem_delete(&(fifo[minor].sem));}static int rtf_open(struct inode *inode, struct file *filp){#define DEFAULT_SIZE 1000 TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_OPEN, MINOR(inode->i_rdev), DEFAULT_SIZE); return rtf_create(MINOR(inode->i_rdev), DEFAULT_SIZE);}static int rtf_fasync(int fd, struct file *filp, int mode){ int minor; minor = MINOR((filp->f_dentry->d_inode)->i_rdev); TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_FASYNC, minor, fd); return fasync_helper(fd, filp, mode, &(fifo[minor].asynq)); if (!mode) { fifo[minor].asynq = 0; }}static int rtf_release(struct inode *inode, struct file *filp){ int minor; minor = MINOR(inode->i_rdev); TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_RELEASE, minor, 0); if (waitqueue_active(&(fifo[minor].pollq))) { wake_up_interruptible(&(fifo[minor].pollq)); } rtf_fasync(-1, filp, 0); set_tsk_need_resched(current); return rtf_destroy(minor);}static ssize_t rtf_read(struct file *filp, char *buf, size_t count, loff_t* ppos){ struct inode *inode = filp->f_dentry->d_inode; unsigned int minor = MINOR(inode->i_rdev); int handler_ret; TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_READ, minor, count); if (filp->f_flags & O_NONBLOCK) { count -= mbx_receive_wp(&(fifo[minor].mbx), buf, count, 1); if (!count) { return -EAGAIN; } } else { count -= mbx_receive_wjo(&(fifo[minor].mbx), buf, count, 1); } if (count) { inode->i_atime = CURRENT_TIME; if ((handler_ret = ((int (*)(int, ...))(fifo[minor].handler))(minor, 'r')) < 0) { return handler_ret; } return count; } return 0; return count;}static ssize_t rtf_write(struct file *filp, const char *buf, size_t count, loff_t* ppos){ struct inode *inode = filp->f_dentry->d_inode; unsigned int minor = MINOR(inode->i_rdev); int handler_ret; TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_WRITE, minor, count); if (filp->f_flags & O_NONBLOCK) { count -= mbx_send_wp(&(fifo[minor].mbx), (char *)buf, count, 1); if (!count) { return -EAGAIN; } } else { count -= mbx_send(&(fifo[minor].mbx), (char *)buf, count, 1); } inode->i_ctime = inode->i_mtime = CURRENT_TIME; if ((handler_ret = ((int (*)(int, ...))(fifo[minor].handler))(minor, 'w')) < 0) { return handler_ret; } return count;}#define DELAY(x) (((x)*HZ + 500)/1000)static int rtf_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ unsigned int minor; FIFO *fifop; fifop = fifo + (minor = MINOR(inode->i_rdev)); TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_IOCTL, minor, cmd); switch(cmd) { case RESET: { return rtf_reset(minor); } case RESIZE: { return rtf_resize(minor, arg); } case SUSPEND_TIMED: { TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_SUSPEND_TIMED, DELAY(arg), 0); current->state = TASK_INTERRUPTIBLE; schedule_timeout(DELAY(arg)); if (signal_pending(current)) { return -ERESTARTSYS; } return 0; } case OPEN_SIZED: { return rtf_create(minor, arg); } case READ_ALL_AT_ONCE: { struct { char *buf; int count; } args; int handler_ret; TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_READ_ALLATONCE, 0, 0); copy_from_user(&args, (void *)arg, sizeof(args)); args.count -= mbx_receive(&(fifop->mbx), args.buf, args.count, 1); if (args.count) { inode->i_atime = CURRENT_TIME; if ((handler_ret = ((int (*)(int, ...))(fifo[minor].handler))(minor, 'r')) < 0) { return handler_ret; } return args.count; } return 0; } case EAVESDROP: { struct { char *buf; int count; } args; copy_from_user(&args, (void *)arg, sizeof(args)); return args.count - mbx_evdrp(&(fifop->mbx), (char **)&args.buf, args.count, 1); } case READ_TIMED: { struct { char *buf; int count, delay; } args; int handler_ret; copy_from_user(&args, (void *)arg, sizeof(args)); TRACE_RTAI_FIFO(TRACE_RTAI_EV_FIFO_READ_TIMED, args.count, DELAY(args.delay)); if (!args.delay) { args.count -= mbx_receive_wp(&(fifop->mbx), args.buf, args.count, 1); if (!args.count) { return -EAGAIN; } } else { args.count -= mbx_receive_timed(&(fifop->mbx), args.buf, args.count, DELAY(args.delay), 1); } if (args.count) { inode->i_atime = CURRENT_TIME;// if ((handler_ret = (fifop->handler)(minor)) < 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -