2008-02-03 21:41:39 +01:00
|
|
|
/***************************************************************************
|
2008-06-23 15:33:07 +02:00
|
|
|
* sam7fc - USB Device Port with logical Serial Port *
|
|
|
|
* *
|
2008-02-03 21:41:39 +01:00
|
|
|
* Copyright (C) 01/2008 by Olaf Rempel *
|
|
|
|
* razzor@kopf-tisch.de *
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation; version 2 of the License *
|
|
|
|
* *
|
|
|
|
* This program is distributed in the hope that it will be useful, *
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
|
|
* GNU General Public License for more details. *
|
|
|
|
* *
|
|
|
|
* You should have received a copy of the GNU General Public License *
|
|
|
|
* along with this program; if not, write to the *
|
|
|
|
* Free Software Foundation, Inc., *
|
|
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
|
|
***************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "AT91SAM7S256.h"
|
|
|
|
#include "at91_pio.h"
|
|
|
|
#include "board.h"
|
2008-02-06 22:18:33 +01:00
|
|
|
#include "fifo.h"
|
2008-06-23 15:33:07 +02:00
|
|
|
#include "telemetrie.h"
|
2008-02-03 21:41:39 +01:00
|
|
|
|
|
|
|
#include "usb_ch9.h"
|
|
|
|
#include "usb_cdc.h"
|
2008-03-04 00:38:18 +01:00
|
|
|
#include "usb_dfu.h"
|
2008-02-03 21:41:39 +01:00
|
|
|
|
|
|
|
#define csr_clear_flags(csr, flags) \
|
|
|
|
while ((csr) & (flags)) \
|
|
|
|
(csr) &= ~(flags);
|
|
|
|
|
|
|
|
#define csr_set_flags(csr, flags) \
|
|
|
|
while (((csr) & (flags)) != (flags)) \
|
|
|
|
(csr) |= (flags);
|
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
struct ep_transfer {
|
2008-02-03 21:41:39 +01:00
|
|
|
uint16_t length;
|
|
|
|
uint16_t curpos;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
char *data;
|
|
|
|
void (*complete_cb)(void);
|
|
|
|
};
|
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
struct ep_ctx {
|
|
|
|
uint16_t maxpktsize;
|
|
|
|
uint16_t flags;
|
|
|
|
|
|
|
|
union {
|
|
|
|
struct ep_transfer *transfer;
|
|
|
|
struct fifo *fifo;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
#define CTX_TRANSFER 0x01 /* ctx use ep_transfer struct */
|
|
|
|
#define CTX_FIFO 0x02 /* ctx use fifo */
|
|
|
|
#define CTX_IN 0x04 /* write to the host */
|
|
|
|
#define CTX_OUT 0x08 /* read from the host */
|
|
|
|
#define CTX_RXBANK0 0x10
|
|
|
|
#define CTX_RXBANK1 0x20
|
|
|
|
|
|
|
|
static struct ep_transfer ep0_transfer;
|
|
|
|
static struct ep_ctx ep_ctx[4];
|
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
static uint16_t current_address;
|
|
|
|
static uint16_t current_config;
|
|
|
|
static uint16_t current_interface;
|
|
|
|
|
2008-06-23 15:33:07 +02:00
|
|
|
static struct comm_device usb_comm;
|
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
static const struct usb_device_descriptor dev_descriptor = {
|
|
|
|
.bLength = sizeof(struct usb_device_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_DEVICE,
|
|
|
|
.bcdUSB = 0x0110,
|
|
|
|
.bMaxPacketSize0 = 8,
|
|
|
|
.idVendor = USB_VENDOR_ID,
|
|
|
|
.idProduct = USB_PRODUCT_ID +1,
|
|
|
|
.bcdDevice = 0x0001,
|
2008-03-04 00:38:18 +01:00
|
|
|
.iProduct = 0x01,
|
2008-02-03 21:41:39 +01:00
|
|
|
.bNumConfigurations = 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct my_config {
|
|
|
|
struct usb_config_descriptor cfg;
|
|
|
|
struct usb_interface_descriptor ctrl_iface;
|
|
|
|
struct usb_cdc_header_desc cdc_header;
|
|
|
|
struct usb_cdc_call_mgmt_descriptor cdc_call_mgmt;
|
|
|
|
struct usb_cdc_acm_descriptor cdc_acm;
|
|
|
|
struct usb_cdc_union_desc cdc_union;
|
|
|
|
struct usb_endpoint_descriptor notify_ep;
|
|
|
|
struct usb_interface_descriptor data_iface;
|
|
|
|
struct usb_endpoint_descriptor dataout_ep;
|
|
|
|
struct usb_endpoint_descriptor datain_ep;
|
2008-03-04 00:38:18 +01:00
|
|
|
struct usb_interface_descriptor dfu_iface;
|
|
|
|
struct usb_dfu_descriptor dfu;
|
2008-02-03 21:41:39 +01:00
|
|
|
} __attribute__ ((packed));
|
|
|
|
|
|
|
|
static const struct my_config cfg_descriptor = {
|
|
|
|
.cfg = {
|
|
|
|
.bLength = sizeof(struct usb_config_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_CONFIG,
|
|
|
|
.wTotalLength = sizeof(struct my_config),
|
2008-03-04 00:38:18 +01:00
|
|
|
.bNumInterfaces = 3,
|
2008-02-03 21:41:39 +01:00
|
|
|
.bConfigurationValue = 1,
|
|
|
|
.bmAttributes = USB_CONFIG_ATT_SELFPOWER | USB_CONFIG_ATT_WAKEUP,
|
|
|
|
.bMaxPower = 50,
|
|
|
|
},
|
|
|
|
.ctrl_iface = {
|
|
|
|
.bLength = sizeof(struct usb_interface_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_INTERFACE,
|
|
|
|
.bInterfaceNumber = 0,
|
|
|
|
.bNumEndpoints = 1,
|
|
|
|
.bInterfaceClass = USB_CLASS_COMM,
|
|
|
|
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
|
|
|
|
.bInterfaceProtocol = 1,
|
|
|
|
},
|
|
|
|
.cdc_header = {
|
|
|
|
.bLength = sizeof(struct usb_cdc_header_desc),
|
|
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
|
|
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
|
|
|
|
.bcdCDC = 0x0110,
|
|
|
|
},
|
|
|
|
.cdc_call_mgmt = {
|
|
|
|
.bLength = sizeof(struct usb_cdc_call_mgmt_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
|
|
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
|
|
|
|
.bmCapabilities = USB_CDC_CALL_MGMT_CAP_CALL_MGMT,
|
|
|
|
.bDataInterface = 1,
|
|
|
|
},
|
|
|
|
.cdc_acm = {
|
|
|
|
.bLength = sizeof(struct usb_cdc_acm_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
|
|
.bDescriptorSubType = USB_CDC_ACM_TYPE,
|
|
|
|
.bmCapabilities = (USB_CDC_CAP_BRK | USB_CDC_CAP_LINE | USB_CDC_COMM_FEATURE),
|
|
|
|
},
|
|
|
|
.cdc_union = {
|
|
|
|
.bLength = sizeof(struct usb_cdc_union_desc),
|
|
|
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
|
|
.bDescriptorSubType = USB_CDC_UNION_TYPE,
|
|
|
|
.bMasterInterface0 = 0,
|
|
|
|
.bSlaveInterface0 = 1,
|
|
|
|
},
|
|
|
|
.notify_ep = {
|
|
|
|
.bLength = sizeof(struct usb_endpoint_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
|
|
.bEndpointAddress = USB_DIR_IN | 0x03,
|
|
|
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
|
|
|
.wMaxPacketSize = 64,
|
|
|
|
.bInterval = 10,
|
|
|
|
},
|
|
|
|
.data_iface = {
|
|
|
|
.bLength = sizeof(struct usb_interface_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_INTERFACE,
|
|
|
|
.bInterfaceNumber = 1,
|
|
|
|
.bNumEndpoints = 2,
|
|
|
|
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
|
|
|
},
|
|
|
|
.dataout_ep = {
|
|
|
|
.bLength = sizeof(struct usb_endpoint_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
|
|
.bEndpointAddress = USB_DIR_OUT | 0x01,
|
|
|
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
|
|
|
.wMaxPacketSize = 64,
|
|
|
|
},
|
|
|
|
.datain_ep = {
|
|
|
|
.bLength = sizeof(struct usb_endpoint_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
|
|
.bEndpointAddress = USB_DIR_IN | 0x02,
|
|
|
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
|
|
|
.wMaxPacketSize = 64,
|
|
|
|
},
|
2008-03-04 00:38:18 +01:00
|
|
|
.dfu_iface = {
|
|
|
|
.bLength = sizeof(struct usb_interface_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_INTERFACE,
|
|
|
|
.bInterfaceNumber = 2,
|
|
|
|
.bInterfaceClass = USB_CLASS_APP_SPEC,
|
|
|
|
.bInterfaceSubClass = 0x01, /* DFU */
|
|
|
|
.bInterfaceProtocol = 0x01,
|
|
|
|
},
|
|
|
|
.dfu = {
|
|
|
|
.bLength = sizeof(struct usb_dfu_descriptor),
|
|
|
|
.bDescriptorType = USB_TYPE_DFU,
|
|
|
|
.bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_CAN_UPLOAD | USB_DFU_MANIFEST_TOL | USB_DFU_WILL_DETACH,
|
|
|
|
.wDetachTimeOut = 0xff00,
|
|
|
|
.wTransferSize = AT91C_IFLASH_PAGE_SIZE,
|
|
|
|
.bcdDFUVersion = 0x0101,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2008-03-04 22:58:59 +01:00
|
|
|
/* not const! */
|
|
|
|
static struct usb_cdc_line_coding cdc_line_coding = {
|
|
|
|
.dwDTERate = 9600,
|
|
|
|
.bCharFormat = USB_CDC_1_STOP_BITS,
|
|
|
|
.bParityType = USB_CDC_NO_PARITY,
|
|
|
|
.bDataBits = 8,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* not const! */
|
|
|
|
static struct dfu_status dfu_status = {
|
2008-03-04 00:38:18 +01:00
|
|
|
.bStatus = DFU_STATUS_OK,
|
|
|
|
.bState = DFU_STATE_appIDLE,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct usb_string_descriptor usb_string0 = {
|
|
|
|
/* String 0 - Language */
|
|
|
|
.bLength = sizeof(struct usb_string_descriptor) + 1 * sizeof(uint16_t),
|
|
|
|
.bDescriptorType = USB_DT_STRING,
|
|
|
|
.wData = { 0x0409 /* English */ },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct usb_string_descriptor usb_string1 = {
|
|
|
|
/* String 1 "sam7fc" */
|
|
|
|
.bLength = sizeof(struct usb_string_descriptor) + 6 * sizeof(uint16_t),
|
|
|
|
.bDescriptorType = USB_DT_STRING,
|
|
|
|
.wData = {
|
|
|
|
0x0073, 0x0061, 0x006d, 0x0037, 0x0066, 0x0063,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct usb_string_descriptor *usb_strings[] = {
|
|
|
|
&usb_string0, &usb_string1,
|
2008-02-03 21:41:39 +01:00
|
|
|
};
|
|
|
|
|
2008-03-04 00:38:18 +01:00
|
|
|
void ep_transfer_send(uint32_t ep, char *data, uint32_t length,
|
2008-02-06 22:18:33 +01:00
|
|
|
void (*complete_cb)(void))
|
2008-02-03 21:41:39 +01:00
|
|
|
{
|
2008-02-06 22:18:33 +01:00
|
|
|
struct ep_ctx *ctx = &ep_ctx[ep];
|
|
|
|
// printf("ep_transfer_send(%ld) size=%ld flags=0x%x\n\r", ep, length, ctx->flags);
|
|
|
|
if (!(ctx->flags & CTX_TRANSFER) || (ctx->flags & (CTX_IN | CTX_OUT)))
|
|
|
|
return;
|
2008-02-03 21:41:39 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
/* from buffer to usb */
|
|
|
|
ctx->flags |= CTX_IN;
|
2008-02-03 21:41:39 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
struct ep_transfer *transfer = ctx->transfer;
|
|
|
|
transfer->length = length;
|
|
|
|
transfer->curpos = 0;
|
|
|
|
transfer->data = data;
|
|
|
|
transfer->complete_cb = complete_cb;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
uint32_t maxsize = ctx->maxpktsize;
|
2008-02-03 21:41:39 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
/* get data from transfer */
|
|
|
|
while (transfer->curpos < transfer->length && maxsize--)
|
|
|
|
AT91C_UDP_FDR[ep] = transfer->data[transfer->curpos++];
|
2008-02-03 21:41:39 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
/* trigger tx */
|
2008-02-03 21:41:39 +01:00
|
|
|
AT91C_UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY;
|
|
|
|
}
|
|
|
|
|
2008-03-04 00:38:18 +01:00
|
|
|
void ep_transfer_receive(uint32_t ep, char *data, uint32_t length,
|
2008-02-06 22:18:33 +01:00
|
|
|
void (*complete_cb)(void))
|
2008-02-03 21:41:39 +01:00
|
|
|
{
|
2008-02-06 22:18:33 +01:00
|
|
|
struct ep_ctx *ctx = &ep_ctx[ep];
|
|
|
|
// printf("ep_transfer_receive(%ld) size=%ld flags=0x%x\n\r", ep, length, ctx->flags);
|
|
|
|
if (!(ctx->flags & CTX_TRANSFER) || (ctx->flags & (CTX_IN | CTX_OUT)))
|
2008-02-03 21:41:39 +01:00
|
|
|
return;
|
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
/* from usb to buffer */
|
|
|
|
ctx->flags |= CTX_OUT;
|
2008-02-03 21:41:39 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
struct ep_transfer *transfer = ctx->transfer;
|
|
|
|
transfer->length = length;
|
|
|
|
transfer->curpos = 0;
|
|
|
|
transfer->data = data;
|
|
|
|
transfer->complete_cb = complete_cb;
|
2008-02-03 21:41:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* stalls the endpoint */
|
2008-02-06 22:18:33 +01:00
|
|
|
static void ep_send_stall(uint32_t ep)
|
2008-02-03 21:41:39 +01:00
|
|
|
{
|
2008-03-11 23:26:56 +01:00
|
|
|
printf("usb stall\n\r");
|
2008-02-03 21:41:39 +01:00
|
|
|
AT91C_UDP_CSR[ep] |= AT91C_UDP_FORCESTALL;
|
|
|
|
}
|
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
static void udp_configure_ep(const struct usb_endpoint_descriptor *desc)
|
|
|
|
{
|
|
|
|
/* get endpoint address, set Max Packet Size */
|
|
|
|
uint32_t ep = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
|
|
|
ep_ctx[ep].maxpktsize = desc->wMaxPacketSize;
|
|
|
|
|
|
|
|
/* get endpoint type (ctrl, iso, bulb, int) */
|
|
|
|
uint32_t eptype = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
|
|
|
if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
|
|
|
|
eptype |= 0x04;
|
|
|
|
} else {
|
|
|
|
ep_ctx[ep].flags |= CTX_RXBANK0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* configure UDP endpoint and enable interrupt */
|
|
|
|
AT91C_UDP_CSR[ep] = AT91C_UDP_EPEDS | (eptype << 8);
|
|
|
|
*AT91C_UDP_IER = (1 << ep);
|
|
|
|
}
|
|
|
|
|
2008-03-11 23:26:56 +01:00
|
|
|
static void udp_print_config(void)
|
|
|
|
{
|
|
|
|
printf("usb: addr=%d cfg=%d if=%d\n\r",
|
|
|
|
current_address, current_config, current_interface);
|
|
|
|
}
|
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
/*
|
|
|
|
* set local address
|
|
|
|
* (USB_REQ_SET_ADDRESS callback)
|
2008-02-04 01:51:29 +01:00
|
|
|
*/
|
2008-02-03 21:41:39 +01:00
|
|
|
static void udp_txcb_setaddress(void)
|
|
|
|
{
|
|
|
|
*AT91C_UDP_FADDR = (AT91C_UDP_FEN | current_address);
|
|
|
|
*AT91C_UDP_GLBSTATE = AT91C_UDP_FADDEN;
|
2008-03-11 23:26:56 +01:00
|
|
|
udp_print_config();
|
2008-02-03 21:41:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* configure endpoints
|
|
|
|
* (USB_REQ_SET_CONFIGURATION callback)
|
2008-02-04 01:51:29 +01:00
|
|
|
*/
|
2008-02-03 21:41:39 +01:00
|
|
|
static void udp_txcb_setconfig(void)
|
|
|
|
{
|
|
|
|
udp_configure_ep(&cfg_descriptor.notify_ep);
|
|
|
|
udp_configure_ep(&cfg_descriptor.datain_ep);
|
|
|
|
udp_configure_ep(&cfg_descriptor.dataout_ep);
|
|
|
|
|
2008-06-23 15:33:07 +02:00
|
|
|
ep_ctx[1].fifo = usb_comm.rxfifo;
|
|
|
|
ep_ctx[1].flags |= CTX_FIFO;
|
|
|
|
|
|
|
|
ep_ctx[2].fifo = usb_comm.txfifo;
|
|
|
|
ep_ctx[2].flags |= CTX_FIFO;
|
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
/* set UDP to "configured" */
|
|
|
|
*AT91C_UDP_GLBSTATE = AT91C_UDP_CONFG;
|
2008-03-11 23:26:56 +01:00
|
|
|
udp_print_config();
|
2008-02-03 21:41:39 +01:00
|
|
|
}
|
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
static void ep_handle_ctrlrequest(struct usb_ctrlrequest *req)
|
2008-02-03 21:41:39 +01:00
|
|
|
{
|
2008-03-11 23:26:56 +01:00
|
|
|
// printf("typ:0x%02x req:0x%02x val:0x%04x idx:0x%04x len:0x%04x\n\r",
|
|
|
|
// req->bRequestType, req->bRequest, req->wValue, req->wIndex, req->wLength);
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
switch (req->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) {
|
|
|
|
case (USB_TYPE_STANDARD | USB_RECIP_DEVICE): /* 0x00/0x80 */
|
|
|
|
switch (req->bRequest) {
|
|
|
|
case USB_REQ_SET_ADDRESS: /* 0x05 */
|
|
|
|
current_address = req->wValue;
|
2008-02-06 22:18:33 +01:00
|
|
|
ep_transfer_send(0, NULL, 0, udp_txcb_setaddress);
|
2008-02-03 21:41:39 +01:00
|
|
|
break;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
case USB_REQ_GET_DESCRIPTOR: /* 0x06 */
|
|
|
|
switch (req->wValue >> 8) {
|
|
|
|
case USB_DT_DEVICE: /* 0x01 */
|
2008-02-06 22:18:33 +01:00
|
|
|
ep_transfer_send(0, (char *)&dev_descriptor,
|
|
|
|
MIN(sizeof(dev_descriptor), req->wLength),
|
|
|
|
NULL);
|
2008-02-03 21:41:39 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case USB_DT_CONFIG: /* 0x02 */
|
2008-02-06 22:18:33 +01:00
|
|
|
ep_transfer_send(0, (char *)&cfg_descriptor,
|
|
|
|
MIN(sizeof(cfg_descriptor), req->wLength),
|
|
|
|
NULL);
|
2008-02-03 21:41:39 +01:00
|
|
|
break;
|
|
|
|
|
2008-03-04 00:38:18 +01:00
|
|
|
case USB_DT_STRING: /* 0x03 */
|
|
|
|
;
|
|
|
|
uint8_t index = req->wValue & 0xFF;
|
|
|
|
if (index < ARRAY_SIZE(usb_strings)) {
|
|
|
|
ep_transfer_send(0, (char *)usb_strings[index],
|
|
|
|
MIN(usb_strings[index]->bLength, req->wLength),
|
|
|
|
NULL);
|
|
|
|
} else {
|
|
|
|
ep_send_stall(0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USB_DT_CS_DEVICE: /* 0x21 */
|
|
|
|
ep_transfer_send(0, (char *)&cfg_descriptor.dfu,
|
|
|
|
MIN(sizeof(cfg_descriptor.dfu), req->wLength),
|
|
|
|
NULL);
|
|
|
|
break;
|
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
default:
|
2008-02-06 22:18:33 +01:00
|
|
|
ep_send_stall(0);
|
2008-02-03 21:41:39 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USB_REQ_SET_CONFIGURATION: /* 0x09 */
|
|
|
|
current_config = req->wValue;
|
2008-02-06 22:18:33 +01:00
|
|
|
ep_transfer_send(0, NULL, 0, udp_txcb_setconfig);
|
2008-02-03 21:41:39 +01:00
|
|
|
break;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
default:
|
2008-02-06 22:18:33 +01:00
|
|
|
ep_send_stall(0);
|
2008-02-03 21:41:39 +01:00
|
|
|
break;
|
2008-02-06 22:18:33 +01:00
|
|
|
}
|
|
|
|
break;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
case (USB_TYPE_STANDARD | USB_RECIP_INTERFACE): /* 0x01/0x81 */
|
2008-02-06 22:18:33 +01:00
|
|
|
switch (req->bRequest) {
|
|
|
|
case USB_REQ_SET_INTERFACE: /* 0x0b */
|
|
|
|
current_interface = req->wValue;
|
2008-03-11 23:26:56 +01:00
|
|
|
ep_transfer_send(0, NULL, 0, udp_print_config);
|
2008-02-03 21:41:39 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2008-02-06 22:18:33 +01:00
|
|
|
ep_send_stall(0);
|
2008-02-03 21:41:39 +01:00
|
|
|
break;
|
2008-02-06 22:18:33 +01:00
|
|
|
}
|
|
|
|
break;
|
2008-02-03 21:41:39 +01:00
|
|
|
|
|
|
|
case (USB_TYPE_CLASS | USB_RECIP_INTERFACE): /* 0x21/0xA1 */
|
|
|
|
// TODO: follow current_interface
|
2008-02-06 22:18:33 +01:00
|
|
|
switch (req->bRequest) {
|
2008-03-04 00:38:18 +01:00
|
|
|
case USB_REQ_DFU_DETACH: /* 0x00 */
|
2008-03-04 22:58:59 +01:00
|
|
|
dfu_status.bStatus = DFU_STATE_appDETACH;
|
2008-03-04 00:38:18 +01:00
|
|
|
ep_transfer_send(0, NULL, 0, NULL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USB_REQ_DFU_GETSTATUS: /* 0x03 */
|
|
|
|
ep_transfer_send(0, (char *)&dfu_status, sizeof(dfu_status), NULL);
|
|
|
|
break;
|
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
case USB_CDC_REQ_SET_LINE_CODING: /* 0x20 */
|
2008-03-04 22:58:59 +01:00
|
|
|
ep_transfer_receive(0, (char *)&cdc_line_coding, sizeof(cdc_line_coding), NULL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USB_CDC_REQ_GET_LINE_CODING: /* 0x21 */
|
|
|
|
ep_transfer_send(0, (char *)&cdc_line_coding, sizeof(cdc_line_coding), NULL);
|
2008-02-03 21:41:39 +01:00
|
|
|
break;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
case USB_CDC_REQ_SET_CONTROL_LINE_STATE: /* 0x22 */
|
|
|
|
ep_transfer_send(0, NULL, 0, NULL);
|
2008-02-03 21:41:39 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2008-02-06 22:18:33 +01:00
|
|
|
ep_send_stall(0);
|
2008-02-03 21:41:39 +01:00
|
|
|
break;
|
2008-02-06 22:18:33 +01:00
|
|
|
}
|
|
|
|
break;
|
2008-02-03 21:41:39 +01:00
|
|
|
|
|
|
|
default:
|
2008-02-06 22:18:33 +01:00
|
|
|
ep_send_stall(0);
|
2008-02-03 21:41:39 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void udp_handle_ep(uint32_t ep)
|
|
|
|
{
|
|
|
|
/* endpoint enabled? */
|
2008-02-06 22:18:33 +01:00
|
|
|
AT91_REG *csr = &AT91C_UDP_CSR[ep];
|
2008-02-03 21:41:39 +01:00
|
|
|
if (!(*csr & AT91C_UDP_EPEDS))
|
|
|
|
return;
|
|
|
|
|
2008-03-04 00:38:18 +01:00
|
|
|
/* clear STALLSENT interrupt */
|
|
|
|
if (*csr & AT91C_UDP_STALLSENT)
|
|
|
|
csr_clear_flags(*csr, (AT91C_UDP_STALLSENT | AT91C_UDP_FORCESTALL));
|
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
/* ctrl request packet? */
|
|
|
|
if (*csr & AT91C_UDP_RXSETUP) {
|
|
|
|
struct usb_ctrlrequest req;
|
|
|
|
uint8_t *p;
|
|
|
|
for (p = (uint8_t *)&req; p < (uint8_t *)(&req +1); p++)
|
|
|
|
*p = AT91C_UDP_FDR[ep];
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-03-04 00:38:18 +01:00
|
|
|
/* ack bank0 *now */
|
|
|
|
if (*csr & AT91C_UDP_RX_DATA_BK0)
|
|
|
|
csr_clear_flags(*csr, AT91C_UDP_RX_DATA_BK0);
|
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
/* set data phase transfer direction */
|
|
|
|
if (req.bRequestType & USB_DIR_IN)
|
|
|
|
*csr |= AT91C_UDP_DIR;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
/* clear interrupt - *MUST* use csr_clear_flags() here */
|
2008-02-03 21:41:39 +01:00
|
|
|
csr_clear_flags(*csr, AT91C_UDP_RXSETUP);
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
ep_handle_ctrlrequest(&req);
|
2008-02-03 21:41:39 +01:00
|
|
|
}
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-03-04 00:38:18 +01:00
|
|
|
void (* transfer_cb)(void) = NULL;
|
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
/* transmit complete? */
|
|
|
|
if (*csr & AT91C_UDP_TXCOMP) {
|
2008-02-06 22:18:33 +01:00
|
|
|
struct ep_ctx *ctx = &ep_ctx[ep];
|
|
|
|
|
|
|
|
if (ctx->flags & CTX_FIFO) {
|
|
|
|
/* get data from fifo */
|
2008-06-23 15:33:07 +02:00
|
|
|
if (fifo_txudp(ctx->fifo, ep, ctx->maxpktsize)) {
|
2008-02-06 22:18:33 +01:00
|
|
|
AT91C_UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY;
|
|
|
|
|
2008-06-23 15:33:07 +02:00
|
|
|
} else {
|
|
|
|
ctx->flags &= ~CTX_IN;
|
|
|
|
}
|
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
} else if ((ctx->flags & (CTX_TRANSFER | CTX_IN)) == (CTX_TRANSFER | CTX_IN)) {
|
|
|
|
/* transfer not complete */
|
|
|
|
struct ep_transfer *transfer = ctx->transfer;
|
|
|
|
if (transfer->length != transfer->curpos) {
|
|
|
|
uint32_t maxsize = ctx->maxpktsize;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
/* get data from transfer */
|
|
|
|
while (transfer->curpos < transfer->length && maxsize--)
|
|
|
|
AT91C_UDP_FDR[ep] = transfer->data[transfer->curpos++];
|
2008-02-03 21:41:39 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
/* trigger tx */
|
|
|
|
AT91C_UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY;
|
|
|
|
|
|
|
|
/* transfer complete, execute callback */
|
|
|
|
} else {
|
|
|
|
ctx->flags &= ~CTX_IN;
|
2008-03-04 00:38:18 +01:00
|
|
|
transfer_cb = transfer->complete_cb;
|
2008-02-06 22:18:33 +01:00
|
|
|
}
|
|
|
|
}
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
/* clear interrupt */
|
|
|
|
*csr &= ~(AT91C_UDP_TXCOMP);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* data ready to read? */
|
|
|
|
if (*csr & (AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RX_DATA_BK1)) {
|
2008-02-06 22:18:33 +01:00
|
|
|
struct ep_ctx *ctx = &ep_ctx[ep];
|
2008-02-03 21:41:39 +01:00
|
|
|
uint16_t len = (*csr & AT91C_UDP_RXBYTECNT) >> 16;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
// TODO: only ep0 status OUT?
|
|
|
|
if (!len && (ctx->flags & CTX_TRANSFER)) {
|
|
|
|
ctx->flags &= ~(CTX_OUT | CTX_IN);
|
|
|
|
ctx->transfer->length = 0;
|
|
|
|
ctx->transfer->curpos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->flags & CTX_FIFO) {
|
|
|
|
fifo_rxudp(ctx->fifo, ep, len);
|
2008-02-03 21:41:39 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
} else if ((ctx->flags & (CTX_TRANSFER | CTX_OUT)) == (CTX_TRANSFER | CTX_OUT)) {
|
|
|
|
|
|
|
|
/* transfer not complete */
|
|
|
|
struct ep_transfer *transfer = ctx->transfer;
|
|
|
|
if (transfer->length != transfer->curpos) {
|
|
|
|
/* get data from transfer */
|
|
|
|
while (transfer->curpos < transfer->length && len--)
|
|
|
|
transfer->data[transfer->curpos++] = AT91C_UDP_FDR[ep];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* test again */
|
|
|
|
if (transfer->length == transfer->curpos) {
|
|
|
|
ctx->flags &= ~CTX_OUT;
|
2008-03-04 22:58:59 +01:00
|
|
|
transfer_cb = transfer->complete_cb;
|
2008-02-06 22:18:33 +01:00
|
|
|
}
|
2008-02-04 01:51:29 +01:00
|
|
|
}
|
2008-02-03 21:41:39 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
if (ctx->flags & CTX_RXBANK0) {
|
|
|
|
if (*csr & AT91C_UDP_RX_DATA_BK0)
|
|
|
|
csr_clear_flags(*csr, AT91C_UDP_RX_DATA_BK0);
|
|
|
|
|
|
|
|
/* all but ep0 have ping pong buffers */
|
|
|
|
if (ep > 0)
|
|
|
|
ctx->flags = (ctx->flags & ~CTX_RXBANK0) | CTX_RXBANK1;
|
2008-02-03 21:41:39 +01:00
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
} else if (ctx->flags & CTX_RXBANK1) {
|
|
|
|
if (*csr & AT91C_UDP_RX_DATA_BK1)
|
|
|
|
csr_clear_flags(*csr, AT91C_UDP_RX_DATA_BK1);
|
|
|
|
|
|
|
|
ctx->flags = (ctx->flags & ~CTX_RXBANK1) | CTX_RXBANK0;
|
|
|
|
}
|
2008-02-03 21:41:39 +01:00
|
|
|
}
|
2008-03-04 00:38:18 +01:00
|
|
|
|
|
|
|
if (transfer_cb)
|
|
|
|
transfer_cb();
|
2008-02-03 21:41:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void udp_isr(void)
|
|
|
|
{
|
|
|
|
uint32_t isr = *AT91C_UDP_ISR;
|
|
|
|
if (isr & AT91C_UDP_ENDBUSRES) {
|
|
|
|
AT91S_UDP *udp = AT91C_BASE_UDP;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
/* reset all endpoints */
|
|
|
|
udp->UDP_RSTEP = (AT91C_UDP_EP0 | AT91C_UDP_EP1 |
|
|
|
|
AT91C_UDP_EP2 | AT91C_UDP_EP3) ;
|
|
|
|
udp->UDP_RSTEP = 0;
|
|
|
|
|
2008-02-06 22:18:33 +01:00
|
|
|
/* init ep0 */
|
|
|
|
struct ep_ctx *ctx = &ep_ctx[0];
|
|
|
|
ctx->maxpktsize = 8;
|
|
|
|
ctx->flags = CTX_TRANSFER | CTX_RXBANK0;
|
|
|
|
ctx->transfer = &ep0_transfer;
|
|
|
|
ctx->transfer->length = 0;
|
|
|
|
ctx->transfer->curpos = 0;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
/* Configure endpoint0 as Control EP */
|
|
|
|
udp->UDP_CSR[0] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL);
|
|
|
|
|
|
|
|
/* enable ep0 Interrupt, disable all others */
|
|
|
|
udp->UDP_IER = AT91C_UDP_EPINT0;
|
|
|
|
udp->UDP_IDR = AT91C_UDP_EPINT1 | AT91C_UDP_EPINT2 | AT91C_UDP_EPINT3 |
|
|
|
|
AT91C_UDP_RXSUSP | AT91C_UDP_RXRSM | AT91C_UDP_SOFINT |
|
|
|
|
AT91C_UDP_WAKEUP;
|
2008-03-04 22:58:59 +01:00
|
|
|
|
|
|
|
if (dfu_status.bStatus == DFU_STATE_appDETACH) {
|
|
|
|
void (* bootloader)(void) = (void *)0x13c000;
|
|
|
|
bootloader();
|
|
|
|
while (1);
|
|
|
|
}
|
2008-02-03 21:41:39 +01:00
|
|
|
}
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
/* Handle Endpoint Interrupts */
|
|
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < 4; i++) {
|
2008-02-06 22:18:33 +01:00
|
|
|
if (isr & *AT91C_UDP_IMR & (1<<i))
|
2008-02-03 21:41:39 +01:00
|
|
|
udp_handle_ep(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clear all unhandled interrupts */
|
2008-02-04 01:51:29 +01:00
|
|
|
*AT91C_UDP_ICR = isr & (AT91C_UDP_RXSUSP | AT91C_UDP_RXRSM |
|
2008-02-03 21:41:39 +01:00
|
|
|
AT91C_UDP_ENDBUSRES | AT91C_UDP_WAKEUP);
|
|
|
|
}
|
|
|
|
|
2008-06-23 15:33:07 +02:00
|
|
|
static void trigger_fifo_tx(void)
|
|
|
|
{
|
|
|
|
struct ep_ctx *ctx = &ep_ctx[2];
|
|
|
|
|
|
|
|
/* currently transmitting, no need to trigger */
|
|
|
|
// TODO: racy?
|
|
|
|
if (ctx->flags & CTX_IN)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (fifo_txudp(ctx->fifo, 2, ctx->maxpktsize)) {
|
|
|
|
ctx->flags |= CTX_IN;
|
|
|
|
AT91C_UDP_CSR[2] |= AT91C_UDP_TXPKTRDY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
void at91_udp_init(void)
|
|
|
|
{
|
|
|
|
/* configure & disable Pullup, disable Pullup von VBUS */
|
|
|
|
AT91PS_PIO pio = AT91C_BASE_PIOA;
|
|
|
|
pio->PIO_CODR = UDP_PULLUP;
|
|
|
|
pio->PIO_PER = UDP_PULLUP;
|
|
|
|
pio->PIO_OER = UDP_PULLUP;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
/* UDPCK (48MHz) = PLLCK / 2 */
|
|
|
|
*AT91C_CKGR_PLLR |= AT91C_CKGR_USBDIV_1;
|
|
|
|
|
|
|
|
/* enable UDP clocks */
|
|
|
|
*AT91C_PMC_SCER = AT91C_PMC_UDP;
|
|
|
|
*AT91C_PMC_PCER = (1 << AT91C_ID_UDP);
|
|
|
|
|
|
|
|
/* enable transmitter */
|
|
|
|
*AT91C_UDP_TXVC &= ~AT91C_UDP_TXVDIS;
|
|
|
|
|
|
|
|
/* clear & disable all UDP interrupts */
|
|
|
|
*AT91C_UDP_IDR = AT91C_UDP_EPINT0 | AT91C_UDP_EPINT1 | AT91C_UDP_EPINT2 |
|
|
|
|
AT91C_UDP_EPINT3 | AT91C_UDP_RXSUSP | AT91C_UDP_RXRSM |
|
|
|
|
AT91C_UDP_SOFINT | AT91C_UDP_WAKEUP;
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
*AT91C_UDP_ICR = AT91C_UDP_RXSUSP | AT91C_UDP_RXRSM | AT91C_UDP_SOFINT |
|
|
|
|
AT91C_UDP_ENDBUSRES | AT91C_UDP_WAKEUP ;
|
|
|
|
|
|
|
|
/* level triggered, own vector */
|
|
|
|
AT91S_AIC *aic = AT91C_BASE_AIC;
|
|
|
|
aic->AIC_SMR[AT91C_ID_UDP] = IRQPRIO_UDP | AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL;
|
|
|
|
aic->AIC_SVR[AT91C_ID_UDP] = (uint32_t)udp_isr;
|
|
|
|
aic->AIC_IECR = (1 << AT91C_ID_UDP);
|
2008-02-04 01:51:29 +01:00
|
|
|
|
2008-06-23 15:33:07 +02:00
|
|
|
usb_comm.rxfifo = fifo_alloc(1024);
|
|
|
|
usb_comm.txfifo = fifo_alloc(1024);
|
|
|
|
usb_comm.trigger_tx = trigger_fifo_tx;
|
|
|
|
|
|
|
|
tdc_register_device(0, &usb_comm);
|
|
|
|
|
2008-02-03 21:41:39 +01:00
|
|
|
pio_trigger_isr(UDP_VBUS_MON);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void udp_vbus_monitor(uint32_t status, uint32_t input)
|
|
|
|
{
|
|
|
|
if (input & UDP_VBUS_MON)
|
|
|
|
/* usb connected -> enable pullup */
|
2008-02-29 19:50:08 +01:00
|
|
|
*AT91C_PIOA_CODR = UDP_PULLUP;
|
2008-02-03 21:41:39 +01:00
|
|
|
else
|
|
|
|
/* usb got diconnected -> disable pullup */
|
2008-02-29 19:50:08 +01:00
|
|
|
*AT91C_PIOA_SODR = UDP_PULLUP;
|
2008-02-03 21:41:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
PIO_PINCHANGE_ISR(UDP_VBUS_MON, udp_vbus_monitor);
|