Needed for parts like *u2 that don't support dual-bank (ping-ping) mode on certain endpoints.
		
			
				
	
	
		
			515 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			515 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
   FTDI serial chip emulation for LUFA
 | 
						|
   Jim Paris <jim@jtan.com>
 | 
						|
*/
 | 
						|
 | 
						|
#include <LUFA/Drivers/USB/USB.h>
 | 
						|
#include "ftdi.h"
 | 
						|
#include "queue.h"
 | 
						|
 | 
						|
/* TX and RX queues */
 | 
						|
static uint8_t ftdi_txqueue_buf[FTDI_TX_QUEUE_LEN];
 | 
						|
static uint8_t ftdi_rxqueue_buf[FTDI_RX_QUEUE_LEN];
 | 
						|
struct queue ftdi_txqueue =
 | 
						|
	QUEUE_INITIALIZER(ftdi_txqueue_buf, sizeof(ftdi_txqueue_buf));
 | 
						|
struct queue ftdi_rxqueue =
 | 
						|
	QUEUE_INITIALIZER(ftdi_rxqueue_buf, sizeof(ftdi_rxqueue_buf));
 | 
						|
 | 
						|
/* Not sure if we need this, but it might make the Windows driver happy. */
 | 
						|
static const uint8_t ftdi_eeprom[] = {
 | 
						|
	0x00, 0x40, 0x03, 0x04, 0x01, 0x60, 0x00, 0x00,
 | 
						|
	0xa0, 0x2d, 0x08, 0x00, 0x00, 0x00, 0x98, 0x0a,
 | 
						|
	0xa2, 0x20, 0xc2, 0x12, 0x23, 0x10, 0x05, 0x00,
 | 
						|
	0x0a, 0x03, 0x46, 0x00, 0x54, 0x00, 0x44, 0x00,
 | 
						|
	0x49, 0x00, 0x20, 0x03, 0x46, 0x00, 0x54, 0x00,
 | 
						|
	0x32, 0x00, 0x33, 0x00, 0x32, 0x00, 0x52, 0x00,
 | 
						|
	0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00,
 | 
						|
	0x20, 0x00, 0x55, 0x00, 0x41, 0x00, 0x52, 0x00,
 | 
						|
	0x54, 0x00, 0x12, 0x03, 0x41, 0x00, 0x36, 0x00,
 | 
						|
	0x30, 0x00, 0x30, 0x00, 0x65, 0x00, 0x4f, 0x00,
 | 
						|
	0x45, 0x00, 0x37, 0x00, 0x81, 0xe1, 0x35, 0x60,
 | 
						|
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0xa5
 | 
						|
};
 | 
						|
 | 
						|
static struct ftdi_status {
 | 
						|
	/* modem status */
 | 
						|
	unsigned reserved_1:1;
 | 
						|
	unsigned reserved_0:3;
 | 
						|
	unsigned cts:1;
 | 
						|
	unsigned dsr:1;
 | 
						|
	unsigned ri:1;
 | 
						|
	unsigned cd:1;
 | 
						|
 | 
						|
	/* ftdi status */
 | 
						|
	unsigned dr:1;
 | 
						|
	unsigned oe:1;
 | 
						|
	unsigned pe:1;
 | 
						|
	unsigned fe:1;
 | 
						|
	unsigned bi:1;
 | 
						|
	unsigned thre:1;
 | 
						|
	unsigned temt:1;
 | 
						|
	unsigned fifoerr:1;
 | 
						|
} ftdi_status = { 0 };
 | 
						|
 | 
						|
static struct ctrl_status {
 | 
						|
	unsigned dtr:1;
 | 
						|
	unsigned rts:1;
 | 
						|
} ctrl_status = { 0 };
 | 
						|
 | 
						|
/* Fdev compatible wrappers */
 | 
						|
static int ftdi_fdev_write(char c, FILE *stream) {
 | 
						|
	ftdi_putchar(c);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
static int ftdi_fdev_read(FILE *stream) {
 | 
						|
	return ftdi_getchar();
 | 
						|
}
 | 
						|
static FILE _ftdi_stream =
 | 
						|
	FDEV_SETUP_STREAM(ftdi_fdev_write, ftdi_fdev_read, _FDEV_SETUP_RW);
 | 
						|
FILE *ftdi_stream = &_ftdi_stream;
 | 
						|
 | 
						|
/* Init */
 | 
						|
static bool ftdi_blocking_in = false;
 | 
						|
static bool ftdi_blocking_out = false;
 | 
						|
void ftdi_init(int flags)
 | 
						|
{
 | 
						|
	/* Reset line state */
 | 
						|
	memset(&ftdi_status, 0, sizeof(struct ftdi_status));
 | 
						|
	ftdi_status.reserved_1 = 1;
 | 
						|
	ftdi_status.cts = 1;
 | 
						|
	ftdi_status.dsr = 1;
 | 
						|
	ftdi_status.ri = 0;
 | 
						|
	ftdi_status.cd = 1;
 | 
						|
 | 
						|
	memset(&ctrl_status, 0, sizeof(struct ctrl_status));
 | 
						|
	ctrl_status.dtr = 0;
 | 
						|
	ctrl_status.rts = 0;
 | 
						|
 | 
						|
	if (flags & FTDI_STDIO) {
 | 
						|
		stdout = ftdi_stream;
 | 
						|
		stdin = ftdi_stream;
 | 
						|
	}
 | 
						|
 | 
						|
	if (flags & FTDI_BLOCKING_IN)
 | 
						|
		ftdi_blocking_in = true;
 | 
						|
	if (flags & FTDI_BLOCKING_OUT)
 | 
						|
		ftdi_blocking_out = true;
 | 
						|
}
 | 
						|
 | 
						|
static inline void ftdi_control_request(void)
 | 
						|
{
 | 
						|
	uint16_t x;
 | 
						|
 | 
						|
	if (!Endpoint_IsSETUPReceived())
 | 
						|
		return;
 | 
						|
 | 
						|
	if ((USB_ControlRequest.bmRequestType & CONTROL_REQTYPE_TYPE)
 | 
						|
	    != REQTYPE_VENDOR)
 | 
						|
		return;
 | 
						|
 | 
						|
	switch (USB_ControlRequest.bRequest) {
 | 
						|
 | 
						|
	case SIO_RESET_REQUEST:
 | 
						|
		/* Reset */
 | 
						|
		switch (USB_ControlRequest.wValue) {
 | 
						|
		case SIO_RESET_SIO:
 | 
						|
			ftdi_init(0);
 | 
						|
			break;
 | 
						|
		case SIO_RESET_PURGE_RX:
 | 
						|
			while (_queue_can_get(&ftdi_rxqueue))
 | 
						|
				(void)_queue_get(&ftdi_rxqueue);
 | 
						|
			break;
 | 
						|
		case SIO_RESET_PURGE_TX:
 | 
						|
			while (_queue_can_get(&ftdi_txqueue))
 | 
						|
				(void)_queue_get(&ftdi_txqueue);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		Endpoint_ClearSETUP();
 | 
						|
		Endpoint_ClearStatusStage();
 | 
						|
		return;
 | 
						|
 | 
						|
	case SIO_READ_EEPROM_REQUEST:
 | 
						|
		/* Return fake EEPROM data */
 | 
						|
		x = (USB_ControlRequest.wIndex & 0xff) * 2;
 | 
						|
		if (x + USB_ControlRequest.wLength > sizeof(ftdi_eeprom))
 | 
						|
			return;
 | 
						|
 | 
						|
		Endpoint_ClearSETUP();
 | 
						|
		Endpoint_Write_Control_Stream_LE((uint8_t *)&ftdi_eeprom[x],
 | 
						|
						 USB_ControlRequest.wLength);
 | 
						|
		Endpoint_ClearStatusStage();
 | 
						|
		return;
 | 
						|
 | 
						|
	case SIO_WRITE_EEPROM_REQUEST:
 | 
						|
	case SIO_ERASE_EEPROM_REQUEST:
 | 
						|
	case SIO_SET_BAUDRATE_REQUEST:
 | 
						|
	case SIO_SET_DATA_REQUEST:
 | 
						|
	case SIO_SET_FLOW_CTRL_REQUEST:
 | 
						|
	case SIO_SET_EVENT_CHAR_REQUEST:
 | 
						|
	case SIO_SET_ERROR_CHAR_REQUEST:
 | 
						|
	case SIO_SET_LATENCY_TIMER_REQUEST:
 | 
						|
	case SIO_SET_BITMODE_REQUEST:
 | 
						|
		/* Ignore these */
 | 
						|
		Endpoint_ClearSETUP();
 | 
						|
		Endpoint_ClearStatusStage();
 | 
						|
		return;
 | 
						|
 | 
						|
	case SIO_SET_MODEM_CTRL_REQUEST:
 | 
						|
		if (USB_ControlRequest.wValue & SIO_SET_DTR_HIGH)
 | 
						|
			ctrl_status.dtr = 1;
 | 
						|
		else if (USB_ControlRequest.wValue & SIO_SET_DTR_LOW)
 | 
						|
			ctrl_status.dtr = 0;
 | 
						|
		if (USB_ControlRequest.wValue & SIO_SET_RTS_HIGH)
 | 
						|
			ctrl_status.rts = 1;
 | 
						|
		else if (USB_ControlRequest.wValue & SIO_SET_RTS_LOW)
 | 
						|
			ctrl_status.rts = 0;
 | 
						|
		Endpoint_ClearSETUP();
 | 
						|
		Endpoint_ClearStatusStage();
 | 
						|
		return;
 | 
						|
 | 
						|
	case SIO_GET_LATENCY_TIMER_REQUEST:
 | 
						|
		/* Return dummy value */
 | 
						|
		if (USB_ControlRequest.wLength != 1)
 | 
						|
			return;
 | 
						|
		Endpoint_ClearSETUP();
 | 
						|
		x = 100;
 | 
						|
		Endpoint_Write_Control_Stream_LE((uint8_t *)&x, 1);
 | 
						|
		Endpoint_ClearStatusStage();
 | 
						|
		return;
 | 
						|
 | 
						|
	case SIO_READ_PINS_REQUEST:
 | 
						|
		/* Return dummy value */
 | 
						|
		if (USB_ControlRequest.wLength != 1)
 | 
						|
			return;
 | 
						|
		Endpoint_ClearSETUP();
 | 
						|
		x = 0;
 | 
						|
		Endpoint_Write_Control_Stream_LE((uint8_t *)&x, 1);
 | 
						|
		Endpoint_ClearStatusStage();
 | 
						|
		return;
 | 
						|
 | 
						|
	case SIO_POLL_MODEM_STATUS_REQUEST:
 | 
						|
		/* Send status */
 | 
						|
		if (USB_ControlRequest.wLength != 2)
 | 
						|
			return;
 | 
						|
		Endpoint_ClearSETUP();
 | 
						|
		Endpoint_Write_Control_Stream_LE((uint8_t *)&ftdi_status, 2);
 | 
						|
		Endpoint_ClearStatusStage();
 | 
						|
		return;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* Device descriptor */
 | 
						|
static const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
 | 
						|
{
 | 
						|
	.Header = {
 | 
						|
		.Size = sizeof(USB_Descriptor_Device_t),
 | 
						|
		.Type = DTYPE_Device
 | 
						|
	},
 | 
						|
 | 
						|
	.USBSpecification = VERSION_BCD(02.00),
 | 
						|
	.Class	    = 0x00,
 | 
						|
	.SubClass	 = 0x00,
 | 
						|
	.Protocol	 = 0x00,
 | 
						|
 | 
						|
	.Endpoint0Size    = FIXED_CONTROL_ENDPOINT_SIZE,
 | 
						|
 | 
						|
	.VendorID	 = 0x0403,  /* FTDI */
 | 
						|
	.ProductID	= 0x6001,  /* FT232 */
 | 
						|
	.ReleaseNumber	  = 0x0600,  /* FT232 */
 | 
						|
 | 
						|
	.ManufacturerStrIndex   = 0x01,
 | 
						|
	.ProductStrIndex	= 0x02,
 | 
						|
	.SerialNumStrIndex      = USE_INTERNAL_SERIAL,
 | 
						|
 | 
						|
	.NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS
 | 
						|
};
 | 
						|
 | 
						|
/* Configuration descriptor */
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
	USB_Descriptor_Configuration_Header_t Config;
 | 
						|
	USB_Descriptor_Interface_t	      FTDI_Interface;
 | 
						|
	USB_Descriptor_Endpoint_t             FTDI_DataInEndpoint;
 | 
						|
	USB_Descriptor_Endpoint_t             FTDI_DataOutEndpoint;
 | 
						|
} USB_Descriptor_Config_t;
 | 
						|
 | 
						|
static const USB_Descriptor_Config_t PROGMEM ConfigurationDescriptor =
 | 
						|
{
 | 
						|
	.Config = {
 | 
						|
		.Header = {
 | 
						|
			.Size = sizeof(USB_Descriptor_Configuration_Header_t),
 | 
						|
			.Type = DTYPE_Configuration
 | 
						|
		},
 | 
						|
 | 
						|
		.TotalConfigurationSize = sizeof(USB_Descriptor_Config_t),
 | 
						|
		.TotalInterfaces	= 1,
 | 
						|
 | 
						|
		.ConfigurationNumber    = 1,
 | 
						|
		.ConfigurationStrIndex  = 0,
 | 
						|
		.ConfigAttributes       = USB_CONFIG_ATTR_RESERVED,
 | 
						|
		.MaxPowerConsumption    = USB_CONFIG_POWER_MA(100)
 | 
						|
	},
 | 
						|
 | 
						|
	.FTDI_Interface = {
 | 
						|
		.Header	= {
 | 
						|
			.Size = sizeof(USB_Descriptor_Interface_t),
 | 
						|
			.Type = DTYPE_Interface
 | 
						|
		},
 | 
						|
		.InterfaceNumber  = 0,
 | 
						|
		.AlternateSetting = 0,
 | 
						|
		.TotalEndpoints	  = 2,
 | 
						|
		.Class    = 0xff,
 | 
						|
		.SubClass = 0xff,
 | 
						|
		.Protocol = 0xff,
 | 
						|
		.InterfaceStrIndex = 0x02,  /* Reuse product string */
 | 
						|
	},
 | 
						|
 | 
						|
	.FTDI_DataInEndpoint = {
 | 
						|
		.Header = {
 | 
						|
			.Size = sizeof(USB_Descriptor_Endpoint_t),
 | 
						|
			.Type = DTYPE_Endpoint
 | 
						|
		},
 | 
						|
		.EndpointAddress = FTDI_TX_EPADDR,
 | 
						|
		.Attributes	 = EP_TYPE_BULK,
 | 
						|
		.EndpointSize	 = FTDI_TXRX_EPSIZE,
 | 
						|
		.PollingIntervalMS = 0x00,
 | 
						|
	},
 | 
						|
 | 
						|
	.FTDI_DataOutEndpoint = {
 | 
						|
		.Header = {
 | 
						|
			.Size = sizeof(USB_Descriptor_Endpoint_t),
 | 
						|
			.Type = DTYPE_Endpoint
 | 
						|
		},
 | 
						|
		.EndpointAddress = FTDI_RX_EPADDR,
 | 
						|
		.Attributes	 = EP_TYPE_BULK,
 | 
						|
		.EndpointSize	 = FTDI_TXRX_EPSIZE,
 | 
						|
		.PollingIntervalMS = 0x00,
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
/* String descriptors */
 | 
						|
#define STRING_DESC(s) { { (sizeof(s)-2)+1+1, DTYPE_String }, s }
 | 
						|
static const USB_Descriptor_String_t PROGMEM string0 = {
 | 
						|
	{ USB_STRING_LEN(1), DTYPE_String }, { LANGUAGE_ID_ENG }
 | 
						|
};
 | 
						|
static const USB_Descriptor_String_t PROGMEM string1 = STRING_DESC(L"LUFA");
 | 
						|
static const USB_Descriptor_String_t PROGMEM string2 = STRING_DESC(L"FT232");
 | 
						|
static const USB_Descriptor_String_t *strings[] = {
 | 
						|
	&string0,
 | 
						|
	&string1,
 | 
						|
	&string2
 | 
						|
};
 | 
						|
 | 
						|
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
 | 
						|
				    const uint8_t wIndex,
 | 
						|
				    const void** const DescriptorAddress)
 | 
						|
{
 | 
						|
	const uint8_t DescriptorType   = (wValue >> 8);
 | 
						|
	const uint8_t DescriptorNumber = (wValue & 0xFF);
 | 
						|
 | 
						|
	const void* Address = NULL;
 | 
						|
	uint16_t Size = 0;
 | 
						|
 | 
						|
	switch (DescriptorType)
 | 
						|
	{
 | 
						|
		case DTYPE_Device:
 | 
						|
			Address = &DeviceDescriptor;
 | 
						|
			Size    = sizeof(USB_Descriptor_Device_t);
 | 
						|
			break;
 | 
						|
		case DTYPE_Configuration:
 | 
						|
			Address = &ConfigurationDescriptor;
 | 
						|
			Size    = sizeof(USB_Descriptor_Config_t);
 | 
						|
			break;
 | 
						|
		case DTYPE_String:
 | 
						|
			if (DescriptorNumber >= (sizeof(strings) /
 | 
						|
						 sizeof(strings[0])))
 | 
						|
				break;
 | 
						|
			Address = strings[DescriptorNumber];
 | 
						|
			Size = pgm_read_byte(
 | 
						|
				&strings[DescriptorNumber]->Header.Size);
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	*DescriptorAddress = Address;
 | 
						|
	return Size;
 | 
						|
}
 | 
						|
 | 
						|
/* LUFA events and USB interrupt handlers */
 | 
						|
 | 
						|
void EVENT_USB_Device_ControlRequest(void)
 | 
						|
{
 | 
						|
	ftdi_control_request();
 | 
						|
}
 | 
						|
 | 
						|
void EVENT_USB_Device_Reset(void)
 | 
						|
{
 | 
						|
	Endpoint_SelectEndpoint(ENDPOINT_CONTROLEP);
 | 
						|
	USB_INT_Enable(USB_INT_RXSTPI);
 | 
						|
}
 | 
						|
 | 
						|
/* Configure endpoint with the best available number of banks. */
 | 
						|
static inline bool ftdi_config_ep(uint8_t address, uint8_t type, uint16_t size)
 | 
						|
{
 | 
						|
	/* Try configuring the bulk endpoint with 2 banks. */
 | 
						|
	if (Endpoint_ConfigureEndpoint(address, type, size, 2))
 | 
						|
		return true;
 | 
						|
	/* On smaller parts, this will fail, so use 1 bank instead. */
 | 
						|
	return Endpoint_ConfigureEndpoint(address, type, size, 1);
 | 
						|
}
 | 
						|
 | 
						|
void EVENT_USB_Device_ConfigurationChanged(void)
 | 
						|
{
 | 
						|
	uint8_t PrevSelectedEndpoint = Endpoint_GetCurrentEndpoint();
 | 
						|
 | 
						|
	/* Configure endpoints */
 | 
						|
 | 
						|
	/* IN */
 | 
						|
	ftdi_config_ep(FTDI_TX_EPADDR, EP_TYPE_BULK, FTDI_TXRX_EPSIZE);
 | 
						|
	Endpoint_SelectEndpoint(FTDI_TX_EPADDR);
 | 
						|
	UEIENX = (1 << TXINE);
 | 
						|
 | 
						|
	/* OUT */
 | 
						|
	ftdi_config_ep(FTDI_RX_EPADDR, EP_TYPE_BULK, FTDI_TXRX_EPSIZE);
 | 
						|
	Endpoint_SelectEndpoint(FTDI_RX_EPADDR);
 | 
						|
	UEIENX = (1 << RXOUTE);
 | 
						|
 | 
						|
	/* Done */
 | 
						|
	Endpoint_SelectEndpoint(PrevSelectedEndpoint);
 | 
						|
}
 | 
						|
 | 
						|
static inline void ftdi_bulk_in(void)
 | 
						|
{
 | 
						|
	int len, i;
 | 
						|
 | 
						|
	/* Data format: ftdi_status structure (2 bytes) followed by the
 | 
						|
	   serial data. */
 | 
						|
	len = _queue_get_len(&ftdi_txqueue);
 | 
						|
 | 
						|
	if (len >= FTDI_TXRX_EPSIZE - 2) {
 | 
						|
		/* more to send */
 | 
						|
		ftdi_status.thre = 0;
 | 
						|
		ftdi_status.temt = 0;
 | 
						|
		len = FTDI_TXRX_EPSIZE - 2;
 | 
						|
	} else {
 | 
						|
		/* transmitter empty */
 | 
						|
		ftdi_status.thre = 0;
 | 
						|
		ftdi_status.temt = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	Endpoint_Write_8(((uint8_t *)&ftdi_status)[0]);
 | 
						|
	Endpoint_Write_8(((uint8_t *)&ftdi_status)[1]);
 | 
						|
 | 
						|
	for (i = 0; i < len; i++)
 | 
						|
		Endpoint_Write_8(_queue_get(&ftdi_txqueue));
 | 
						|
 | 
						|
	/* If that packet was empty, disable the IN interrupt.  We'll
 | 
						|
	   reenable it when we have more data to send. */
 | 
						|
	if (len == 0) {
 | 
						|
		UEIENX &= ~(1 << TXINE);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Send it */
 | 
						|
	Endpoint_ClearIN();
 | 
						|
}
 | 
						|
 | 
						|
static inline void ftdi_bulk_out(void)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	int len;
 | 
						|
 | 
						|
	len = Endpoint_BytesInEndpoint();
 | 
						|
 | 
						|
	if (_queue_get_free(&ftdi_rxqueue) < len) {
 | 
						|
		/* We can't deal with this right now.  Disable OUT
 | 
						|
		   interrupt; we'll reenable after reading some bytes */
 | 
						|
		UEIENX &= ~(1 << RXOUTE);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	for (i = 0; i < len; i++) {
 | 
						|
		/* This will discard incoming bytes if queue is full */
 | 
						|
		_queue_put(&ftdi_rxqueue, Endpoint_Read_8());
 | 
						|
	}
 | 
						|
	Endpoint_ClearOUT();
 | 
						|
}
 | 
						|
 | 
						|
ISR(USB_COM_vect, ISR_BLOCK)
 | 
						|
{
 | 
						|
	uint8_t PrevSelectedEndpoint = Endpoint_GetCurrentEndpoint();
 | 
						|
 | 
						|
	if (UEINT & (1 << ENDPOINT_CONTROLEP)) {
 | 
						|
		Endpoint_SelectEndpoint(ENDPOINT_CONTROLEP);
 | 
						|
 | 
						|
		if (Endpoint_IsSETUPReceived())
 | 
						|
			USB_Device_ProcessControlRequest();
 | 
						|
	}
 | 
						|
 | 
						|
	if (UEINT & (1 << (FTDI_TX_EPADDR & ENDPOINT_EPNUM_MASK))) {
 | 
						|
		Endpoint_SelectEndpoint(FTDI_TX_EPADDR);
 | 
						|
 | 
						|
		if (Endpoint_IsINReady()) {
 | 
						|
			/* Handle IN (device-to-host) */
 | 
						|
			ftdi_bulk_in();
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (UEINT & (1 << (FTDI_RX_EPADDR & ENDPOINT_EPNUM_MASK))) {
 | 
						|
		Endpoint_SelectEndpoint(FTDI_RX_EPADDR);
 | 
						|
 | 
						|
		if (Endpoint_IsOUTReceived()) {
 | 
						|
			/* Handle OUT (host-to-device) */
 | 
						|
			ftdi_bulk_out();
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	Endpoint_SelectEndpoint(PrevSelectedEndpoint);
 | 
						|
}
 | 
						|
 | 
						|
/* Get / put routines */
 | 
						|
 | 
						|
void ftdi_putchar(uint8_t c)
 | 
						|
{
 | 
						|
	/* Block until we can enqueue it, or drop if nonblocking */
 | 
						|
	while (queue_put(&ftdi_txqueue, c) == -1)
 | 
						|
		if (ftdi_blocking_out == false)
 | 
						|
			return;
 | 
						|
 | 
						|
	/* Enable the IN endpoint interrupt, since we have data to send. */
 | 
						|
	if (USB_DeviceState == DEVICE_STATE_Configured) {
 | 
						|
		Endpoint_SelectEndpoint(FTDI_TX_EPADDR);
 | 
						|
		UEIENX |= (1 << TXINE);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int ftdi_getchar(void)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	/* Block until byte is ready, or return 0 if nonblocking */
 | 
						|
	while ((ret = queue_get(&ftdi_rxqueue)) < 0)
 | 
						|
		if (ftdi_blocking_in == false)
 | 
						|
			return -1;
 | 
						|
 | 
						|
	/* Reenable the OUT endpoint interrupt, since we read some data */
 | 
						|
	if (USB_DeviceState == DEVICE_STATE_Configured) {
 | 
						|
		Endpoint_SelectEndpoint(FTDI_RX_EPADDR);
 | 
						|
		UEIENX |= (1 << RXOUTE);
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
bool ftdi_can_get(void)
 | 
						|
{
 | 
						|
	return queue_can_get(&ftdi_rxqueue);
 | 
						|
}
 | 
						|
 | 
						|
bool ftdi_get_dtr(void)
 | 
						|
{
 | 
						|
	return ctrl_status.dtr;
 | 
						|
}
 |