Skip to content
ftdi_sio.c 66.8 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
			     v, priv->interface, 
			     buf, 0, WDR_TIMEOUT);
	
	if (rv < 0) {
		dbg("Unable to write event character: %i", rv);
		return -EIO;
	}
	
	return count;
}

static DEVICE_ATTR(latency_timer, S_IWUSR | S_IRUGO, show_latency_timer, store_latency_timer);
static DEVICE_ATTR(event_char, S_IWUSR, NULL, store_event_char);

static void create_sysfs_attrs(struct usb_serial *serial)
{	
	struct ftdi_private *priv;
	struct usb_device *udev;

	dbg("%s",__FUNCTION__);
	
	priv = usb_get_serial_port_data(serial->port[0]);
	udev = serial->dev;
	
	/* XXX I've no idea if the original SIO supports the event_char
	 * sysfs parameter, so I'm playing it safe.  */
	if (priv->chip_type != SIO) {
		dbg("sysfs attributes for %s", ftdi_chip_name[priv->chip_type]);
		device_create_file(&udev->dev, &dev_attr_event_char);
		if (priv->chip_type == FT232BM || priv->chip_type == FT2232C) {
			device_create_file(&udev->dev, &dev_attr_latency_timer);
		}
	}
}

static void remove_sysfs_attrs(struct usb_serial *serial)
{
	struct ftdi_private *priv;
	struct usb_device *udev;

	dbg("%s",__FUNCTION__);	

	priv = usb_get_serial_port_data(serial->port[0]);
	udev = serial->dev;
	
	/* XXX see create_sysfs_attrs */
	if (priv->chip_type != SIO) {
		device_remove_file(&udev->dev, &dev_attr_event_char);
		if (priv->chip_type == FT232BM || priv->chip_type == FT2232C) {
			device_remove_file(&udev->dev, &dev_attr_latency_timer);
		}
	}
	
}

/*
 * ***************************************************************************
 * FTDI driver specific functions
 * ***************************************************************************
 */

/* Probe function to check for special devices */
static int ftdi_sio_probe (struct usb_serial *serial, const struct usb_device_id *id)
{
	usb_set_serial_data(serial, (void *)id->driver_info);

	return (0);
}

/* attach subroutine */
static int ftdi_sio_attach (struct usb_serial *serial)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct usb_serial_port *port = serial->port[0];
	struct ftdi_private *priv;
	struct ftdi_sio_quirk *quirk;
Linus Torvalds's avatar
Linus Torvalds committed
	
	dbg("%s",__FUNCTION__);

	priv = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL);
	if (!priv){
		err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct ftdi_private));
		return -ENOMEM;
	}
	memset(priv, 0, sizeof(*priv));

	spin_lock_init(&priv->rx_lock);
        init_waitqueue_head(&priv->delta_msr_wait);
	/* This will push the characters through immediately rather
	   than queue a task to deliver them */
	priv->flags = ASYNC_LOW_LATENCY;

	/* Increase the size of read buffers */
Linus Torvalds's avatar
Linus Torvalds committed
	port->bulk_in_buffer = kmalloc (BUFSZ, GFP_KERNEL);
	if (!port->bulk_in_buffer) {
		kfree (priv);
		return -ENOMEM;
	}
	if (port->read_urb) {
		port->read_urb->transfer_buffer = port->bulk_in_buffer;
		port->read_urb->transfer_buffer_length = BUFSZ;
	}

	INIT_WORK(&priv->rx_work, ftdi_process_read, port);

Linus Torvalds's avatar
Linus Torvalds committed
	/* Free port's existing write urb and transfer buffer. */
	if (port->write_urb) {
		usb_free_urb (port->write_urb);
		port->write_urb = NULL;
	}
	kfree(port->bulk_out_buffer);
	port->bulk_out_buffer = NULL;
Linus Torvalds's avatar
Linus Torvalds committed

	usb_set_serial_port_data(serial->port[0], priv);

	ftdi_determine_type (serial->port[0]);
Linus Torvalds's avatar
Linus Torvalds committed
	create_sysfs_attrs(serial);

	/* Check for device requiring special set up. */
	quirk = (struct ftdi_sio_quirk *)usb_get_serial_data(serial);
	if (quirk && quirk->setup) {
		quirk->setup(serial);
Linus Torvalds's avatar
Linus Torvalds committed
	}
	
	return (0);
} /* ftdi_sio_attach */
/* Setup for the USB-UIRT device, which requires hardwired
 * baudrate (38400 gets mapped to 312500) */
Linus Torvalds's avatar
Linus Torvalds committed
/* Called from usbserial:serial_probe */
static void ftdi_USB_UIRT_setup (struct usb_serial *serial)
{
Linus Torvalds's avatar
Linus Torvalds committed
	struct ftdi_private *priv;

	dbg("%s",__FUNCTION__);

	priv = usb_get_serial_port_data(serial->port[0]);
	priv->flags |= ASYNC_SPD_CUST;
	priv->custom_divisor = 77;
	priv->force_baud = B38400;
} /* ftdi_USB_UIRT_setup */
Linus Torvalds's avatar
Linus Torvalds committed

/* Setup for the HE-TIRA1 device, which requires hardwired
 * baudrate (38400 gets mapped to 100000) and RTS-CTS enabled.  */
static void ftdi_HE_TIRA1_setup (struct usb_serial *serial)
{
Linus Torvalds's avatar
Linus Torvalds committed
	struct ftdi_private *priv;

	dbg("%s",__FUNCTION__);

	priv = usb_get_serial_port_data(serial->port[0]);
	priv->flags |= ASYNC_SPD_CUST;
	priv->custom_divisor = 240;
	priv->force_baud = B38400;
	priv->force_rtscts = 1;
} /* ftdi_HE_TIRA1_setup */
Linus Torvalds's avatar
Linus Torvalds committed


/* ftdi_shutdown is called from usbserial:usb_serial_disconnect 
 *   it is called when the usb device is disconnected
 *
 *   usbserial:usb_serial_disconnect
 *      calls __serial_close for each open of the port
 *      shutdown is called then (ie ftdi_shutdown)
 */


static void ftdi_shutdown (struct usb_serial *serial)
{ /* ftdi_shutdown */
	
	struct usb_serial_port *port = serial->port[0];
	struct ftdi_private *priv = usb_get_serial_port_data(port);

	dbg("%s", __FUNCTION__);

	remove_sysfs_attrs(serial);
	
	/* all open ports are closed at this point 
         *    (by usbserial.c:__serial_close, which calls ftdi_close)  
	 */

	if (priv) {
		usb_set_serial_port_data(port, NULL);
		kfree(priv);
	}
} /* ftdi_shutdown */


static int  ftdi_open (struct usb_serial_port *port, struct file *filp)
{ /* ftdi_open */
	struct termios tmp_termios;
	struct usb_device *dev = port->serial->dev;
	struct ftdi_private *priv = usb_get_serial_port_data(port);
	unsigned long flags;
	
	int result = 0;
	char buf[1]; /* Needed for the usb_control_msg I think */

	dbg("%s", __FUNCTION__);


	port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;

	/* No error checking for this (will get errors later anyway) */
	/* See ftdi_sio.h for description of what is reset */
	usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
			FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, 
			FTDI_SIO_RESET_SIO, 
			priv->interface, buf, 0, WDR_TIMEOUT);

	/* Termios defaults are set by usb_serial_init. We don't change
	   port->tty->termios - this would loose speed settings, etc.
	   This is same behaviour as serial.c/rs_open() - Kuba */

	/* ftdi_set_termios  will send usb control messages */
	ftdi_set_termios(port, &tmp_termios);

	/* FIXME: Flow control might be enabled, so it should be checked -
	   we have no control of defaults! */
	/* Turn on RTS and DTR since we are not flow controlling by default */
	if (set_dtr(port, HIGH) < 0) {
		err("%s Error from DTR HIGH urb", __FUNCTION__);
	}
	if (set_rts(port, HIGH) < 0){
		err("%s Error from RTS HIGH urb", __FUNCTION__);
	}

	/* Not throttled */
	spin_lock_irqsave(&priv->rx_lock, flags);
	priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
	spin_unlock_irqrestore(&priv->rx_lock, flags);

	/* Start reading from the device */
