+static ssize_t __sync_serial_read(struct file *file,
+ char __user *buf,
+ size_t count,
+ loff_t *ppos,
+ struct timespec *ts)
+{
+ unsigned long flags;
+ int dev = MINOR(file->f_dentry->d_inode->i_rdev);
+ int avail;
+ struct sync_port *port;
+ unsigned char *start;
+ unsigned char *end;
+
+ if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) {
+ DEBUG(pr_info("Invalid minor %d\n", dev));
+ return -ENODEV;
+ }
+ port = &ports[dev];
+
+ if (!port->started)
+ sync_serial_start_port(port);
+
+ /* Calculate number of available bytes */
+ /* Save pointers to avoid that they are modified by interrupt */
+ spin_lock_irqsave(&port->lock, flags);
+ start = port->readp;
+ end = port->writep;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ while ((start == end) && !port->in_buffer_len) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ wait_event_interruptible(port->in_wait_q,
+ !(start == end && !port->full));
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ spin_lock_irqsave(&port->lock, flags);
+ start = port->readp;
+ end = port->writep;
+ spin_unlock_irqrestore(&port->lock, flags);
+ }
+
+ DEBUGREAD(pr_info("R%d c %d ri %u wi %u /%u\n",
+ dev, count,
+ start - port->flip, end - port->flip,
+ port->in_buffer_size));
+
+ /* Lazy read, never return wrapped data. */
+ if (end > start)
+ avail = end - start;
+ else
+ avail = port->flip + port->in_buffer_size - start;
+
+ count = count > avail ? avail : count;
+ if (copy_to_user(buf, start, count))
+ return -EFAULT;
+
+ /* If timestamp requested, find timestamp of first returned byte
+ * and copy it.
+ * N.B: Applications that request timstamps MUST read data in
+ * chunks that are multiples of IN_DESCR_SIZE.
+ * Otherwise the timestamps will not be aligned to the data read.
+ */
+ if (ts != NULL) {
+ int idx = port->read_ts_idx;
+ memcpy(ts, &port->timestamp[idx], sizeof(struct timespec));
+ port->read_ts_idx += count / IN_DESCR_SIZE;
+ if (port->read_ts_idx >= NBR_IN_DESCR)
+ port->read_ts_idx = 0;
+ }
+
+ spin_lock_irqsave(&port->lock, flags);
+ port->readp += count;
+ /* Check for wrap */
+ if (port->readp >= port->flip + port->in_buffer_size)
+ port->readp = port->flip;
+ port->in_buffer_len -= count;
+ port->full = 0;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ DEBUGREAD(pr_info("r %d\n", count));
+
+ return count;
+}
+
+static ssize_t sync_serial_input(struct file *file, unsigned long arg)
+{
+ struct ssp_request req;
+ int count;
+ int ret;
+
+ /* Copy the request structure from user-mode. */
+ ret = copy_from_user(&req, (struct ssp_request __user *)arg,
+ sizeof(struct ssp_request));
+
+ if (ret) {
+ DEBUG(pr_info("sync_serial_input copy from user failed\n"));
+ return -EFAULT;
+ }
+
+ /* To get the timestamps aligned, make sure that 'len'
+ * is a multiple of IN_DESCR_SIZE.
+ */
+ if ((req.len % IN_DESCR_SIZE) != 0) {
+ DEBUG(pr_info("sync_serial: req.len %x, IN_DESCR_SIZE %x\n",
+ req.len, IN_DESCR_SIZE));
+ return -EFAULT;
+ }
+
+ /* Do the actual read. */
+ /* Note that req.buf is actually a pointer to user space. */
+ count = __sync_serial_read(file, req.buf, req.len,
+ NULL, &req.ts);
+
+ if (count < 0) {
+ DEBUG(pr_info("sync_serial_input read failed\n"));
+ return count;
+ }
+
+ /* Copy the request back to user-mode. */
+ ret = copy_to_user((struct ssp_request __user *)arg, &req,
+ sizeof(struct ssp_request));
+
+ if (ret) {
+ DEBUG(pr_info("syncser input copy2user failed\n"));
+ return -EFAULT;
+ }
+
+ /* Return the number of bytes read. */
+ return count;
+}
+
+
+static int sync_serial_ioctl_unlocked(struct file *file,
+ unsigned int cmd, unsigned long arg)