static int pty_unlink(FAR struct inode *inode) { FAR struct pty_dev_s *dev; FAR struct pty_devpair_s *devpair; DEBUGASSERT(inode != NULL && inode->i_private != NULL); dev = inode->i_private; devpair = dev->pd_devpair; DEBUGASSERT(dev->pd_devpair != NULL); /* Get exclusive access */ pty_semtake(devpair); /* Indicate that the driver has been unlinked */ devpair->pp_unlinked = true; /* If there are no further open references to the driver, then commit * Hara-Kiri now. */ if (devpair->pp_nopen == 0) { pty_destroy(devpair); return OK; } pty_semgive(devpair); return OK; }
static int pty_close(FAR struct file *filep) { FAR struct inode *inode; FAR struct pty_dev_s *dev; FAR struct pty_devpair_s *devpair; DEBUGASSERT(filep != NULL && filep->f_inode != NULL); inode = filep->f_inode; dev = inode->i_private; DEBUGASSERT(dev != NULL && dev->pd_devpair != NULL); devpair = dev->pd_devpair; /* Get exclusive access */ pty_semtake(devpair); #ifdef CONFIG_PSEUDOTERM_SUSV1 /* Did the (single) master just close its reference? */ if (dev->pd_master) { /* Yes, then we are essentially unlinked and when all of the * slaves close there references, then the PTY should be * destroyed. */ devpair->pp_unlinked = true; } #endif /* Is this the last open reference? If so, was the driver previously * unlinked? */ DEBUGASSERT(devpair->pp_nopen > 0); if (devpair->pp_nopen <= 1 && devpair->pp_unlinked) { /* Yes.. Free the device pair now (without freeing the semaphore) */ pty_destroy(devpair); return OK; } else { /* Otherwise just decrement the open count */ devpair->pp_nopen--; } pty_semgive(devpair); return OK; }
static int pty_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct inode *inode; FAR struct pty_dev_s *dev; FAR struct pty_devpair_s *devpair; int ret; DEBUGASSERT(filep != NULL && filep->f_inode != NULL); inode = filep->f_inode; dev = inode->i_private; DEBUGASSERT(dev != NULL && dev->pd_devpair != NULL); devpair = dev->pd_devpair; /* Get exclusive access */ pty_semtake(devpair); /* Handle IOCTL commands */ switch (cmd) { /* PTY IOCTL commands would be handled here */ case TIOCGPTN: /* Get Pty Number (of pty-mux device): FAR int* */ { #ifdef CONFIG_DISABLE_PSEUDOFS_OPERATIONS ret = -ENOSYS; #else FAR int *ptyno = (FAR int *)((uintptr_t)arg); if (ptyno == NULL) { ret = -EINVAL; } else { *ptyno = (int)devpair->pp_minor; ret = OK; } #endif } break; case TIOCSPTLCK: /* Lock/unlock Pty: int */ { if (arg == 0) { int sval; /* Unlocking */ sched_lock(); devpair->pp_locked = false; /* Release any waiting threads */ do { DEBUGVERIFY(nxsem_getvalue(&devpair->pp_slavesem, &sval)); if (sval < 0) { nxsem_post(&devpair->pp_slavesem); } } while (sval < 0); sched_unlock(); ret = OK; } else { /* Locking */ devpair->pp_locked = true; ret = OK; } } break; case TIOCGPTLCK: /* Get Pty lock state: FAR int* */ { FAR int *ptr = (FAR int *)((uintptr_t)arg); if (ptr == NULL) { ret = -EINVAL; } else { *ptr = (int)devpair->pp_locked; ret = OK; } } break; #ifdef CONFIG_SERIAL_TERMIOS case TCGETS: { FAR struct termios *termiosp = (FAR struct termios *)arg; if (!termiosp) { ret = -EINVAL; break; } /* And update with flags from this layer */ termiosp->c_iflag = dev->pd_iflag; termiosp->c_oflag = dev->pd_oflag; termiosp->c_lflag = 0; ret = OK; } break; case TCSETS: { FAR struct termios *termiosp = (FAR struct termios *)arg; if (!termiosp) { ret = -EINVAL; break; } /* Update the flags we keep at this layer */ dev->pd_iflag = termiosp->c_iflag; dev->pd_oflag = termiosp->c_oflag; ret = OK; } break; #endif /* Get the number of bytes that are immediately available for reading * from the source pipe. */ case FIONREAD: { ret = file_ioctl(&dev->pd_src, cmd, arg); } break; /* Get the number of bytes waiting in the sink pipe (FIONWRITE) or the * number of unused bytes in the sink pipe (FIONSPACE). */ case FIONWRITE: case FIONSPACE: { ret = file_ioctl(&dev->pd_sink, cmd, arg); } break; /* Any unrecognized IOCTL commands will be passed to the contained * pipe driver. * * REVISIT: We know for a fact that the pipe driver only supports * FIONREAD, FIONWRITE, FIONSPACE and PIPEIOC_POLICY. The first two * are handled above and PIPEIOC_POLICY should not be managed by * applications -- it can break the PTY! */ default: { #if 0 ret = file_ioctl(&dev->pd_src, cmd, arg); if (ret >= 0 || ret == -ENOTTY) { ret = file_ioctl(&dev->pd_sink, cmd, arg); } #else ret = ENOTTY; #endif } break; } pty_semgive(devpair); return ret; }
static int pty_open(FAR struct file *filep) { FAR struct inode *inode; FAR struct pty_dev_s *dev; FAR struct pty_devpair_s *devpair; int ret; DEBUGASSERT(filep != NULL && filep->f_inode != NULL); inode = filep->f_inode; dev = inode->i_private; DEBUGASSERT(dev != NULL && dev->pd_devpair != NULL); devpair = dev->pd_devpair; /* Wait if this is an attempt to open the slave device and the slave * device is locked. */ if (!dev->pd_master) { /* Slave... Check if the slave driver is locked. We need to lock the * scheduler while we are running to prevent asyncrhonous modification * of pp_locked by pty_ioctl(). */ sched_lock(); while (devpair->pp_locked) { /* Wait until unlocked. We will also most certainly suspend here. */ (void)nxsem_wait(&devpair->pp_slavesem); /* Get exclusive access to the device structure. This might also * cause suspension. */ pty_semtake(devpair); /* Check again in case something happened asynchronously while we * were suspended. */ if (devpair->pp_locked) { /* This cannot suspend because we have the scheduler locked. * So pp_locked cannot change asyncrhonously between this test * and the redundant test at the top of the loop. */ pty_semgive(devpair); } } sched_unlock(); } else { /* Master ... Get exclusive access to the device structure */ pty_semtake(devpair); } #ifndef CONFIG_PSEUDOTERM_SUSV1 /* If one side of the driver has been unlinked, then refuse further * opens. * * NOTE: We ignore this case in the SUSv1 case. In the SUSv1 case, the * master side is always unlinked. */ if (devpair->pp_unlinked) { ret = -EIDRM; } else #endif { /* Increment the count of open references on the driver */ devpair->pp_nopen++; DEBUGASSERT(devpair->pp_nopen > 0); ret = OK; } pty_semgive(devpair); return ret; }
static int pty_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct inode *inode; FAR struct pty_dev_s *dev; FAR struct pty_devpair_s *devpair; int ret; DEBUGASSERT(filep != NULL && filep->f_inode != NULL); inode = filep->f_inode; dev = inode->i_private; DEBUGASSERT(dev != NULL && dev->pd_devpair != NULL); devpair = dev->pd_devpair; /* Get exclusive access */ pty_semtake(devpair); /* Handle IOCTL commands */ switch (cmd) { /* PTY IOCTL commands would be handled here */ case TIOCGPTN: /* Get Pty Number (of pty-mux device): FAR int* */ { #ifdef CONFIG_DISABLE_PSEUDOFS_OPERATIONS ret = -ENOSYS; #else FAR int *ptyno = (FAR int *)((uintptr_t)arg); if (ptyno == NULL) { ret = -EINVAL; } else { *ptyno = (int)devpair->pp_minor; ret = OK; } #endif } break; case TIOCSPTLCK: /* Lock/unlock Pty: int */ { if (arg == 0) { int sval; /* Unlocking */ sched_lock(); devpair->pp_locked = false; /* Release any waiting threads */ do { DEBUGVERIFY(sem_getvalue(&devpair->pp_slavesem, &sval)); if (sval < 0) { sem_post(&devpair->pp_slavesem); } } while (sval < 0); sched_unlock(); ret = OK; } else { /* Locking */ devpair->pp_locked = true; ret = OK; } } break; case TIOCGPTLCK: /* Get Pty lock state: FAR int* */ { FAR int *ptr = (FAR int *)((uintptr_t)arg); if (ptr == NULL) { ret = -EINVAL; } else { *ptr = (int)devpair->pp_locked; ret = OK; } } break; /* Any unrecognized IOCTL commands will be passed to the contained * pipe driver. */ default: { ret = file_ioctl(&dev->pd_src, cmd, arg); if (ret >= 0 || ret == -ENOTTY) { ret = file_ioctl(&dev->pd_sink, cmd, arg); } } break; } pty_semgive(devpair); return ret; }