unsigned int cctdev_poll(struct file *filp, poll_table *wait)
{
	struct cctdev_dev *dev = filp->private_data;
	int minor_num;
	unsigned int mask = 0;

	F_ENTER();

	minor_num = MINOR(dev->cdev.dev);

	down(&txCittyBuf[minor_num].gSem);

	poll_wait(filp, &txCittyBuf[minor_num].gInq, wait);

	if (txCittyBuf[minor_num].iBufOut != txCittyBuf[minor_num].iBufIn)
		mask |= POLLIN | POLLRDNORM;

	mask |= POLLOUT | POLLWRNORM;

	up(&txCittyBuf[minor_num].gSem);

	F_LEAVE();

	return mask;

}
/* the real citty_ioctl function.
 * The above is done to get the small functions*/
static int citty_ioctl(struct tty_struct *tty,
		       unsigned int cmd, unsigned long arg)
{
	struct file *file = NULL;
	F_ENTER();

	switch (cmd) {
	case TIOCGSERIAL:
		return citty_ioctl_tiocgserial(tty, file, cmd, arg);
	case TIOCMIWAIT:
		return citty_ioctl_tiocmiwait(tty, file, cmd, arg);
	case TIOCGICOUNT:
		return citty_ioctl_tiocgicount(tty, file, cmd, arg);
	case TCSETS:
		return citty_ioctl_tcsets(tty, file, cmd, arg);
	case TCGETS:              /* 0x5401 ioctls.h */
		return citty_ioctl_tcgets(tty, file, cmd, arg);
	case TCSETSF:             /* 0x5404 */
	case TCSETAF:             /* 0x5408 */

		return 0;         /* has to return zero for qtopia to work */
	default:
		PDEBUG("citty_ioctl cmd: %d.\n", cmd);
		return -ENOIOCTLCMD;           /* for PPPD to work? */

		break;
	}

	F_LEAVE();

}
static int citty_ioctl_tiocgicount(struct tty_struct *tty, struct file *file,
				   unsigned int cmd, unsigned long arg)
{
	struct citty_port *citty = tty->driver_data;

	F_ENTER();
	if (cmd == TIOCGICOUNT) {
		struct async_icount cnow = citty->icount;
		struct serial_icounter_struct icount;

		icount.cts      = cnow.cts;
		icount.dsr      = cnow.dsr;
		icount.rng      = cnow.rng;
		icount.dcd      = cnow.dcd;
		icount.rx       = cnow.rx;
		icount.tx       = cnow.tx;
		icount.frame    = cnow.frame;
		icount.overrun  = cnow.overrun;
		icount.parity   = cnow.parity;
		icount.brk      = cnow.brk;
		icount.buf_overrun = cnow.buf_overrun;

		if (copy_to_user((void __user *)arg, &icount, sizeof(icount)))
			return -EFAULT;
		return 0;
	}

	F_LEAVE();
	return -ENOIOCTLCMD;
}
static void __exit citty_exit(void)
{
	struct citty_port *citty = NULL;
	int i;

	F_ENTER();

	/* unregister devices  */
	for (i = 0; i < CITTY_TTY_MINORS; ++i) {
		down(&sem_lock_tty[i]);
		citty = citty_table[i];
		if (citty) {
			citty_table[i] = NULL;
			kfree(citty);
		}
		up(&sem_lock_tty[i]);
		tty_port_destroy(&citty_port_table[i]);
		tty_unregister_device(citty_tty_driver, i);
		cci_free_buffer(&txCittyBuf[i]);
	}

	/* unregister driver */
	tty_unregister_driver(citty_tty_driver);
	put_tty_driver(citty_tty_driver);

	/* clean up for the cctdev stuff */
	cctdev_cleanup_module();

	F_LEAVE();
}
int cctdev_open(struct inode *inode, struct file *filp)
{
	struct cctdev_dev *dev;	/* device information */

	F_ENTER();

	dev = container_of(inode->i_cdev, struct cctdev_dev, cdev);

	filp->private_data = dev;	/* for other methods */

	/* now trim to 0 the length of the device if open was write-only */
	/*
	   if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {

	   if (down_interruptible(&dev->sem))
	   return -ERESTARTSYS;

	   up(&dev->sem);
	   }
	 */

	/* used to keep track of how many readers */
	down(&dev->sem);
	if (filp->f_mode & FMODE_READ)
		dev->nreaders++;
	if (filp->f_mode & FMODE_WRITE)
		dev->nwriters++;
	up(&dev->sem);

	F_LEAVE();

	return nonseekable_open(inode, filp);	/* success */
}
static int citty_write_room(struct tty_struct *tty)
{
	struct citty_port *citty = NULL;
	int index = tty->index;
	int room = -EINVAL;

	F_ENTER();

	down(&sem_lock_tty[index]);
	citty = citty_table[index];
	if (!citty) {
		up(&sem_lock_tty[index]);
		return -ENODEV;
	}

	if (!citty->port->count) {
		PDEBUG("citty_write_room: no port is open.");
		/* port was not opened */
		goto exit;
	}

	/* calculate how much room is left in the device */
	/* CHECKPOINT */
	/* room = CITTY_BUF_SIZE * spacefree(  &txCittyBuf ); */
	room = CITTY_BUF_SIZE * spacefree(&txCittyBuf[tty->index]);

exit:
	up(&sem_lock_tty[index]);

	F_LEAVE();

	return room;
}
void cci_init_buffer(struct buf_struct *buffer)
{
	int i;

	F_ENTER();

	for (i = 0; i < NUM_CITTY_BUF; i++) {
		buffer->pBuf[i] = kmalloc(CITTY_BUF_SIZE, GFP_KERNEL);
		if (!buffer->pBuf[i])
			printk(KERN_ERR "Failed to allocate memory.\n");

		buffer->iBufIn = 0;
		buffer->iBufOut = 0;

	}

	sema_init(&(buffer->gSem), 1);

	/* init the queue */
	init_waitqueue_head(&(buffer->gInq));
	init_waitqueue_head(&(buffer->gOutq));

	F_LEAVE();

}
static void __exit cidatatty_exit(void)
{
	struct cidatatty_port *cidatatty;
	int i;

	F_ENTER();

	/* unregister device */
	for (i = 0; i < CIDATATTY_TTY_MINORS; ++i) {
		cidatatty = cidatatty_table[i].data_port;
		if (cidatatty) {
			/* close the port */
			while (cidatatty->port.count)
				do_close(cidatatty);

			cidatatty_table[i].data_port = NULL;
			tty_port_destroy(&cidatatty->port);
			kfree(cidatatty);
		}
		tty_unregister_device(cidatatty_tty_driver, i);
	}

	/* unregister driver */
	tty_unregister_driver(cidatatty_tty_driver);

	cctdatadev_cleanup_module();
	F_LEAVE();
}
static void citty_close(struct tty_struct *tty, struct file *file)
{
	struct citty_port *citty = NULL;
	int index = tty->index;

	F_ENTER();

	down(&sem_lock_tty[index]);
	citty = citty_table[index];
	/* Modified by Rovin Yu: release citty and related resource */
	if (citty) {
		if (!citty->port->count) {
			/* port was never opened */
			goto exit;
		}
		--citty->port->count;
		PDEBUG("citty_close: index is %d, count is %d\n",
				index, citty->port->count);
		if (citty->port->count <= 0) {
			citty_table[index] = NULL;
			kfree(citty);
		}
	}
exit:
	up(&sem_lock_tty[index]);
	F_LEAVE();
}
static void __exit citty_exit(void)
{
	struct citty_serial *citty;
	int i;

	F_ENTER();
	for (i = 0; i < CITTY_TTY_MINORS; ++i)
		tty_unregister_device(citty_tty_driver, i);

	tty_unregister_driver(citty_tty_driver);
	put_tty_driver(citty_tty_driver);

	/*  free the memory */
	for (i = 0; i < CITTY_TTY_MINORS; ++i)
	{
		down(&sem[i]);
		citty = citty_table[i];
		if (citty)
		{
			kfree(citty);
			citty_table[i] = NULL;

			//cci_free_buffer(&txCittyBuf[i]);

		}
		up(&sem[i]);
		cci_free_buffer(&txCittyBuf[i]); //Modified by Rovin to move here
	}

	/* clean up for the cctdev stuff */
	cctdev_cleanup_module();

	F_LEAVE();
}
static int citty_ioctl(struct tty_struct *tty, struct file *file,
		       unsigned int cmd, unsigned long arg)