Linus Torvalds's avatar
Linus Torvalds committed
	usb_fill_bulk_urb(port->read_urb, dev,
		      usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress),
		      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
		      ftdi_read_bulk_callback, port);
	result = usb_submit_urb(port->read_urb, GFP_KERNEL);
	if (result)
		err("%s - failed submitting read urb, error %d", __FUNCTION__, result);


	return result;
} /* ftdi_open */



/* 
 * usbserial:__serial_close  only calls ftdi_close if the point is open
 *
 *   This only gets called when it is the last close
 *   
 *   
 */

static void ftdi_close (struct usb_serial_port *port, struct file *filp)
{ /* ftdi_close */
	unsigned int c_cflag = port->tty->termios->c_cflag;
	struct ftdi_private *priv = usb_get_serial_port_data(port);
	char buf[1];

	dbg("%s", __FUNCTION__);

	if (c_cflag & HUPCL){
		/* Disable flow control */
		if (usb_control_msg(port->serial->dev, 
				    usb_sndctrlpipe(port->serial->dev, 0),
				    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
				    0, priv->interface, buf, 0,
				    WDR_TIMEOUT) < 0) {
			err("error from flowcontrol urb");
		}	    

		/* drop DTR */
		if (set_dtr(port, LOW) < 0){
			err("Error from DTR LOW urb");
		}
		/* drop RTS */
		if (set_rts(port, LOW) < 0) {
			err("Error from RTS LOW urb");
		}
	} /* Note change no line if hupcl is off */

	/* cancel any scheduled reading */
	cancel_delayed_work(&priv->rx_work);
	flush_scheduled_work();
Linus Torvalds's avatar
Linus Torvalds committed
	
	/* shutdown our bulk read */
	if (port->read_urb)
		usb_kill_urb(port->read_urb);
} /* ftdi_close */


  
/* The SIO requires the first byte to have:
 *  B0 1
 *  B1 0
 *  B2..7 length of message excluding byte 0
 *
 * The new devices do not require this byte
 */
static int ftdi_write (struct usb_serial_port *port,
			   const unsigned char *buf, int count)
{ /* ftdi_write */
	struct ftdi_private *priv = usb_get_serial_port_data(port);
	struct urb *urb;
	unsigned char *buffer;
	int data_offset ;       /* will be 1 for the SIO and 0 otherwise */
	int status;
	int transfer_size;

	dbg("%s port %d, %d bytes", __FUNCTION__, port->number, count);

	if (count == 0) {
		dbg("write request of 0 bytes");
		return 0;
	}
	
	data_offset = priv->write_offset;
        dbg("data_offset set to %d",data_offset);

	/* Determine total transfer size */
	transfer_size = count;
	if (data_offset > 0) {
		/* Original sio needs control bytes too... */
		transfer_size += (data_offset *
				((count + (PKTSZ - 1 - data_offset)) /
				 (PKTSZ - data_offset)));
	}

	buffer = kmalloc (transfer_size, GFP_ATOMIC);
	if (!buffer) {
		err("%s ran out of kernel memory for urb ...", __FUNCTION__);
		return -ENOMEM;
	}

	urb = usb_alloc_urb(0, GFP_ATOMIC);
	if (!urb) {
		err("%s - no more free urbs", __FUNCTION__);
		kfree (buffer);
		return -ENOMEM;
	}

	/* Copy data */
	if (data_offset > 0) {
		/* Original sio requires control byte at start of each packet. */
		int user_pktsz = PKTSZ - data_offset;
		int todo = count;
		unsigned char *first_byte = buffer;
		const unsigned char *current_position = buf;

		while (todo > 0) {
			if (user_pktsz > todo) {
				user_pktsz = todo;
			}
			/* Write the control byte at the front of the packet*/
			*first_byte = 1 | ((user_pktsz) << 2); 
			/* Copy data for packet */
			memcpy (first_byte + data_offset,
				current_position, user_pktsz);
			first_byte += user_pktsz + data_offset;
			current_position += user_pktsz;
			todo -= user_pktsz;
		}
	} else {
		/* No control byte required. */
		/* Copy in the data to send */
		memcpy (buffer, buf, count);
	}

	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, transfer_size, buffer);

	/* fill the buffer and send it */
	usb_fill_bulk_urb(urb, port->serial->dev, 
		      usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress),
		      buffer, transfer_size,
		      ftdi_write_bulk_callback, port);

	status = usb_submit_urb(urb, GFP_ATOMIC);
	if (status) {
		err("%s - failed submitting write urb, error %d", __FUNCTION__, status);
		count = status;
		kfree (buffer);
	}

	/* we are done with this urb, so let the host driver
	 * really free it when it is finished with it */
	usb_free_urb (urb);

	dbg("%s write returning: %d", __FUNCTION__, count);
	return count;
} /* ftdi_write */


/* This function may get called when the device is closed */

static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
{
	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;

	/* free up the transfer buffer, as usb_free_urb() does not do this */
	kfree (urb->transfer_buffer);

	dbg("%s - port %d", __FUNCTION__, port->number);
	
	if (urb->status) {
		dbg("nonzero write bulk status received: %d", urb->status);
		return;
	}

	schedule_work(&port->work);
} /* ftdi_write_bulk_callback */


static int ftdi_write_room( struct usb_serial_port *port )
{
	dbg("%s - port %d", __FUNCTION__, port->number);

	/*
	 * We really can take anything the user throws at us
	 * but let's pick a nice big number to tell the tty
	 * layer that we have lots of free space
	 */
	return 2048;
} /* ftdi_write_room */


static int ftdi_chars_in_buffer (struct usb_serial_port *port)
{ /* ftdi_chars_in_buffer */
	dbg("%s - port %d", __FUNCTION__, port->number);

	/* 
	 * We can't really account for how much data we
	 * have sent out, but hasn't made it through to the
	 * device, so just tell the tty layer that everything
	 * is flushed.
	 */
	return 0;
} /* ftdi_chars_in_buffer */



static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
{ /* ftdi_read_bulk_callback */
	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
	struct tty_struct *tty;
	struct ftdi_private *priv;

	if (urb->number_of_packets > 0) {
		err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__,
		    urb->transfer_buffer_length, urb->actual_length, urb->number_of_packets );
		err("%s transfer_flags %x ", __FUNCTION__,urb->transfer_flags );
	}

	dbg("%s - port %d", __FUNCTION__, port->number);

	if (port->open_count <= 0)
		return;

	tty = port->tty;
	if (!tty) {
		dbg("%s - bad tty pointer - exiting",__FUNCTION__);
		return;
	}

	priv = usb_get_serial_port_data(port);
	if (!priv) {
		dbg("%s - bad port private data pointer - exiting", __FUNCTION__);
		return;
	}

	if (urb != port->read_urb) {
		err("%s - Not my urb!", __FUNCTION__);
	}

	if (urb->status) {
		/* This will happen at close every time so it is a dbg not an err */
		dbg("(this is ok on close) nonzero read bulk status received: %d", urb->status);
		return;
	}

	ftdi_process_read(port);

} /* ftdi_read_bulk_callback */


