📄 usbdrv.c
字号:
/* Name: usbdrv.c * Project: AVR USB driver * Author: Christian Starkjohann * Creation Date: 2004-12-29 * Tabsize: 4 * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH * License: Proprietary, free under certain conditions. See Documentation. * This Revision: $Id: usbdrv.c 20 2005-02-20 16:39:43Z cs $ */#include <avr/io.h>#include <avr/pgmspace.h>#include "usbdrv.h"#include "oddebug.h"/*General Description:This module implements the C-part of the USB driver. See usbdrv.h for adocumentation of the entire driver.*//* ------------------------------------------------------------------------- *//* raw USB registers / interface to assembler code: *//* usbRxBuf MUST be in 1 byte addressable range (because usbInputBuf is only 1 byte) */static char usbRxBuf[2][USB_BUFSIZE];/* raw RX buffer: PID, 8 bytes data, 2 bytes CRC */uchar usbNDeviceId = 0xff;/* inverted, assigned during enumeration, defaults to 0xff */uchar usbInputBuf; /* ptr to raw buffer used for receiving */uchar usbAppBuf; /* ptr to raw buffer passed to app for processing */char usbRxLen; /* = 0; number of bytes in usbAppBuf; 0 means free */uchar usbCurrentTok; /* last token received (inverse of) */uchar usbRxToken; /* token for data we received */char usbMsgLen = -1; /* remaining number of bytes, no msg to send if negative (see usbMsgPtr) */char usbTxLen = -1; /* number of bytes to transmit with next IN token */uchar usbTxBuf[USB_BUFSIZE];/* data to transmit with next IN, free if usbTxLen == -1 */#ifdef USB_HAVE_ENDPOINT1uchar usbRxEndp; /* endpoint which was addressed (1 bit in MSB) */char usbTxLen1 = -1; /* TX count for endpoint 1 */uchar usbTxBuf1[USB_BUFSIZE];/* TX data for endpoint 1 */#endifuchar usbAckBuf[1] = {USBPID_ACK}; /* transmit buffer for ack tokens */uchar usbNakBuf[1] = {USBPID_NAK}; /* transmit buffer for nak tokens *//* USB status registers / not shared with asm code */static uchar *usbMsgPtr; /* data to transmit next -- ROM or RAM address */static char usbPkgCount; /* counts data packets, negative if ROM address -- reset for new msg */static uchar usbNewDeviceId; /* = 0; device ID which should be set after status phase */static uchar usbIsReset; /* = 0; USB bus is in reset phase *//*optimizing hints:- do not post/pre inc/dec integer values in operations- assign value of PRG_RDB() to register variables and don't use side effects in arg- use narrow scope for variables which should be in X/Y/Z register- assign char sized expressions to variables to force 8 bit arithmetics*//* ------------------------------------------------------------------------- */static char usbDescrDevice[] PROGMEM = { /* USB device descriptor */ 18, /* sizeof(usbDescrDevice): length of descriptor in bytes */ 1, /* descriptor type */ 0x01, 0x01, /* USB version supported */ 0xff, /* device class */ 0, /* device subclass */ 0, /* protocol */ 8, /* max packet size */ 0xeb, 0x03, /* vendor ID: 0x03eb = ATMEL */ 0x53, 0x6a, /* device ID */ 0x00, 0x01, /* device version number */ 1, /* manufacturer string index */ 2, /* product string index */ 0, /* serial number string index */ 1, /* number of configurations */};static char usbDescrConfig[] PROGMEM = { /* USB configuration descriptor */ 9, /* sizeof(usbDescrConfig): length of descriptor in bytes */ 2, /* descriptor type */ 18, 0, /* total length of data returned (including inlined descriptors) */ 1, /* number of interfaces in this configuration */ 1, /* index of this configuration */ 0, /* configuration name string index */#if USB_IS_SELFPOWERED USBATTR_SELFPOWER, /* attributes */#else USBATTR_BUSPOWER, /* attributes */#endif 10, /* max USB current in 2mA units *//* interface descriptor follows inline: */ 9, /* sizeof(usbDescrInterface): length of descriptor in bytes */ 4, /* descriptor type */ 0, /* index of this interface */ 0, /* alternate setting for this interface */ 0, /* endpoints excl 0: number of endpoint descriptors to follow */ 0, /* interface class */ 0, /* interface subclass */ 0, /* interface protocol */ 0, /* string index for interface *//* endpoint descriptors would follow inline */};static char usbDescrString0[] PROGMEM = { /* language descriptor */ 4, /* sizeof(usbDescrString0): length of descriptor in bytes */ 3, /* descriptor type */ 0x09, 0x04, /* language index (0x0409 = US-English) */};static int usbDescrString1[] PROGMEM = { 26 | (3<<8), /* length of descriptor in bytes | descriptor type */ 'w', 'w', 'w', '.', 'o', 'b', 'd', 'e', 'v', '.', 'a', 't'};static int usbDescrString2[] PROGMEM = { 24 | (3<<8), /* length of descriptor in bytes | descriptor type */ 'P', 'o', 'w', 'e', 'r', 'S', 'w', 'i', 't', 'c', 'h',};/* We don't use prog_int or prog_int16_t for compatibility with older libc * versions. *//* ------------------------------------------------------------------------- */void usbSendMsg(uchar *data, uchar len, uchar options){ usbMsgPtr = data; usbMsgLen = len; usbTxLen = -1; /* abort any pending sends */ usbPkgCount = options;}static void usbProcessRx(uchar *data, uchar len){/* We use if() cascades because the compare is done byte-wise while switch() * is int-based. The if() cascades are therefore more efficient. */ DBG2(0x10 + (usbRxToken == (uchar)~USBPID_SETUP), data, len); if(usbRxToken == (uchar)~USBPID_SETUP){ static uchar statusReply[2]; uchar *replyData = statusReply, replyLen = 0, replyOptions = USBSND_DATA1_RAM; uchar recipient = data[0] & 0x1f; /* assign arith ops to variables to enforce byte size */ uchar type = data[0] & (3 << 5); if(len == 8){ /* all valid setup requests have length 8 */ if(type == USBRQ_TYPE_STANDARD << 5){ if(data[1] == 0){/* GET_STATUS */ statusReply[1] = 0; statusReply[0] = recipient == USBRQ_RCPT_DEVICE ? USB_IS_SELFPOWERED : 0; replyLen = 2; }else if(data[1] == 5){ /* SET_ADDRESS */ usbNewDeviceId = data[2]; }else if(data[1] == 6){ /* GET_DESCRIPTOR */ replyOptions = USBSND_DATA1_ROM; if(data[3] == 1){ /* descriptor type requested */ replyData = (uchar *)usbDescrDevice; replyLen = sizeof(usbDescrDevice); }else if(data[3] == 2){ replyData = (uchar *)usbDescrConfig; replyLen = sizeof(usbDescrConfig); }else if(data[3] == 3){ /* string descriptor */ if(data[2] == 0){ /* descriptor index */ replyData = (uchar *)usbDescrString0; replyLen = sizeof(usbDescrString0); }else if(data[2] == 1){ replyData = (uchar *)usbDescrString1; replyLen = sizeof(usbDescrString1); }else if(data[2] == 2){ replyData = (uchar *)usbDescrString2; replyLen = sizeof(usbDescrString2); } } }else if(data[1] == 8){ /* GET_CONFIGURATION */ statusReply[0] = 1; /* no config required */ replyLen = 1; }else if(data[1] == 10){ /* GET_INTERFACE */ statusReply[0] = 0; replyLen = 1; }else{ /* the following requests can be ignored, send default reply */ /* 1: CLEAR_FEATURE, 3: SET_FEATURE, 7: SET_DESCRIPTOR */ /* 9: SET_CONFIGURATION, 11: SET_INTERFACE, 12: SYNCH_FRAME */ } }else if(type == USBRQ_TYPE_VENDOR << 5){ replyLen = usbVendorSetup(data, statusReply); } } if(!data[7] && replyLen > data[6]){ /* max length is in data[7]:data[6] */ replyLen = data[6]; } usbSendMsg(replyData, replyLen, replyOptions); /* send reply */ }else{ /* out request */ if(len == 0){ /* ACK -- to SETUP answer */ /* just ignore */ } }}/* ------------------------------------------------------------------------- */static void usbBuildTxBlock(void){uchar len, i, *p, *r;unsigned crc; i = USBPID_DATA0; if(usbPkgCount & 1) i = USBPID_DATA1; usbTxBuf[0] = i; usbPkgCount++; /* do not post-inc above because gcc produces bad code */ if(usbMsgLen >= 8){ len = 8; usbMsgLen -= len; }else{ /* this is the final terminating block */ len = usbMsgLen; usbMsgLen = -1; } if(len < 128){ /* transmit may have been aborted due to reception of out or setup token */ i = len; p = &usbTxBuf[1]; r = usbMsgPtr; if(usbPkgCount < 0){ /* ROM data */ while(i--){ char c = PRG_RDB(r); /* use separate statement -- compiler does not like PRG_RDB() */ *p++ = c; r++; } }else{ /* RAM data */ while(i--) *p++ = *r++; } usbMsgPtr = r; crc = usbCrc16(&usbTxBuf[1], len); *p++ = crc; *p++ = crc >> 8; usbTxLen = len + 4; /* len must be given including sync byte */ DBG2(0x20, usbTxBuf, usbTxLen-1); }}static inline uchar isNotSE0(void){uchar rval;/* We want to do * return (USBIN & USBMASK); * here, but the compiler does int-expansion acrobatics. * We can avoid this by assigning to a char-sized variable. */ rval = USBIN & USBMASK; return rval;}/* ------------------------------------------------------------------------- */void usbPoll(void){uchar i, *p; if(usbRxLen > 0){ p = (uchar *)(unsigned)(usbAppBuf); i = usbRxLen; do{ *p = ~*p; p++; }while(--i);/* We could check CRC16 here -- but ACK has already been sent anyway. If you * need data integrity checks with this driver, check the CRC in your app * code and report errors back to the host. Since the ACK was already sent, * retries must be handled on application level. * unsigned crc = usbCrc16((uchar *)(unsigned)(usbAppBuf + 1), usbRxLen - 3); */ if((i = usbRxLen - 3) < 128){ usbProcessRx((uchar *)(unsigned)(usbAppBuf + 1), i); } usbRxLen = 0; } if(usbMsgLen >= 0 && usbTxLen < 0) usbBuildTxBlock(); if(usbNewDeviceId && usbTxLen < 0){ /* tx system idle: all blocks sent */ usbNDeviceId = ~usbNewDeviceId; DBG1(1, &usbNewDeviceId, 1); usbNewDeviceId = 0; } if(isNotSE0()){ /* SE0 state */ usbIsReset = 0; }else{ /* check whether SE0 lasts for more than 2.5us (3.75 bit times) */ if(!usbIsReset){ for(i=100;i;i--){ if(isNotSE0()) break; } if(!i){ usbIsReset = 1; usbNDeviceId = ~0; usbNewDeviceId = 0; DBG1(0xff, 0, 0); } } }}/* ------------------------------------------------------------------------- */void usbInit(void){ usbInputBuf = (uchar)usbRxBuf[0]; usbAppBuf = (uchar)usbRxBuf[1]; MCUCR |= (1 << ISC00) | (1 << ISC01); /* rising edge interrupt */ GIMSK |= (1 << INT0);}/* ------------------------------------------------------------------------- */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -