/***************************************************************************** USB-Template: User defined USB device V1.1.1 (c) 2010-2011 Reusch Elektronik, Dipl.-Ing. (FH) Rainer Reusch Homepage: http://products.reworld.eu/index.htm Supported devices: - AT90USB82, AT90USB162 - ATMEGA8U2, ATMEGA16U2, ATMEGA32U2 - ATMEGA8U4, ATMEGA16U4, ATMEGA32U4 - AT90USB646, AT90USB647, AT90USB1286, AT90USB1287 Supported modules: - U2DIL-AT90USB162 - U2DIL-ATMEGA32U2 - U4DIL-ATMEGA32U4 - U6DIL-AT90USB1286 USB Function Implementation Created: 2010-01-07 Changed: 2011-08-02 *****************************************************************************/ // Usually no changes are necessary in this file! #include #include #include "usb_user_config.h" #include "usb_user.h" //----------------------------------------------------------------------------- // Internal Variables //----------------------------------------------------------------------------- // USB State static volatile uint8_t usb_stat = USB_STATUS_DISCONNECTED; // USB configuration, selected by host (0=none) // In this version only 0 or 1 is possible (support of only one configuration) static volatile uint8_t usb_conf = 0; // USB interface, selected by host // In this version the value 0 and optional more cfgs are possible (see usbconfig.h) #if (NUMINTERFACES>1) static volatile uint8_t usb_if = 0xFF; // 0xFF: no interface selected #endif //----------------------------------------------------------------------------- // Descriptors //----------------------------------------------------------------------------- // configuration descriptor typedef struct { uint8_t bLength; uint8_t bDescriptorType; uint16_t wTotalLength; uint8_t bNumInterfaces; uint8_t bConfigurationValue; uint8_t iConfiguration; uint8_t bmAttributes; uint8_t bMaxPower; } cfg_desc; // interface descriptor typedef struct { uint8_t bLength; uint8_t bDescriptorType; uint8_t bInterfaceNumber; uint8_t bAlternateSetting; uint8_t bNumEndpoints; uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; uint8_t iInterface; } if_desc; // endpoint descriptor typedef struct { uint8_t bLength; uint8_t bDescriptorType; uint8_t bEndpointAddress; uint8_t bmAttributes; uint16_t wMaxPacketSize; uint8_t bInterval; } ep_desc; //----------------------------------------------------------------------------- // USB Initializing //----------------------------------------------------------------------------- #if defined (__AVR_AT90USB1286__) || (__AVR_AT90USB1287__) #define AT128X #endif void usb_init(void) { #ifdef MEGA4 UHWCON = _BV(UVREGE); // enable PAD regulator #endif #ifdef MEGA6 UHWCON = _BV(UIMOD)|_BV(UVREGE); // set device mode, enable PAD regulator #endif USBCON = _BV(USBE) | _BV(FRZCLK); // enable USB #if (F_CPU==16000000) // 16MHz #ifdef MEGA2 PLLCSR = _BV(PLLE) | _BV(PLLP0); // config PLL, 16 MHz xtal #endif #ifdef MEGA4 PLLCSR = _BV(PINDIV) | _BV(PLLE); // config PLL, 16 MHz xtal #endif #ifdef MEGA6 #ifdef AT128X PLLCSR = _BV(PLLP2) | _BV(PLLP0) | _BV(PLLE); // config PLL, 16 MHz xtal (AT90USB128x) #else PLLCSR = _BV(PLLP2) | _BV(PLLP1) | _BV(PLLE); // config PLL, 16 MHz xtal (AT90USB64x and ATMEGA32U6) #endif #endif #else // 8MHz #ifdef MEGA6 PLLCSR = _BV(PLLP1) | _BV(PLLP0) |_BV(PLLE); // config PLL, 8 MHz xtal #else PLLCSR = _BV(PLLE); // config PLL, 8 MHz xtal #endif #endif while (!(PLLCSR & _BV(PLOCK))) ; // wait for PLL lock #ifdef MEGA4_6 USBCON = _BV(USBE)|_BV(OTGPADE); // start USB clock #else USBCON = _BV(USBE); // start USB clock #endif UDCON = 0; // enable attach resistor usb_conf = 0; #if (NUMINTERFACES>1) usb_if = 0xFF; #endif #ifdef MEGA2 // port for VBUS detection VBUSDIR &= ~_BV(VBUSPIN); // port as input #endif UDIEN = _BV(EORSTE); // enable "end of reset" interrupt } //----------------------------------------------------------------------------- // Initializing Endpoints //----------------------------------------------------------------------------- #if (NUMINTERFACES>1) void usb_endpoints(uint8_t ifnumber) // with interface number #else void usb_endpoints(void) #endif { #if (NUMINTERFACES>1) #define EPC EP_CONFIG[ifnumber][i] #define IN_TRANSFER EP_CONFIG[ifnumber][i].ep_type & 0x01 #else #define EPC EP_CONFIG[i] #define IN_TRANSFER EP_CONFIG[i].ep_type & 0x01 #endif uint8_t i; for (i=0; i1) uint8_t usb_interface(void) { return usb_if; } #endif //----------------------------------------------------------------------------- // Internal used USB routines and macros //----------------------------------------------------------------------------- #define USB_SEND_IN UEINTX = ~(1<maxsize) len=maxsize; do { usb_wait_in(); if (i & _BV(RXOUTI)) return; // cancel // send IN package n = (len < ENDPOINT0_SIZE) ? len : ENDPOINT0_SIZE; for (i = n; i; i--) UEDATX = isRAM ? *addr++ : pgm_read_byte(addr++); len -= n; USB_SEND_IN; } while (len || n == ENDPOINT0_SIZE); } //----------------------------------------------------------------------------- // Free FIFO Buffer //----------------------------------------------------------------------------- void usb_freebuffer(uint8_t endpoint) { UENUM = endpoint; // select endpoint UERST = _BV(endpoint); // endpoint FIFO reset UERST = 0; } //----------------------------------------------------------------------------- // Data Check (Host to Device) //----------------------------------------------------------------------------- #ifdef MEGA4_6 uint16_t usb_rxavail(uint8_t endpoint) #else uint8_t usb_rxavail(uint8_t endpoint) #endif { UENUM = endpoint; // select endpoint #ifdef MEGA4_6 return ((uint16_t)(UEBCHX)<<8)+UEBCLX; #else return UEBCLX; #endif } //----------------------------------------------------------------------------- // Data Receive (Host to Device) //----------------------------------------------------------------------------- #ifdef MEGA4_6 uint16_t usb_rxdata(uint8_t endpoint, uint8_t *buffer, uint16_t maxdatasize) #else uint8_t usb_rxdata(uint8_t endpoint, uint8_t *buffer, uint8_t maxdatasize) #endif { #ifdef MEGA4_6 uint16_t r, i; #else uint8_t r, i; #endif UENUM = endpoint; // select endpoint #ifdef MEGA4_6 r = ((uint16_t)(UEBCHX)<<8)+UEBCLX; #else r = UEBCLX; #endif if (r>maxdatasize) r = maxdatasize; if (r) { // data available in input endpoint for (i=0; i1) usb_if = 0xFF; #endif } } //----------------------------------------------------------------------------- // Endpoint Interrupts //----------------------------------------------------------------------------- // Interrupts, triggered by incoming data in an endpoint, are handled here. ISR(USB_COM_vect) { #if (NUMINTERFACES>1) #define EPCO EP_CONFIG[j][i] #else #define EPCO EP_CONFIG[i] #endif uint8_t intbits; uint8_t i, j, n, s; cfg_desc *cfg; if_desc *ifp; ep_desc *epp; #if (USESN==2) // serial number in RAM struct usb_string_descriptor_ram buf; #endif struct usb_control_request ucr; if (UEINT & 0x01) { // handle interrupts of endpoint 0 (control transfers) UENUM = 0; intbits = UEINTX; // save interrupt flags of the endpoint if (intbits & _BV(RXSTPI)) { // control transfer, setup ucr.bmRequestType = UEDATX; ucr.bRequest = UEDATX; ucr.wValue = UEDATX; ucr.wValue |= (UEDATX << 8); ucr.wIndex = UEDATX; ucr.wIndex |= (UEDATX << 8); ucr.wLength = UEDATX; ucr.wLength |= (UEDATX << 8); UEINTX = ~(_BV(RXSTPI) | _BV(RXOUTI) | _BV(TXINI)); if (ucr.bRequest == GET_DESCRIPTOR) { switch (ucr.wValue) { case 0x0100: // device descriptor usb_desc_out(false,&device_descriptor[0],pgm_read_byte(&device_descriptor[0]),ucr.wLength); break; case 0x0200: // configuration descriptor // get number of activated endpoints n=0; #if (NUMINTERFACES>1) for (j=0; jbLength = sizeof(cfg_desc); cfg->bDescriptorType = 2; cfg->wTotalLength = s; cfg->bNumInterfaces = NUMINTERFACES; cfg->bConfigurationValue = 1; cfg->iConfiguration = 0; cfg->bmAttributes = POWERING; cfg->bMaxPower = MAXPOWER>>1; // initialize interface descriptor ifp = (if_desc *)((uint16_t)(cfg)+sizeof(cfg_desc)); #if (NUMINTERFACES>1) for (j=0; jbLength = sizeof(if_desc); ifp->bDescriptorType = 4; ifp->bInterfaceNumber = j; ifp->bAlternateSetting = 0; ifp->bNumEndpoints = n; ifp->bInterfaceClass = 0xFF; ifp->bInterfaceSubClass = 0x00; ifp->bInterfaceProtocol = 0xFF; ifp->iInterface = 0; epp = (ep_desc *)((uint16_t)(ifp)+sizeof(if_desc)); if (n) { // endpoints for (i=0; ibLength = sizeof(ep_desc); epp->bDescriptorType = 5; epp->bEndpointAddress = (i+1)|(EPCO.ep_type<<7); epp->bmAttributes = EP_TRANSFER(EPCO.ep_type); epp->wMaxPacketSize = EPCO.ep_size; epp->bInterval = 0; epp = (ep_desc *)((uint16_t)(epp)+sizeof(ep_desc)); } } } #if (NUMINTERFACES>1) ifp = (if_desc *)epp; } #endif // finally usb_desc_out(true,(uint8_t *)cfg,s,ucr.wLength); free(cfg); break; case 0x0300: // String 0 usb_desc_out(false,(uint8_t *)&string0.bLength,pgm_read_byte(&string0.bLength),ucr.wLength); break; case 0x0301: // String 1 usb_desc_out(false,(uint8_t *)&string1.bLength,pgm_read_byte(&string1.bLength),ucr.wLength); break; case 0x0302: // String 2 usb_desc_out(false,(uint8_t *)&string2.bLength,pgm_read_byte(&string2.bLength),ucr.wLength); break; #if (USESN>0) case 0x0303: // String 3, serial number #if (USESN==1) // Seriennummer im Flash usb_desc_out(false,(uint8_t *)&string3.bLength,pgm_read_byte(&string3.bLength),ucr.wLength); #endif #if (USESN==2) // serial number in the RAM buf.bDescriptorType=3; // has to be 3 always i = 0; while (RAMSN[i]) { buf.wString[i] = RAMSN[i]; i++; } buf.bLength=2*i+2; // total length of the data set usb_desc_out(true,(uint8_t *)&buf,buf.bLength,ucr.wLength); #endif break; #endif default: STALL; // stall } return; } if (ucr.bRequest == SET_ADDRESS) { USB_SEND_IN; usb_wait_in_ready(); UDADDR = ucr.wValue | _BV(ADDEN); return; } if (ucr.bRequest == SET_CONFIGURATION && ucr.bmRequestType == 0) { // another configuration will be chosen if (ucr.wValue==1) { // configuration 1 will be chosen usb_conf = ucr.wValue; USB_SEND_IN; #if (NUMINTERFACES==1) usb_endpoints(); // initialize endpoints #else usb_if = 0; // select interface 0 usb_endpoints(usb_if); // initialize endpoints #endif } else { // other configurations are not supported in this version STALL; // stall } return; } if (ucr.bRequest == GET_CONFIGURATION && ucr.bmRequestType == 0x80) { usb_wait_in_ready(); UEDATX = usb_conf; USB_SEND_IN; return; } if (ucr.bRequest == GET_STATUS) { usb_wait_in_ready(); i = 0; #ifdef SUPPORT_ENDPOINT_HALT if (ucr.bmRequestType == 0x82) { UENUM = ucr.wIndex; if (UECONX & _BV(STALLRQ)) i = 1; UENUM = 0; } #endif UEDATX = i; UEDATX = 0; USB_SEND_IN; return; } #if (NUMINTERFACES>1) if (ucr.bRequest == SET_INTERFACE && (ucr.bmRequestType == 0x20 || ucr.bmRequestType == 0)) { // another interface will be chosen if (ucr.wIndex= 1 && i <= MAX_ENDPOINT) { USB_SEND_IN; UENUM = i; if (ucr.bRequest == SET_FEATURE) { UECONX = _BV(STALLRQ)|_BV(EPEN); } else { UECONX = _BV(STALLRQC)|_BV(RSTDT)|_BV(EPEN); UERST = _BV(i); UERST = 0; } return; } } #endif #ifdef USERDEFCONTROLS // handle user defined control requests if (!usb_controlrequest(&ucr)) UECONX = _BV(STALLRQ) | _BV(EPEN); // stall return; #endif } UECONX = _BV(STALLRQ) | _BV(EPEN); // stall } // handle interrupts for further endpoints for (i=1; i<=MAX_ENDPOINT; i++) { if (UEINT & _BV(i)) { UENUM=i; // select endpoint intbits = UEINTX; // save interrupt bits of the endpoint if (intbits & _BV(RXOUTI)) { // interrupt occured by incoming data #ifdef MEGA4_6 if (((uint16_t)(UEBCHX)<<8)+UEBCLX) { // data available in input endpoint usb_ep(i,((uint16_t)(UEBCHX)<<8)+UEBCLX); #else if (UEBCLX) { // data available in input endpoint usb_ep(i,UEBCLX); #endif UENUM=i; // reselect endpoint (if changed by handling routine) } UEINTX = ~(_BV(RXOUTI)|_BV(STALLEDI)); // clear interrupt flags UEINTX = 0x7F; // free bank (FIFOCON), has to be executed after RXOUTI! return; } STALL; // stall } } STALL; // stall }