view usb.c @ 26:350e8655cbb7

- Remove some #if'd out code. - Use STRING_DESCRIPTOR types instead of rolling one for each type of string. - Fix comment typo.
author darius
date Tue, 13 Dec 2005 10:53:48 +1030
parents 845934a4e7fe
children 4e417d84365e
line wrap: on
line source

#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <util/delay.h>

#include "usb.h"

#define EP0_SIZE 16

#define D12_MODE_0	0x14	/* Endpoint config = 0, SoftConnect = 1, IRQ Mode = 1,
				 * Clock running = 0, No Lazy Clock = 0 
				 */
#define D12_MODE_1	0x02	/* SOF mode = 0, Set-to-one = 0, Clock div = 2 (16Mhz) */
/* Debugging stuff */
void		uart_putsP(const char *addr);
void		uart_puts(const char *addr);
int		uart_putc(char c);
void		uart_puts_dec(uint8_t a, uint8_t l);
void		uart_puts_hex(uint8_t a);

/* USB administrivia */
uint8_t deviceaddress;
uint8_t deviceconfigured;

/* End point buffer points and such */
const uint8_t *pSendBuffer;
uint8_t BytesToSend;
static uint8_t send_Bytes;
static uint8_t exact_Bytes;

static const uint8_t *pSendBuffer2;
static uint8_t BytesToSend2;

/* Data packet buffer */
static uint8_t send_packet2;
static uint8_t packet2[270];
static uint16_t packetlen2;

/* XXX: Not actually used */
void (*bootloader)(void) = (void*)0xe000;

/* Device/endpoint/etc descriptions */
const USB_DEVICE_DESCRIPTOR DeviceDescriptor = {
    sizeof(USB_DEVICE_DESCRIPTOR),	/* bLength */
    TYPE_DEVICE_DESCRIPTOR,		/* bDescriptorType */
    0x0110,				/* bcdUSB USB Version 1.1 */
    0,					/* bDeviceClass */
    0,					/* bDeviceSubclass */
    0,					/* bDeviceProtocol */
    EP0_SIZE,				/* bMaxPacketSize in Bytes */
    0x4753,				/* idVendor (inofficial GS) */
    0x0001,				/* idProduct */
    0x0100,				/* bcdDevice */
    1,					/* iManufacturer String Index */
    2,					/* iProduct String Index */
    3,					/* iSerialNumber String Index */
    1					/* bNumberConfigurations */
};

const USB_CONFIG_DATA ConfigurationDescriptor = {
    {	/* configuration descriptor */
	sizeof(USB_CONFIGURATION_DESCRIPTOR),	/* bLength */
	TYPE_CONFIGURATION_DESCRIPTOR,		/* bDescriptorType */
	sizeof(USB_CONFIG_DATA),		/* wTotalLength */
	2,					/* bNumInterfaces */
	1,					/* bConfigurationValue */
	0,					/* iConfiguration String Index */
	0x80,					/* bmAttributes Bus Powered, No Remote Wakeup */
	100/2					/* bMaxPower in mA */
    },
    {	/* interface descriptor */
	sizeof(USB_INTERFACE_DESCRIPTOR),	/* bLength */
	TYPE_INTERFACE_DESCRIPTOR,		/* bDescriptorType */
	0,					/* bInterface Number */
	0,					/* bAlternateSetting */
	2,					/* bNumEndpoints */
	0xFF,					/* bInterfaceClass (Vendor specific) */
	0x02,					/* bInterfaceSubClass */
	0x00,					/* bInterfaceProtocol */
	0					/* iInterface String Index */
    },
    {	/* endpoint descriptor */
	sizeof(USB_ENDPOINT_DESCRIPTOR),	/* bLength */
	TYPE_ENDPOINT_DESCRIPTOR,		/* bDescriptorType */
	0x02,					/* bEndpoint Address EP2 OUT */
	0x02,					/* bmAttributes - Bulk */
	0x0040,					/* wMaxPacketSize */
	0x00					/* bInterval */
    },
    {	/* endpoint descriptor */
	sizeof(USB_ENDPOINT_DESCRIPTOR),	/* bLength */
	TYPE_ENDPOINT_DESCRIPTOR,		/* bDescriptorType */
	0x82,					/* bEndpoint Address EP2 IN */
	0x02,					/* bmAttributes - Bulk */
	0x0040,					/* wMaxPacketSize */
	0x00					/* bInterval */
    },
    {	/* interface descriptor */
	sizeof(USB_INTERFACE_DESCRIPTOR),	/* bLength */
	TYPE_INTERFACE_DESCRIPTOR,		/* bDescriptorType */
	1,					/* bInterface Number */
	0,					/* bAlternateSetting */
	2,					/* bNumEndpoints */
	0xFF,					/* bInterfaceClass (Vendor specific) */
	0x02,					/* bInterfaceSubClass */
	0x00,					/* bInterfaceProtocol */
	0					/* iInterface String Index */
    },
    {	/* endpoint descriptor */
	sizeof(USB_ENDPOINT_DESCRIPTOR),	/* bLength */
	TYPE_ENDPOINT_DESCRIPTOR,		/* bDescriptorType */
	0x01,					/* bEndpoint Address EP1 OUT */
	0x02,					/* bmAttributes - Bulk */
	0x0010,					/* wMaxPacketSize */
	0x00					/* bInterval */
    },
    {	/* endpoint descriptor */
	sizeof(USB_ENDPOINT_DESCRIPTOR),	/* bLength */
	TYPE_ENDPOINT_DESCRIPTOR,		/* bDescriptorType */
	0x81,					/* bEndpoint Address EP1 IN */
	0x02,					/* bmAttributes - Bulk */
	0x0010,					/* wMaxPacketSize */
	0x00					/* bInterval */
    }
};
const LANGID_DESCRIPTOR LANGID_Descriptor = {	/* LANGID String Descriptor
						 * Zero */
	sizeof(LANGID_DESCRIPTOR),		/* bLength - must match string below */
	TYPE_STRING_DESCRIPTOR,			/* bDescriptorType */
	0x0409					/* LANGID US English */
};

STRING_DESCRIPTOR Manufacturer_Descriptor = {
    sizeof(STRING_DESCRIPTOR) + 32,			/* bLength */
    TYPE_STRING_DESCRIPTOR,				/* bDescriptorType */
    "G\0e\0n\0e\0s\0i\0s\0 \0S\0o\0f\0t\0w\0a\0r\0e\0" 	/* ManufacturerString in
							 * UNICODE */
};

STRING_DESCRIPTOR Product_Descriptor = {
    sizeof(STRING_DESCRIPTOR) + 48,		/* bLength */
    TYPE_STRING_DESCRIPTOR,			/* bDescriptorType */
    /* ProductString in
     * UNICODE */
    "R\0S\0""-\0""4\0""8\0""5\0"" \0M\0u\0l\0t\0i\0d\0r\0o\0p\0 \0A\0d\0a\0p\0t\0e\0r\0"
    /* XXX: dunno why I need the double quote magic above.. */
};   

STRING_DESCRIPTOR Serial_Descriptor;
STRING_DESCRIPTOR EE_Serial_Descriptor  __attribute__ ((section (".eeprom"))) = { /* SerialString 3 */
    sizeof(STRING_DESCRIPTOR) + 20,	/* bLength - must match string below */
    TYPE_STRING_DESCRIPTOR,		/* bDescriptorType */
    "1\02\03\0"
};

/*
 * The PDIUSBD12 is wired up like so
 *
 *  PDI		     AVR	
 * ======================
 * D7:0     <=>     PA7:0	   
 * INT_N     =>     PB0
 * RD_N     <=      PB1
 * WR_N     <=      PB2
 * A0       <=      PB3 (0 = data, 1 = cmd)
 * SUSPEND  <=>     PB4
 */
uint8_t
d12_get_data(void) {
    uint8_t data;

    PORTB &= ~_BV(PB3);	/* Data phase */
    DDRA = 0x00;	/* Set to input */
    PORTB &= ~_BV(PB1);	/* Pull RD_N low */
    PORTB &= ~_BV(PB1);	/* Delay 40ns */
    PORTB &= ~_BV(PB1);
    PORTB &= ~_BV(PB1);
    data = PINA;	/* Read the data */
    PORTB |= _BV(PB1);	/* Pull RD_N high */
    return(data);
}

void
set_d12_data(uint8_t data) {
    PORTB &= ~_BV(PB3);	/* Data phase */
    DDRA = 0xff;	/* Set to output */
    PORTA = data;	/* Put the data on the bus */
    PORTB &= ~_BV(PB2);	/* Pull WR_N low */
    PORTB &= ~_BV(PB2);	/* Delay 40ns */
    PORTB &= ~_BV(PB2);
    PORTB &= ~_BV(PB2);
    PORTB |= _BV(PB2);	/* Pull WR_N high */
    PORTB |= _BV(PB2);	/* Delay 40 ns */
    PORTB |= _BV(PB2);	 
    PORTB |= _BV(PB2);	 
    DDRA = 0x00;	/* Back to input */
}

void
set_d12_cmd(uint8_t cmd) {
    PORTB |= _BV(PB3);	/* Command phase */
    DDRA = 0xff;	/* Set to output */
    PORTA = cmd;	/* Put the data on the bus */
    PORTB &= ~_BV(PB2);	/* Pull WR_N low */
    PORTB &= ~_BV(PB2);	/* Delay 40ns */
    PORTB &= ~_BV(PB2);
    PORTB &= ~_BV(PB2);
    PORTB |= _BV(PB2);	/* Pull WR_N high */
    PORTB |= _BV(PB2);	/* Delay 40ns */
    PORTB |= _BV(PB2);
    PORTB |= _BV(PB2);
    DDRA = 0x00;	/* Back to input */
}

void 
d12_write_cmd(uint8_t command, const uint8_t *buffer, uint8_t count) {
    uint8_t i;

    set_d12_cmd(command);
    if (count) {
	for (i = 0; i < count; i++) {
	    set_d12_data(buffer[i]);
	}
    }
}

void 
d12_read_cmd(uint8_t command, uint8_t *buffer, uint8_t count) {
    uint8_t i;

    set_d12_cmd(command);
    if (count) {
	for (i = 0; i < count; i++) {
	    buffer[i] = d12_get_data();
	}
    }
}

/* Set up the PDIUSBD12 */
void
usb_init(void) {
    uint8_t	buffer[2];
    
    /* pull EE_Serial_Descriptor into RAM */
    eeprom_read_block(&Serial_Descriptor, &EE_Serial_Descriptor, EE_Serial_Descriptor.bLength);

    /* Set Address to zero (default) and enable function */
    buffer[0] = 0x80;
    d12_write_cmd(D12_SET_ADDRESS_ENABLE, buffer, 1);

    /* Enable function generic endpoints */
    buffer[0] = 0x01;
    d12_write_cmd(D12_SET_ENDPOINT_ENABLE, buffer, 1);
 
    /* Configure the device (soft connect off) */
    buffer[0] = D12_MODE_0 & 0xef;
    buffer[1] = D12_MODE_1;
    d12_write_cmd(D12_SET_MODE, buffer, 2);
    
    /* Delay long enough for the PC to notice the disconnect */
    _delay_us(1000);
    
    buffer[0] |= 0x10; /* Soft connect on */
    d12_write_cmd(D12_SET_MODE, buffer, 2);

    /* Endpoint 2 IN/OUT IRQ enable */
    buffer[0] = 0xc0;
    d12_write_cmd(D12_SET_DMA, buffer, 1);
}

/* Process an interrupt */
void
usb_intr(void) {
    uint8_t	irq[2];
    uint8_t	buffer[8];
    
    d12_read_cmd(D12_READ_INTERRUPT_REGISTER, (uint8_t *)&irq, 2);

    /* Why do we get interrupts when this is 0? */
    if (irq[0] == 0)
	return;
    
    if (irq[0] & D12_INT_BUS_RESET) {
	uart_putsP(PSTR("Bus reset\n\r"));
	usb_init();
	return;
    }
    
    if (irq[0] & D12_INT_SUSPEND) {
	uart_putsP(PSTR("Suspend change\n\r"));
    }
	
    if (irq[0] & D12_INT_EP0_IN) {
	d12_read_cmd(D12_READ_LAST_TRANSACTION + D12_ENDPOINT_EP0_IN, buffer, 1);

	/* Handle any outgoing data for EP0 */
	d12_write_buffer_ep0();
    }
    
    /* Handle configuration and misc stuff */
    if (irq[0] & D12_INT_EP0_OUT) {
	d12_ep0_irq();
    }
    
    /* EPx_IN is when the host has had a packet of data and is expecting more */
    if (irq[0] & D12_INT_EP1_IN) {
	/* XXX: Not yet implemented */
    }
    
    /* EPx_OUT is when we have gotten a packet from the host */
    if (irq[0] & D12_INT_EP1_OUT) {
	/* XXX: Not yet implemented */
    }

    if (irq[0] & D12_INT_EP2_IN) {
	d12_read_cmd(D12_READ_LAST_TRANSACTION + D12_ENDPOINT_EP2_IN, buffer, 1);
	d12_send_data_ep2();
    }
    
    if (irq[0] & D12_INT_EP2_OUT) {
	d12_read_cmd(D12_READ_LAST_TRANSACTION + D12_ENDPOINT_EP2_OUT, buffer, 1);
	d12_receive_data_ep2();
    }
}

void
usb_gendata(void) {
    packet2[0] = 'a';
    packet2[1] = 'b';
    packet2[2] = 'c';
    packet2[3] = '\n';
    packet2[4] = '\r';
    BytesToSend2 = 5;
    pSendBuffer2 = (uint8_t *)&packet2[0];
    send_packet2 = 1;

    /* Kick off the data transfer */
    d12_send_data_ep2();
}

void
d12_ep0_irq(void) {
    uint8_t		buffer[2];
    USB_SETUP_REQUEST	setuppkt;

    d12_read_cmd(D12_READ_LAST_TRANSACTION + D12_ENDPOINT_EP0_OUT, buffer, 1);

    if (buffer[0] & D12_LAST_TRAN_SETUP) {
	/* Read the setup packet */
	d12_read_endpt(D12_ENDPOINT_EP0_OUT, (uint8_t *)&setuppkt);
	
	/* Ack the packet to EP0_OUT */
	d12_write_cmd(D12_ENDPOINT_EP0_OUT, NULL, 0);
	d12_write_cmd(D12_ACK_SETUP, NULL, 0);
	d12_write_cmd(D12_CLEAR_BUFFER, NULL, 0);
	
	/* Ack the packet to EP0_IN */
	d12_write_cmd(D12_ENDPOINT_EP0_IN, NULL, 0);
	d12_write_cmd(D12_ACK_SETUP, NULL, 0);
	
	/* It's a new xfer, so forget about any old one */
	send_Bytes = 0;
	
	/* Parse request type */
	switch (setuppkt.bmRequestType & 0x7f) {
	    case STANDARD_DEVICE_REQUEST:
		switch (setuppkt.bRequest) {
		    case GET_STATUS:
			/* Get status request should return remote
			 * wakeup and self powered status
			 */
			buffer[0] = 0x01;
			buffer[1] = 0x00;
			d12_write_endpt(D12_ENDPOINT_EP0_IN, buffer, 2);
			break;
		    case CLEAR_FEATURE:
		    case SET_FEATURE:
			/* We don't support DEVICE_REMOTE_WAKEUP or
			 * TEST_MODE
			 */
			d12_stallctrlendpt();
			break;
			
		    case SET_ADDRESS:
			deviceaddress = setuppkt.wValue | 0x80;
			d12_write_cmd(D12_SET_ADDRESS_ENABLE, &deviceaddress, 1);
			d12_write_endpt(D12_ENDPOINT_EP0_IN, NULL, 0);
			break;

		    case GET_DESCRIPTOR:	
			d12_getdescriptor(&setuppkt);
			break;
			
		    case GET_CONFIGURATION:
			d12_write_endpt(D12_ENDPOINT_EP0_IN, &deviceconfigured, 1);
			break;
			
		    case SET_CONFIGURATION:
			deviceconfigured = setuppkt.wValue & 0xff;
			d12_write_endpt(D12_ENDPOINT_EP0_IN, NULL, 0);
			break;
			

		    case SET_DESCRIPTOR:
		    default:
			/* Unsupported, stall */
			d12_stallctrlendpt();
			break;
		}
		break;
		
	    case STANDARD_INTERFACE_REQUEST:
		switch (setuppkt.bRequest) {
		    case GET_STATUS:
			/* Should return 0, 0 (reserved) */
			buffer[0] = 0x00;
			buffer[1] = 0x00;
			d12_write_endpt(D12_ENDPOINT_EP0_IN, buffer, 2);
			break;
			
		    case SET_INTERFACE:
			if (setuppkt.wIndex == 0 && setuppkt.wValue == 0)
			    d12_write_endpt(D12_ENDPOINT_EP0_IN, NULL, 0);
			else
			    d12_stallctrlendpt();
			break;

		    case GET_INTERFACE:
			/* Can only handle interface 0 ... */
			if (setuppkt.wIndex == 0) {
			    buffer[0] = 0;
			    d12_write_endpt(D12_ENDPOINT_EP0_IN, buffer, 1);
			    break;
			}
			/* .. otherwise fall through to error */

		    case CLEAR_FEATURE:
		    case SET_FEATURE:
		    default:
			d12_stallctrlendpt();
			break;
		}
		break;
		
	    case STANDARD_ENDPOINT_REQUEST:
		switch (setuppkt.bRequest) {
		    case CLEAR_FEATURE:
		    case SET_FEATURE:
			/* Halt(stall) is required to be implemented on
			 * interrupt and bulk endpoints.
			 */
			if (setuppkt.wValue == ENDPOINT_HALT) {
			    if (setuppkt.bRequest == CLEAR_FEATURE)
				buffer[0] = 0x00;
			    else
				buffer[0] = 0x01;
			    switch (setuppkt.wIndex & 0xFF) {
				case 0x01:
				    d12_write_cmd(D12_SET_ENDPOINT_STATUS + \
						  D12_ENDPOINT_EP1_OUT, buffer, 1);
				    break;
				case 0x81:
				    d12_write_cmd(D12_SET_ENDPOINT_STATUS + \
						  D12_ENDPOINT_EP1_IN, buffer, 1);
				    break;
				case 0x02:
				    d12_write_cmd(D12_SET_ENDPOINT_STATUS + \
						  D12_ENDPOINT_EP2_OUT, buffer, 1);
				    break;
				case 0x82:
				    d12_write_cmd(D12_SET_ENDPOINT_STATUS + \
						  D12_ENDPOINT_EP2_IN, buffer, 1);
				    break;
				default:	/* Invalid Endpoint -
						 * RequestError */
				    d12_stallctrlendpt();
				    break;
			    }
			    d12_write_endpt(D12_ENDPOINT_EP0_IN, NULL, 0);
			} else {
			    /*
			     * No other Features for Endpoint -
			     * Request Error
			     */
			    d12_stallctrlendpt();
			}
			break;

		    case GET_STATUS:
			/*
			 * Get Status Request to Endpoint should
			 * return Halt Status in D0 for Interrupt and Bulk
			 */
			switch (setuppkt.wIndex & 0xFF) {
			    case 0x01:
				d12_read_cmd(D12_READ_ENDPOINT_STATUS + \
					       D12_ENDPOINT_EP1_OUT, buffer, 1);
				break;
			    case 0x81:
				d12_read_cmd(D12_READ_ENDPOINT_STATUS + \
					       D12_ENDPOINT_EP1_IN, buffer, 1);
				break;
			    case 0x02:
				d12_read_cmd(D12_READ_ENDPOINT_STATUS + \
					       D12_ENDPOINT_EP2_OUT, buffer, 1);
				break;
			    case 0x82:
				d12_read_cmd(D12_READ_ENDPOINT_STATUS + \
					       D12_ENDPOINT_EP2_IN, buffer, 1);
				break;
			    default:	/* Invalid Endpoint -
					 * RequestError */
				d12_stallctrlendpt();
				break;
			}
			if (buffer[0] & 0x08)
			    buffer[0] = 0x01;
			else
			    buffer[0] = 0x00;
			buffer[1] = 0x00;
			d12_write_endpt(D12_ENDPOINT_EP0_IN, buffer, 2);
			break;

		    default:
			/* Unsupported - Request Error - Stall */
			d12_stallctrlendpt();
			break;
		}
		break;
	    case VENDOR_DEVICE_REQUEST:
	    case VENDOR_ENDPOINT_REQUEST:
		switch (setuppkt.bRequest) {
		    case VENDOR_RESET:
			d12_write_endpt(D12_ENDPOINT_EP0_IN, NULL, 0);
			_delay_us(1000);
			/* disconnect from USB */
			buffer[0] = D12_MODE_0 & 0xef;
			buffer[1] = D12_MODE_1;
			d12_write_cmd(D12_SET_MODE, buffer, 2);
			_delay_us(1000);
			cli();
			reset();
			/* NOT REACHED */
			break;
		    case VENDOR_UPDATE:
			d12_write_endpt(D12_ENDPOINT_EP0_IN, NULL, 0);
			_delay_us(1000);
			/* disconnect from USB */
			buffer[0] = D12_MODE_0 & 0xef;
			buffer[1] = D12_MODE_1;
			d12_write_cmd(D12_SET_MODE, buffer, 2);
			_delay_us(1000);
			cli();
			bootloader();
			/* NOT REACHED */
			break;
		    default:
			d12_stallctrlendpt();
			break;
		}
		break;
	    case VENDOR_INTERFACE_REQUEST:
		switch (setuppkt.bRequest) {
		    default:
			d12_stallctrlendpt();
			break;
		}
		break;
	    default:
		d12_stallctrlendpt();
		break;
	}
    } else {
	/* This is a Data Packet */
    }
		
}

/* Reset the micro */
static void
reset(void) {
    MCUCR = _BV(IVCE);
    MCUCR = 0;
    WDTCR = _BV(WDE);
    for (;;)
	;
}

void 
d12_getdescriptor(USB_SETUP_REQUEST *setuppkt) {
    switch ((setuppkt->wValue & 0xff00) >> 8) {
	case TYPE_DEVICE_DESCRIPTOR:
	    pSendBuffer = (const uint8_t *)&DeviceDescriptor;
	    BytesToSend = DeviceDescriptor.bLength;
	    send_Bytes = 1;
	    if (BytesToSend >= setuppkt->wLength) {
		BytesToSend = setuppkt->wLength;
		exact_Bytes = 1;
	    } else {
		exact_Bytes = 0;
	    }
	    d12_write_buffer_ep0();
	    break;

	case TYPE_CONFIGURATION_DESCRIPTOR:
	    pSendBuffer = (const uint8_t *)&ConfigurationDescriptor;
	    BytesToSend = sizeof(ConfigurationDescriptor);
	    send_Bytes = 1;
	    if (BytesToSend >= setuppkt->wLength) {
		BytesToSend = setuppkt->wLength;
		exact_Bytes = 1;
	    } else {
		exact_Bytes = 0;
	    }
	    d12_write_buffer_ep0();
	    break;

	case TYPE_STRING_DESCRIPTOR:
	    switch (setuppkt->wValue & 0xFF) {

		case 0:
		    pSendBuffer = (const uint8_t *)&LANGID_Descriptor;
		    BytesToSend = LANGID_Descriptor.bLength;
		    break;

		case 1:
		    pSendBuffer = (const uint8_t *)&Manufacturer_Descriptor;
		    BytesToSend = Manufacturer_Descriptor.bLength;
		    break;

		case 2:
		    pSendBuffer = (const uint8_t *)&Product_Descriptor;
		    BytesToSend = Product_Descriptor.bLength;
		    break;

		case 3:
		    pSendBuffer = (const uint8_t *)&Serial_Descriptor;
		    BytesToSend = Serial_Descriptor.bLength;
		    break;

		default:
		    pSendBuffer = NULL;
		    BytesToSend = 0;
	    }
	    send_Bytes = 1;
	    if (BytesToSend >= setuppkt->wLength) {
		BytesToSend = setuppkt->wLength;
		exact_Bytes = 1;
	    } else {
		exact_Bytes = 0;
	    }
	    d12_write_buffer_ep0();
	    break;
	default:
	    d12_stallctrlendpt();
	    break;
    }
}


void 
d12_stallctrlendpt(void) {
    uint8_t   Buffer[] = {0x01};
    /*
     * 9.2.7 RequestError - return STALL PID in response to next DATA
     * Stage Transaction
     */
    d12_write_cmd(D12_SET_ENDPOINT_STATUS + D12_ENDPOINT_EP0_IN, Buffer, 1);
    /* or in the status stage of the message. */
    d12_write_cmd(D12_SET_ENDPOINT_STATUS + D12_ENDPOINT_EP0_OUT, Buffer, 1);
}
    
uint8_t
d12_read_endpt(uint8_t endpt, uint8_t *buffer) {
    uint8_t d12header[2];
    uint8_t status = 0;
    uint8_t i;

    /* Select Endpoint */
    d12_read_cmd(endpt, &status, 1);

    /* Check if Buffer is Full */
    if (status & 0x01) {
	set_d12_cmd(D12_READ_BUFFER);
	d12header[0] = d12_get_data();
	d12header[1] = d12_get_data();
	if (d12header[1]) {
	    for (i = 0; i < d12header[1]; i++)
		buffer[i] = d12_get_data();
	}
	/* Allow new packets to be accepted */
	d12_write_cmd(D12_CLEAR_BUFFER, NULL, 0);
	
    }
    return d12header[1];
}

void 
d12_write_endpt(uint8_t endpt, const uint8_t *buffer, uint8_t bytes) {
    uint8_t status = 0;
    uint8_t i;

    /* Select Endpoint */
    d12_read_cmd(endpt, &status, 1);
    /* Write Header */
    set_d12_cmd(D12_WRITE_BUFFER);
    set_d12_data(0x00);
    set_d12_data(bytes);
    /* Write Packet */
    if (bytes) {
	for (i = 0; i < bytes; i++) {
	    set_d12_data(buffer[i]);
	}
    }
    /* Validate Buffer */
    d12_write_cmd(D12_VALIDATE_BUFFER, NULL, 0);
}

void 
d12_write_buffer_ep0(void) {
    uint8_t BufferStatus = 1;
    
    if (send_Bytes == 0) {
	return;
    }
	
    /* Read Buffer Full Status */
    d12_read_cmd(D12_ENDPOINT_EP0_IN, &BufferStatus, 1);
    if (BufferStatus != 0) {/* Buffer Full */
	return;
    }
    
    if (BytesToSend == 0) {
	/*
	 * If BytesToSend is Zero and we get called again, assume
	 * buffer is smaller
	 * than Setup Request Size and indicate end by sending Zero
	 * Length packet
	 */
	d12_write_endpt(D12_ENDPOINT_EP0_IN, NULL, 0);
	send_Bytes = 0;
    } else if (BytesToSend >= EP0_SIZE) {	
	/* Write another EP0_SIZE Bytes to buffer and send */
	d12_write_endpt(D12_ENDPOINT_EP0_IN, pSendBuffer, EP0_SIZE);
	pSendBuffer += EP0_SIZE;
	BytesToSend -= EP0_SIZE;
	if (BytesToSend == 0 && exact_Bytes) {
	    send_Bytes = 0;
	}
    } else {
	/* Buffer must have less than EP0_SIZE bytes left */	
	d12_write_endpt(D12_ENDPOINT_EP0_IN, pSendBuffer, BytesToSend);
	
	BytesToSend = 0;
	send_Bytes = 0;
    }
}

void
d12_send_data_ep2(void) {
    uint8_t BufferStatus = 1;

    /* Read Buffer Full Status */
    d12_read_cmd(D12_ENDPOINT_EP2_IN, &BufferStatus, 1);
    if (BufferStatus == 0) {   /* Buffer Empty */
	if (BytesToSend2 == 0) {
	    /* Nothing to do */
	    send_packet2 = 0;
	} else if (BytesToSend2 >= 64) {
	    /* Write another 64 Bytes to buffer and send */
	    d12_write_endpt(D12_ENDPOINT_EP2_IN, pSendBuffer2, 64);
	    pSendBuffer2 += 64;
	    BytesToSend2 -= 64;
	} else {
	    /* Buffer must have less than 64 bytes left */
	    d12_write_endpt(D12_ENDPOINT_EP2_IN, pSendBuffer2, BytesToSend2);
	    BytesToSend2 = 0;
	    send_packet2 = 0;
	}
    }
}

void
d12_receive_data_ep2(void) {
    uint8_t D12Header[2];
    uint8_t bytes;
    uint8_t BufferStatus = 0;
    uint8_t i;

    /* Select Endpoint */
    d12_read_cmd(D12_ENDPOINT_EP2_OUT, &BufferStatus, 1);
    /* Check if Buffer is Full */
    if (BufferStatus & 0x01) {
	/* Read header */
	set_d12_cmd(D12_READ_BUFFER);
	D12Header[0] = d12_get_data();
	D12Header[1] = d12_get_data();
	bytes = D12Header[1];
	/* TODO check if the packet is not too long */
	uart_putsP(PSTR("Got data\n\r"));
	for (i = 0; i < bytes; i++) {
	    packet2[packetlen2 + i] = d12_get_data();
	    uart_puts_hex(packet2[packetlen2 + i]);
	    uart_putsP(PSTR(" "));
	}
	uart_putsP(PSTR("\n\r"));
	packetlen2 += bytes;
	/* Allow new packets to be accepted */
	d12_write_cmd(D12_CLEAR_BUFFER, NULL, 0);
	if (bytes == 0 || bytes < 64) { /* request complete */
	    pSendBuffer2 = packet2;
	    send_packet2 = 1;
	    packetlen2 = 0;
	    d12_send_data_ep2();
	}
    }
}