#endif
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
	struct file *file = NULL;
#endif
	F_ENTER();

	switch (cmd)
	{
	case TIOCGSERIAL:
		return citty_ioctl_tiocgserial(tty, file, cmd, arg);
	case TIOCMIWAIT:
		return citty_ioctl_tiocmiwait(tty, file, cmd, arg);
	case TIOCGICOUNT:
		return citty_ioctl_tiocgicount(tty, file, cmd, arg);
	case TCSETS:
		return citty_ioctl_tcsets(tty, file, cmd, arg);
	case TCGETS:                    //0x5401 ioctls.h
		return citty_ioctl_tcgets(tty, file, cmd, arg);
	case TCSETSF:                   //0x5404
	case TCSETAF:                   //0x5408

		return 0;               //has to return zero for qtopia to work
	default:
		PDEBUG("citty_ioctl cmd: %d.\n", cmd);
		return -ENOIOCTLCMD;           // for PPPD to work?

		break;
	}

	F_LEAVE();

}
static int __init citty_init(void)
{
	int retval;
	int i;

	F_ENTER();

	/* allocate the tty driver */
	citty_tty_driver = alloc_tty_driver(CITTY_TTY_MINORS);
	if (!citty_tty_driver)
		return -ENOMEM;

	/* initialize the tty driver */
	citty_tty_driver->owner = THIS_MODULE;
	citty_tty_driver->driver_name = "citty_tty";
	citty_tty_driver->name = "citty";
	citty_tty_driver->major = CITTY_TTY_MAJOR;
	citty_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
	citty_tty_driver->subtype = SERIAL_TYPE_NORMAL;
	citty_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
	citty_tty_driver->init_termios = tty_std_termios;
	/* B115200 | CS8 | CREAD | HUPCL | CLOCAL; */
	citty_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
	citty_tty_driver->init_termios.c_iflag = IGNBRK | IGNCR | IGNPAR;
	citty_tty_driver->init_termios.c_oflag = 0;
	citty_tty_driver->init_termios.c_lflag = 0;

	tty_set_operations(citty_tty_driver, &serial_ops);

	/* register the tty driver */
	retval = tty_register_driver(citty_tty_driver);
	if (retval) {
		printk(KERN_ERR "failed to register citty tty driver");
		put_tty_driver(citty_tty_driver);
		citty_tty_driver = NULL;
		return retval;
	}

	/* register tty devices */
	for (i = 0; i < CITTY_TTY_MINORS; ++i) {
		/* Init buffer */
		cci_init_buffer(&txCittyBuf[i]);
		sema_init(&sem_lock_tty[i], 1);

		tty_port_init(&citty_port_table[i]);
		tty_port_register_device(&citty_port_table[i],
				citty_tty_driver, i, NULL);
	}

	printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "\n");

	cctdev_init_module();

	F_LEAVE();
	return retval;
}
static void cidatatty_close(struct tty_struct *tty, struct file *file)
{
	struct cidatatty_port *cidatatty = tty->driver_data;

	F_ENTER();

	if (cidatatty)
		do_close(cidatatty);

	F_LEAVE();
}
static int citty_open(struct tty_struct *tty, struct file *file)
{
	struct citty_serial *citty;
	int index;

	F_ENTER();
	schedule();
	/* initialize the pointer in case something fails */
	tty->driver_data = NULL;

	/* get the serial object associated with this tty pointer */
	index = tty->index;
	if (!cctdev_ready || (cctdev_devices[index].nreaders == 0 && cctdev_devices[index].nwriters == 0))
		return -EAGAIN;

	down(&sem[index]);
	citty = citty_table[index];
	if (citty == NULL)
	{
		/* first time accessing this device, let's create it */
		citty = kmalloc(sizeof(*citty), GFP_KERNEL);
		if (!citty)
		{
			up(&sem[index]);
			return -ENOMEM;
		}
		citty->open_count = 0;

		citty_table[index] = citty;
	}

	/* save our structure within the tty structure */
	tty->driver_data = citty;

	/* vcy */
	tty->flags = TTY_NO_WRITE_SPLIT | tty->flags;

	citty->tty = tty;

	++citty->open_count;
	if (citty->open_count == 1)
	{
		/* this is the first time this port is opened */
		/* do any hardware initialization needed here */


	}

	up(&sem[index]);

	F_LEAVE();
	return 0;
}
Esempio n. 15
0
static int citty_open(struct tty_struct *tty, struct file *file)
{
	struct citty_port *citty = NULL;
	int index = tty->index;

	F_ENTER();
	schedule();
	/* initialize the pointer in case something fails */
	tty->driver_data = NULL;

	/* get the serial object associated with this tty pointer */
	if (!cctdev_ready
	    || (cctdev_devices[index].nreaders == 0
		&& cctdev_devices[index].nwriters == 0))
		return -EAGAIN;

	mutex_lock(&mutex_lock_tty[index]);
	citty = citty_table[index];
	if (!citty) {
		citty = kmalloc(sizeof(*citty), GFP_KERNEL);
		if (!citty) {
			mutex_unlock(&mutex_lock_tty[index]);
			return -ENOMEM;
		}
		/* save tty_port structure within the our structure */
		citty_table[index] = citty;
		citty->port = &citty_port_table[index];
		citty->port->count = 0;
	}

	/* do the real open operation */
	/* save our structure within the tty structure */
	tty->driver_data = citty;

	/* vcy */
	tty->flags = TTY_NO_WRITE_SPLIT | tty->flags;

	/* save tty_struct within the tty_port structure */
	citty->port->tty = tty;

	++citty->port->count;
	if (citty->port->count == 1) {
		/* this is the first time this port is opened */
		/* do any hardware initialization needed here */
	}

	mutex_unlock(&mutex_lock_tty[index]);

	F_LEAVE();
	return 0;
}
static int citty_write(struct tty_struct *tty, const unsigned char *buffer,
		       int count)
{
	struct citty_port *citty = NULL;
	int index = tty->index;
	int retval = -EINVAL;

	F_ENTER();
	/* for some reason, this function is called with count == 0 */
	if (count <= 0) {
		printk(KERN_ERR "Error: count is %d.\n", count);
		return 0;
	}

	down(&sem_lock_tty[index]);
	citty = citty_table[index];
	if (!citty) {
		up(&sem_lock_tty[index]);
		PDEBUG("Warning: citty_write: citty is NULL\n");
		return -ENODEV;
	}

	if (!citty->port->count) {
		printk(KERN_ERR "Error: citty_write: port was not open\n");
		/* port was not opened */
		goto exit;
	}
#ifdef DEBUG_BUF_CONTENT
	/* int i; */
	printk(KERN_DEBUG "CITTY Tx Buffer datalen is %d\n data:", count);
	for (i = 0; i < count; i++)
		printk(KERN_DEBUG "%02x(%c)", buffer[i]&0xff, buffer[i]&0xff);
	/*    printk(KERN_DEBUG " %02x", buffer[i]&0xff ); */
	printk(KERN_DEBUG "\n");
#endif

	/*
	 * packet is ready for transmission:
	 * write the packet to TxCitty buffer
	 */
	retval =
	    write_citty_buffer(&txCittyBuf[tty->index], buffer, count,
			       COPY_FROM_CITTY);

exit:
	up(&sem_lock_tty[index]);

	F_LEAVE();

	return retval;
}
static int citty_ioctl_tcgets(struct tty_struct *tty, struct file *file,
			      unsigned int cmd, unsigned long arg)
{
	F_ENTER();

	if (copy_to_user((void *)arg, tty->termios, sizeof(struct termios)))
	{
		printk("Failed to copy to user for tcgets.\n");
		return -EFAULT;
	}
	F_LEAVE();

	return 0;
}
/*
 * Set up the char_dev structure for this device.
 */
static void cctdev_setup_cdev(struct cctdev_dev *dev, int index)
{
	int err, devno = MKDEV(cctdev_major, cctdev_minor + index);

	F_ENTER();

	cdev_init(&dev->cdev, &cctdev_fops);
	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &cctdev_fops;
	err = cdev_add(&dev->cdev, devno, 1);
	/* Fail gracefully if need be */
	if (err)
		printk(KERN_NOTICE "Error %d adding cctdev%d", err, index);

	F_LEAVE();

}
static int citty_tiocmset(struct tty_struct *tty, struct file *file,
			  unsigned int set, unsigned int clear)
#endif
{
	struct citty_serial *citty = tty->driver_data;
	unsigned int mcr = citty->mcr;

	F_ENTER();
	if (set & TIOCM_RTS)
		mcr |= MCR_RTS;
	if (set & TIOCM_DTR)
		mcr |= MCR_RTS;
	if (set & TIOCM_LOOP)
		mcr |= MCR_RTS;
	if (set & TIOCM_CTS)
		mcr |= MCR_RTS;
	if (set & TIOCM_RI)
		mcr |= MCR_RTS;
	if (set & TIOCM_DSR)
		mcr |= MCR_RTS;
	if (set & TIOCM_CAR)
		mcr |= MCR_RTS;

	if (clear & TIOCM_RTS)
		mcr &= ~MCR_RTS;
	if (clear & TIOCM_DTR)
		mcr &= ~MCR_RTS;
	if (clear & TIOCM_LOOP)
		mcr &= ~MCR_RTS;
	if (clear & TIOCM_CTS)
		mcr &= ~MCR_RTS;
	if (clear & TIOCM_CAR)
		mcr &= ~MCR_RTS;
	if (clear & TIOCM_RI)
		mcr &= ~MCR_RTS;
	if (clear & TIOCM_DSR)
		mcr &= ~MCR_RTS;

	/* set the new MCR value in the device */
	citty->mcr = mcr;

	F_LEAVE();
	return 0;
}
static int citty_ioctl_tiocmiwait(struct tty_struct *tty, struct file *file,
				  unsigned int cmd, unsigned long arg)
{
	struct citty_serial *citty = tty->driver_data;

	F_ENTER();

	if (cmd == TIOCMIWAIT)
	{
		DECLARE_WAITQUEUE(wait, current);
		struct async_icount cnow;
		struct async_icount cprev;

		cprev = citty->icount;
		while (1)
		{
			add_wait_queue(&citty->wait, &wait);
			set_current_state(TASK_INTERRUPTIBLE);
			schedule();
			remove_wait_queue(&citty->wait, &wait);

			/* see if a signal woke us up */
			if (signal_pending(current))
				return -ERESTARTSYS;

			cnow = citty->icount;
			if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
			    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
				return -EIO;  /* no change => error */
			if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
			    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
			    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
			    ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) )
			{
				return 0;
			}
			cprev = cnow;
		}

	}

	F_LEAVE();
	return -ENOIOCTLCMD;
}
int cctdatadev_open(struct inode *inode, struct file *filp)
{
	struct cctdatadev_dev *dev;
	F_ENTER();
#if 1
	dev = container_of(inode->i_cdev, struct cctdatadev_dev, cdev);

	filp->private_data = dev;	/* for other methods */

	/* used to keep track of how many readers */
	if (filp->f_mode & FMODE_READ)
		dev->nreaders++;
	if (filp->f_mode & FMODE_WRITE)
		dev->nwriters++;
#endif
	F_LEAVE();

	return nonseekable_open(inode, filp);	/* success */
}
/*******************************************************************
*  FUNCTION: cctdev_read
*
*  DESCRIPTION: To pass the data from TxBuffer to CI
*
*  RETURNS: utlFAILED or utlSUCCESS
*
*******************************************************************/
ssize_t cctdev_read(struct file *filp, char __user *buf, size_t count,
		    loff_t *f_pos)
{
	struct cctdev_dev *dev = filp->private_data;
	ssize_t retval = 0;
	int minor_num;

	F_ENTER();

	/* Extract Minor Number*/
	minor_num = MINOR(dev->cdev.dev);

	PDEBUG("txCittyBuf[%d].iBufOut: %d, iBufIn: %d\n", minor_num, txCittyBuf[minor_num].iBufOut, txCittyBuf[minor_num].iBufIn);

	retval = read_citty_buffer( buf, &txCittyBuf[minor_num], COPY_TO_USER);

	F_LEAVE();

	return retval;
}
static int citty_tiocmget(struct tty_struct *tty)
{
	struct citty_port *citty = tty->driver_data;
	unsigned int result = 0;
	unsigned int msr = citty->msr;
	unsigned int mcr = citty->mcr;

	F_ENTER();

	result = ((mcr & MCR_DTR)  ? TIOCM_DTR  : 0) |  /* DTR is set */
		 ((mcr & MCR_RTS)  ? TIOCM_RTS  : 0) |  /* RTS is set */
		 ((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) |  /* LOOP is set */
		 ((msr & MSR_CTS)  ? TIOCM_CTS  : 0) |  /* CTS is set */
		 ((msr & MSR_CD)   ? TIOCM_CAR  : 0) |  /* CD is set */
		 ((msr & MSR_RI)   ? TIOCM_RI   : 0) |  /* RI is set */
		 ((msr & MSR_DSR)  ? TIOCM_DSR  : 0);   /* DSR is set */

	F_LEAVE();
	return result;
}
static int citty_tiocmget(struct tty_struct *tty, struct file *file)
#endif
{
	struct citty_serial *citty = tty->driver_data;
	unsigned int result = 0;
	unsigned int msr = citty->msr;
	unsigned int mcr = citty->mcr;

	F_ENTER();

	result = ((mcr & MCR_DTR)  ? TIOCM_DTR  : 0) |  /* DTR is set */
		 ((mcr & MCR_RTS)  ? TIOCM_RTS  : 0) |  /* RTS is set */
		 ((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) |  /* LOOP is set */
		 ((msr & MSR_CTS)  ? TIOCM_CTS  : 0) |  /* CTS is set */
		 ((msr & MSR_CD)   ? TIOCM_CAR  : 0) |  /* Carrier detect is set*/
		 ((msr & MSR_RI)   ? TIOCM_RI   : 0) |  /* Ring Indicator is set */
		 ((msr & MSR_DSR)  ? TIOCM_DSR  : 0);   /* DSR is set */

	F_LEAVE();
	return result;
}
Esempio n. 25
0
int cctdev_open(struct inode *inode, struct file *filp)
{
	struct cctdev_dev *dev;	/* device information */

	F_ENTER();

	dev = container_of(inode->i_cdev, struct cctdev_dev, cdev);

	filp->private_data = dev;	/* for other methods */

	/* used to keep track of how many readers */
	mutex_lock(&dev->lock);
	if (filp->f_mode & FMODE_READ)
		dev->nreaders++;
	if (filp->f_mode & FMODE_WRITE)
		dev->nwriters++;
	mutex_unlock(&dev->lock);

	F_LEAVE();

	return nonseekable_open(inode, filp);	/* success */
}
static int cidatatty_ioctl_tiocgserial(struct tty_struct *tty,
				       struct file *file, unsigned int cmd,
				       unsigned long arg)
{
	struct cidatatty_port *cidatatty = tty->driver_data;

	F_ENTER();

	if (cmd == TIOCGSERIAL) {
		struct serial_struct tmp;

		if (!arg)
			return -EFAULT;

		memset(&tmp, 0, sizeof(tmp));

		tmp.type                = cidatatty->serial.type;
		tmp.line                = cidatatty->serial.line;
		tmp.port                = cidatatty->serial.port;
		tmp.irq                 = cidatatty->serial.irq;
		tmp.flags               = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
		tmp.xmit_fifo_size      = cidatatty->serial.xmit_fifo_size;
		tmp.baud_base           = cidatatty->serial.baud_base;
		tmp.close_delay         = 5 * HZ;
		tmp.closing_wait        = 30 * HZ;
		tmp.custom_divisor      = cidatatty->serial.custom_divisor;
		tmp.hub6                = cidatatty->serial.hub6;
		tmp.io_type             = cidatatty->serial.io_type;

		if (copy_to_user
		    ((void __user *)arg, &tmp, sizeof(struct serial_struct)))
			return -EFAULT;
		return 0;
	}

	F_LEAVE();
	return -ENOIOCTLCMD;
}
static int citty_ioctl_tcsets(struct tty_struct *tty, struct file *file,
			      unsigned int cmd, unsigned long arg)
{
	F_ENTER();

	memcpy((void *)&tty->termios, (void *)arg, sizeof(struct ktermios));
	/*
	   struct termios * new_termios = (struct termios *) arg;

	   tty->termios->c_iflag = new_termios->c_iflag;
	   tty->termios->c_oflag = new_termios->c_oflag;
	   tty->termios->c_cflag = new_termios->c_cflag;
	   tty->termios->c_lflag = new_termios->c_lflag;
	   tty->termios->c_line  = new_termios->c_line;

	   int i;

	   for(i = 0; i <NCCS; i++)
	   tty->termios->c_cc[i] = new_termios->c_cc[i];
	 */
	F_LEAVE();

	return 0;
}
static int citty_read_proc(char *page, char **start, off_t off, int count,
			   int *eof, void *data)
{
	struct citty_serial *citty;
	off_t begin = 0;
	int length = 0;
	int i;

	F_ENTER();

	length += sprintf(page, "cittyserinfo:1.0 driver:%s\n", DRIVER_VERSION);
	for (i = 0; i < CITTY_TTY_MINORS && length < PAGE_SIZE; ++i)
	{
		citty = citty_table[i];
		if (citty == NULL)
			continue;

		length += sprintf(page + length, "%d\n", i);
		if ((length + begin) > (off + count))
			goto done;
		if ((length + begin) < off)
		{
			begin += length;
			length = 0;
		}
	}
	*eof = 1;
 done:
	if (off >= (length + begin))
		return 0;
	*start = page + (off - begin);

	F_LEAVE();

	return (count < begin + length - off) ? count : begin + length - off;
}
static int citty_read_proc(struct file *filp, char __user *buf, size_t count,
			   loff_t *f_pos)
{
	struct citty_port *citty = NULL;
	int length = 0;
	int i;
	char temp[256] = { 0 };
	int rc = -EFAULT;

	F_ENTER();

	for (i = 0; i < CITTY_TTY_MINORS; ++i) {
		citty = citty_table[i];
		if (citty == NULL)
			continue;

		length +=
		    snprintf(temp + length, sizeof(temp) - length, "%d,", i);
	}
	if (length > 0)
		temp[length - 1] = '\0';
	if (length + 1 > count) {
		printk(KERN_ERR "%s: error: no enough space.\n", __func__);
		goto err_exit;
	}
	if (copy_to_user(buf, (void *)&temp, length + 1)) {
		printk(KERN_ERR "%s: copy_to_user failed.\n", __func__);
		goto err_exit;
	}
	printk(KERN_DEBUG "citty read proc: %s\n", temp);
	rc = 0;

err_exit:
	F_LEAVE();
	return rc;
}
static void citty_set_termios(struct tty_struct *tty,
			      struct ktermios *old_termios)
{
	unsigned int cflag;

	F_ENTER();
	cflag = tty->termios.c_cflag;

	/* check that they really want us to change something */
	if (old_termios) {
		if ((cflag == old_termios->c_cflag) &&
		    (RELEVANT_IFLAG(tty->termios.c_iflag) ==
		     RELEVANT_IFLAG(old_termios->c_iflag))) {
			PDEBUG(" - nothing to change...\n");
			return;
		}
	}

	/* get the byte size */
	switch (cflag & CSIZE) {
	case CS5:
		PDEBUG(" - data bits = 5\n");
		break;
	case CS6:
		PDEBUG(" - data bits = 6\n");
		break;
	case CS7:
		PDEBUG(" - data bits = 7\n");
		break;
	default:
	case CS8:
		PDEBUG(" - data bits = 8\n");
		break;
	}

	/* determine the parity */
	if (cflag & PARENB)
		if (cflag & PARODD)
			PDEBUG(" - parity = odd\n");
		else
			PDEBUG(" - parity = even\n");
	else
		PDEBUG(" - parity = none\n");

	/* figure out the stop bits requested */
	if (cflag & CSTOPB)
		PDEBUG(" - stop bits = 2\n");
	else
		PDEBUG(" - stop bits = 1\n");

	/* figure out the hardware flow control settings */
	if (cflag & CRTSCTS)
		PDEBUG(" - RTS/CTS is enabled\n");
	else
		PDEBUG(" - RTS/CTS is disabled\n");

	/* determine software flow control */
	/* if we are implementing XON/XOFF, set the start and
	 * stop character in the device */
	if (I_IXOFF(tty) || I_IXON(tty)) {
		/* CHECKPOINT */
		/* Invalid code here;
		 * seems software flow control is not supported
		 */
#if 0
		unsigned char stop_char  = STOP_CHAR(tty);
		unsigned char start_char = START_CHAR(tty);

		/* if we are implementing INBOUND XON/XOFF */
		if (I_IXOFF(tty))
			PDEBUG(" - INBOUND XON/XOFF is enabled, " \
			       "XON = %2x, XOFF = %2x", start_char, stop_char);
		else
			PDEBUG(" - INBOUND XON/XOFF is disabled");

		/* if we are implementing OUTBOUND XON/XOFF */
		if (I_IXON(tty))
			PDEBUG(" - OUTBOUND XON/XOFF is enabled, " \
			       "XON = %2x, XOFF = %2x", start_char, stop_char);
		else
			PDEBUG(" - OUTBOUND XON/XOFF is disabled");
#endif
	}

	/* get the baud rate wanted */
	PDEBUG(" - baud rate = %d", tty_get_baud_rate(tty));

	F_LEAVE();
}