📄 usb.c
字号:
// ======================================================================// USB driver//// Entry points:// usb_init() - enable the USB interrupt// usb_poll() - poll for incoming packets and process them//// This code communicates with the interrupt handler through a number of// global variables, including two input buffers and one output buffer.// Packets are queued for transmission by copying them into the output// buffer. The interrupt handler will transmit such a packet on the// reception of an IN packet.//// Standard SETUP packets are handled here. Non-standard SETUP packets// are forwarded to the application code by calling usb_setup(). The// macros USBTINY_CALLBACK_IN and USBTINY_CALLBACK_OUT control whether// the callback functions usb_in() and usb_out() will be called for IN// and OUT transfers.//// Maximum stack usage (gcc 3.4.3 & 4.1.0) of usb_poll(): 5 bytes plus// possible additional stack usage in usb_setup(), usb_in() or usb_out().//// Copyright 2006-2008 Dick Streefland//// This is free software, licensed under the terms of the GNU General// Public License as published by the Free Software Foundation.// ======================================================================#include <avr/pgmspace.h>#include <avr/interrupt.h>#include "def.h"#include "usb.h"#define LE(word) (word) & 0xff, (word) >> 8// ----------------------------------------------------------------------// USB constants// ----------------------------------------------------------------------enum{ DESCRIPTOR_TYPE_DEVICE = 1, DESCRIPTOR_TYPE_CONFIGURATION, DESCRIPTOR_TYPE_STRING, DESCRIPTOR_TYPE_INTERFACE, DESCRIPTOR_TYPE_ENDPOINT,};// ----------------------------------------------------------------------// Interrupt handler interface// ----------------------------------------------------------------------byte_t usb_rx_buf[2*USB_BUFSIZE]; // two input buffersbyte_t usb_rx_off; // buffer offset: 0 or USB_BUFSIZEbyte_t usb_rx_len; // buffer size, 0 means emptybyte_t usb_rx_token; // PID of token packet: SETUP or OUTbyte_t usb_tx_buf[USB_BUFSIZE]; // output bufferbyte_t usb_tx_len; // output buffer size, 0 means emptybyte_t usb_address; // assigned device addressbyte_t usb_new_address; // new device address// ----------------------------------------------------------------------// Local data// ----------------------------------------------------------------------enum{ TX_STATE_IDLE = 0, // transmitter idle TX_STATE_RAM, // usb_tx_data is a RAM address TX_STATE_ROM, // usb_tx_data is a ROM address TX_STATE_CALLBACK, // call usb_in() to obtain transmit data};static byte_t usb_tx_state; // TX_STATE_*, see enum abovestatic byte_t usb_tx_total; // total transmit sizestatic byte_t* usb_tx_data; // pointer to data to transmit#if defined USBTINY_VENDOR_NAMEstruct{ byte_t length; byte_t type; int string[sizeof(USBTINY_VENDOR_NAME)-1];} string_vendor PROGMEM ={ 2 * sizeof(USBTINY_VENDOR_NAME), DESCRIPTOR_TYPE_STRING, { CAT2(L, USBTINY_VENDOR_NAME) }};# define VENDOR_NAME_ID 1#else# define VENDOR_NAME_ID 0#endif#if defined USBTINY_DEVICE_NAMEstruct{ byte_t length; byte_t type; int string[sizeof(USBTINY_DEVICE_NAME)-1];} string_device PROGMEM ={ 2 * sizeof(USBTINY_DEVICE_NAME), DESCRIPTOR_TYPE_STRING, { CAT2(L, USBTINY_DEVICE_NAME) }};# define DEVICE_NAME_ID 2#else# define DEVICE_NAME_ID 0#endif#if defined USBTINY_SERIALstruct{ byte_t length; byte_t type; int string[sizeof(USBTINY_SERIAL)-1];} string_serial PROGMEM ={ 2 * sizeof(USBTINY_SERIAL), DESCRIPTOR_TYPE_STRING, { CAT2(L, USBTINY_SERIAL) }};# define SERIAL_ID 3#else# define SERIAL_ID 0#endif#if VENDOR_NAME_ID || DEVICE_NAME_ID || SERIAL_IDstatic byte_t string_langid [] PROGMEM ={ 4, // bLength DESCRIPTOR_TYPE_STRING, // bDescriptorType (string) LE(0x0409), // wLANGID[0] (American English)};#endif// Device Descriptorstatic byte_t descr_device [18] PROGMEM ={ 18, // bLength DESCRIPTOR_TYPE_DEVICE, // bDescriptorType LE(0x0101), // bcdUSB USBTINY_DEVICE_CLASS, // bDeviceClass USBTINY_DEVICE_SUBCLASS, // bDeviceSubClass USBTINY_DEVICE_PROTOCOL, // bDeviceProtocol 8, // bMaxPacketSize0 LE(USBTINY_VENDOR_ID), // idVendor LE(USBTINY_DEVICE_ID), // idProduct LE(USBTINY_DEVICE_VERSION), // bcdDevice VENDOR_NAME_ID, // iManufacturer DEVICE_NAME_ID, // iProduct SERIAL_ID, // iSerialNumber 1, // bNumConfigurations};// Configuration Descriptorstatic byte_t descr_config [] PROGMEM ={ 9, // bLength DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType LE(9+9+7*USBTINY_ENDPOINT), // wTotalLength 1, // bNumInterfaces 1, // bConfigurationValue 0, // iConfiguration (USBTINY_MAX_POWER ? 0x80 : 0xc0), // bmAttributes (USBTINY_MAX_POWER + 1) / 2, // MaxPower // Standard Interface Descriptor 9, // bLength DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType 0, // bInterfaceNumber 0, // bAlternateSetting USBTINY_ENDPOINT, // bNumEndpoints USBTINY_INTERFACE_CLASS, // bInterfaceClass USBTINY_INTERFACE_SUBCLASS, // bInterfaceSubClass USBTINY_INTERFACE_PROTOCOL, // bInterfaceProtocol 0, // iInterface#if USBTINY_ENDPOINT // Additional Endpoint 7, // bLength DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType USBTINY_ENDPOINT_ADDRESS, // bEndpointAddress USBTINY_ENDPOINT_TYPE, // bmAttributes LE(8), // wMaxPacketSize USBTINY_ENDPOINT_INTERVAL, // bInterval#endif};// ----------------------------------------------------------------------// Inspect an incoming packet.// ----------------------------------------------------------------------static void usb_receive ( byte_t* data, byte_t rx_len ){ byte_t len; byte_t type; byte_t limit; usb_tx_state = TX_STATE_RAM; len = 0; if ( usb_rx_token == USB_PID_SETUP ) { limit = data[6]; if ( data[7] ) { limit = 255; } type = data[0] & 0x60; if ( type == 0x00 ) { // Standard request if ( data[1] == 0 ) // GET_STATUS { len = 2;#if USBTINY_MAX_POWER == 0 data[0] = (data[0] == 0x80);#else data[0] = 0;#endif data[1] = 0; } else if ( data[1] == 5 ) // SET_ADDRESS { usb_new_address = data[2]; } else if ( data[1] == 6 ) // GET_DESCRIPTOR { usb_tx_state = TX_STATE_ROM; if ( data[3] == 1 ) { // DEVICE data = (byte_t*) &descr_device; len = sizeof(descr_device); } else if ( data[3] == 2 ) { // CONFIGURATION data = (byte_t*) &descr_config; len = sizeof(descr_config); }#if VENDOR_NAME_ID || DEVICE_NAME_ID || SERIAL_ID else if ( data[3] == 3 ) { // STRING if ( data[2] == 0 ) { data = (byte_t*) &string_langid; len = sizeof(string_langid); }#if VENDOR_NAME_ID else if ( data[2] == VENDOR_NAME_ID ) { data = (byte_t*) &string_vendor; len = sizeof(string_vendor); }#endif#if DEVICE_NAME_ID else if ( data[2] == DEVICE_NAME_ID ) { data = (byte_t*) &string_device; len = sizeof(string_device); }#endif#if SERIAL_ID else if ( data[2] == SERIAL_ID ) { data = (byte_t*) &string_serial; len = sizeof(string_serial); }#endif }#endif } else if ( data[1] == 8 ) // GET_CONFIGURATION { data[0] = 1; // return bConfigurationValue len = 1; } else if ( data[1] == 10 ) // GET_INTERFACE { data[0] = 0; len = 1; } } else { // Class or Vendor request len = usb_setup( data );#if USBTINY_CALLBACK_IN if ( len == 0xff ) { usb_tx_state = TX_STATE_CALLBACK; }#endif } if ( len > limit ) { len = limit; } usb_tx_data = data; }#if USBTINY_CALLBACK_OUT else if ( rx_len > 0 ) { // usb_rx_token == USB_PID_OUT usb_out( data, rx_len ); }#endif usb_tx_total = len; usb_tx_buf[0] = USB_PID_DATA0; // next data packet will be DATA1}// ----------------------------------------------------------------------// Load the transmit buffer with the next packet.// ----------------------------------------------------------------------static void usb_transmit ( void ){ byte_t len; byte_t* src; byte_t* dst; byte_t i; byte_t b; usb_tx_buf[0] ^= (USB_PID_DATA0 ^ USB_PID_DATA1); len = usb_tx_total; if ( len > 8 ) { len = 8; } dst = usb_tx_buf + 1; if ( len > 0 ) {#if USBTINY_CALLBACK_IN if ( usb_tx_state == TX_STATE_CALLBACK ) { len = usb_in( dst, len ); } else#endif { src = usb_tx_data; if ( usb_tx_state == TX_STATE_RAM ) { for ( i = 0; i < len; i++ ) { *dst++ = *src++; } } else // usb_tx_state == TX_STATE_ROM { for ( i = 0; i < len; i++ ) { b = pgm_read_byte( src ); src++; *dst++ = b; } } usb_tx_data = src; } usb_tx_total -= len; } crc( usb_tx_buf + 1, len ); usb_tx_len = len + 3; if ( len < 8 ) { // this is the last packet usb_tx_state = TX_STATE_IDLE; }}// ----------------------------------------------------------------------// Initialize the low-level USB driver.// ----------------------------------------------------------------------extern void usb_init ( void ){ USB_INT_CONFIG |= USB_INT_CONFIG_SET; USB_INT_ENABLE |= (1 << USB_INT_ENABLE_BIT); sei();}// ----------------------------------------------------------------------// Poll USB driver:// - check for incoming USB packets// - refill an empty transmit buffer// - check for USB bus reset// ----------------------------------------------------------------------extern void usb_poll ( void ){ byte_t i; // check for incoming USB packets if ( usb_rx_len != 0 ) { usb_receive( usb_rx_buf + USB_BUFSIZE - usb_rx_off + 1, usb_rx_len - 3 ); usb_tx_len = 0; // abort pending transmission usb_rx_len = 0; // accept next packet } // refill an empty transmit buffer, when the transmitter is active if ( usb_tx_len == 0 && usb_tx_state != TX_STATE_IDLE ) { usb_transmit(); } // check for USB bus reset for ( i = 10; i > 0 && ! (USB_IN & USB_MASK_DMINUS); i-- ) { } if ( i == 0 ) { // SE0 for more than 2.5uS is a reset usb_new_address = 0; usb_address = 0; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -