From da6677b3d9f9add26ef6a8362d6f73c68e5ddd7c Mon Sep 17 00:00:00 2001 From: Werner Almesberger Date: Sun, 13 Feb 2011 23:46:36 -0300 Subject: [PATCH] atusb/fw3/: adaptation of the f32xbase USB stack --- atusb/fw3/Makefile | 108 +++++++++++++ atusb/fw3/atusb.c | 34 +++++ atusb/fw3/board.h | 54 +++++++ atusb/fw3/descr.c | 99 ++++++++++++ atusb/fw3/ep0.c | 194 ++++++++++++++++++++++++ atusb/fw3/spi.c | 45 ++++++ atusb/fw3/spi.h | 15 ++ atusb/fw3/usb2/usb.c | 349 +++++++++++++++++++++++++++++++++++++++++++ atusb/fw3/usb2/usb.h | 146 ++++++++++++++++++ 9 files changed, 1044 insertions(+) create mode 100644 atusb/fw3/Makefile create mode 100644 atusb/fw3/atusb.c create mode 100644 atusb/fw3/board.h create mode 100644 atusb/fw3/descr.c create mode 100644 atusb/fw3/ep0.c create mode 100644 atusb/fw3/spi.c create mode 100644 atusb/fw3/spi.h create mode 100644 atusb/fw3/usb2/usb.c create mode 100644 atusb/fw3/usb2/usb.h diff --git a/atusb/fw3/Makefile b/atusb/fw3/Makefile new file mode 100644 index 0000000..fa744b0 --- /dev/null +++ b/atusb/fw3/Makefile @@ -0,0 +1,108 @@ +# +# Makefile - Makefile of the ATUSB firmware +# +# Written 2010-2011 by Werner Almesberger +# Copyright 2010-2011 by Werner Almesberger +# +# 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; either version 2 of the License, or +# (at your option) any later version. +# + +SHELL = /bin/bash + +NAME = atusb + +CFLAGS = -g -Wall -Wextra -Wshadow -Werror -Wno-unused \ + -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes + +CHIP=atmega32u2 + +AVR_PREFIX = $(BIN_PATH) avr- +CC = $(AVR_PREFIX)gcc +OBJCOPY = $(AVR_PREFIX)objcopy +#OBJDUMP = $(AVR_PREFIX)objdump + +FreakUSB = usb +#USB_OBJS = usb.o ctrl.o usb_buf.o ep.o hw.o isr.o +USB_OBJS = usb.o +OBJS = atusb.o board.o spi.o descr.o ep0.o $(USB_OBJS) + +#vpath %.c $(FreakUSB)/usb/ +#vpath %.c $(FreakUSB)/hw/at90usbxx2/ +vpath %.c usb2/ + +CFLAGS += -I../fw/include \ + -Iusb2 + -DNUM_EPS=1 +# -I$(FreakUSB)/usb -I$(FreakUSB)/hw/at90usbxx2 \ + +# ----- Verbosity control ----------------------------------------------------- + +CC_normal := $(CC) +BUILD_normal := +DEPEND_normal := $(CPP) $(CFLAGS) -MM -MG + +CC_quiet = @echo " CC " $@ && $(CC_normal) +BUILD_quiet = @echo " BUILD " $@ && $(BUILD_normal) +DEPEND_quiet = @$(DEPEND_normal) + +ifeq ($(V),1) + CC = $(CC_normal) + BUILD = $(BUILD_normal) + DEPEND = $(DEPEND_normal) +else + CC = $(CC_quiet) + BUILD = $(BUILD_quiet) + DEPEND = $(DEPEND_quiet) +endif + +# ----- Rules ----------------------------------------------------------------- + +.PHONY: all clean upload prog + +all: $(NAME).bin + +$(NAME).elf: $(OBJS) + $(CC) $(CFLAGS) -mmcu=$(CHIP) -o $@ $(OBJS) + +%.bin: %.elf + $(BUILD) $(OBJCOPY) -j .text -j .data -O binary $< $@ + +# ----- Cleanup --------------------------------------------------------------- + +clean: + rm -f $(NAME).bin $(NAME).elf $(OBJS) $(OBJS:.o=.d) + +# ----- Dependencies ---------------------------------------------------------- + +%.o: %.c + $(CC) $(CFLAGS) -mmcu=$(CHIP) -Os -c $< + $(DEPEND) $< | \ + sed -e \ + '/^\(.*:\)\? */{p;s///;s/ *\\\?$$/ /;s/ */:\n/g;H;}' \ + -e '$${g;p;}' -e d >$*.d; \ + [ "$${PIPESTATUS[*]}" = "0 0" ] || { rm -f $*.d; exit 1; } + +-include $(OBJS:.o=.d) + +# ----- Programming and device control ---------------------------------------- + +upload: $(NAME).bin + scp $(NAME).bin jlime: + +prog: + ssh jlime avrdude -F -p $(CHIP) -c nanonote_atusb -e \ + -U flash:w:$(NAME).bin:r \ + -U lfuse:w:0x60:m # external clock, slow start-up + +on: + ssh jlime poke 0x10010318 4 + +off: + ssh jlime poke 0x10010314 4 + +reset: + ssh jlime poke 0x10010318 2048 + ssh jlime poke 0x10010314 2048 diff --git a/atusb/fw3/atusb.c b/atusb/fw3/atusb.c new file mode 100644 index 0000000..d25fec5 --- /dev/null +++ b/atusb/fw3/atusb.c @@ -0,0 +1,34 @@ +#include + +#include + +#define F_CPU 8000000UL +#include + +#include "usb.h" + +#include "board.h" +#include "spi.h" +#include "atusb/ep0.h" + + +int main(void) +{ + board_init(); + spi_init(); + reset_rf(); + + /* now we should be at 8 MHz */ + +#if 0 + led(1); + _delay_ms(100); + led(0); +#endif + + usb_init(); + ep0_init(); + + while (1) + usb_poll(); +} diff --git a/atusb/fw3/board.h b/atusb/fw3/board.h new file mode 100644 index 0000000..ada425f --- /dev/null +++ b/atusb/fw3/board.h @@ -0,0 +1,54 @@ +#ifndef BOARD_H +#define BOARD_H + +#include + + +#define LED_PORT B +#define LED_BIT 6 +#define nRST_RF_PORT C +#define nRST_RF_BIT 7 +#define SLP_TR_PORT B +#define SLP_TR_BIT 4 + +#define SCLK_PORT D +#define SCLK_BIT 5 +#define MOSI_PORT D +#define MOSI_BIT 3 + +#define MISO_PORT D +#define MISO_BIT 2 +#define nSS_PORT D +#define nSS_BIT 1 +#define IRQ_RF_PORT D +#define IRQ_RF_BIT 0 + + +#define SET_2(p, b) PORT##p |= 1 << (b) +#define CLR_2(p, b) PORT##p &= ~(1 << (b)) +#define IN_2(p, b) DDR##p &= ~(1 << (b)) +#define OUT_2(p, b) DDR##p |= 1 << (b) +#define PIN_2(p, b) ((PIN##p >> (b)) & 1) + +#define SET_1(p, b) SET_2(p, b) +#define CLR_1(p, b) CLR_2(p, b) +#define IN_1(p, b) IN_2(p, b) +#define OUT_1(p, b) OUT_2(p, b) +#define PIN_1(p, b) PIN_2(p, b) + +#define SET(n) SET_1(n##_PORT, n##_BIT) +#define CLR(n) CLR_1(n##_PORT, n##_BIT) +#define IN(n) IN_1(n##_PORT, n##_BIT) +#define OUT(n) OUT_1(n##_PORT, n##_BIT) +#define PIN(n) PIN_1(n##_PORT, n##_BIT) + +void reset_rf(void); +uint8_t read_irq(void); +void led(int on); +void panic(void); +void board_init(void); + +void rf_init(void); +void rf_send(const char *s); + +#endif /* !BOARD_H */ diff --git a/atusb/fw3/descr.c b/atusb/fw3/descr.c new file mode 100644 index 0000000..2c7a41c --- /dev/null +++ b/atusb/fw3/descr.c @@ -0,0 +1,99 @@ +/* + * atspi/descr.c - USB descriptors + * + * Written 2008-2011 by Werner Almesberger + * Copyright 2008-2011 Werner Almesberger + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include "usb.h" + + +#define USB_VENDOR 0x20b7 /* Qi Hardware */ +#define USB_PRODUCT 0x1540 /* ben-wpan atusb */ + +#define LE(x) ((uint16_t) (x) & 0xff), ((uint16_t) (x) >> 8) + +/* + * Device descriptor + */ + +const uint8_t device_descriptor[18] = { + 18, /* bLength */ + USB_DT_DEVICE, /* bDescriptorType */ + LE(0x200), /* bcdUSB */ + USB_CLASS_VENDOR_SPEC, /* bDeviceClass */ + 0x00, /* bDeviceSubClass */ + 0x00, /* bDeviceProtocol */ + EP0_SIZE, /* bMaxPacketSize */ + LE(USB_VENDOR), /* idVendor */ + LE(USB_PRODUCT), /* idProduct */ + LE(0x0001), /* bcdDevice */ + 0, /* iManufacturer */ + 0, /* iProduct */ + 0, /* iSerialNumber */ + 1 /* bNumConfigurations */ +}; + + +/* + * Our configuration + * + * We're always bus-powered. + */ + +const uint8_t config_descriptor[] = { + 9, /* bLength */ + USB_DT_CONFIG, /* bDescriptorType */ +#if 0 + LE(9+9+7+7), /* wTotalLength */ +#else + LE(9+9), /* wTotalLength */ +#endif + 1, /* bNumInterfaces */ + 1, /* bConfigurationValue (> 0 !) */ + 0, /* iConfiguration */ + USB_ATTR_BUS_POWERED, /* bmAttributes */ + 50/2, /* bMaxPower (50 mA) */ + + /* Interface #0 */ + + 9, /* bLength */ + USB_DT_INTERFACE, /* bDescriptorType */ + 0, /* bInterfaceNumber */ + 0, /* bAlternateSetting */ +#if 0 + 2, /* bNumEndpoints */ +#else + 0, +#endif + USB_CLASS_VENDOR_SPEC, /* bInterfaceClass */ + 0, /* bInterfaceSubClass */ + 0, /* bInterfaceProtocol */ + 0, /* iInterface */ + +#if 0 + /* EP OUT */ + + 7, /* bLength */ + USB_DT_ENDPOINT, /* bDescriptorType */ + 0x01, /* bEndPointAddress */ + 0x02, /* bmAttributes (bulk) */ + LE(EP1_SIZE), /* wMaxPacketSize */ + 0, /* bInterval */ + + /* EP IN */ + + 7, /* bLength */ + USB_DT_ENDPOINT, /* bDescriptorType */ + 0x81, /* bEndPointAddress */ + 0x02, /* bmAttributes (bulk) */ + LE(EP1_SIZE), /* wMaxPacketSize */ + 0, /* bInterval */ +#endif +}; diff --git a/atusb/fw3/ep0.c b/atusb/fw3/ep0.c new file mode 100644 index 0000000..443ba9f --- /dev/null +++ b/atusb/fw3/ep0.c @@ -0,0 +1,194 @@ +/* + * atspi/ep0.c - EP0 extension protocol + * + * Written 2008-2010 by Werner Almesberger + * Copyright 2008-2010 Werner Almesberger + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include + +#include + +#ifndef NULL +#define NULL 0 +#endif + +//#include "regs.h" +//#include "uart.h" +//#include "usb.h" + +#include "usb.h" + +#include "at86rf230.h" +#include "atusb/ep0.h" +//#include "version.h" +#include "board.h" +#include "spi.h" + + +#define HW_TYPE HW_TYPE_110131 +static const char *build_date = "today"; +static unsigned build_number = 42; + +#define debug(...) +#define error(...) + + +static const uint8_t id[] = { EP0ATUSB_MAJOR, EP0ATUSB_MINOR, HW_TYPE }; +static uint8_t buf[MAX_PSDU+3]; /* command, PHDR, and LQI */ +static uint8_t size; + + +static void do_buf_write(void *user) +{ + uint8_t i; + + spi_begin(); + for (i = 0; i != size; i++) + spi_send(buf[i]); + spi_end(); +} + + +#define BUILD_OFFSET 7 /* '#' plus "65535" plus ' ' */ + + +static int my_setup(struct setup_request *setup) +{ + unsigned tmp; + uint8_t i; + + switch (setup->bmRequestType | setup->bRequest << 8) { + case ATUSB_FROM_DEV(ATUSB_ID): + debug("ATUSB_ID\n"); + if (setup->wLength > 3) + return 0; + usb_send(&eps[0], id, setup->wLength, NULL, NULL); + return 1; + case ATUSB_FROM_DEV(ATUSB_BUILD): + debug("ATUSB_BUILD\n"); + tmp = build_number; + for (i = BUILD_OFFSET-2; tmp; i--) { + buf[i] = (tmp % 10)+'0'; + tmp /= 10; + } + buf[i] = '#'; + buf[BUILD_OFFSET-1] = ' '; + for (size = 0; build_date[size]; size++) + buf[BUILD_OFFSET+size] = build_date[size]; + size += BUILD_OFFSET-i; + if (size > setup->wLength) + return 0; + usb_send(&eps[0], buf+i, size, NULL, NULL); + return 1; + +#ifdef NOTYET + case ATUSB_TO_DEV(ATUSB_RESET): + debug("ATUSB_RESET\n"); + RSTSRC = SWRSF; + while (1); +#endif + + case ATUSB_TO_DEV(ATUSB_RF_RESET): + debug("ATUSB_RF_RESET\n"); + reset_rf(); + //ep_send_zlp(EP_CTRL); + return 1; + + case ATUSB_FROM_DEV(ATUSB_POLL_INT): + debug("ATUSB_POLL_INT\n"); + if (setup->wLength < 1) + return 0; + *buf = read_irq(); + usb_send(&eps[0], buf, 1, NULL, NULL); + return 1; + + case ATUSB_TO_DEV(ATUSB_REG_WRITE): + debug("ATUSB_REG_WRITE\n"); + spi_begin(); + spi_send(AT86RF230_REG_WRITE | setup->wIndex); + spi_send(setup->wValue); + spi_end(); + //ep_send_zlp(EP_CTRL); + return 1; + case ATUSB_FROM_DEV(ATUSB_REG_READ): + debug("ATUSB_REG_READ\n"); + spi_begin(); + spi_send(AT86RF230_REG_READ | setup->wIndex); + *buf = spi_recv(); + spi_end(); + usb_send(&eps[0], buf, 1, NULL, NULL); + return 1; + + case ATUSB_TO_DEV(ATUSB_BUF_WRITE): + debug("ATUSB_BUF_WRITE\n"); + if (setup->wLength < 1) + return 0; + if (setup->wLength > MAX_PSDU) + return 0; + buf[0] = AT86RF230_BUF_WRITE; + buf[1] = setup->wLength; + size = setup->wLength+2; + usb_recv(&eps[0], buf+2, setup->wLength, do_buf_write, NULL); + return 1; + case ATUSB_FROM_DEV(ATUSB_BUF_READ): + debug("ATUSB_BUF_READ\n"); + if (setup->wLength < 2) /* PHR+LQI */ + return 0; + if (setup->wLength > MAX_PSDU+2) /* PHR+PSDU+LQI */ + return 0; + spi_begin(); + spi_send(AT86RF230_BUF_READ); + size = spi_recv(); + if (size >= setup->wLength) + size = setup->wLength-1; + for (i = 0; i != size+1; i++) + buf[i] = spi_recv(); + spi_end(); + usb_send(&eps[0], buf, size+1, NULL, NULL); + return 1; + + case ATUSB_TO_DEV(ATUSB_SRAM_WRITE): + debug("ATUSB_SRAM_WRITE\n"); + if (setup->wIndex > SRAM_SIZE) + return 0; + if (setup->wIndex+setup->wLength > SRAM_SIZE) + return 0; + buf[0] = AT86RF230_SRAM_WRITE; + buf[1] = setup->wIndex; + size = setup->wLength+2; + usb_recv(&eps[0], buf+2, setup->wLength, do_buf_write, NULL); + return 1; + case ATUSB_TO_DEV(ATUSB_SRAM_READ): + debug("ATUSB_SRAM_READ\n"); + if (setup->wIndex > SRAM_SIZE) + return 0; + if (setup->wIndex+setup->wLength > SRAM_SIZE) + return 0; + spi_begin(); + spi_send(AT86RF230_SRAM_READ); + spi_send(setup->wIndex); + for (i = 0; i != size; i++) + buf[i] = spi_recv(); + spi_end(); + usb_send(&eps[0], buf, size, NULL, NULL); + return 1; + + default: + error("Unrecognized SETUP: 0x%02x 0x%02x ...\n", + setup->bmRequestType, setup->bRequest); + return 0; + } +} + + +void ep0_init(void) +{ + user_setup = my_setup; +} diff --git a/atusb/fw3/spi.c b/atusb/fw3/spi.c new file mode 100644 index 0000000..585c360 --- /dev/null +++ b/atusb/fw3/spi.c @@ -0,0 +1,45 @@ +#include + +#include + +#include "board.h" +#include "spi.h" + + +void spi_begin(void) +{ + CLR(nSS); +} + + +uint8_t spi_io(uint8_t v) +{ +// while (!(UCSR1A & 1 << UDRE1)); + UDR1 = v; + while (!(UCSR1A & 1 << RXC1)); + return UDR1; +} + + +void spi_end(void) +{ +// while (!(UCSR1A & 1 << TXC1)); + SET(nSS); +} + + +void spi_init(void) +{ + SET(nSS); + OUT(SCLK); + OUT(MOSI); + OUT(nSS); + IN(MISO); + + UBRR1 = 0; /* set bit rate to zero to begin */ + UCSR1C = 1 << UMSEL11 | 1 << UMSEL10; + /* set MSPI, MSB first, SPI data mode 0 */ + UCSR1B = 1 << RXEN1 | 1 << TXEN1; + /* enable receiver and transmitter */ + UBRR1 = 0; /* reconfirm the bit rate */ +} diff --git a/atusb/fw3/spi.h b/atusb/fw3/spi.h new file mode 100644 index 0000000..5c514da --- /dev/null +++ b/atusb/fw3/spi.h @@ -0,0 +1,15 @@ +#ifndef SPI_H +#define SPI_H + +#include + + +void spi_begin(void); +uint8_t spi_io(uint8_t v); +void spi_end(void); +void spi_init(void); + +#define spi_send(v) (void) spi_io(v) +#define spi_recv(v) spi_io(0) + +#endif /* !SPI_H */ diff --git a/atusb/fw3/usb2/usb.c b/atusb/fw3/usb2/usb.c new file mode 100644 index 0000000..60d30a4 --- /dev/null +++ b/atusb/fw3/usb2/usb.c @@ -0,0 +1,349 @@ +/* + * u/usb.c - USB hardware setup and standard device requests + * + * Written 2008-2011 by Werner Almesberger + * Copyright 2008-2011 Werner Almesberger + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * Known issues: + * - no suspend/resume + * - EP0-sized packets cause an (otherwise harmless) SUEND at the end of the + * packet + * - #ifdef hell + */ + +/* + * This code follows the register read/write sequences from the examples in + * SiLabs/MCU/Examples/C8051F326_7/USB_Interrupt/Firmware/F326_USB_Main.c and + * SiLabs/MCU/Examples/C8051F326_7/USB_Interrupt/Firmware/F326_USB_ISR.c + * + * More resources: + * http://www.beyondlogic.org/usbnutshell/usb1.htm + */ + +#include + +#define F_CPU 8000000UL +#include + +#include +#include "usb.h" +#include "../board.h" + + +#ifndef NULL +#define NULL 0 +#endif + +#define NUM_EPS 1 + +#if 1 +extern void panic(void); +#define BUG_ON(cond) do { if (cond) panic(); } while (0) +#else +#define BUG_ON(cond) +#endif + +struct ep_descr eps[5]; + +int (*user_setup)(struct setup_request *setup); +int (*user_get_descriptor)(uint8_t type, uint8_t index, + const uint8_t * const *reply, uint8_t *size); +void (*user_reset)(void); + + +static uint8_t addr; + + +void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf, + uint8_t size, void (*callback)(void *user), void *user) +{ + BUG_ON(ep->state); + ep->state = state; + ep->buf = buf; + ep->end = buf+size; + ep->callback = callback; + ep->user = user; +} + + +static uint16_t usb_read_word(void) +{ + uint8_t low; + + low = UEDATX; + return low | UEDATX << 8; +} + + +static int get_descriptor(uint8_t type, uint8_t index, uint16_t length) +{ + const uint8_t *reply; + uint8_t size; + + switch (type) { + case USB_DT_DEVICE: + reply = device_descriptor; + size = reply[0]; + break; + case USB_DT_CONFIG: + if (index) + return 0; + reply = config_descriptor; + size = reply[2]; + break; + default: + if (!user_get_descriptor) + return 0; + if (!user_get_descriptor(type, index, &reply, &size)) + return 0; + } + if (length < size) + size = length; + usb_send(&eps[0], reply, size, NULL, NULL); + return 1; +} + + +static void enable_addr(void *user) +{ + while (!(UEINTX & (1 << TXINI))); + UDADDR = addr | 1 << ADDEN; +} + + +/* + * Process a SETUP packet. Hardware ensures that length is 8 bytes. + */ + + +static int handle_setup(void) +{ + struct setup_request setup; + + BUG_ON(UEBCLX < 8); + + setup.bmRequestType = UEDATX; + setup.bRequest = UEDATX; + setup.wValue = usb_read_word(); + setup.wIndex = usb_read_word(); + setup.wLength = usb_read_word(); + +// UEINTX &= ~(1 << RXSTPI); + + switch (setup.bmRequestType | setup.bRequest << 8) { + + /* + * Device request + * + * See http://www.beyondlogic.org/usbnutshell/usb6.htm + */ + + case FROM_DEVICE(GET_STATUS): + if (setup.wLength != 2) + return 0; + usb_send(&eps[0], "\000", 2, NULL, NULL); + break; + case TO_DEVICE(CLEAR_FEATURE): + break; + case TO_DEVICE(SET_FEATURE): + return 0; + case TO_DEVICE(SET_ADDRESS): + addr = setup.wValue; + UDADDR = addr; + usb_send(&eps[0], NULL, 0, enable_addr, NULL); + break; + case FROM_DEVICE(GET_DESCRIPTOR): + if (!get_descriptor(setup.wValue >> 8, setup.wValue, + setup.wLength)) + return 0; + break; + case TO_DEVICE(SET_DESCRIPTOR): + return 0; + case FROM_DEVICE(GET_CONFIGURATION): + usb_send(&eps[0], "", 1, NULL, NULL); + break; + case TO_DEVICE(SET_CONFIGURATION): + if (setup.wValue != config_descriptor[5]) + return 0; + break; + + /* + * Interface request + */ + + case FROM_INTERFACE(GET_STATUS): + return 0; + case TO_INTERFACE(CLEAR_FEATURE): + return 0; + case TO_INTERFACE(SET_FEATURE): + return 0; + case FROM_INTERFACE(GET_INTERFACE): + return 0; + case TO_INTERFACE(SET_INTERFACE): + { + const uint8_t *interface_descriptor = + config_descriptor+9; + + if (setup.wIndex != interface_descriptor[2] || + setup.wValue != interface_descriptor[3]) + return 0; + } + break; + + /* + * Endpoint request + */ + + case FROM_ENDPOINT(GET_STATUS): + return 0; + case TO_ENDPOINT(CLEAR_FEATURE): + return 0; + case TO_ENDPOINT(SET_FEATURE): + return 0; + case FROM_ENDPOINT(SYNCH_FRAME): + return 0; + + default: + if (!user_setup) + return 0; + if (!user_setup(&setup)) + return 0; + } + + if (!(setup.bmRequestType & 0x80) && eps[0].state == EP_IDLE) + usb_send(&eps[0], NULL, 0, NULL, NULL); + return 1; +} + + +static int ep_rx(struct ep_descr *ep) +{ + uint8_t size; + + size = UEBCLX; + if (size > ep->end-ep->buf) + return 0; + while (size--) + *ep->buf++ = UEDATX; + if (ep->buf == ep->end) { + ep->state = EP_IDLE; + if (ep->callback) + ep->callback(ep->user); + if (ep == &eps[0]) + usb_send(ep, NULL, 0, NULL, NULL); + } + return 1; +} + + +static void ep_tx(struct ep_descr *ep) +{ + uint8_t size = ep->end-ep->buf; + uint8_t left; + + if (size > ep->size) + size = ep->size; + for (left = size; left; left--) + UEDATX = *ep->buf++; + if (size == ep->size) + return; + ep->state = EP_IDLE; +} + + +static void handle_ep(int n) +{ + struct ep_descr *ep = eps+n; + int res; + + UENUM = n; + if (UEINTX & (1 << RXSTPI)) { + /* @@@ EP_RX. EP_TX: cancel */ + if (!handle_setup()) + goto stall; + UEINTX &= ~(1 << RXSTPI); + } + if (UEINTX & (1 << RXOUTI)) { + /* @@ EP_TX: cancel */ + if (ep->state != EP_RX) + goto stall; + if (!ep_rx(ep)) + goto stall; +// UEINTX &= ~(1 << RXOUTI); + UEINTX &= ~(1 << RXOUTI | 1 << FIFOCON); + } + if (UEINTX & (1 << STALLEDI)) { + ep->state = EP_IDLE; + UEINTX &= ~(1 << STALLEDI); + } + if (UEINTX & (1 << TXINI)) { + /* @@ EP_RX: cancel */ + if (ep->state == EP_TX) { + ep_tx(ep); + UEINTX &= ~(1 << TXINI); + if (ep->state == EP_IDLE && ep->callback) + ep->callback(ep->user); + } + } + return; + +stall: + UEINTX &= ~(1 << RXSTPI | 1 << RXOUTI | 1 << STALLEDI); + ep->state = EP_IDLE; + UECONX |= 1 << STALLRQ; +} + + +void usb_poll(void) +{ + uint8_t flags, i; + + flags = UEINT; + for (i = 0; i != NUM_EPS; i++) + if (1 || flags & (1 << i)) + handle_ep(i); + /* @@@ USB bus reset */ +} + + +static void ep_init(void) +{ + UENUM = 0; + UECONX = (1 << RSTDT) | (1 << EPEN); /* enable */ + UECFG0X = 0; /* control, direction is ignored */ + UECFG1X = 3 << EPSIZE0; /* 64 bytes */ + UECFG1X |= 1 << ALLOC; + + while (!(UESTA0X & (1 << CFGOK))); + + eps[0].state = EP_IDLE; + eps[0].size = 64; +} + + +void usb_init(void) +{ + USBCON |= 1 << FRZCLK; /* freeze the clock */ + + /* enable the PLL and wait for it to lock */ + PLLCSR &= ~(1 << PLLP2 | 1 << PLLP1 | 1 << PLLP0); + PLLCSR |= 1 << PLLE; + while (!(PLLCSR & (1 << PLOCK))); + + USBCON &= ~(1 << USBE); /* reset the controller */ + USBCON |= 1 << USBE; + + USBCON &= ~(1 << FRZCLK); /* thaw the clock */ + + UDCON &= ~(1 << DETACH); /* attach the pull-up */ + UDCON |= 1 << RSTCPU; /* reset CPU on bus reset */ + + ep_init(); +} diff --git a/atusb/fw3/usb2/usb.h b/atusb/fw3/usb2/usb.h new file mode 100644 index 0000000..a5fd92e --- /dev/null +++ b/atusb/fw3/usb2/usb.h @@ -0,0 +1,146 @@ +/* + * common/usb.h - USB hardware setup and standard device requests + * + * Written 2008, 2009 by Werner Almesberger + * Copyright 2008, 2009 Werner Almesberger + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + + +#ifndef USB_H +#define USB_H + + +#include + + +/* + * Descriptor types + * + * Reuse libusb naming scheme (/usr/include/usb.h) + */ + +#define USB_DT_DEVICE 1 +#define USB_DT_CONFIG 2 +#define USB_DT_STRING 3 +#define USB_DT_INTERFACE 4 +#define USB_DT_ENDPOINT 5 + +/* + * Device classes + * + * Reuse libusb naming scheme (/usr/include/usb.h) + */ + +#define USB_CLASS_PER_INTERFACE 0xfe +#define USB_CLASS_VENDOR_SPEC 0xff + +/* + * Configuration attributes + */ + +#define USB_ATTR_BUS_POWERED 0x80 +#define USB_ATTR_SELF_POWERED 0x40 +#define USB_ATTR_REMOTE_WAKEUP 0x20 + +/* + * Setup request types + */ + +#define TO_DEVICE(req) (0x00 | (req) << 8) +#define FROM_DEVICE(req) (0x80 | (req) << 8) +#define TO_INTERFACE(req) (0x01 | (req) << 8) +#define FROM_INTERFACE(req) (0x81 | (req) << 8) +#define TO_ENDPOINT(req) (0x02 | (req) << 8) +#define FROM_ENDPOINT(req) (0x82 | (req) << 8) + +/* + * Setup requests + */ + +#define GET_STATUS 0x00 +#define CLEAR_FEATURE 0x01 +#define SET_FEATURE 0x03 +#define SET_ADDRESS 0x05 +#define GET_DESCRIPTOR 0x06 +#define SET_DESCRIPTOR 0x07 +#define GET_CONFIGURATION 0x08 +#define SET_CONFIGURATION 0x09 +#define GET_INTERFACE 0x0a +#define SET_INTERFACE 0x0b +#define SYNCH_FRAME 0x0c + + +/* + * Odd. sdcc seems to think "x" assumes the size of the destination, i.e., + * uint8_t. Hence the cast. + */ + +#define LE(x) ((uint16_t) (x) & 0xff), ((uint16_t) (x) >> 8) + +#define LO(x) (((uint8_t *) &(x))[0]) +#define HI(x) (((uint8_t *) &(x))[1]) + + +#ifdef LOW_SPEED +#define EP0_SIZE 8 +#else +#define EP0_SIZE 64 +#endif + +#define EP1_SIZE 64 /* simplify */ + + +enum ep_state { + EP_IDLE, + EP_RX, + EP_TX, + EP_STALL, +}; + +struct ep_descr { + enum ep_state state; + uint8_t *buf; + uint8_t *end; + uint8_t size; + void (*callback)(void *user); + void *user; +}; + +struct setup_request { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +}; + + +extern const uint8_t device_descriptor[]; +extern const uint8_t config_descriptor[]; +extern struct ep_descr eps[]; + +extern int (*user_setup)(struct setup_request *setup); +extern int (*user_get_descriptor)(uint8_t type, uint8_t index, + const uint8_t * const *reply, uint8_t *size); +extern void (*user_reset)(void); + + +#define usb_left(ep) ((ep)->end-(ep)->buf) +#define usb_send(ep, buf, size, callback, user) \ + usb_io(ep, EP_TX, (void *) buf, size, callback, user) +#define usb_recv(ep, buf, size, callback, user) \ + usb_io(ep, EP_RX, buf, size, callback, user) + +void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf, + uint8_t size, void (*callback)(void *user), void *user); + + +void usb_init(void); +void usb_poll(void); + +#endif /* !USB_H */