int pipecommon_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) { FAR struct inode *inode = filep->f_inode; FAR struct pipe_dev_s *dev = inode->i_private; pollevent_t eventset; pipe_ndx_t nbytes; int ret = OK; int i; DEBUGASSERT(dev && fds); /* Are we setting up the poll? Or tearing it down? */ pipecommon_semtake(&dev->d_bfsem); if (setup) { /* This is a request to set up the poll. Find an available * slot for the poll structure reference */ for (i = 0; i < CONFIG_DEV_PIPE_NPOLLWAITERS; i++) { /* Find an available slot */ if (!dev->d_fds[i]) { /* Bind the poll structure and this slot */ dev->d_fds[i] = fds; fds->priv = &dev->d_fds[i]; break; } } if (i >= CONFIG_DEV_PIPE_NPOLLWAITERS) { fds->priv = NULL; ret = -EBUSY; goto errout; } /* Should immediately notify on any of the requested events? * First, determine how many bytes are in the buffer */ if (dev->d_wrndx >= dev->d_rdndx) { nbytes = dev->d_wrndx - dev->d_rdndx; } else { nbytes = (CONFIG_DEV_PIPE_SIZE-1) + dev->d_wrndx - dev->d_rdndx; } /* Notify the POLLOUT event if the pipe is not full, but only if * there is readers. */ eventset = 0; if (nbytes < (CONFIG_DEV_PIPE_SIZE-1)) { eventset |= POLLOUT; } /* Notify the POLLIN event if the pipe is not empty */ if (nbytes > 0) { eventset |= POLLIN; } /* Notify the POLLHUP event if the pipe is empty and no writers */ if (nbytes == 0 && dev->d_nwriters <= 0) { eventset |= POLLHUP; } /* Change POLLOUT to POLLERR, if no readers and policy 0. */ if ((eventset | POLLOUT) && PIPE_IS_POLICY_0(dev->d_flags) && dev->d_nreaders <= 0) { eventset |= POLLERR; } if (eventset) { pipecommon_pollnotify(dev, eventset); } } else { /* This is a request to tear down the poll. */ struct pollfd **slot = (struct pollfd **)fds->priv; #ifdef CONFIG_DEBUG if (!slot) { ret = -EIO; goto errout; } #endif /* Remove all memory of the poll setup */ *slot = NULL; fds->priv = NULL; } errout: sem_post(&dev->d_bfsem); return ret; }
ssize_t pipecommon_write(FAR struct file *filep, FAR const char *buffer, size_t len) { struct inode *inode = filep->f_inode; struct pipe_dev_s *dev = inode->i_private; ssize_t nwritten = 0; ssize_t last; int nxtwrndx; int sval; DEBUGASSERT(dev); pipe_dumpbuffer("To PIPE:", (uint8_t*)buffer, len); if (len == 0) { return 0; } /* At present, this method cannot be called from interrupt handlers. That is * because it calls sem_wait (via pipecommon_semtake below) and sem_wait cannot * be called from interrupt level. This actually happens fairly commonly * IF dbg() is called from interrupt handlers and stdout is being redirected * via a pipe. In that case, the debug output will try to go out the pipe * (interrupt handlers should use the lldbg() APIs). * * On the other hand, it would be very valuable to be able to feed the pipe * from an interrupt handler! TODO: Consider disabling interrupts instead * of taking semaphores so that pipes can be written from interrupt handlers */ DEBUGASSERT(up_interrupt_context() == false); /* Make sure that we have exclusive access to the device structure */ if (sem_wait(&dev->d_bfsem) < 0) { return ERROR; } /* Loop until all of the bytes have been written */ last = 0; for (;;) { /* Calculate the write index AFTER the next byte is written */ nxtwrndx = dev->d_wrndx + 1; if (nxtwrndx >= CONFIG_DEV_PIPE_SIZE) { nxtwrndx = 0; } /* Would the next write overflow the circular buffer? */ if (nxtwrndx != dev->d_rdndx) { /* No... copy the byte */ dev->d_buffer[dev->d_wrndx] = *buffer++; dev->d_wrndx = nxtwrndx; /* Is the write complete? */ if (++nwritten >= len) { /* Yes.. Notify all of the waiting readers that more data is available */ while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval < 0) { sem_post(&dev->d_rdsem); } /* Notify all poll/select waiters that they can write to the FIFO */ pipecommon_pollnotify(dev, POLLIN); /* Return the number of bytes written */ sem_post(&dev->d_bfsem); return len; } } else { /* There is not enough room for the next byte. Was anything written in this pass? */ if (last < nwritten) { /* Yes.. Notify all of the waiting readers that more data is available */ while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval < 0) { sem_post(&dev->d_rdsem); } } last = nwritten; /* If O_NONBLOCK was set, then return partial bytes written or EGAIN */ if (filep->f_oflags & O_NONBLOCK) { if (nwritten == 0) { nwritten = -EAGAIN; } sem_post(&dev->d_bfsem); return nwritten; } /* There is more to be written.. wait for data to be removed from the pipe */ sched_lock(); sem_post(&dev->d_bfsem); pipecommon_semtake(&dev->d_wrsem); sched_unlock(); pipecommon_semtake(&dev->d_bfsem); } } }
int pipecommon_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct inode *inode = filep->f_inode; FAR struct pipe_dev_s *dev = inode->i_private; int ret = -EINVAL; #ifdef CONFIG_DEBUG /* Some sanity checking */ if (dev == NULL) { return -EBADF; } #endif pipecommon_semtake(&dev->d_bfsem); switch (cmd) { case PIPEIOC_POLICY: { if (arg != 0) { PIPE_POLICY_1(dev->d_flags); } else { PIPE_POLICY_0(dev->d_flags); } ret = OK; } break; case FIONREAD: { int count; /* Determine the number of bytes available in the buffer */ if (dev->d_wrndx < dev->d_rdndx) { count = (CONFIG_DEV_PIPE_SIZE - dev->d_rdndx) + dev->d_wrndx; } else { count = dev->d_wrndx - dev->d_rdndx; } *(FAR int *)arg = count; ret = 0; } break; case FIONWRITE: { int count; /* Determine the number of bytes free in the buffer */ if (dev->d_wrndx < dev->d_rdndx) { count = (dev->d_rdndx - dev->d_wrndx) - 1; } else { count = ((CONFIG_DEV_PIPE_SIZE - dev->d_wrndx) + dev->d_rdndx) - 1; } *(FAR int *)arg = count; ret = 0; } break; default: break; } sem_post(&dev->d_bfsem); return ret; }
int pipecommon_close(FAR struct file *filep) { struct inode *inode = filep->f_inode; struct pipe_dev_s *dev = inode->i_private; int sval; DEBUGASSERT(dev && dev->d_refs > 0); /* Make sure that we have exclusive access to the device structure. * NOTE: close() is supposed to return EINTR if interrupted, however * I've never seen anyone check that. */ pipecommon_semtake(&dev->d_bfsem); /* Decrement the number of references on the pipe. Check if there are * still outstanding references to the pipe. */ /* Check if the decremented reference count would go to zero */ if (--dev->d_refs > 0) { /* No more references.. If opened for writing, decrement the count of * writers on the pipe instance. */ if ((filep->f_oflags & O_WROK) != 0) { /* If there are no longer any writers on the pipe, then notify all of the * waiting readers that they must return end-of-file. */ if (--dev->d_nwriters <= 0) { while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval < 0) { sem_post(&dev->d_rdsem); } /* Inform poll readers that other end closed. */ pipecommon_pollnotify(dev, POLLHUP); } } /* If opened for reading, decrement the count of readers on the pipe * instance. */ if ((filep->f_oflags & O_RDOK) != 0) { if (--dev->d_nreaders <= 0) { if (PIPE_IS_POLICY_0(dev->d_flags)) { /* Inform poll writers that other end closed. */ pipecommon_pollnotify(dev, POLLERR); } } } } /* What is the buffer management policy? Do we free the buffe when the * last client closes the pipe policy 0, or when the buffer becomes empty. * In the latter case, the buffer data will remain valid and can be * obtained when the pipe is re-opened. */ else if (PIPE_IS_POLICY_0(dev->d_flags) || dev->d_wrndx == dev->d_rdndx) { /* Policy 0 or the buffer is empty ... deallocate the buffer now. */ kmm_free(dev->d_buffer); dev->d_buffer = NULL; /* And reset all counts and indices */ dev->d_wrndx = 0; dev->d_rdndx = 0; dev->d_refs = 0; dev->d_nwriters = 0; dev->d_nreaders = 0; #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS /* If, in addition, we have been unlinked, then also need to free the * device structure as well to prevent a memory leak. */ if (PIPE_IS_UNLINKED(dev->d_flags)) { pipecommon_freedev(dev); return OK; } #endif } sem_post(&dev->d_bfsem); return OK; }
int pipecommon_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct inode *inode = filep->f_inode; FAR struct pipe_dev_s *dev = inode->i_private; int ret = -EINVAL; #ifdef CONFIG_DEBUG_FEATURES /* Some sanity checking */ if (dev == NULL) { return -EBADF; } #endif pipecommon_semtake(&dev->d_bfsem); switch (cmd) { case PIPEIOC_POLICY: { if (arg != 0) { PIPE_POLICY_1(dev->d_flags); } else { PIPE_POLICY_0(dev->d_flags); } ret = OK; } break; case FIONWRITE: /* Number of bytes waiting in send queue */ case FIONREAD: /* Number of bytes available for reading */ { int count; /* Determine the number of bytes written to the buffer. This is, * of course, also the number of bytes that may be read from the * buffer. * * d_rdndx - index to remove next byte from the buffer * d_wrndx - Index to next location to add a byte to the buffer. */ if (dev->d_wrndx < dev->d_rdndx) { count = (dev->d_bufsize - dev->d_rdndx) + dev->d_wrndx; } else { count = dev->d_wrndx - dev->d_rdndx; } *(FAR int *)((uintptr_t)arg) = count; ret = 0; } break; /* Free space in buffer */ case FIONSPACE: { int count; /* Determine the number of bytes free in the buffer. * * d_rdndx - index to remove next byte from the buffer * d_wrndx - Index to next location to add a byte to the buffer. */ if (dev->d_wrndx < dev->d_rdndx) { count = (dev->d_rdndx - dev->d_wrndx) - 1; } else { count = ((dev->d_bufsize - dev->d_wrndx) + dev->d_rdndx) - 1; } *(FAR int *)((uintptr_t)arg) = count; ret = 0; } break; default: break; } sem_post(&dev->d_bfsem); return ret; }