static void ftdi_process_read (void *param)
Linus Torvalds's avatar
Linus Torvalds committed
{ /* ftdi_process_read */
	struct usb_serial_port *port = (struct usb_serial_port*)param;
Linus Torvalds's avatar
Linus Torvalds committed
	struct urb *urb;
	struct tty_struct *tty;
	struct ftdi_private *priv;
	char error_flag;
       	unsigned char *data;

	int i;
	int result;
	int need_flip;
	int packet_offset;
Linus Torvalds's avatar
Linus Torvalds committed

	dbg("%s - port %d", __FUNCTION__, port->number);

	if (port->open_count <= 0)
		return;

	tty = port->tty;
	if (!tty) {
		dbg("%s - bad tty pointer - exiting",__FUNCTION__);
		return;
	}

	priv = usb_get_serial_port_data(port);
	if (!priv) {
		dbg("%s - bad port private data pointer - exiting", __FUNCTION__);
		return;
	}

	urb = port->read_urb;
	if (!urb) {
		dbg("%s - bad read_urb pointer - exiting", __FUNCTION__);
		return;
	}

	data = urb->transfer_buffer;

	if (priv->rx_processed) {
		dbg("%s - already processed: %d bytes, %d remain", __FUNCTION__,
				priv->rx_processed,
				urb->actual_length - priv->rx_processed);
Linus Torvalds's avatar
Linus Torvalds committed
	} else {
		/* The first two bytes of every read packet are status */
		if (urb->actual_length > 2) {
			usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
		} else {
			dbg("Status only: %03oo %03oo",data[0],data[1]);
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed


	/* TO DO -- check for hung up line and handle appropriately: */
	/*   send hangup  */
	/* See acm.c - you do a tty_hangup  - eg tty_hangup(tty) */
	/* if CD is dropped and the line is not CLOCAL then we should hangup */

	need_flip = 0;
	for (packet_offset = priv->rx_processed; packet_offset < urb->actual_length; packet_offset += PKTSZ) {
		int length;

Linus Torvalds's avatar
Linus Torvalds committed
		/* Compare new line status to the old one, signal if different */
		/* N.B. packet may be processed more than once, but differences
		 * are only processed once.  */
Linus Torvalds's avatar
Linus Torvalds committed
		if (priv != NULL) {
			char new_status = data[packet_offset+0] & FTDI_STATUS_B0_MASK;
			if (new_status != priv->prev_status) {
				priv->diff_status |= new_status ^ priv->prev_status;
				wake_up_interruptible(&priv->delta_msr_wait);
				priv->prev_status = new_status;
			}
		}

		length = min(PKTSZ, urb->actual_length-packet_offset)-2;
		if (length < 0) {
			err("%s - bad packet length: %d", __FUNCTION__, length+2);
			length = 0;
		}

		/* have to make sure we don't overflow the buffer
		   with tty_insert_flip_char's */
		if (tty->flip.count+length > TTY_FLIPBUF_SIZE) {
			tty_flip_buffer_push(tty);
			need_flip = 0;

			if (tty->flip.count != 0) {
				/* flip didn't work, this happens when ftdi_process_read() is
				 * called from ftdi_unthrottle, because TTY_DONT_FLIP is set */
				dbg("%s - flip buffer push failed", __FUNCTION__);
				break;
			}
		}
		if (priv->rx_flags & THROTTLED) {
			dbg("%s - throttled", __FUNCTION__);
			break;
		}
		if (tty->ldisc.receive_room(tty)-tty->flip.count < length) {
			/* break out & wait for throttling/unthrottling to happen */
			dbg("%s - receive room low", __FUNCTION__);
			break;
		}

Linus Torvalds's avatar
Linus Torvalds committed
		/* Handle errors and break */
		error_flag = TTY_NORMAL;
		/* Although the device uses a bitmask and hence can have multiple */
		/* errors on a packet - the order here sets the priority the */
		/* error is returned to the tty layer  */

		if ( data[packet_offset+1] & FTDI_RS_OE ) {
			error_flag = TTY_OVERRUN;
			dbg("OVERRRUN error");
		}
		if ( data[packet_offset+1] & FTDI_RS_BI ) {
			error_flag = TTY_BREAK;
			dbg("BREAK received");
		}
		if ( data[packet_offset+1] & FTDI_RS_PE ) {
			error_flag = TTY_PARITY;
			dbg("PARITY error");
		}
		if ( data[packet_offset+1] & FTDI_RS_FE ) {
			error_flag = TTY_FRAME;
			dbg("FRAMING error");
		}
		if (length > 0) {
			for (i = 2; i < length+2; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
				/* Note that the error flag is duplicated for 
				   every character received since we don't know
				   which character it applied to */
				tty_insert_flip_char(tty, data[packet_offset+i], error_flag);
			}
			need_flip = 1;
		}

#ifdef NOT_CORRECT_BUT_KEEPING_IT_FOR_NOW
		/* if a parity error is detected you get status packets forever
		   until a character is sent without a parity error.
		   This doesn't work well since the application receives a never
		   ending stream of bad data - even though new data hasn't been sent.
		   Therefore I (bill) have taken this out.
		   However - this might make sense for framing errors and so on 
		   so I am leaving the code in for now.
		*/
		else {
			if (error_flag != TTY_NORMAL){
				dbg("error_flag is not normal");
				/* In this case it is just status - if that is an error send a bad character */
				if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
					tty_flip_buffer_push(tty);
				}
				tty_insert_flip_char(tty, 0xff, error_flag);
				need_flip = 1;
			}
		}
#endif
	} /* "for(packet_offset=0..." */

	/* Low latency */
	if (need_flip) {
		tty_flip_buffer_push(tty);
	}

	if (packet_offset < urb->actual_length) {
		/* not completely processed - record progress */
		priv->rx_processed = packet_offset;
		dbg("%s - incomplete, %d bytes processed, %d remain",
				__FUNCTION__, packet_offset,
				urb->actual_length - packet_offset);
		/* check if we were throttled while processing */
		spin_lock_irqsave(&priv->rx_lock, flags);
		if (priv->rx_flags & THROTTLED) {
			priv->rx_flags |= ACTUALLY_THROTTLED;
			spin_unlock_irqrestore(&priv->rx_lock, flags);
			dbg("%s - deferring remainder until unthrottled",
					__FUNCTION__);
			return;
		}
		spin_unlock_irqrestore(&priv->rx_lock, flags);
		/* if the port is closed stop trying to read */
		if (port->open_count > 0){
			/* delay processing of remainder */
			schedule_delayed_work(&priv->rx_work, 1);
		} else {
			dbg("%s - port is closed", __FUNCTION__);
		}
		return;
	}

	/* urb is completely processed */
	priv->rx_processed = 0;

Linus Torvalds's avatar
Linus Torvalds committed
1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000
	/* if the port is closed stop trying to read */
	if (port->open_count > 0){
		/* Continue trying to always read  */
		usb_fill_bulk_urb(port->read_urb, port->serial->dev, 
			      usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress),
			      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
			      ftdi_read_bulk_callback, port);

		result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
		if (result)
			err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
	}

	return;
} /* ftdi_process_read */


static void ftdi_break_ctl( struct usb_serial_port *port, int break_state )
{
	struct ftdi_private *priv = usb_get_serial_port_data(port);
	__u16 urb_value = 0; 
	char buf[1];
	
	/* break_state = -1 to turn on break, and 0 to turn off break */
	/* see drivers/char/tty_io.c to see it used */
	/* last_set_data_urb_value NEVER has the break bit set in it */

	if (break_state) {
		urb_value = priv->last_set_data_urb_value | FTDI_SIO_SET_BREAK;
	} else {
		urb_value = priv->last_set_data_urb_value; 
	}

	
	if (usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
			    FTDI_SIO_SET_DATA_REQUEST, 
			    FTDI_SIO_SET_DATA_REQUEST_TYPE,
			    urb_value , priv->interface,
			    buf, 0, WDR_TIMEOUT) < 0) {
		err("%s FAILED to enable/disable break state (state was %d)", __FUNCTION__,break_state);
	}	   

	dbg("%s break state is %d - urb is %d", __FUNCTION__,break_state, urb_value);
	
}


/* old_termios contains the original termios settings and tty->termios contains
 * the new setting to be used
 * WARNING: set_termios calls this with old_termios in kernel space
 */

static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_termios)
{ /* ftdi_termios */
	struct usb_device *dev = port->serial->dev;
	unsigned int cflag = port->tty->termios->c_cflag;
	struct ftdi_private *priv = usb_get_serial_port_data(port);
	__u16 urb_value; /* will hold the new flags */
	char buf[1]; /* Perhaps I should dynamically alloc this? */
	
	// Added for xon/xoff support
	unsigned int iflag = port->tty->termios->c_iflag;
	unsigned char vstop;
	unsigned char vstart;
	
	dbg("%s", __FUNCTION__);

	/* Force baud rate if this device requires it, unless it is set to B0. */
	if (priv->force_baud && ((port->tty->termios->c_cflag & CBAUD) != B0)) {
		dbg("%s: forcing baud rate for this device", __FUNCTION__);
		port->tty->termios->c_cflag &= ~CBAUD;
		port->tty->termios->c_cflag |= priv->force_baud;
	}

	/* Force RTS-CTS if this device requires it. */
	if (priv->force_rtscts) {
		dbg("%s: forcing rtscts for this device", __FUNCTION__);
		port->tty->termios->c_cflag |= CRTSCTS;
	}

	cflag = port->tty->termios->c_cflag;

	/* FIXME -For this cut I don't care if the line is really changing or 
	   not  - so just do the change regardless  - should be able to 
	   compare old_termios and tty->termios */
	/* NOTE These routines can get interrupted by 
	   ftdi_sio_read_bulk_callback  - need to examine what this 
           means - don't see any problems yet */
	
	/* Set number of data bits, parity, stop bits */
	
	urb_value = 0;
	urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
		      FTDI_SIO_SET_DATA_STOP_BITS_1);
	urb_value |= (cflag & PARENB ? 
		      (cflag & PARODD ? FTDI_SIO_SET_DATA_PARITY_ODD : 
		       FTDI_SIO_SET_DATA_PARITY_EVEN) :
		      FTDI_SIO_SET_DATA_PARITY_NONE);
	if (cflag & CSIZE) {
		switch (cflag & CSIZE) {
		case CS5: urb_value |= 5; dbg("Setting CS5"); break;
		case CS6: urb_value |= 6; dbg("Setting CS6"); break;
		case CS7: urb_value |= 7; dbg("Setting CS7"); break;
		case CS8: urb_value |= 8; dbg("Setting CS8"); break;
		default:
			err("CSIZE was set but not CS5-CS8");
		}
	}

	/* This is needed by the break command since it uses the same command - but is
	 *  or'ed with this value  */
	priv->last_set_data_urb_value = urb_value;
	
	if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
			    FTDI_SIO_SET_DATA_REQUEST, 
			    FTDI_SIO_SET_DATA_REQUEST_TYPE,
			    urb_value , priv->interface,
			    buf, 0, 100) < 0) {
		err("%s FAILED to set databits/stopbits/parity", __FUNCTION__);
	}	   

	/* Now do the baudrate */
	if ((cflag & CBAUD) == B0 ) {
		/* Disable flow control */
		if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
				    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
				    0, priv->interface, 
				    buf, 0, WDR_TIMEOUT) < 0) {
			err("%s error from disable flowcontrol urb", __FUNCTION__);
		}	    
		/* Drop RTS and DTR */
		if (set_dtr(port, LOW) < 0){
			err("%s Error from DTR LOW urb", __FUNCTION__);
		}
		if (set_rts(port, LOW) < 0){
			err("%s Error from RTS LOW urb", __FUNCTION__);
		}	
		
	} else {
		/* set the baudrate determined before */
		if (change_speed(port)) {
			err("%s urb failed to set baurdrate", __FUNCTION__);
		}
		/* Ensure  RTS and DTR are raised */
		else if (set_dtr(port, HIGH) < 0){
			err("%s Error from DTR HIGH urb", __FUNCTION__);
		}
		else if (set_rts(port, HIGH) < 0){
			err("%s Error from RTS HIGH urb", __FUNCTION__);
		}	
	}

	/* Set flow control */
	/* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
	if (cflag & CRTSCTS) {
		dbg("%s Setting to CRTSCTS flow control", __FUNCTION__);
		if (usb_control_msg(dev, 
				    usb_sndctrlpipe(dev, 0),
				    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
				    0 , (FTDI_SIO_RTS_CTS_HS | priv->interface),
				    buf, 0, WDR_TIMEOUT) < 0) {
			err("urb failed to set to rts/cts flow control");
		}		
		
	} else { 
		/*
		 * Xon/Xoff code
		 *
		 * Check the IXOFF status in the iflag component of the termios structure
		 * if IXOFF is not set, the pre-xon/xoff code is executed.
		*/
		if (iflag & IXOFF) {
			dbg("%s  request to enable xonxoff iflag=%04x",__FUNCTION__,iflag);
			// Try to enable the XON/XOFF on the ftdi_sio
			// Set the vstart and vstop -- could have been done up above where
			// a lot of other dereferencing is done but that would be very
			// inefficient as vstart and vstop are not always needed
			vstart=port->tty->termios->c_cc[VSTART];
			vstop=port->tty->termios->c_cc[VSTOP];
			urb_value=(vstop << 8) | (vstart);

			if (usb_control_msg(dev,
					    usb_sndctrlpipe(dev, 0),
					    FTDI_SIO_SET_FLOW_CTRL_REQUEST,
					    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
					    urb_value , (FTDI_SIO_XON_XOFF_HS
							 | priv->interface),
					    buf, 0, WDR_TIMEOUT) < 0) {
				err("urb failed to set to xon/xoff flow control");
			}
		} else {
			/* else clause to only run if cfag ! CRTSCTS and iflag ! XOFF */
			/* CHECKME Assuming XON/XOFF handled by tty stack - not by device */
			dbg("%s Turning off hardware flow control", __FUNCTION__);
			if (usb_control_msg(dev, 
					    usb_sndctrlpipe(dev, 0),
					    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
					    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
					    0, priv->interface, 
					    buf, 0, WDR_TIMEOUT) < 0) {
				err("urb failed to clear flow control");
			}				
		}
		
	}
	return;
} /* ftdi_termios */


static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
{
	struct ftdi_private *priv = usb_get_serial_port_data(port);
	unsigned char buf[2];
	int ret;

	dbg("%s TIOCMGET", __FUNCTION__);
	switch (priv->chip_type) {
	case SIO:
		/* Request the status from the device */
		if ((ret = usb_control_msg(port->serial->dev, 
					   usb_rcvctrlpipe(port->serial->dev, 0),
					   FTDI_SIO_GET_MODEM_STATUS_REQUEST, 
					   FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
					   0, 0, 
					   buf, 1, WDR_TIMEOUT)) < 0 ) {
			err("%s Could not get modem status of device - err: %d", __FUNCTION__,
			    ret);
			return(ret);
		}
		break;
	case FT8U232AM:
	case FT232BM:
	case FT2232C:
		/* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same
		   format as the data returned from the in point */
		if ((ret = usb_control_msg(port->serial->dev, 
					   usb_rcvctrlpipe(port->serial->dev, 0),
					   FTDI_SIO_GET_MODEM_STATUS_REQUEST, 
					   FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
					   0, priv->interface, 
					   buf, 2, WDR_TIMEOUT)) < 0 ) {
			err("%s Could not get modem status of device - err: %d", __FUNCTION__,
			    ret);
			return(ret);
		}
		break;
	default:
		return -EFAULT;
		break;
	}
	
	return  (buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
		(buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
		(buf[0]  & FTDI_SIO_RI_MASK  ? TIOCM_RI  : 0) |
		(buf[0]  & FTDI_SIO_RLSD_MASK ? TIOCM_CD  : 0) |
		priv->last_dtr_rts;			
}

static int ftdi_tiocmset(struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear)
{
	int ret;
	
	dbg("%s TIOCMSET", __FUNCTION__);
	if (set & TIOCM_DTR){
		if ((ret = set_dtr(port, HIGH)) < 0) {
			err("Urb to set DTR failed");
			return(ret);
		}
	}
	if (set & TIOCM_RTS) {
		if ((ret = set_rts(port, HIGH)) < 0){
			err("Urb to set RTS failed");
			return(ret);
		}
	}
	
	if (clear & TIOCM_DTR){
		if ((ret = set_dtr(port, LOW)) < 0){
			err("Urb to unset DTR failed");
			return(ret);
		}
	}	
	if (clear & TIOCM_RTS) {
		if ((ret = set_rts(port, LOW)) < 0){
			err("Urb to unset RTS failed");
			return(ret);
		}
	}
	return(0);
}


static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
{
	struct ftdi_private *priv = usb_get_serial_port_data(port);

	dbg("%s cmd 0x%04x", __FUNCTION__, cmd);

	/* Based on code from acm.c and others */
	switch (cmd) {

	case TIOCGSERIAL: /* gets serial port data */
		return get_serial_info(port, (struct serial_struct __user *) arg);

	case TIOCSSERIAL: /* sets serial port data */
		return set_serial_info(port, (struct serial_struct __user *) arg);

	/*
	 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
	 * - mask passed in arg for lines of interest
	 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
	 * Caller should use TIOCGICOUNT to see which one it was.
	 *
	 * This code is borrowed from linux/drivers/char/serial.c
	 */
	case TIOCMIWAIT: