diff --git a/cpu/at91sam7s/Makefile.at91sam7s b/cpu/at91sam7s/Makefile.at91sam7s index 2b54b4bfb..e5fe22fb2 100644 --- a/cpu/at91sam7s/Makefile.at91sam7s +++ b/cpu/at91sam7s/Makefile.at91sam7s @@ -5,7 +5,7 @@ CONTIKI_CPU=$(CONTIKI)/cpu/at91sam7s ### Define the source files we have in the AT91SAM7S port -CONTIKI_CPU_DIRS = . dbg-io loader +CONTIKI_CPU_DIRS = . dbg-io loader usb AT91SAM7S = clock.c debug-uart.c interrupt-utils.c newlib-syscalls.c sys-interrupt.c rtimer-arch.c rtimer-arch-interrupt.c uip-log.c @@ -14,10 +14,12 @@ APPDIRS += $(CONTIKI)/cpu/at91sam7s/loader ELFLOADER = elfloader-otf.c elfloader-arm.c symtab.c cfs-ram.c +USB = usb-proto.c usb-handler.c usb-interrupt.c cdc-acm.c descriptors.c string-descriptors.c + TARGETLIBS = random.c dbg-printf.c dbg-puts.c dbg-putchar.c dbg-sprintf.c strformat.c CONTIKI_TARGET_SOURCEFILES += $(AT91SAM7S) $(SYSAPPS) $(ELFLOADER) \ - $(TARGETLIBS) $(UIPDRIVERS) + $(TARGETLIBS) $(UIPDRIVERS) $(USB) CONTIKI_SOURCEFILES += $(CONTIKI_TARGET_SOURCEFILES) @@ -33,6 +35,8 @@ NM = arm-elf-nm OBJCOPY = arm-elf-objcopy STRIP = arm-elf-strip +XSLTPROC=xsltproc + PROJECT_OBJECTFILES += ${addprefix $(OBJECTDIR)/,$(CONTIKI_TARGET_MAIN:.c=.o)} LINKERSCRIPT = $(CONTIKI_CPU)/AT91SAM7S64-ROM.ld @@ -54,7 +58,7 @@ ARM_FLAGS= CFLAGSNO = -I. -I$(CONTIKI)/core -I$(CONTIKI_CPU) -I$(CONTIKI_CPU)/loader \ - -I$(CONTIKI_CPU)/dbg-io\ + -I$(CONTIKI_CPU)/dbg-io \ -I$(CONTIKI)/platform/$(TARGET) \ ${addprefix -I,$(APPDIRS)} \ -DWITH_UIP -DWITH_ASCII -DMCK=$(MCK) \ @@ -94,6 +98,7 @@ $(OBJECTDIR)/interrupt-utils.o: interrupt-utils.c $(OBJECTDIR)/%.o: %.c $(CC) $(CFLAGS) $(THUMB_FLAGS) -c $< -o $@ +CUSTOM_RULE_S_TO_OBJECTDIR_O = yes %.o: %.S $(CC) $(CFLAGS) $(ARM_FLAGS) $< -c @@ -150,6 +155,10 @@ endif empty-symbols.c: @${CONTIKI}/tools/make-empty-symbols +$(CONTIKI_CPU)/usb/string-descriptors.c: \ + $(CONTIKI_CPU)/usb/string-descriptors.xml + $(XSLTPROC) $(CONTIKI_CPU)/usb/string-descriptors.xslt $^ >$@ + upload_ocd_%: %.bin cp $< /tmp/openocd_write.bin # Clear lock bits @@ -170,4 +179,4 @@ ocd_reset: $(OBJECTDIR)/elfloader.o: echo -n >$@ -.PRECIOUS: %-nosyms.$(TARGET) \ No newline at end of file +.PRECIOUS: %-nosyms.$(TARGET) diff --git a/cpu/at91sam7s/usb/cdc-acm.c b/cpu/at91sam7s/usb/cdc-acm.c new file mode 100644 index 000000000..898ca4f6f --- /dev/null +++ b/cpu/at91sam7s/usb/cdc-acm.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include + +static unsigned int +handle_cdc_acm_requests() +{ + printf("CDC request %02x %02x\n", usb_setup_buffer.bmRequestType, usb_setup_buffer.bRequest); + switch(usb_setup_buffer.bmRequestType) { + case 0x21: /* CDC interface OUT requests */ + /* Check if it's the right interface */ + if (usb_setup_buffer.wIndex != 0) return 0; + switch(usb_setup_buffer.bRequest) { + case SET_CONTROL_LINE_STATE: + if (usb_setup_buffer.wValue & 0x02) { + puts("Carrier on"); + } else { + puts("Carrier off"); + } + if (usb_setup_buffer.wValue & 0x01) { + puts("DTE on"); + } else { + puts("DTE off"); + } + usb_send_ctrl_status(); + return 1; + + case SEND_ENCAPSULATED_COMMAND: + printf("Got CDC command: length %d\n", usb_ctrl_data_len); + usb_send_ctrl_status(); + return 1; + + + case SET_LINE_CODING: + if (usb_ctrl_data_len == 7) { + static const char parity_char[] = {'N', 'O', 'E', 'M', 'S'}; + static const char *stop_bits_str[] = {"1","1.5","2"}; + const struct usb_cdc_line_coding *coding = + (const struct usb_cdc_line_coding *)usb_ctrl_data_buffer; + char parity = ((coding->bParityType > 4) + ? '?' : parity_char[coding->bParityType]); + const char *stop_bits = ((coding->bCharFormat > 2) + ? "?" : stop_bits_str[coding->bCharFormat]); + printf("Got CDC line coding: %ld/%d/%c/%s\n", + coding->dwDTERate, coding->bDataBits, parity, stop_bits); + usb_send_ctrl_status(); + } else { + return 0; + } + return 1; + } + break; + case 0xa1: /* CDC interface IN requests */ + if (usb_setup_buffer.wIndex != 0) return 0; + switch(usb_setup_buffer.bRequest) { + case GET_ENCAPSULATED_RESPONSE: + printf("CDC response"); + usb_send_ctrl_status(); + return 1; + } + } + return 0; +} + +static const struct USBRequestHandler cdc_acm_request_handler = + { + 0x21, 0x7f, + 0x00, 0x00, + handle_cdc_acm_requests + }; + +static struct USBRequestHandlerHook cdc_acm_request_hook = + { + NULL, + &cdc_acm_request_handler + }; + +void +usb_cdc_acm_setup() +{ + usb_register_request_handler(&cdc_acm_request_hook); +} diff --git a/cpu/at91sam7s/usb/cdc-acm.h b/cpu/at91sam7s/usb/cdc-acm.h new file mode 100644 index 000000000..4a85dc208 --- /dev/null +++ b/cpu/at91sam7s/usb/cdc-acm.h @@ -0,0 +1,7 @@ +#ifndef __CDC_ACM_H__UFV6K50827__ +#define __CDC_ACM_H__UFV6K50827__ + +void +usb_cdc_acm_setup(); + +#endif /* __CDC_ACM_H__UFV6K50827__ */ diff --git a/cpu/at91sam7s/usb/cdc.h b/cpu/at91sam7s/usb/cdc.h new file mode 100644 index 000000000..cd5923385 --- /dev/null +++ b/cpu/at91sam7s/usb/cdc.h @@ -0,0 +1,202 @@ +#ifndef __CDC_H__K1Q26ESJOC__ +#define __CDC_H__K1Q26ESJOC__ +#include +/* Communication Class */ +/* Class code */ +#define CDC 0x02 + +/* Interface subclass codes */ +#define CDC_RESERVED 0x00 +#define DIRECT_LINE_CONTROL_MODEL 0x01 +#define ABSTRACT_CONTROL_MODEL 0x02 +#define TELEPHONE_CONTROL_MODEL 0x03 +#define MULTI_CHANNEL_CONTROL_MODEL 0x04 +#define CAPI_CONTROL_MODEL 0x05 +#define ETHERNET_NETWORKING_CONTROL_MODEL 0x06 +#define ATM_NETWORKING_CONTROL_MODEL 0x07 + +/* Protocols */ +#define V_25TER_PROTOCOL 0x01 + +/* Requests */ +#define SEND_ENCAPSULATED_COMMAND 0x00 +#define GET_ENCAPSULATED_RESPONSE 0x01 +#define SET_COMM_FEATURE 0x02 +#define GET_COMM_FEATURE 0x03 +#define CLEAR_COMM_FEATURE 0x04 + +#define SET_AUX_LINE_STATE 0x10 +#define SET_HOOK_STATE 0x11 +#define PULSE_SETUP 0x12 +#define SEND_PULSE 0x13 +#define SET_PULSE_TIME 0x14 +#define RING_AUX_JACK 0x15 + +#define SET_LINE_CODING 0x20 +#define GET_LINE_CODING 0x21 +#define SET_CONTROL_LINE_STATE 0x22 +#define SEND_BREAK 0x23 + +#define SET_RINGER_PARMS 0x30 +#define GET_RINGER_PARMS 0x31 +#define SET_OPERATION_PARMS 0x32 +#define GET_OPERATION_PARMS 0x33 +#define SET_LINE_PARMS 0x34 +#define GET_LINE_PARMS 0x35 +#define DIAL_DIGITS 0x36 + +#define SET_UNIT_PARAMETER 0x37 +#define GET_UNIT_PARAMETER 0x38 +#define CLEAR_UNIT_PARAMETER 0x39 + +#define GET_PROFILE 0x3a + +#define SET_ETHERNET_MULTICAST_FILTERS 0x40 +#define GET_ETHERNET_MULTICAST_FILTERS 0x41 +#define GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER 0x42 +#define SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER 0x43 +#define GET_ETHERNET_STATISTIC 0x44 + +#define SET_ATM_D ATA_FORMAT 0x50 +#define GET_ATM_DEVICE_STATISTICS 0x51 +#define SET_ATM_DEFAULT_VC 0x52 +#define GET_ATM_VC_STATISTICS 0x53 + + +/* Notifications */ +#define NETWORK_CONNECTION 0x00 +#define RESPONSE_AVAILABLE 0x01 + +#define AUX_JACK_HOOK_STATE 0x08 +#define RING_DETECT 0x09 + +#define SERIAL_STATE 0x20 + +#define CALL_STATE_CHANGE 0x28 +#define LINE_STATE_CHANGE 0x29 +#define CONNECTION_SPEED_CHANGE 0x2a + +/* Data interface */ + +/* Class code */ +#define CDC_DATA 0x0a + +/* Protocols */ +#define I_430_PROTOCOL 0x30 +#define ISO_IEC_3_1993_PROTOCOL 0x31 +#define TRANSPARENT_PROTOCOL 0x32 +#define Q_921M_PROTOCOL 0x50 +#define Q_921_PROTOCOL 0x51 +#define Q_921TM_PROTOCOL 0x52 +#define V_42BIS_PROTOCOL 0x90 +#define Q_931_PROTOCOL 0x91 +#define V_120_PROTOCOL 0x93 +#define CDC_PROTOCOL 0xfe + +/* Descriptor subtypes */ + +#define CDC_FUNC_DESCR_HEADER 0x00 +#define CDC_FUNC_DESCR_CALL_MGMNT 0x01 +#define CDC_FUNC_DESCR_ABSTRACT_CTRL_MGMNT 0x02 +#define CDC_FUNC_DESCR_DIRECT_LINE_MGMNT 0x03 +#define CDC_FUNC_DESCR_RINGER_MGMNT 0x04 +#define CDC_FUNC_DESCR_TEL_STATE 0x05 +#define CDC_FUNC_DESCR_UNION 0x06 +#define CDC_FUNC_DESCR_COUNTRY 0x07 +#define CDC_FUNC_DESCR_TEL_MODE 0x08 +#define CDC_FUNC_DESCR_USB_TERM 0x09 +#define CDC_FUNC_DESCR_NET_TERM 0x0a +#define CDC_FUNC_DESCR_PROTOCOL_UNIT 0x0b +#define CDC_FUNC_DESCR_EXTENSION_UNIT 0x0c +#define CDC_FUNC_DESCR_MULTICH_MGMNT 0x0d +#define CDC_FUNC_DESCR_CAPI_MGMNT 0x0e +#define CDC_FUNC_DESCR_ETHERNET 0x0f +#define CDC_FUNC_DESCR_ATM 0x10 + + + +struct usb_cdc_header_func_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* CS_INTERFACE descriptor type */ + Uchar bDescriptorSubtype; /* CDC_FUNC_DESCR_HEADER subtype */ + Uint16 bcdCDC; /* Revision of class specification */ +} BYTE_ALIGNED; + +struct usb_cdc_call_mgmnt_func_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* CS_INTERFACE descriptor type */ + Uchar bDescriptorSubtype; /* CDC_FUNC_DESCR_CALL_MGMNT subtype */ + Uchar bmCapabilities; /* Capabilities */ + Uchar bDataInterface; /* Management data interface */ +} BYTE_ALIGNED; + +struct usb_cdc_abstract_ctrl_mgmnt_func_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* CS_INTERFACE descriptor type */ + Uchar bDescriptorSubtype; /* CDC_FUNC_DESCR_ABSTRACT_CTRL_MGMNT subtype*/ + Uchar bmCapabilities; /* Capabilities */ +} BYTE_ALIGNED; + +struct usb_cdc_direct_line_mgmnt_func_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* CS_INTERFACE descriptor type */ + Uchar bDescriptorSubtype; /* CDC_FUNC_DESCR_DIRECT_LINE_MGMNT subtype*/ + Uchar bmCapabilities; /* Capabilities */ +} BYTE_ALIGNED; + +struct usb_cdc_ringer_mgmnt_func_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* CS_INTERFACE descriptor type */ + Uchar bDescriptorSubtype; /* CDC_FUNC_DESCR_RINGER_MGMNT subtype*/ + Uchar bRingerVolSteps; /* Ringer volume steps */ + Uchar bNumRingerPatterns; /* Number of ringer patterns supported */ +} BYTE_ALIGNED; + +struct usb_cdc_tel_mode_func_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* CS_INTERFACE descriptor type */ + Uchar bDescriptorSubtype; /* CDC_FUNC_DESCR_TEL_MODE subtype*/ + Uchar bmCapabilities; /* Capabilities */ +} BYTE_ALIGNED; + +struct usb_cdc_tel_state_func_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* CS_INTERFACE descriptor type */ + Uchar bDescriptorSubtype; /* CDC_FUNC_DESCR_TEL_STATE subtype*/ + Uchar bmCapabilities; /* Capabilities */ +} BYTE_ALIGNED; + +struct usb_cdc_union_func_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* CS_INTERFACE descriptor type */ + Uchar bDescriptorSubtype; /* CDC_FUNC_DESCR_UNION subtype*/ + Uchar bMasterInterface; /* Master interface for union */ + Uchar bSlaveInterface[1]; /* Slave interfaces in union */ +} BYTE_ALIGNED; + +struct usb_cdc_country_func_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* CS_INTERFACE descriptor type */ + Uchar bDescriptorSubtype; /* CDC_FUNC_DESCR_COUNTRY subtype*/ + Uchar iCountryCodeRelDate; /* Release date for country codes */ + Uint16 wCountryCode[1]; /* Country codes */ +} BYTE_ALIGNED; + +struct usb_cdc_line_coding +{ + Uint32 dwDTERate; + Uchar bCharFormat; + Uchar bParityType; + Uchar bDataBits; +} BYTE_ALIGNED; + +#endif /* __CDC_H__K1Q26ESJOC__ */ diff --git a/cpu/at91sam7s/usb/descriptors.c b/cpu/at91sam7s/usb/descriptors.c new file mode 100644 index 000000000..691101c0b --- /dev/null +++ b/cpu/at91sam7s/usb/descriptors.c @@ -0,0 +1,124 @@ +#include "descriptors.h" +#include + +const struct usb_st_device_descriptor device_descriptor = + { + sizeof(struct usb_st_device_descriptor), + DEVICE, + 0x0210, + CDC, + 0, + 0, + CTRL_EP_SIZE, + 0xffff, + 0xffff, + 0x0030, + 2, + 1, + 3, + 1 + }; + +const struct configuration_st { + struct usb_st_configuration_descriptor configuration; + struct usb_st_interface_descriptor comm; + struct usb_cdc_header_func_descriptor header; + struct usb_cdc_abstract_ctrl_mgmnt_func_descriptor abstract_ctrl; + struct usb_cdc_union_func_descriptor union_descr; + struct usb_cdc_call_mgmnt_func_descriptor call_mgmt; +#if 1 + struct usb_st_endpoint_descriptor ep_notification; +#endif + struct usb_st_interface_descriptor data; + struct usb_st_endpoint_descriptor ep_in; + struct usb_st_endpoint_descriptor ep_out; +} BYTE_ALIGNED configuration_block = + { + /* Configuration */ + { + sizeof(configuration_block.configuration), + CONFIGURATION, + sizeof(configuration_block), + 2, + 1, + 0, + 0x80, + 50 + }, + { + sizeof(configuration_block.comm), + INTERFACE, + 0, + 0, + 1, + CDC, + ABSTRACT_CONTROL_MODEL, + V_25TER_PROTOCOL, + 0 + }, + { + sizeof(configuration_block.header), + CS_INTERFACE, + CDC_FUNC_DESCR_HEADER, + 0x0110 + }, + { + sizeof(configuration_block.abstract_ctrl), + CS_INTERFACE, + CDC_FUNC_DESCR_ABSTRACT_CTRL_MGMNT, + 0 + }, + { + sizeof(configuration_block.union_descr), + CS_INTERFACE, + CDC_FUNC_DESCR_UNION, + 0, /* Master */ + {1} /* Slave */ + }, + { + sizeof(configuration_block.call_mgmt), + CS_INTERFACE, + CDC_FUNC_DESCR_CALL_MGMNT, + 0x02, + 1 /* data interface */ + }, + { + sizeof(configuration_block.ep_notification), + ENDPOINT, + 0x83, + 0x03, + 8, + 100 + }, + { + sizeof(configuration_block.data), + INTERFACE, + 1, + 0, + 2, + CDC_DATA, + 0, + TRANSPARENT_PROTOCOL, + 0 + }, + { + sizeof(configuration_block.ep_in), + ENDPOINT, + 0x81, + 0x02, + 64, + 0 + }, + { + sizeof(configuration_block.ep_out), + ENDPOINT, + 0x02, + 0x02, + 64, + 0 + } + + }; + +const struct usb_st_configuration_descriptor const *configuration_head = +(struct usb_st_configuration_descriptor const*)&configuration_block; diff --git a/cpu/at91sam7s/usb/descriptors.h b/cpu/at91sam7s/usb/descriptors.h new file mode 100644 index 000000000..74f05284c --- /dev/null +++ b/cpu/at91sam7s/usb/descriptors.h @@ -0,0 +1,9 @@ +#ifndef __DESCRIPTORS_H__RPFUB8O7OV__ +#define __DESCRIPTORS_H__RPFUB8O7OV__ + +#include "usb.h" +#include + +extern const struct usb_st_device_descriptor device_descriptor; +extern const struct usb_st_configuration_descriptor const *configuration_head; +#endif /* __DESCRIPTORS_H__RPFUB8O7OV__ */ diff --git a/cpu/at91sam7s/usb/string-descriptors.dtd b/cpu/at91sam7s/usb/string-descriptors.dtd new file mode 100644 index 000000000..66f7469f3 --- /dev/null +++ b/cpu/at91sam7s/usb/string-descriptors.dtd @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/cpu/at91sam7s/usb/string-descriptors.h b/cpu/at91sam7s/usb/string-descriptors.h new file mode 100644 index 000000000..6d0ffea5f --- /dev/null +++ b/cpu/at91sam7s/usb/string-descriptors.h @@ -0,0 +1,16 @@ +#include "usb.h" +struct usb_st_string_language_map +{ + Uint16 lang_id; + const struct usb_st_string_descriptor * const *descriptors; +}; + +struct usb_st_string_languages +{ + Uchar num_lang; + Uchar max_index; + const struct usb_st_language_descriptor *lang_descr; + const struct usb_st_string_language_map map[1]; +}; + +extern const struct usb_st_string_languages * const string_languages; diff --git a/cpu/at91sam7s/usb/string-descriptors.xml b/cpu/at91sam7s/usb/string-descriptors.xml new file mode 100644 index 000000000..01d14f2c4 --- /dev/null +++ b/cpu/at91sam7s/usb/string-descriptors.xml @@ -0,0 +1,19 @@ + + + + 0x0409 + 0x041d + + + + Serial interface + Serieport + + + Fluffware + + + 0.01 + + + diff --git a/cpu/at91sam7s/usb/string-descriptors.xslt b/cpu/at91sam7s/usb/string-descriptors.xslt new file mode 100644 index 000000000..eab453326 --- /dev/null +++ b/cpu/at91sam7s/usb/string-descriptors.xslt @@ -0,0 +1,129 @@ + + + + + + #include "string-descriptors.h" + + + + static const struct { + struct usb_st_string_descriptor base; + Uint16 chars[ + + ]; + } string_descriptor_ + + _ + + + + + + all + + + = {{ + + , 3, {' + + '}}, { + + + + }}; + + + + + + static const struct usb_st_string_descriptor * string_table_ + + [] = { + + &string_descriptor_ + + _ + + + + + + all + + + + No string found for index + + and language + + + + + .base, + + }; + + + + static const struct { + struct usb_st_language_descriptor base; + Uint16 langs[ + + ]; + } language_descriptor = { + { + + , 3, { + + }}, { + + + , + + }}; + + + static const struct { + struct usb_st_string_languages base; + struct usb_st_string_language_map map[ + + ]; } + string_languages_full={{ + + , + + , &language_descriptor.base + , {{ + + , string_table_ + + }}}, { + + { + + , string_table_ + + }, + + } }; + const struct usb_st_string_languages * const string_languages = &string_languages_full.base; + + + + + + ' + + ' + + , + + + + + + + + + diff --git a/cpu/at91sam7s/usb/usb-api.h b/cpu/at91sam7s/usb/usb-api.h new file mode 100644 index 000000000..19026fbf8 --- /dev/null +++ b/cpu/at91sam7s/usb/usb-api.h @@ -0,0 +1,60 @@ +#ifndef __USB_API_H__SYN81IFYBN__ +#define __USB_API_H__SYN81IFYBN__ + +#include + +void +usb_setup(void); + +void +usb_set_user_process(struct process *p); + +void +usb_setup_bulk_endpoint(unsigned char addr, + unsigned char *buffer, unsigned int buf_size); +void +usb_setup_interrupt_endpoint(unsigned char addr, + unsigned char *buffer, unsigned int buf_size); + +/* Get a pointer to a buffer dat of length lenp. + To which USB data can be written. */ +void +usb_send_buffer_get(unsigned char ep_addr, unsigned int offset, + unsigned char **dat, unsigned int *lenp); + +/* Notify the USB subsystem that data has been written to the buffer returned + by usb_send_buffer_get. */ +void +usb_send_buffer_commit(unsigned char ep_addr, unsigned int len); + +unsigned int +usb_send_data(unsigned char ep_addr, + const unsigned char *dat, unsigned int len); + +unsigned int +usb_recv_data(unsigned char ep_addr, unsigned char *dat, unsigned int len); + +void +usb_disable_endpoint(unsigned char addr); + +/* Asynchronous */ +#define USB_USER_MSG_TYPE_CONFIG 0x01 +#define USB_USER_MSG_TYPE_SUSPEND 0x02 +#define USB_USER_MSG_TYPE_RESUME 0x03 + +/* Synchronous, the supplied data is only valid during the event */ +#define USB_USER_MSG_TYPE_EP_OUT(ep_addr) ((((ep_addr) & 0x7f)<<4) | 0x01) +#define USB_USER_MSG_TYPE_EP_IN(ep_addr) ((((ep_addr) & 0x7f)<<4) | 0x02) + +struct usb_user_msg { + unsigned int type; + union { + /* For EPx_OUT */ + unsigned short length; + /* For CONFIG */ + unsigned char config; + } data; +}; + + +#endif /* __USB_API_H__SYN81IFYBN__ */ diff --git a/cpu/at91sam7s/usb/usb-config.h b/cpu/at91sam7s/usb/usb-config.h new file mode 100644 index 000000000..28ae9c179 --- /dev/null +++ b/cpu/at91sam7s/usb/usb-config.h @@ -0,0 +1,16 @@ +#ifndef __USB_CONFIG_H__LEIURX7H18__ +#define __USB_CONFIG_H__LEIURX7H18__ + +#ifndef CTRL_EP_SIZE +#define CTRL_EP_SIZE 8 +#endif + +#ifndef NON_CTRL_XFER_SIZE +#define NON_CTRL_XFER_SIZE 64 +#endif + +#ifndef MAX_CTRL_DATA +#define MAX_CTRL_DATA 128 +#endif + +#endif /* __USB_CONFIG_H__LEIURX7H18__ */ diff --git a/cpu/at91sam7s/usb/usb-handler.c b/cpu/at91sam7s/usb/usb-handler.c new file mode 100644 index 000000000..92a5f822f --- /dev/null +++ b/cpu/at91sam7s/usb/usb-handler.c @@ -0,0 +1,424 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_PULLUP_PIN AT91C_PIO_PA16 + +static unsigned short usb_device_status; +static unsigned char usb_configuration_value; + +static struct process * user_process = NULL; + +static struct USBRequestHandlerHook *usb_request_handler_hooks = NULL; + +static const unsigned char zero_byte = 0; +static const unsigned short zero_word = 0; + +static void +notify_user(struct usb_user_msg* msg) +{ + if (user_process) { + process_post(user_process, PROCESS_EVENT_MSG, msg); + } +} + +void +usb_set_user_process(struct process *p) +{ + user_process = p; +} + +static void +get_device_descriptor() +{ + usb_send_ctrl_response((unsigned char*)&device_descriptor, sizeof(device_descriptor)); +} + +static void +get_string_descriptor() +{ + if (LOW_BYTE(usb_setup_buffer.wValue) == 0) { + usb_send_ctrl_response((const unsigned char*)string_languages->lang_descr, + string_languages->lang_descr->bLength); + } else { + unsigned char l; + const struct usb_st_string_descriptor *descriptor; + const struct usb_st_string_descriptor * const *table; + const struct usb_st_string_language_map *map; + if (LOW_BYTE(usb_setup_buffer.wValue) > string_languages->max_index) { + usb_error_stall(); + return; + } + l = string_languages->num_lang; + map = string_languages->map; + table = map->descriptors; /* Use first table if language not found */ + while (l > 0) { + if (map->lang_id == usb_setup_buffer.wIndex) { + table = map->descriptors; + break; + } + map++; + l--; + } + printf("Lang id %04x = table %p\n", usb_setup_buffer.wIndex, (void*)table); + descriptor = table[LOW_BYTE(usb_setup_buffer.wValue) - 1]; + usb_send_ctrl_response((const unsigned char*)descriptor, + descriptor->bLength); + } +} + +static void +get_configuration_descriptor() +{ + usb_send_ctrl_response((unsigned char*)configuration_head, + configuration_head->wTotalLength); +} + +static void +get_configuration() +{ + usb_send_ctrl_response((unsigned char*)&usb_configuration_value, + sizeof(usb_configuration_value)); +} + +/* Returns true if the configuration value changed */ +static int +set_configuration() +{ + if (usb_configuration_value != LOW_BYTE(usb_setup_buffer.wValue)) { + usb_configuration_value = LOW_BYTE(usb_setup_buffer.wValue); + if (usb_configuration_value > 0) { + *AT91C_UDP_GLBSTATE |= AT91C_UDP_CONFG; + } else { + *AT91C_UDP_GLBSTATE &= ~AT91C_UDP_CONFG; + } + usb_send_ctrl_status(); + return 1; + } else { + usb_send_ctrl_status(); + return 0; + } +} + +static void +get_device_status() +{ + puts("get_device_status"); + usb_send_ctrl_response((const unsigned char*)&usb_device_status, + sizeof(usb_device_status)); +} + +static void +get_endpoint_status() +{ + puts("get_endpoint_status"); + if ((usb_setup_buffer.wIndex & 0x7f) == 0) { + usb_send_ctrl_response((const unsigned char*)&zero_word, + sizeof(zero_word)); + } else { + volatile USBEndpoint *ec; + ec = usb_find_endpoint(usb_setup_buffer.wIndex); + if (ec) { + usb_send_ctrl_response((const unsigned char*)&ec->status, sizeof(ec->status)); + } else { + usb_error_stall(); + } + } +} + +static void +get_interface_status() +{ + puts("get_interface_status"); + usb_send_ctrl_response((const unsigned char*)&zero_word, + sizeof(zero_word)); +} + +static void +get_interface() +{ + puts("get_interface"); + if (usb_configuration_value == 0) usb_error_stall(); + else { + usb_send_ctrl_response(&zero_byte, + sizeof(zero_byte)); + } +} + +static struct usb_user_msg config_msg = {USB_USER_MSG_TYPE_CONFIG}; +static struct usb_user_msg io_msg[3]; + +static unsigned int +handle_standard_requests() +{ + switch(usb_setup_buffer.bmRequestType) { + case 0x80: /* standard device IN requests */ + switch(usb_setup_buffer.bRequest) { + case GET_DESCRIPTOR: + switch (HIGH_BYTE(usb_setup_buffer.wValue)) { + case DEVICE: + get_device_descriptor(); + break; + case CONFIGURATION: + get_configuration_descriptor(); + break; + case STRING: + get_string_descriptor(); + break; + default: + /* Unknown descriptor */ + return 0; + } + break; + case GET_CONFIGURATION: + get_configuration(); + break; + case GET_STATUS: + get_device_status(); + break; + case GET_INTERFACE: + get_interface(); + break; + default: + return 0; + } + break; + case 0x81: /* standard interface IN requests */ + switch(usb_setup_buffer.bRequest) { + case GET_STATUS: + get_interface_status(); + break; +#ifdef HID_ENABLED + case GET_DESCRIPTOR: + switch (USB_setup_buffer.wValue.byte.high) { + case REPORT: + get_report_descriptor(); + break; + } + break; +#endif + default: + return 0; + } + break; + case 0x82: /* standard endpoint IN requests */ + switch(usb_setup_buffer.bRequest) { + case GET_STATUS: + get_endpoint_status(); + break; + default: + return 0; + } + break; + case 0x00: /* standard device OUT requests */ + switch(usb_setup_buffer.bRequest) { + case SET_ADDRESS: + printf("Address: %d\n", LOW_BYTE(usb_setup_buffer.wValue)); + usb_set_address(); + usb_send_ctrl_status(); + break; +#if SETABLE_STRING_DESCRIPTORS > 0 + case SET_DESCRIPTOR: + if (usb_setup_buffer.wValue.byte.high == STRING) { + set_string_descriptor(); + } else { + return 0; + } + break; +#endif + case SET_CONFIGURATION: + if (set_configuration()) { + config_msg.data.config = LOW_BYTE(usb_setup_buffer.wValue); + notify_user(&config_msg); + } + break; + default: + return 0; + } + break; + case 0x02: + switch(usb_setup_buffer.bRequest) { + case SET_FEATURE: + case CLEAR_FEATURE: + if (usb_setup_buffer.wValue == ENDPOINT_HALT_FEATURE) { + volatile USBEndpoint *ep = usb_find_endpoint(usb_setup_buffer.wIndex); + if (ep) { + usb_halt_endpoint(ep->addr, usb_setup_buffer.bRequest== SET_FEATURE); + usb_send_ctrl_status(); + } else { + usb_error_stall(); + } + } else { + usb_error_stall(); + } + break; + default: + return 0; + } + break; +#ifdef HID_ENABLED + case 0xa1: /* class specific interface IN request*/ + switch(USB_setup_buffer.bRequest) { + case GET_HID_REPORT: + puts("Get report\n"); + send_ctrl_response((code u_int8_t*)&zero_byte, + sizeof(zero_byte)); + break; + case GET_HID_IDLE: + puts("Get idle\n"); + send_ctrl_response((code u_int8_t*)&zero_byte, + sizeof(zero_byte)); + break; + default: + return 0; + } + break; + case 0x21: /* class specific interface OUT request*/ + switch(USB_setup_buffer.bRequest) { + case SET_HID_IDLE: + puts("Set idle\n"); + send_ctrl_status(); + break; + default: + return 0; + } + break; +#endif + default: + return 0; + } + return 1; +} + +static const struct USBRequestHandler standard_request_handler = + { + 0x00, 0x60, + 0x00, 0x00, + handle_standard_requests + }; + +static struct USBRequestHandlerHook standard_request_hook = + { + NULL, + &standard_request_handler + }; + +PROCESS(usb_process, "USB process"); + +PROCESS_THREAD(usb_process, ev , data) +{ + PROCESS_BEGIN(); + puts("USB process started"); + while(1) { + PROCESS_WAIT_EVENT(); + if (ev == PROCESS_EVENT_EXIT) break; + if (ev == PROCESS_EVENT_POLL) { + if (usb_events & USB_EVENT_RESET) { + printf("Reset\n"); + usb_clear_events(USB_EVENT_RESET); + } + if (usb_events & USB_EVENT_EP(0)) { + /* puts("Endpoint 0"); */ + if (usb_endpoint_events[0] & USB_EP_EVENT_SETUP) { + struct USBRequestHandlerHook *hook = usb_request_handler_hooks; +#if 0 + puts("Setup"); + { + unsigned int i; + for (i = 0; i< 8; i++) printf(" %02x", ((unsigned char*)&usb_setup_buffer)[i]); + putchar('\n'); + } +#endif + while(hook) { + const struct USBRequestHandler *handler = hook->handler; + /* Check if the handler matches the request */ + if (((handler->request_type ^ usb_setup_buffer.bmRequestType) + & handler->request_type_mask) == 0 + && ((handler->request ^ usb_setup_buffer.bRequest) + & handler->request_mask) == 0) { + if (handler->handler_func()) break; + } + hook = hook->next; + } + if (!hook) { + /* No handler found */ + usb_error_stall(); + } + usb_clear_ep_events(0, USB_EP_EVENT_SETUP); + } + usb_clear_events(USB_EVENT_EP(0)); + } + + { + unsigned int e; + for (e = 1; e <= 3; e++) { + if (usb_events & USB_EVENT_EP(e)) { + if (usb_endpoint_events[e] & (USB_EP_EVENT_OUT|USB_EP_EVENT_IN)) { + volatile USBEndpoint *ep = usb_find_endpoint(e); + struct usb_user_msg *msg = &io_msg[e-1]; + if (usb_endpoint_events[e] & USB_EP_EVENT_OUT) { + msg->type = USB_USER_MSG_TYPE_EP_OUT(e); + msg->data.length = ep->buf_len; + } else { + msg->type = USB_USER_MSG_TYPE_EP_IN(e); + msg->data.length = ep->buf_size_mask + 1 - ep->buf_len; + } + notify_user(msg); + usb_clear_ep_events(e, USB_EP_EVENT_OUT|USB_EP_EVENT_IN); + usb_clear_events(USB_EVENT_EP(ep->addr)); + } + } + } + } + } + } + PROCESS_END(); +} + + +void +usb_setup(void) +{ + /* Assume 96MHz PLL frequency */ + *AT91C_CKGR_PLLR = ((*AT91C_CKGR_PLLR & ~AT91C_CKGR_USBDIV) + | AT91C_CKGR_USBDIV_1); + /* Enable 48MHz USB clock */ + *AT91C_PMC_SCER = AT91C_PMC_UDP; + /* Enable USB main clock */ + *AT91C_PMC_PCER = (1 << AT91C_ID_UDP); + + /* Enable pullup */ + *AT91C_PIOA_PER = USB_PULLUP_PIN; + *AT91C_PIOA_OER = USB_PULLUP_PIN; + *AT91C_PIOA_CODR = USB_PULLUP_PIN; + + usb_register_request_handler(&standard_request_hook); + process_start(&usb_process, NULL); + usb_handler_process = &usb_process; + + /* Enable usb_interrupt */ + AT91C_AIC_SMR[AT91C_ID_UDP] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | 4; + AT91C_AIC_SVR[AT91C_ID_UDP] = (unsigned long) usb_int; + *AT91C_AIC_IECR = (1 << AT91C_ID_UDP); + usb_init_endpoints(); +} + +void +usb_register_request_handler(struct USBRequestHandlerHook *hook) +{ + struct USBRequestHandlerHook **prevp = &usb_request_handler_hooks; + /* Find last hook */ + while(*prevp) { + prevp = &(*prevp)->next; + } + /* Add last */ + *prevp = hook; + hook->next = NULL; +} diff --git a/cpu/at91sam7s/usb/usb-interrupt.c b/cpu/at91sam7s/usb/usb-interrupt.c new file mode 100644 index 000000000..e434f2496 --- /dev/null +++ b/cpu/at91sam7s/usb/usb-interrupt.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +static void +usb_int_safe (void) __attribute__((noinline)); + +static void +usb_int_safe (void) +{ + unsigned int int_status; + /* putchar('*'); */ + int_status = *AT91C_UDP_ISR & *AT91C_UDP_IMR; + + if (int_status & (AT91C_UDP_EP1 | AT91C_UDP_EP2 | AT91C_UDP_EP3)) { + usb_epx_int(); + } else if (int_status & AT91C_UDP_ENDBUSRES) { + usb_reset(); + *AT91C_UDP_ICR = AT91C_UDP_ENDBUSRES; + } else if (int_status & AT91C_UDP_RXSUSP) { + /* puts("Suspend"); */ + *AT91C_UDP_ICR = AT91C_UDP_RXSUSP; + } else if (int_status & AT91C_UDP_RXRSM) { + /* puts("Resume"); */ + *AT91C_UDP_ICR = AT91C_UDP_RXRSM; + } else if (int_status & AT91C_UDP_SOFINT) { + /* puts("SOF"); */ + *AT91C_UDP_ICR = AT91C_UDP_SOFINT; + } else if (int_status & AT91C_UDP_WAKEUP) { + /* puts("Wakeup"); */ + *AT91C_UDP_ICR = AT91C_UDP_WAKEUP; + } else if (int_status & AT91C_UDP_EP0) { + usb_ep0_int(); + } else { + puts("Other USB interrupt"); + } + /* putchar('<'); */ + +} + +void NACKEDFUNC +usb_int (void) +{ + ISR_STORE(); + ISR_ENABLE_NEST(); + usb_int_safe(); + ISR_DISABLE_NEST(); + *AT91C_AIC_EOICR = 0; + ISR_RESTORE(); +} diff --git a/cpu/at91sam7s/usb/usb-interrupt.h b/cpu/at91sam7s/usb/usb-interrupt.h new file mode 100644 index 000000000..9d0c194be --- /dev/null +++ b/cpu/at91sam7s/usb/usb-interrupt.h @@ -0,0 +1,7 @@ +#ifndef __USB_INTERRUPT_H__Z1DQCUBTAL__ +#define __USB_INTERRUPT_H__Z1DQCUBTAL__ + +void +usb_int (void); + +#endif /* __USB_INTERRUPT_H__Z1DQCUBTAL__ */ diff --git a/cpu/at91sam7s/usb/usb-proto.c b/cpu/at91sam7s/usb/usb-proto.c new file mode 100644 index 000000000..be93351cb --- /dev/null +++ b/cpu/at91sam7s/usb/usb-proto.c @@ -0,0 +1,600 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifndef AT91C_UDP_STALLSENT +#define AT91C_UDP_STALLSENT AT91C_UDP_ISOERROR +#endif + +/* Bits that won't effect the state if they're written at a specific level. + */ +/* Bits that should be written as 1 */ +#define NO_EFFECT_BITS (AT91C_UDP_TXCOMP | AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RXSETUP \ + | AT91C_UDP_ISOERROR | AT91C_UDP_RX_DATA_BK1) +/* Also includes bits that should be written as 0 */ +#define NO_EFFECT_MASK (NO_EFFECT_BITS | AT91C_UDP_TXPKTRDY) + +#define RXBYTECNT(s) (((s)>>16)&0x7ff) + +/* Index in endpoint array */ +#define EP_INDEX(addr) (((addr) & 0x7f) - 1) +/* Number of hardware endpoint */ +#define EP_HW_NUM(addr) ((addr) & 0x7f) + +static inline void +udp_set_ep_ctrl_flags(AT91_REG *reg, unsigned int flags, + unsigned int write_mask, unsigned int check_mask) +{ + while ( (*reg & check_mask) != (flags & check_mask)) { + *reg = (*reg & ~write_mask) | flags; + } +} + +#define UDP_SET_EP_CTRL_FLAGS(reg, flags, mask) \ +udp_set_ep_ctrl_flags((reg), \ + (NO_EFFECT_BITS & ~(mask)) | ((flags) & (mask)), (mask) | NO_EFFECT_MASK,\ + (mask)) + + +void +usb_error_stall() +{ + /* Disable all USB events */ + *AT91C_AIC_IDCR = (1 << AT91C_ID_UDP); + /* Set stall state */ + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[0], + AT91C_UDP_FORCESTALL, AT91C_UDP_FORCESTALL); + /* Reenable interrupt */ + *AT91C_AIC_IECR = (1 << AT91C_ID_UDP); + puts("Stalled"); +} + +volatile unsigned char usb_events = 0; +struct process *usb_handler_process = NULL; + +#define NUM_EP 4 +volatile unsigned char usb_endpoint_events[NUM_EP] = {0,0,0,0}; +static volatile USBEndpoint usb_endpoints[NUM_EP - 1]; + +volatile unsigned char usb_flags = 0; +#define USB_FLAG_ADDRESS_PENDING 0x01 +#define USB_FLAG_RECEIVING_CTRL 0x04 +#define USB_FLAG_SEND_ZLP 0x08 /* If the last packet has max length, + then it needs to be followed by a + zero length packet to mark the + end. */ + +static unsigned short usb_ctrl_send_len = 0; +static const unsigned char *usb_ctrl_send_pos = NULL; + +unsigned char usb_ctrl_data_buffer[MAX_CTRL_DATA]; +unsigned short usb_ctrl_data_len = 0; + +static void +write_endpoint(unsigned int hw_ep, + const unsigned char *buffer, unsigned short len) +{ + AT91_REG *fdr = &AT91C_UDP_FDR[hw_ep]; + { +#if 0 + unsigned int i; + printf("Sending: "); + for (i = 0; i< len; i++) printf(" %02x", buffer[i]); + putchar('\n'); +#endif + } + + while(len > 0) { + *fdr = *buffer++; + len--; + } + /* Start transmission */ + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[hw_ep], + AT91C_UDP_TXPKTRDY, AT91C_UDP_TXPKTRDY); + +} + +static void +write_ctrl() +{ + if (usb_ctrl_send_pos) { + unsigned int xfer_len = usb_ctrl_send_len; + /* Check if FIFO is ready */ + if (AT91C_UDP_CSR[0] & AT91C_UDP_TXPKTRDY) return; + if (xfer_len > CTRL_EP_SIZE) xfer_len = CTRL_EP_SIZE; + write_endpoint(0, usb_ctrl_send_pos, xfer_len); + if (xfer_len < CTRL_EP_SIZE) { + /* Last packet, stop sending */ + usb_ctrl_send_pos = NULL; + } else { + usb_ctrl_send_pos += xfer_len; + usb_ctrl_send_len -= xfer_len; + if (usb_ctrl_send_len == 0 && !(usb_flags & USB_FLAG_SEND_ZLP)) { + usb_ctrl_send_pos = NULL; + } + } + } +} + +static unsigned char +read_buffered_endpoint(volatile USBEndpoint *ep) +{ + unsigned char len; + unsigned char mask = ep->buf_size_mask; + unsigned char *buffer = ep->buffer; + unsigned char pos = (ep->buf_pos + ep->buf_len) & mask; + AT91_REG *fdr = &AT91C_UDP_FDR[EP_HW_NUM(ep->addr)]; + len = RXBYTECNT(AT91C_UDP_CSR[EP_HW_NUM(ep->addr)]); + if (mask + 1 - ep->buf_len < len) return 0; + ep->buf_len += len; + while(len-- > 0) { + buffer[pos] = *fdr; + pos = (pos + 1) & mask; + } + return 1; +} + +unsigned int +usb_recv_data(unsigned char ep_addr, unsigned char *dat, unsigned int len) +{ + volatile USBEndpoint *ep = &usb_endpoints[EP_INDEX(ep_addr)]; + unsigned char mask = ep->buf_size_mask; + *AT91C_UDP_IDR = 1<buffer; + unsigned char pos = ep->buf_pos; + if (ep->buf_len < len) len = ep->buf_len; + ep->buf_len -= len; + l = len; + while(l-- > 0) { + *dat++ = to[pos]; + pos = (pos + 1) & mask; + } + ep->buf_pos = pos; + } + ep->flags &= ~USB_EP_FLAGS_RECV_BLOCKED; + *AT91C_UDP_IER = 1<buf_pos; + unsigned int xfer_len = ep->buf_len; + unsigned int mask = ep->buf_size_mask; + const unsigned char *buf_tmp = ep->buffer; + AT91_REG *fdr = &AT91C_UDP_FDR[EP_HW_NUM(ep->addr)]; + if (!(ep->flags & USB_EP_FLAGS_TRANSMITTING) + && !(AT91C_UDP_CSR[EP_HW_NUM(ep->addr)] & AT91C_UDP_TXPKTRDY)) { + if (xfer_len > NON_CTRL_XFER_SIZE) xfer_len = NON_CTRL_XFER_SIZE; + ep->buf_len -= xfer_len; + /* printf("Writing %d to 0x%02x\n", xfer_len, ep->addr); */ + while(xfer_len > 0) { + *fdr = buf_tmp[pos]; + pos = (pos + 1) & mask; + xfer_len--; + } + + ep->flags |= USB_EP_FLAGS_TRANSMITTING; + /* Start transmission */ + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[EP_HW_NUM(ep->addr)], + AT91C_UDP_TXPKTRDY, AT91C_UDP_TXPKTRDY); + ep->buf_pos = pos; + } + } + restoreIRQ(irq); +} + +static void +write_send_buffer(unsigned char *buffer, const unsigned char *dat, + unsigned int len) +{ + while(len-- > 0) { + *buffer++ = *dat++; + } +} + +void +usb_send_buffer_get(unsigned char ep_addr, unsigned int offset, + unsigned char **dat, unsigned int *lenp) +{ + unsigned int pos; + volatile USBEndpoint *ep = &usb_endpoints[EP_INDEX(ep_addr)]; + unsigned int size = ep->buf_size_mask + 1; + unsigned int len; + *AT91C_UDP_IDR = 1<buf_len; + pos = (ep->buf_pos + offset + ep->buf_len) & (size - 1); + *AT91C_UDP_IER = 1<= len) { + len = 0; + } else { + len -= offset; + } + if (pos + len > size) len = size - pos; + *dat = &ep->buffer[pos]; + *lenp = len; +} + +void +usb_send_buffer_commit(unsigned char ep_addr, unsigned int len) +{ + volatile USBEndpoint *ep = &usb_endpoints[EP_INDEX(ep_addr)]; + *AT91C_UDP_IDR = 1<buf_len += len; + write_buffered_endpoint(ep); + *AT91C_UDP_IER = 1< 0) { + usb_send_buffer_get(ep_addr, 0, &write_pos, &write_len); + if (write_len == 0) break; + if (write_len > len) write_len = len; + write_send_buffer(write_pos, dat, write_len); + /* printf("Pos: %p, len %d\n", write_pos, write_len); */ + usb_send_buffer_commit(ep_addr, write_len); + dat += write_len; + len -= write_len; + } + return full_len - len; +} + + +void +usb_send_ctrl_response(const unsigned char *buffer, unsigned short len) +{ + if (AT91C_UDP_CSR[0] & AT91C_UDP_TXPKTRDY) return; + *AT91C_UDP_IDR = AT91C_UDP_EP0; + if (len >= usb_setup_buffer.wLength) { + len = usb_setup_buffer.wLength; /* Truncate if too long */ + usb_flags &= ~USB_FLAG_SEND_ZLP; + } else { + /* Send ZLP if the response is shorter than requested */ + usb_flags |= USB_FLAG_SEND_ZLP; + } + usb_ctrl_send_pos = buffer; + usb_ctrl_send_len = len; + write_ctrl(); + *AT91C_UDP_IER = AT91C_UDP_EP0; +} + +void +usb_send_ctrl_status() +{ + *AT91C_UDP_IDR = AT91C_UDP_EP0; + /* Start transmission */ + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[0], + AT91C_UDP_TXPKTRDY, AT91C_UDP_TXPKTRDY); + *AT91C_UDP_IER = AT91C_UDP_EP0; +} + +static void +notify_process(unsigned char events) +{ + usb_events |= events; + if (usb_handler_process) { + process_poll(usb_handler_process); + } +} + +void +usb_reset() +{ + /* Setup endpoint 0 */ + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[0], + AT91C_UDP_EPTYPE_CTRL | AT91C_UDP_EPEDS, + AT91C_UDP_EPTYPE | AT91C_UDP_EPEDS); + + /* Enable interrupt for control endpoint */ + *AT91C_UDP_IER = AT91C_UDP_EP0; + notify_process(USB_EVENT_RESET); +} + +struct USB_request_st usb_setup_buffer; + + + +static void +read_fifo0(unsigned char *buffer, unsigned int length) +{ + unsigned int r; + for (r = 0; r < length; r++) { + *buffer++ = AT91C_UDP_FDR[0]; + } +} + +void +usb_ep0_int() +{ + unsigned int status; + status = AT91C_UDP_CSR[0]; +#if 0 + printf("status: %08x\n", status); +#endif + if (status & AT91C_UDP_STALLSENT) { + /* Acknowledge */ + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[0],0, AT91C_UDP_STALLSENT); + } + if (status & AT91C_UDP_RXSETUP) { + usb_ctrl_send_pos = NULL; /* Cancel any pending control data + transmission */ + if (RXBYTECNT(status) == 8) { + read_fifo0((unsigned char*)&usb_setup_buffer, 8); + + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[0], + ((usb_setup_buffer.bmRequestType & 0x80) + ? AT91C_UDP_DIR : 0), + AT91C_UDP_DIR); + usb_ctrl_data_len = 0; + if ((usb_setup_buffer.bmRequestType & 0x80) != 0 + || usb_setup_buffer.wLength == 0) { + usb_endpoint_events[0] |= USB_EP_EVENT_SETUP; + notify_process(USB_EVENT_EP(0)); + } else { + if (usb_setup_buffer.wLength > MAX_CTRL_DATA) { + /* stall */ + usb_error_stall(); + } else { + usb_flags |= USB_FLAG_RECEIVING_CTRL; + } + } + } else { + usb_error_stall(); + } + /* Acknowledge SETUP */ + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[0],0, AT91C_UDP_RXSETUP); + } else if (status & (AT91C_UDP_RX_DATA_BK1 | AT91C_UDP_RX_DATA_BK0)) { + puts("IN"); + if (usb_flags & USB_FLAG_RECEIVING_CTRL) { + unsigned int len; + unsigned int left = MAX_CTRL_DATA - usb_ctrl_data_len; + len = RXBYTECNT(status); + if (len > left) { + /* stall */ + usb_error_stall(); + } else { + unsigned char *buf_tmp = usb_ctrl_data_buffer + usb_ctrl_data_len; + usb_ctrl_data_len += len; + if (usb_ctrl_data_len == usb_setup_buffer.wLength + || len < CTRL_EP_SIZE) { + usb_flags &= ~USB_FLAG_RECEIVING_CTRL; + usb_endpoint_events[0] |= USB_EP_EVENT_SETUP; + notify_process(USB_EVENT_EP(0)); + } + while(len-- > 0) *buf_tmp++ = AT91C_UDP_FDR[0]; + } + } else { + if (RXBYTECNT(status) > 0) { + puts("Discarded input"); + } + } + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[0],0, + AT91C_UDP_RX_DATA_BK1 | AT91C_UDP_RX_DATA_BK0); + } + if (status & AT91C_UDP_TXCOMP) { + /* puts("TX complete"); */ + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[0],0, AT91C_UDP_TXCOMP); + if (usb_flags & USB_FLAG_ADDRESS_PENDING) { + *AT91C_UDP_FADDR = AT91C_UDP_FEN | LOW_BYTE(usb_setup_buffer.wValue); + *AT91C_UDP_GLBSTATE |= AT91C_UDP_FADDEN; + usb_flags &= ~USB_FLAG_ADDRESS_PENDING; + printf("Address changed: %d\n", *AT91C_UDP_FADDR & 0x7f); + } else { + if(usb_ctrl_send_pos) { + write_ctrl(); + } + } + } + +} + +void +usb_epx_int() +{ + unsigned int ep_index; + /* Handle enabled interrupts */ + unsigned int int_status = *AT91C_UDP_ISR & *AT91C_UDP_IMR; + for (ep_index = 0; ep_index < NUM_EP-1; ep_index++) { + volatile USBEndpoint *ep = &usb_endpoints[ep_index]; + unsigned int ep_num = EP_HW_NUM(ep->addr); + unsigned int ep_mask; + if (ep->addr != 0) { /* skip if not configured */ + ep_mask = 1<addr, status); +#endif + if (status & AT91C_UDP_STALLSENT) { + /* Acknowledge */ + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[ep_num],0, AT91C_UDP_STALLSENT); + } + if (status & AT91C_UDP_TXCOMP) { + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[ep_num],0, AT91C_UDP_TXCOMP); + ep->flags &= ~USB_EP_FLAGS_TRANSMITTING; + if (ep->buf_len > 0) { + write_buffered_endpoint(ep); + /* Tell the application that there's more room in the buffer */ + usb_endpoint_events[ep_num] |= USB_EP_EVENT_IN; + notify_process(USB_EVENT_EP(ep_num)); + } + } + if (status & (AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RX_DATA_BK1)) { + unsigned char read_cnt; + read_cnt = read_buffered_endpoint(ep); + if (read_cnt == 0) { + *AT91C_UDP_IDR = 1<flags |= USB_EP_FLAGS_RECV_BLOCKED; + } else { + if (status & AT91C_UDP_RX_DATA_BK1) { + /* Ping-pong */ + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[ep_num],0, + (ep->flags & USB_EP_FLAGS_BANK_1_RECV_NEXT) + ? AT91C_UDP_RX_DATA_BK1 + : AT91C_UDP_RX_DATA_BK0); + ep->flags ^= USB_EP_FLAGS_BANK_1_RECV_NEXT; + } else { + /* Ping-pong or single buffer */ + UDP_SET_EP_CTRL_FLAGS(&AT91C_UDP_CSR[ep_num],0, + AT91C_UDP_RX_DATA_BK0); + ep->flags |= USB_EP_FLAGS_BANK_1_RECV_NEXT; + } + } + usb_endpoint_events[ep_num] |= USB_EP_EVENT_OUT; + notify_process(ep_mask); + } + } + } + } +} + + +/* Clear usb events from non-interrupt code */ +void +usb_clear_events(unsigned events) +{ + /* Disable allUSB events */ + *AT91C_AIC_IDCR = (1 << AT91C_ID_UDP); + usb_events &= ~events; + /* Reenable interrupt */ + *AT91C_AIC_IECR = (1 << AT91C_ID_UDP); +} + +void +usb_clear_ep_events(unsigned int ep, unsigned int events) +{ + /* Disable all USB events */ + *AT91C_AIC_IDCR = (1 << AT91C_ID_UDP); + usb_endpoint_events[ep] &= ~events; + /* Reenable interrupt */ + *AT91C_AIC_IECR = (1 << AT91C_ID_UDP); +} + +void +usb_set_address() +{ + usb_flags |= USB_FLAG_ADDRESS_PENDING; + /* The actual setting of the address is done when the status packet + is sent. */ +} + + +static void +setup_endpoint(unsigned char addr, + unsigned char *buffer, unsigned int buf_size, + unsigned int type) +{ + volatile USBEndpoint *ep; + /* Check if the address points to an existing endpoint */ + if (EP_INDEX(addr) >= (sizeof(usb_endpoints)/sizeof(usb_endpoints[0]))) { + return; + } + ep = &usb_endpoints[EP_INDEX(addr)]; + ep->addr = addr; + ep->buf_size_mask = buf_size - 1; + ep->buffer = buffer; + ep->buf_len = 0; + ep->buf_pos = 0; + ep->status = 0; + *AT91C_UDP_IDR = 1<= (sizeof(usb_endpoints)/sizeof(usb_endpoints[0]))) { + return; + } + *AT91C_UDP_IDR = 1<addr = 0; + ctxt->buf_size_mask = 0; + ctxt->buf_len = 0; + ctxt->buf_pos = 0; + ctxt->buffer = 0; + ctxt->status = 0; + ctxt->flags = 0; +} + +void +usb_init_endpoints() +{ + unsigned int i; + for (i = 0; i < NUM_EP-1; i++) { + init_ep(&usb_endpoints[i]); + } +} + +volatile USBEndpoint* +usb_find_endpoint(unsigned char epaddr) +{ + if (EP_INDEX(epaddr) >= NUM_EP - 1) return 0; + return &usb_endpoints[EP_INDEX(epaddr)]; +} + +void +usb_halt_endpoint(unsigned char ep_addr, unsigned int halt) +{ + *AT91C_UDP_IDR = 1< +#include + +#ifndef NULL +#define NULL 0 +#endif + +typedef struct _USBEndpoint USBEndpoint; +struct _USBEndpoint +{ + unsigned char addr; + unsigned char buf_size_mask; /* mask for the buffer index. This implies + that the buffer size must be a power of + 2 */ + unsigned char buf_len; + unsigned char buf_pos; + unsigned char* buffer; + unsigned short status; + unsigned short flags; +}; + +#define USB_EP_FLAGS_BANK_1_RECV_NEXT 0x01 /* The next packet received + should be read from bank 1 + if possible */ + +#define USB_EP_FLAGS_RECV_BLOCKED 0x02 /* Recetpion is blocked. + Interrupt turned off */ + +#define USB_EP_FLAGS_TRANSMITTING 0x04 /* Waiting for TXCOMP before sending + more data */ + +/* Read only */ +struct USBRequestHandler +{ + unsigned char request_type; + unsigned char request_type_mask; + unsigned char request; + unsigned char request_mask; + /* Returns true if it handled the request, if false let another handler try*/ + unsigned int (*handler_func)(); +}; + +/* Must be writeable */ +struct USBRequestHandlerHook +{ + struct USBRequestHandlerHook *next; + const struct USBRequestHandler * const handler; +}; + +void +usb_register_request_handler(struct USBRequestHandlerHook *hook); + +void +usb_reset(); + +void +usb_ep0_int(); + +void +usb_epx_int(); + +void +usb_clear_events(unsigned events); + +void +usb_clear_ep_events(unsigned int ep, unsigned int events); + +void +usb_error_stall(); + +void +usb_send_ctrl_response(const unsigned char *buffer, unsigned short len); + +void +usb_send_ctrl_status(); + +void +usb_set_address(); + + +void +usb_set_configuration_value(unsigned char c); + +unsigned char +usb_get_configuration_value(); + +void +usb_init_endpoints(); + +volatile USBEndpoint* +usb_find_endpoint(unsigned char epaddr); + +void +usb_halt_endpoint(unsigned char ep_addr, unsigned int halt); + +extern volatile unsigned char usb_events; +extern struct process *usb_handler_process; +extern struct USB_request_st usb_setup_buffer; + +extern unsigned char usb_ctrl_data_buffer[]; +extern unsigned short usb_ctrl_data_len; + +#define USB_EVENT_EP(ep) (0x01<<(ep)) +#define USB_EVENT_RESET 0x10 +#define USB_EVENT_SUPEND 0x20 +#define USB_EVENT_RESUME 0x40 +#define USB_EVENT_CONFIG 0x08 + +extern volatile unsigned char usb_endpoint_events[]; + +#define USB_EP_EVENT_SETUP 0x01 +#define USB_EP_EVENT_OUT 0x02 +#define USB_EP_EVENT_IN 0x04 + +#endif /* __USB_PROTO_H__RVJQ2JAGM4__ */ diff --git a/cpu/at91sam7s/usb/usb.h b/cpu/at91sam7s/usb/usb.h new file mode 100644 index 000000000..86dc3419a --- /dev/null +++ b/cpu/at91sam7s/usb/usb.h @@ -0,0 +1,185 @@ +#ifndef __USB_H__6PFTDPIMZM__ +#define __USB_H__6PFTDPIMZM__ +#include + +/* Adapted from usb_kbd_enum.h in c5131-usb-kbd-light-1_0_2 package from + Atmel */ + +/* These definitions assume a little endian architecture */ + +#ifdef __GNUC__ +#define BYTE_ALIGNED __attribute__ ((__packed__)) +#else +#define BYTE_ALIGNED +#endif + +#define LOW_BYTE(x) ((unsigned char)x) +#define HIGH_BYTE(x) ((unsigned char)(x>>8)) + +typedef uint8_t Uchar; +typedef uint16_t Uint16; +typedef uint32_t Uint32; + +/*_____ S T A N D A R D R E Q U E S T S __________________________________*/ + +#define GET_STATUS 0x00 +#define GET_DEVICE 0x01 +#define CLEAR_FEATURE 0x01 /* see FEATURES below */ +#define GET_STRING 0x03 +#define SET_FEATURE 0x03 /* see FEATURES below */ +#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 + +#define GET_DEVICE_DESCRIPTOR 1 +#define GET_CONFIGURATION_DESCRIPTOR 4 + +#define REQUEST_DEVICE_STATUS 0x80 +#define REQUEST_INTERFACE_STATUS 0x81 +#define REQUEST_ENDPOINT_STATUS 0x82 +#define ZERO_TYPE 0x00 +#define INTERFACE_TYPE 0x01 +#define ENDPOINT_TYPE 0x02 + +/*_____ D E S C R I P T O R T Y P E S ____________________________________*/ + +#define DEVICE 0x01 +#define CONFIGURATION 0x02 +#define STRING 0x03 +#define INTERFACE 0x04 +#define ENDPOINT 0x05 + +/* HID specific */ +#define HID 0x21 +#define REPORT 0x22 +/* *** */ + +/*_____ S T A N D A R D F E A T U R E S __________________________________*/ + +#define DEVICE_REMOTE_WAKEUP_FEATURE 0x01 +#define ENDPOINT_HALT_FEATURE 0x00 + +/*_____ D E V I C E S T A T U S ___________________________________________*/ + +#define SELF_POWERED 1 + +/*_____ D E V I C E S T A T E _____________________________________________*/ + +#define ATTACHED 0 +#define POWERED 1 +#define DEFAULT 2 +#define ADDRESSED 3 +#define CONFIGURED 4 +#define SUSPENDED 5 + +#define USB_CONFIG_BUSPOWERED 0x80 +#define USB_CONFIG_SELFPOWERED 0x40 +#define USB_CONFIG_REMOTEWAKEUP 0x20 + +/* Class specific */ +#define CS_INTERFACE 0x24 +#define CS_ENDPOINT 0x25 + +/*_________________________________________________________ S T R U C T _____*/ +/*_____ U S B D E V I C E R E Q U E S T _________________________________*/ + +struct USB_request_st +{ + Uchar bmRequestType; /* Characteristics of the request */ + Uchar bRequest; /* Specific request */ + Uint16 wValue; + Uint16 wIndex; /* field that varies according to request */ + Uint16 wLength; /* Number of bytes to transfer if Data */ +}; + + +/*_____ U S B D E V I C E D E S C R I P T O R ___________________________*/ + +struct usb_st_device_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* DEVICE descriptor type */ + Uint16 bscUSB; /* Binay Coded Decimal Spec. release */ + Uchar bDeviceClass; /* Class code assigned by the USB */ + Uchar bDeviceSubClass; /* Sub-class code assigned by the USB */ + Uchar bDeviceProtocol; /* Protocol code assigned by the USB */ + Uchar bMaxPacketSize0; /* Max packet size for EP0 */ + Uint16 idVendor; /* Vendor ID. ATMEL = 0x03EB */ + Uint16 idProduct; /* Product ID assigned by the manufacturer */ + Uint16 bcdDevice; /* Device release number */ + Uchar iManufacturer; /* Index of manu. string descriptor */ + Uchar iProduct; /* Index of prod. string descriptor */ + Uchar iSerialNumber; /* Index of S.N. string descriptor */ + Uchar bNumConfigurations; /* Number of possible configurations */ +} BYTE_ALIGNED; + + +/*_____ U S B C O N F I G U R A T I O N D E S C R I P T O R _____________*/ + +struct usb_st_configuration_descriptor +{ + Uchar bLength; /* size of this descriptor in bytes */ + Uchar bDescriptorType; /* CONFIGURATION descriptor type */ + Uint16 wTotalLength; /* total length of data returned */ + Uchar bNumInterfaces; /* number of interfaces for this conf. */ + Uchar bConfigurationValue; /* value for SetConfiguration resquest */ + Uchar iConfiguration; /* index of string descriptor */ + Uchar bmAttibutes; /* Configuration characteristics */ + Uchar MaxPower; /* maximum power consumption */ +} BYTE_ALIGNED; + + +/*_____ U S B I N T E R F A C E D E S C R I P T O R _____________________*/ + +struct usb_st_interface_descriptor +{ + Uchar bLength; /* size of this descriptor in bytes */ + Uchar bDescriptorType; /* INTERFACE descriptor type */ + Uchar bInterfaceNumber; /* Number of interface */ + Uchar bAlternateSetting; /* value to select alternate setting */ + Uchar bNumEndpoints; /* Number of EP except EP 0 */ + Uchar bInterfaceClass; /* Class code assigned by the USB */ + Uchar bInterfaceSubClass; /* Sub-class code assigned by the USB */ + Uchar bInterfaceProtocol; /* Protocol code assigned by the USB */ + Uchar iInterface; /* Index of string descriptor */ +} BYTE_ALIGNED; + + +/*_____ U S B E N D P O I N T D E S C R I P T O R _______________________*/ + +struct usb_st_endpoint_descriptor +{ + Uchar bLength; /* Size of this descriptor in bytes */ + Uchar bDescriptorType; /* ENDPOINT descriptor type */ + Uchar bEndpointAddress; /* Address of the endpoint */ + Uchar bmAttributes; /* Endpoint's attributes */ + Uint16 wMaxPacketSize; /* Maximum packet size for this EP */ + Uchar bInterval; /* Interval for polling EP in ms */ +/* Uchar bRefresh; */ +/* Uchar bSynchAddress; */ +} BYTE_ALIGNED; + + +/*_____ U S B S T R I N G D E S C R I P T O R _______________*/ + +struct usb_st_string_descriptor +{ + Uchar bLength; /* size of this descriptor in bytes */ + Uchar bDescriptorType; /* STRING descriptor type */ + Uint16 wstring[1];/* unicode characters */ +} BYTE_ALIGNED; + + +struct usb_st_language_descriptor +{ + Uchar bLength; /* size of this descriptor in bytes */ + Uchar bDescriptorType; /* STRING descriptor type */ + Uint16 wlangid[1]; /* language id */ +} BYTE_ALIGNED; + +#endif /* __USB_H__6PFTDPIMZM__ */