commit
10d1a7d514
@ -0,0 +1,86 @@ |
|||||||
|
#!/usr/bin/python3 |
||||||
|
class SHA1 (object): |
||||||
|
_h0, _h1, _h2, _h3, _h4, = ( |
||||||
|
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0) |
||||||
|
def __init__(self, message): |
||||||
|
length = bin(len(message) * 8)[2:].rjust(64, "0") |
||||||
|
while len(message) > 64: |
||||||
|
self._handle(''.join(bin(i)[2:].rjust(8, "0") |
||||||
|
for i in message[:64])) |
||||||
|
message = message[64:] |
||||||
|
message = ''.join(bin(i)[2:].rjust(8, "0") for i in message) + "1" |
||||||
|
message += "0" * ((448 - len(message) % 512) % 512) + length |
||||||
|
for i in range(len(message) // 512): |
||||||
|
self._handle(message[i * 512:i * 512 + 512]) |
||||||
|
|
||||||
|
|
||||||
|
def _handle(self, chunk): |
||||||
|
|
||||||
|
lrot = lambda x, n: (x << n) | (x >> (32 - n)) |
||||||
|
w = [] |
||||||
|
|
||||||
|
for j in range(len(chunk) // 32): |
||||||
|
w.append(int(chunk[j * 32:j * 32 + 32], 2)) |
||||||
|
|
||||||
|
for i in range(16, 80): |
||||||
|
w.append(lrot(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1) |
||||||
|
& 0xffffffff) |
||||||
|
|
||||||
|
#print(w) |
||||||
|
|
||||||
|
a = self._h0 |
||||||
|
b = self._h1 |
||||||
|
c = self._h2 |
||||||
|
d = self._h3 |
||||||
|
e = self._h4 |
||||||
|
|
||||||
|
for i in range(80): |
||||||
|
|
||||||
|
if i <= i <= 19: |
||||||
|
f, k = d ^ (b & (c ^ d)), 0x5a827999 |
||||||
|
elif 20 <= i <= 39: |
||||||
|
f, k = b ^ c ^ d, 0x6ed9eba1 |
||||||
|
elif 40 <= i <= 59: |
||||||
|
f, k = (b & c) | (d & (b | c)), 0x8f1bbcdc |
||||||
|
elif 60 <= i <= 79: |
||||||
|
f, k = b ^ c ^ d, 0xca62c1d6 |
||||||
|
|
||||||
|
temp = lrot(a, 5) + f + e + k + w[i] & 0xffffffff |
||||||
|
a, b, c, d, e = temp, a, lrot(b, 30), c, d |
||||||
|
|
||||||
|
self._h0 = (self._h0 + a) & 0xffffffff |
||||||
|
self._h1 = (self._h1 + b) & 0xffffffff |
||||||
|
self._h2 = (self._h2 + c) & 0xffffffff |
||||||
|
self._h3 = (self._h3 + d) & 0xffffffff |
||||||
|
self._h4 = (self._h4 + e) & 0xffffffff |
||||||
|
|
||||||
|
|
||||||
|
def _digest(self): |
||||||
|
return (self._h0, self._h1, self._h2, self._h3, self._h4) |
||||||
|
|
||||||
|
def hexdigest(self): |
||||||
|
return ''.join(hex(i)[2:].rjust(8, "0") |
||||||
|
for i in self._digest()) |
||||||
|
|
||||||
|
def digest(self): |
||||||
|
hexdigest = self.hexdigest() |
||||||
|
return bytes(int(hexdigest[i * 2:i * 2 + 2], 16) |
||||||
|
for i in range(len(hexdigest) // 2)) |
||||||
|
|
||||||
|
#H(K XOR opad, H(K XOR ipad, text)) |
||||||
|
k = b"\x0b"*20 + b"\x00"*(64-20) |
||||||
|
text = b"Hi There" |
||||||
|
ipad=b"" |
||||||
|
opad=b"" |
||||||
|
for i in range(64): |
||||||
|
ipad+=bytes(chr(k[i] ^ 0x36),"ASCII") |
||||||
|
opad+=bytes(chr(k[i] ^ 0x5C),"ASCII") |
||||||
|
|
||||||
|
inner = SHA1( ipad+text ).digest() |
||||||
|
outer = SHA1( opad+inner ).hexdigest() |
||||||
|
print("inner:",SHA1( ipad+text ).hexdigest() ) |
||||||
|
print("result:",outer) |
||||||
|
|
||||||
|
|
||||||
|
lrot = lambda x, n: (x << n) | (x >> (32 - n)) |
||||||
|
|
||||||
@ -0,0 +1,66 @@ |
|||||||
|
|
||||||
|
#include "hmac.h" |
||||||
|
|
||||||
|
#define KEYLEN 20 |
||||||
|
static const uint8_t secret[KEYLEN]={0x11};
|
||||||
|
|
||||||
|
void hmac(uint8_t* msg, uint8_t msglen){ |
||||||
|
uint8_t i; |
||||||
|
/*
|
||||||
|
ipad = the byte 0x36 repeated B times
|
||||||
|
opad = the byte 0x5C repeated B times. |
||||||
|
|
||||||
|
To compute HMAC over the data `text' we perform |
||||||
|
|
||||||
|
H(K XOR opad, H(K XOR ipad, text)) |
||||||
|
|
||||||
|
B is length of hash in bytes, K is the secret and "," ist the string concatination operator |
||||||
|
*/ |
||||||
|
|
||||||
|
/* inner Hash */ |
||||||
|
sha1_init(); |
||||||
|
/*k xor ipad*/ |
||||||
|
for(i=0;i<KEYLEN;i++){ |
||||||
|
sha1_addbyte(secret[i]^IPAD); |
||||||
|
} |
||||||
|
/*if key is shorter than blocksize, treat the rest as 0es*/ |
||||||
|
for(i=KEYLEN;i<SHA1_B;i++){ |
||||||
|
sha1_addbyte(IPAD); |
||||||
|
} |
||||||
|
/* text */ |
||||||
|
for(i=0;i<msglen;i++){ |
||||||
|
sha1_addbyte(msg[i]); |
||||||
|
} |
||||||
|
sha1_fill(SHA1_B+msglen); |
||||||
|
|
||||||
|
/* save the result */ |
||||||
|
memcpy(hmac_result,sha1_digest,SHA1_SIZE); |
||||||
|
#ifdef TO_LITTLE_ENDIAN32 |
||||||
|
for(i=0; i<SHA1_SIZE/4; i++){ |
||||||
|
((uint32_t*)hmac_result)[i]=TO_LITTLE_ENDIAN32(((uint32_t*)hmac_result)[i]); |
||||||
|
} |
||||||
|
#endif //TO_LITTLE_ENDIAN32
|
||||||
|
|
||||||
|
/* outer hash */ |
||||||
|
sha1_init(); |
||||||
|
/*k xor opad*/ |
||||||
|
for(i=0;i<KEYLEN;i++){ |
||||||
|
sha1_addbyte(secret[i]^OPAD); |
||||||
|
} |
||||||
|
/*if key is shorter than blocksize, treat the rest as 0es*/ |
||||||
|
for(i=KEYLEN;i<SHA1_B;i++){ |
||||||
|
sha1_addbyte(OPAD); |
||||||
|
} |
||||||
|
for(i=0;i<SHA1_SIZE;i++){ |
||||||
|
sha1_addbyte(hmac_result[i]); |
||||||
|
} |
||||||
|
sha1_fill(SHA1_B+SHA1_SIZE); |
||||||
|
|
||||||
|
/* save the result */ |
||||||
|
memcpy(hmac_result,sha1_digest,SHA1_SIZE); |
||||||
|
#ifdef TO_LITTLE_ENDIAN32 |
||||||
|
for(i=0; i<SHA1_SIZE/4; i++){ |
||||||
|
((uint32_t*)hmac_result)[i]=TO_LITTLE_ENDIAN32(((uint32_t*)hmac_result)[i]); |
||||||
|
} |
||||||
|
#endif //TO_LITTLE_ENDIAN32
|
||||||
|
} |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
|
||||||
|
#ifndef _HMAC_H_ |
||||||
|
#define _HMAC_H_ |
||||||
|
|
||||||
|
#define IPAD 0x36 |
||||||
|
#define OPAD 0x5C |
||||||
|
|
||||||
|
#define SHA1_B 64 |
||||||
|
#define SHA1_SIZE 20 |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include "sha1.h" |
||||||
|
|
||||||
|
uint8_t hmac_result[SHA1_SIZE]; |
||||||
|
|
||||||
|
void hmac(uint8_t* msg,uint8_t msglen); |
||||||
|
|
||||||
|
#endif //_HMAC_H_
|
||||||
@ -0,0 +1,60 @@ |
|||||||
|
|
||||||
|
#include "main.h" |
||||||
|
|
||||||
|
static uint8_t reportBuffer[64]; |
||||||
|
|
||||||
|
const PROGMEM char usbHidReportDescriptor[35] = { /* USB report descriptor */ |
||||||
|
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||||
|
0x09, 0x06, // USAGE (Keyboard)
|
||||||
|
0xa1, 0x01, // COLLECTION (Application)
|
||||||
|
0x05, 0x07, // USAGE_PAGE (Keyboard)
|
||||||
|
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
|
||||||
|
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
|
||||||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||||
|
0x75, 0x01, // REPORT_SIZE (1)
|
||||||
|
0x95, 0x08, // REPORT_COUNT (8)
|
||||||
|
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||||
|
0x95, 0x01, // REPORT_COUNT (1)
|
||||||
|
0x75, 0x08, // REPORT_SIZE (8)
|
||||||
|
0x25, 0x65, // LOGICAL_MAXIMUM (101)
|
||||||
|
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
|
||||||
|
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
|
||||||
|
0x81, 0x00, // INPUT (Data,Ary,Abs)
|
||||||
|
0xc0 // END_COLLECTION
|
||||||
|
}; |
||||||
|
|
||||||
|
usbMsgLen_t usbFunctionSetup(uchar data[8]) { |
||||||
|
usbRequest_t *rq = (void *)data; |
||||||
|
usbMsgPtr = reportBuffer; |
||||||
|
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { /* HID class request */ |
||||||
|
if(rq->bRequest == USBRQ_HID_GET_REPORT) { /* wValue: ReportType (highbyte), ReportID (lowbyte) */ |
||||||
|
/* since we have only one report type, we can ignore the report-ID */ |
||||||
|
//buildReport();
|
||||||
|
return sizeof(reportBuffer);
|
||||||
|
}/*else if(rq->bRequest == USBRQ_HID_GET_IDLE){
|
||||||
|
usbMsgPtr = &idleRate; |
||||||
|
return 1; |
||||||
|
}else if(rq->bRequest == USBRQ_HID_SET_IDLE){ |
||||||
|
idleRate = rq->wValue.bytes[1]; |
||||||
|
}*/ |
||||||
|
} else { |
||||||
|
/* ignore vendor type requests, we don't use any */ |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main(void){ |
||||||
|
usbDeviceDisconnect(); |
||||||
|
_delay_ms(200); |
||||||
|
usbDeviceConnect(); |
||||||
|
usbInit();
|
||||||
|
//printf("OTP: %d\n", get_otp_from_cnt(7,6) );
|
||||||
|
for(;;){ |
||||||
|
|
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,18 @@ |
|||||||
|
#include "usbdrv.h" |
||||||
|
#include "sha1.h" |
||||||
|
#include "hmac.h" |
||||||
|
#include "trunc.h" |
||||||
|
#include "oddebug.h" |
||||||
|
#include "usbconfig.h" |
||||||
|
|
||||||
|
#include <util/delay.h> |
||||||
|
|
||||||
|
#define _PORT_BTN B |
||||||
|
#define _NUM_BTN 3 |
||||||
|
|
||||||
|
#define PORT_BTN PORT_PORT_BTN |
||||||
|
#define DDR_BTN DDR_PORT_BTN |
||||||
|
#define P_BTN P_PORT_BTN_NUM_BTN |
||||||
|
#define BTN_VECT INT_NUM_BTN_vect |
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,136 @@ |
|||||||
|
#include "sha1.h" |
||||||
|
#define H1 0x67452301 |
||||||
|
#define H2 0xEFCDAB89 |
||||||
|
#define H3 0x98BADCFE |
||||||
|
#define H4 0x10325476 |
||||||
|
#define H5 0xC3D2E1F0 |
||||||
|
|
||||||
|
#define K1 0x5A827999 |
||||||
|
#define K2 0x6ED9EBA1 |
||||||
|
#define K3 0x8F1BBCDC |
||||||
|
#define K4 0xCA62C1D6 |
||||||
|
|
||||||
|
#define _LROT32(x,n) (( (uint32_t)(x) << n ) | ( (uint32_t)(x) >>(32-n) )) |
||||||
|
#define _W(n) (n>=16 ? chunk_e[n-16] : chunk[n]) |
||||||
|
|
||||||
|
|
||||||
|
static uint32_t chunk[16]; |
||||||
|
static uint8_t chunk_cnt; |
||||||
|
|
||||||
|
void sha1_addbyte(uint8_t c){ |
||||||
|
uint8_t* p=(uint8_t*)chunk; |
||||||
|
uint8_t i; |
||||||
|
p[chunk_cnt]=c; |
||||||
|
chunk_cnt++; |
||||||
|
if(chunk_cnt==64){ |
||||||
|
/*chunk extension*/ |
||||||
|
uint32_t a,b,c,d,e,chunk_e[64]; |
||||||
|
|
||||||
|
#ifdef TO_LITTLE_ENDIAN32 |
||||||
|
for(i=0; i<16; i++){ |
||||||
|
chunk[i]=TO_LITTLE_ENDIAN32(chunk[i]); |
||||||
|
//printf("%08x",chunk[i]);
|
||||||
|
} |
||||||
|
//printf("\n");
|
||||||
|
#endif //TO_LITTLE_ENDIAN32
|
||||||
|
|
||||||
|
for(i=0; i<64; i++){ |
||||||
|
chunk_e[i]= _LROT32( _W(i+16-3) ^ _W(i+16-8) ^ _W(i+16-14) ^ _W(i+16-16), 1 );
|
||||||
|
} |
||||||
|
|
||||||
|
a=sha1_digest[0]; |
||||||
|
b=sha1_digest[1]; |
||||||
|
c=sha1_digest[2]; |
||||||
|
d=sha1_digest[3]; |
||||||
|
e=sha1_digest[4]; |
||||||
|
|
||||||
|
/*main loop*/ |
||||||
|
for(i=0;i<80;i++){ |
||||||
|
uint32_t f,k,temp; |
||||||
|
|
||||||
|
if(i<20){ |
||||||
|
k=K1; |
||||||
|
f=(b & c) | ((~b) & d); |
||||||
|
}else if(i<40){ |
||||||
|
k=K2; |
||||||
|
f=b ^ c ^ d; |
||||||
|
}else if(i<60){ |
||||||
|
k=K3;
|
||||||
|
f=(b & c) | (b & d) | (c & d); |
||||||
|
}else{ |
||||||
|
k=K4; |
||||||
|
f=b ^ c ^ d; |
||||||
|
} |
||||||
|
temp = e + _LROT32(a,5) + f + k + _W(i); |
||||||
|
e = d; |
||||||
|
d = c; |
||||||
|
c = _LROT32(b,30); |
||||||
|
b = a; |
||||||
|
a = temp; |
||||||
|
} |
||||||
|
|
||||||
|
sha1_digest[0]+=a; |
||||||
|
sha1_digest[1]+=b; |
||||||
|
sha1_digest[2]+=c; |
||||||
|
sha1_digest[3]+=d; |
||||||
|
sha1_digest[4]+=e; |
||||||
|
|
||||||
|
chunk_cnt=0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void sha1_fill(int32_t len){ |
||||||
|
/*"append a 1-bit to the message"*/ |
||||||
|
/*"append 0-bits to the message to obtain a length of len%512=448"*/ |
||||||
|
/*"append length of message in bits as 64-bit integer"*/ |
||||||
|
uint8_t j; |
||||||
|
for(j=0;j<(120-(len-(len/64)*64+1))%64+1;j++){ |
||||||
|
if(j==0){ |
||||||
|
sha1_addbyte(0x80); |
||||||
|
}else{ |
||||||
|
sha1_addbyte(0x00); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
{ |
||||||
|
uint64_t l=len*8; |
||||||
|
/*pay attention to the endiness here*/ |
||||||
|
sha1_addbyte( ((char*)(&l))[7]); |
||||||
|
sha1_addbyte( ((char*)(&l))[6]); |
||||||
|
sha1_addbyte( ((char*)(&l))[5]); |
||||||
|
sha1_addbyte( ((char*)(&l))[4]); |
||||||
|
sha1_addbyte( ((char*)(&l))[3]); |
||||||
|
sha1_addbyte( ((char*)(&l))[2]); |
||||||
|
sha1_addbyte( ((char*)(&l))[1]); |
||||||
|
sha1_addbyte( ((char*)(&l))[0]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void sha1(char* msg, int32_t len){ |
||||||
|
int32_t i; |
||||||
|
uint8_t j; |
||||||
|
sha1_init(); |
||||||
|
|
||||||
|
for(i=0; i<=len; i+=64){ |
||||||
|
if(i+64>=len){ |
||||||
|
for(j=0;j<len-i;j++){ |
||||||
|
sha1_addbyte(msg[i+j]); |
||||||
|
} |
||||||
|
sha1_fill(len); |
||||||
|
continue; |
||||||
|
} |
||||||
|
for(j=0;j<64;j++){ |
||||||
|
sha1_addbyte(msg[i+j]); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void sha1_init(void){ |
||||||
|
chunk_cnt=0; |
||||||
|
sha1_digest[0]=H1; |
||||||
|
sha1_digest[1]=H2; |
||||||
|
sha1_digest[2]=H3; |
||||||
|
sha1_digest[3]=H4; |
||||||
|
sha1_digest[4]=H5; |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,15 @@ |
|||||||
|
#ifndef _SHA1_H_ |
||||||
|
#define _SHA1_H_ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#define TO_LITTLE_ENDIAN32(x) ( ((x>>24) | ((x>>8)&0xFF00)) | ((x<<24) | ((x<<8)&0xFF0000)) ) |
||||||
|
|
||||||
|
uint32_t sha1_digest[5]; |
||||||
|
|
||||||
|
void sha1_init(void); |
||||||
|
void sha1(char* msg, int32_t len); |
||||||
|
void sha1_fill(int32_t l); |
||||||
|
void sha1_addbyte(uint8_t c); |
||||||
|
|
||||||
|
#endif //_SHA1_H_
|
||||||
@ -0,0 +1,29 @@ |
|||||||
|
|
||||||
|
#include "trunc.h" |
||||||
|
|
||||||
|
int32_t get_otp(uint8_t* hmac, uint8_t digits){ |
||||||
|
uint8_t n; |
||||||
|
uint32_t dt; |
||||||
|
|
||||||
|
/*Step 1 generate HMAC-SHA1*/ |
||||||
|
/*Step 2 get DT(HMAC) */ |
||||||
|
/* get the lower 4-bits and treat them as a number n*/ |
||||||
|
/* DT is the 32bit integer at HMAC[n]+HMAC[n+1]+HMAC[n+2]+HMAC[n+3]*/ |
||||||
|
n=hmac[SHA1_SIZE-1] & 0x0F; |
||||||
|
dt= (uint32_t)(hmac[n]&0x7F)<<(uint32_t)24 | |
||||||
|
(uint32_t)(hmac[n+1])<<(uint32_t)16 |
|
||||||
|
(uint32_t)(hmac[n+2])<<(uint32_t)8 |
|
||||||
|
(uint32_t)(hmac[n+3]); |
||||||
|
/*Step 3 return dt mod 10^digits*/ |
||||||
|
switch(digits){ |
||||||
|
case 6: return dt % 1000000; |
||||||
|
case 7: return dt % 10000000; |
||||||
|
default: case 8: return dt % 100000000; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int32_t get_otp_from_cnt(uint64_t cnt, uint8_t digits){ |
||||||
|
cnt=TO_LITTLE_ENDIAN64(cnt); |
||||||
|
hmac((uint8_t*)(&cnt),8); |
||||||
|
return get_otp(hmac_result,digits); |
||||||
|
} |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
#ifndef _TRUNC_H_ |
||||||
|
#define _TRUNC_H_ |
||||||
|
|
||||||
|
#include "hmac.h" |
||||||
|
|
||||||
|
#define TO_LITTLE_ENDIAN64(x) (x & 0xFF00000000000000ul)>>(7*8) |\ |
||||||
|
(x & 0x00FF000000000000ul)>>(5*8) |\
|
||||||
|
(x & 0x0000FF0000000000ul)>>(3*8) |\
|
||||||
|
(x & 0x000000FF00000000ul)>>(1*8) |\
|
||||||
|
(x & 0x00000000FF000000ul)<<(1*8) |\
|
||||||
|
(x & 0x0000000000FF0000ul)<<(3*8) |\
|
||||||
|
(x & 0x000000000000FF00ul)<<(5*8) |\
|
||||||
|
(x & 0x00000000000000FFul)<<(7*8)
|
||||||
|
|
||||||
|
int32_t get_otp(uint8_t* hmac, uint8_t digits); |
||||||
|
int32_t get_otp_from_cnt(uint64_t cnt, uint8_t digits); |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,268 @@ |
|||||||
|
/* Name: usbconfig.h
|
||||||
|
* Project: AVR USB driver |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2007-06-23 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) |
||||||
|
* This Revision: $Id: usbconfig.h 537 2008-02-28 21:13:01Z cs $ |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __usbconfig_h_included__ |
||||||
|
#define __usbconfig_h_included__ |
||||||
|
|
||||||
|
/* ---------------------------- Hardware Config ---------------------------- */ |
||||||
|
|
||||||
|
#define USB_CFG_IOPORTNAME B |
||||||
|
/* This is the port where the USB bus is connected. When you configure it to
|
||||||
|
* "B", the registers PORTB, PINB and DDRB will be used. |
||||||
|
*/ |
||||||
|
#define USB_CFG_DMINUS_BIT 4 |
||||||
|
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
|
||||||
|
* This may be any bit in the port. |
||||||
|
*/ |
||||||
|
#define USB_CFG_DPLUS_BIT 3 |
||||||
|
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.
|
||||||
|
* This may be any bit in the port. Please note that D+ must also be connected |
||||||
|
* to interrupt pin INT0! |
||||||
|
*/ |
||||||
|
#define USB_CFG_CLOCK_KHZ (F_CPU/1000) |
||||||
|
/* Clock rate of the AVR in MHz. Legal values are 12000, 16000 or 16500.
|
||||||
|
* The 16.5 MHz version of the code requires no crystal, it tolerates +/- 1% |
||||||
|
* deviation from the nominal frequency. All other rates require a precision |
||||||
|
* of 2000 ppm and thus a crystal! |
||||||
|
* Default if not specified: 12 MHz |
||||||
|
*/ |
||||||
|
|
||||||
|
/* ----------------------- Optional Hardware Config ------------------------ */ |
||||||
|
|
||||||
|
/* #define USB_CFG_PULLUP_IOPORTNAME D */ |
||||||
|
/* If you connect the 1.5k pullup resistor from D- to a port pin instead of
|
||||||
|
* V+, you can connect and disconnect the device from firmware by calling |
||||||
|
* the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h). |
||||||
|
* This constant defines the port on which the pullup resistor is connected. |
||||||
|
*/ |
||||||
|
/* #define USB_CFG_PULLUP_BIT 4 */ |
||||||
|
/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined
|
||||||
|
* above) where the 1.5k pullup resistor is connected. See description |
||||||
|
* above for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* --------------------------- Functional Range ---------------------------- */ |
||||||
|
|
||||||
|
#define USB_CFG_HAVE_INTRIN_ENDPOINT 1 |
||||||
|
/* Define this to 1 if you want to compile a version with two endpoints: The
|
||||||
|
* default control endpoint 0 and an interrupt-in endpoint 1. |
||||||
|
*/ |
||||||
|
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 |
||||||
|
/* Define this to 1 if you want to compile a version with three endpoints: The
|
||||||
|
* default control endpoint 0, an interrupt-in endpoint 1 and an interrupt-in |
||||||
|
* endpoint 3. You must also enable endpoint 1 above. |
||||||
|
*/ |
||||||
|
#define USB_CFG_IMPLEMENT_HALT 0 |
||||||
|
/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature
|
||||||
|
* for endpoint 1 (interrupt endpoint). Although you may not need this feature, |
||||||
|
* it is required by the standard. We have made it a config option because it |
||||||
|
* bloats the code considerably. |
||||||
|
*/ |
||||||
|
#define USB_CFG_INTR_POLL_INTERVAL 10 |
||||||
|
/* If you compile a version with endpoint 1 (interrupt-in), this is the poll
|
||||||
|
* interval. The value is in milliseconds and must not be less than 10 ms for |
||||||
|
* low speed devices. |
||||||
|
*/ |
||||||
|
#define USB_CFG_IS_SELF_POWERED 0 |
||||||
|
/* Define this to 1 if the device has its own power supply. Set it to 0 if the
|
||||||
|
* device is powered from the USB bus. |
||||||
|
*/ |
||||||
|
#define USB_CFG_MAX_BUS_POWER 200 |
||||||
|
/* Set this variable to the maximum USB bus power consumption of your device.
|
||||||
|
* The value is in milliamperes. [It will be divided by two since USB |
||||||
|
* communicates power requirements in units of 2 mA.] |
||||||
|
*/ |
||||||
|
#define USB_CFG_IMPLEMENT_FN_WRITE 0 |
||||||
|
/* Set this to 1 if you want usbFunctionWrite() to be called for control-out
|
||||||
|
* transfers. Set it to 0 if you don't need it and want to save a couple of |
||||||
|
* bytes. |
||||||
|
*/ |
||||||
|
#define USB_CFG_IMPLEMENT_FN_READ 0 |
||||||
|
/* Set this to 1 if you need to send control replies which are generated
|
||||||
|
* "on the fly" when usbFunctionRead() is called. If you only want to send |
||||||
|
* data from a static buffer, set it to 0 and return the data from |
||||||
|
* usbFunctionSetup(). This saves a couple of bytes. |
||||||
|
*/ |
||||||
|
#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0 |
||||||
|
/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoint 1.
|
||||||
|
* You must implement the function usbFunctionWriteOut() which receives all |
||||||
|
* interrupt/bulk data sent to endpoint 1. |
||||||
|
*/ |
||||||
|
#define USB_CFG_HAVE_FLOWCONTROL 0 |
||||||
|
/* Define this to 1 if you want flowcontrol over USB data. See the definition
|
||||||
|
* of the macros usbDisableAllRequests() and usbEnableAllRequests() in |
||||||
|
* usbdrv.h. |
||||||
|
*/ |
||||||
|
#ifndef __ASSEMBLER__ |
||||||
|
extern void usbEventResetReady(void); |
||||||
|
#endif |
||||||
|
//#define USB_RESET_HOOK(isReset) if(!isReset){usbEventResetReady();}
|
||||||
|
/* This macro is a hook if you need to know when an USB RESET occurs. It has
|
||||||
|
* one parameter which distinguishes between the start of RESET state and its |
||||||
|
* end. |
||||||
|
*/ |
||||||
|
#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 1 |
||||||
|
/* define this macro to 1 if you want the function usbMeasureFrameLength()
|
||||||
|
* compiled in. This function can be used to calibrate the AVR's RC oscillator. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* -------------------------- Device Description --------------------------- */ |
||||||
|
|
||||||
|
#define USB_CFG_VENDOR_ID 0x81, 0x17 |
||||||
|
/* USB vendor ID for the device, low byte first. If you have registered your
|
||||||
|
* own Vendor ID, define it here. Otherwise you use obdev's free shared |
||||||
|
* VID/PID pair. Be sure to read USBID-License.txt for rules! |
||||||
|
* This template uses obdev's shared VID/PID pair for HIDs: 0x16c0/0x5df. |
||||||
|
* Use this VID/PID pair ONLY if you understand the implications! |
||||||
|
*/ |
||||||
|
#define USB_CFG_DEVICE_ID 0x9f, 0x0c |
||||||
|
/* This is the ID of the product, low byte first. It is interpreted in the
|
||||||
|
* scope of the vendor ID. If you have registered your own VID with usb.org |
||||||
|
* or if you have licensed a PID from somebody else, define it here. Otherwise |
||||||
|
* you use obdev's free shared VID/PID pair. Be sure to read the rules in |
||||||
|
* USBID-License.txt! |
||||||
|
* This template uses obdev's shared VID/PID pair for HIDs: 0x16c0/0x5df. |
||||||
|
* Use this VID/PID pair ONLY if you understand the implications! |
||||||
|
*/ |
||||||
|
#define USB_CFG_DEVICE_VERSION 0x04, 0x01 |
||||||
|
/* Version number of the device: Minor number first, then major number.
|
||||||
|
*/ |
||||||
|
#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't' |
||||||
|
#define USB_CFG_VENDOR_NAME_LEN 8 |
||||||
|
#undef USB_CFG_VENDOR_NAME |
||||||
|
#undef USB_CFG_VENDOR_NAME_LEN |
||||||
|
/* These two values define the vendor name returned by the USB device. The name
|
||||||
|
* must be given as a list of characters under single quotes. The characters |
||||||
|
* are interpreted as Unicode (UTF-16) entities. |
||||||
|
* If you don't want a vendor name string, undefine these macros. |
||||||
|
* ALWAYS define a vendor name containing your Internet domain name if you use |
||||||
|
* obdev's free shared VID/PID pair. See the file USBID-License.txt for |
||||||
|
* details. |
||||||
|
*/ |
||||||
|
#define USB_CFG_DEVICE_NAME 'U', 'S', 'B', 't', 'i', 'n', 'y', 'S', 'P', 'I' |
||||||
|
#define USB_CFG_DEVICE_NAME_LEN 10 |
||||||
|
/* Same as above for the device name. If you don't want a device name, undefine
|
||||||
|
* the macros. See the file USBID-License.txt before you assign a name if you |
||||||
|
* use a shared VID/PID. |
||||||
|
*/ |
||||||
|
/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */ |
||||||
|
/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */ |
||||||
|
/* Same as above for the serial number. If you don't want a serial number,
|
||||||
|
* undefine the macros. |
||||||
|
* It may be useful to provide the serial number through other means than at |
||||||
|
* compile time. See the section about descriptor properties below for how |
||||||
|
* to fine tune control over USB descriptors such as the string descriptor |
||||||
|
* for the serial number. |
||||||
|
*/ |
||||||
|
#define USB_CFG_DEVICE_CLASS 0 |
||||||
|
#define USB_CFG_DEVICE_SUBCLASS 0 |
||||||
|
/* See USB specification if you want to conform to an existing device class.
|
||||||
|
*/ |
||||||
|
#define USB_CFG_INTERFACE_CLASS 0xff /* HID */ |
||||||
|
#define USB_CFG_INTERFACE_SUBCLASS 0 /* no boot interface */ |
||||||
|
#define USB_CFG_INTERFACE_PROTOCOL 0 /* no protocol */ |
||||||
|
/* See USB specification if you want to conform to an existing device class or
|
||||||
|
* protocol. |
||||||
|
*/ |
||||||
|
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 0 /* total length of report descriptor */ |
||||||
|
/* Define this to the length of the HID report descriptor, if you implement
|
||||||
|
* an HID device. Otherwise don't define it or define it to 0. |
||||||
|
* Since this template defines a HID device, it must also specify a HID |
||||||
|
* report descriptor length. You must add a PROGMEM character array named |
||||||
|
* "usbHidReportDescriptor" to your code which contains the report descriptor. |
||||||
|
* Don't forget to keep the array and this define in sync! |
||||||
|
*/ |
||||||
|
|
||||||
|
/* #define USB_PUBLIC static */ |
||||||
|
/* Use the define above if you #include usbdrv.c instead of linking against it.
|
||||||
|
* This technique saves a couple of bytes in flash memory. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* ------------------- Fine Control over USB Descriptors ------------------- */ |
||||||
|
/* If you don't want to use the driver's default USB descriptors, you can
|
||||||
|
* provide our own. These can be provided as (1) fixed length static data in |
||||||
|
* flash memory, (2) fixed length static data in RAM or (3) dynamically at |
||||||
|
* runtime in the function usbFunctionDescriptor(). See usbdrv.h for more |
||||||
|
* information about this function. |
||||||
|
* Descriptor handling is configured through the descriptor's properties. If |
||||||
|
* no properties are defined or if they are 0, the default descriptor is used. |
||||||
|
* Possible properties are: |
||||||
|
* + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched |
||||||
|
* at runtime via usbFunctionDescriptor(). |
||||||
|
* + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found |
||||||
|
* in static memory is in RAM, not in flash memory. |
||||||
|
* + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash), |
||||||
|
* the driver must know the descriptor's length. The descriptor itself is |
||||||
|
* found at the address of a well known identifier (see below). |
||||||
|
* List of static descriptor names (must be declared PROGMEM if in flash): |
||||||
|
* char usbDescriptorDevice[]; |
||||||
|
* char usbDescriptorConfiguration[]; |
||||||
|
* char usbDescriptorHidReport[]; |
||||||
|
* char usbDescriptorString0[]; |
||||||
|
* int usbDescriptorStringVendor[]; |
||||||
|
* int usbDescriptorStringDevice[]; |
||||||
|
* int usbDescriptorStringSerialNumber[]; |
||||||
|
* Other descriptors can't be provided statically, they must be provided |
||||||
|
* dynamically at runtime. |
||||||
|
* |
||||||
|
* Descriptor properties are or-ed or added together, e.g.: |
||||||
|
* #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18)) |
||||||
|
* |
||||||
|
* The following descriptors are defined: |
||||||
|
* USB_CFG_DESCR_PROPS_DEVICE |
||||||
|
* USB_CFG_DESCR_PROPS_CONFIGURATION |
||||||
|
* USB_CFG_DESCR_PROPS_STRINGS |
||||||
|
* USB_CFG_DESCR_PROPS_STRING_0 |
||||||
|
* USB_CFG_DESCR_PROPS_STRING_VENDOR |
||||||
|
* USB_CFG_DESCR_PROPS_STRING_PRODUCT |
||||||
|
* USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER |
||||||
|
* USB_CFG_DESCR_PROPS_HID |
||||||
|
* USB_CFG_DESCR_PROPS_HID_REPORT |
||||||
|
* USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver) |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#define USB_CFG_DESCR_PROPS_DEVICE 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_STRINGS 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_0 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_HID 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_HID_REPORT 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_UNKNOWN 0 |
||||||
|
|
||||||
|
/* ----------------------- Optional MCU Description ------------------------ */ |
||||||
|
|
||||||
|
/* The following configurations have working defaults in usbdrv.h. You
|
||||||
|
* usually don't need to set them explicitly. Only if you want to run |
||||||
|
* the driver on a device which is not yet supported or with a compiler |
||||||
|
* which is not fully supported (such as IAR C) or if you use a differnt |
||||||
|
* interrupt than INT0, you may have to define some of these. |
||||||
|
*/ |
||||||
|
/* #define USB_INTR_CFG MCUCR */ |
||||||
|
/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */ |
||||||
|
/* #define USB_INTR_CFG_CLR 0 */ |
||||||
|
/* #define USB_INTR_ENABLE GIMSK */ |
||||||
|
/* #define USB_INTR_ENABLE_BIT INT0 */ |
||||||
|
/* #define USB_INTR_PENDING GIFR */ |
||||||
|
/* #define USB_INTR_PENDING_BIT INTF0 */ |
||||||
|
// use PCINT1 instead of INT0
|
||||||
|
#define USB_INTR_CFG PCMSK |
||||||
|
#define USB_INTR_CFG_SET (1<<USB_CFG_DPLUS_BIT) |
||||||
|
#define USB_INTR_ENABLE_BIT PCIE |
||||||
|
#define USB_INTR_PENDING_BIT PCIF |
||||||
|
#define USB_INTR_VECTOR SIG_PIN_CHANGE |
||||||
|
|
||||||
|
//#define USB_INTR_VECTOR INT0_vect
|
||||||
|
//#define USB_INTR_VECTOR PCINT9_vect
|
||||||
|
|
||||||
|
#endif /* __usbconfig_h_included__ */ |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
*.hex |
||||||
|
*.elf |
||||||
|
*.o |
||||||
@ -0,0 +1,318 @@ |
|||||||
|
This file documents changes in the firmware-only USB driver for atmel's AVR |
||||||
|
microcontrollers. New entries are always appended to the end of the file. |
||||||
|
Scroll down to the bottom to see the most recent changes. |
||||||
|
|
||||||
|
2005-04-01: |
||||||
|
- Implemented endpoint 1 as interrupt-in endpoint. |
||||||
|
- Moved all configuration options to usbconfig.h which is not part of the |
||||||
|
driver. |
||||||
|
- Changed interface for usbVendorSetup(). |
||||||
|
- Fixed compatibility with ATMega8 device. |
||||||
|
- Various minor optimizations. |
||||||
|
|
||||||
|
2005-04-11: |
||||||
|
- Changed interface to application: Use usbFunctionSetup(), usbFunctionRead() |
||||||
|
and usbFunctionWrite() now. Added configuration options to choose which |
||||||
|
of these functions to compile in. |
||||||
|
- Assembler module delivers receive data non-inverted now. |
||||||
|
- Made register and bit names compatible with more AVR devices. |
||||||
|
|
||||||
|
2005-05-03: |
||||||
|
- Allow address of usbRxBuf on any memory page as long as the buffer does |
||||||
|
not cross 256 byte page boundaries. |
||||||
|
- Better device compatibility: works with Mega88 now. |
||||||
|
- Code optimization in debugging module. |
||||||
|
- Documentation updates. |
||||||
|
|
||||||
|
2006-01-02: |
||||||
|
- Added (free) default Vendor- and Product-IDs bought from voti.nl. |
||||||
|
- Added USBID-License.txt file which defines the rules for using the free |
||||||
|
shared VID/PID pair. |
||||||
|
- Added Readme.txt to the usbdrv directory which clarifies administrative |
||||||
|
issues. |
||||||
|
|
||||||
|
2006-01-25: |
||||||
|
- Added "configured state" to become more standards compliant. |
||||||
|
- Added "HALT" state for interrupt endpoint. |
||||||
|
- Driver passes the "USB Command Verifier" test from usb.org now. |
||||||
|
- Made "serial number" a configuration option. |
||||||
|
- Minor optimizations, we now recommend compiler option "-Os" for best |
||||||
|
results. |
||||||
|
- Added a version number to usbdrv.h |
||||||
|
|
||||||
|
2006-02-03: |
||||||
|
- New configuration variable USB_BUFFER_SECTION for the memory section where |
||||||
|
the USB rx buffer will go. This defaults to ".bss" if not defined. Since |
||||||
|
this buffer MUST NOT cross 256 byte pages (not even touch a page at the |
||||||
|
end), the user may want to pass a linker option similar to |
||||||
|
"-Wl,--section-start=.mybuffer=0x800060". |
||||||
|
- Provide structure for usbRequest_t. |
||||||
|
- New defines for USB constants. |
||||||
|
- Prepared for HID implementations. |
||||||
|
- Increased data size limit for interrupt transfers to 8 bytes. |
||||||
|
- New macro usbInterruptIsReady() to query interrupt buffer state. |
||||||
|
|
||||||
|
2006-02-18: |
||||||
|
- Ensure that the data token which is sent as an ack to an OUT transfer is |
||||||
|
always zero sized. This fixes a bug where the host reports an error after |
||||||
|
sending an out transfer to the device, although all data arrived at the |
||||||
|
device. |
||||||
|
- Updated docs in usbdrv.h to reflect changed API in usbFunctionWrite(). |
||||||
|
|
||||||
|
* Release 2006-02-20 |
||||||
|
|
||||||
|
- Give a compiler warning when compiling with debugging turned on. |
||||||
|
- Added Oleg Semyonov's changes for IAR-cc compatibility. |
||||||
|
- Added new (optional) functions usbDeviceConnect() and usbDeviceDisconnect() |
||||||
|
(also thanks to Oleg!). |
||||||
|
- Rearranged tests in usbPoll() to save a couple of instructions in the most |
||||||
|
likely case that no actions are pending. |
||||||
|
- We need a delay between the SET ADDRESS request until the new address |
||||||
|
becomes active. This delay was handled in usbPoll() until now. Since the |
||||||
|
spec says that the delay must not exceed 2ms, previous versions required |
||||||
|
aggressive polling during the enumeration phase. We have now moved the |
||||||
|
handling of the delay into the interrupt routine. |
||||||
|
- We must not reply with NAK to a SETUP transaction. We can only achieve this |
||||||
|
by making sure that the rx buffer is empty when SETUP tokens are expected. |
||||||
|
We therefore don't pass zero sized data packets from the status phase of |
||||||
|
a transfer to usbPoll(). This change MAY cause troubles if you rely on |
||||||
|
receiving a less than 8 bytes long packet in usbFunctionWrite() to |
||||||
|
identify the end of a transfer. usbFunctionWrite() will NEVER be called |
||||||
|
with a zero length. |
||||||
|
|
||||||
|
* Release 2006-03-14 |
||||||
|
|
||||||
|
- Improved IAR C support: tiny memory model, more devices |
||||||
|
- Added template usbconfig.h file under the name usbconfig-prototype.h |
||||||
|
|
||||||
|
* Release 2006-03-26 |
||||||
|
|
||||||
|
- Added provision for one more interrupt-in endpoint (endpoint 3). |
||||||
|
- Added provision for one interrupt-out endpoint (endpoint 1). |
||||||
|
- Added flowcontrol macros for USB. |
||||||
|
- Added provision for custom configuration descriptor. |
||||||
|
- Allow ANY two port bits for D+ and D-. |
||||||
|
- Merged (optional) receive endpoint number into global usbRxToken variable. |
||||||
|
- Use USB_CFG_IOPORTNAME instead of USB_CFG_IOPORT. We now construct the |
||||||
|
variable name from the single port letter instead of computing the address |
||||||
|
of related ports from the output-port address. |
||||||
|
|
||||||
|
* Release 2006-06-26 |
||||||
|
|
||||||
|
- Updated documentation in usbdrv.h and usbconfig-prototype.h to reflect the |
||||||
|
new features. |
||||||
|
- Removed "#warning" directives because IAR does not understand them. Use |
||||||
|
unused static variables instead to generate a warning. |
||||||
|
- Do not include <avr/io.h> when compiling with IAR. |
||||||
|
- Introduced USB_CFG_DESCR_PROPS_* in usbconfig.h to configure how each |
||||||
|
USB descriptor should be handled. It is now possible to provide descriptor |
||||||
|
data in Flash, RAM or dynamically at runtime. |
||||||
|
- STALL is now a status in usbTxLen* instead of a message. We can now conform |
||||||
|
to the spec and leave the stall status pending until it is cleared. |
||||||
|
- Made usbTxPacketCnt1 and usbTxPacketCnt3 public. This allows the |
||||||
|
application code to reset data toggling on interrupt pipes. |
||||||
|
|
||||||
|
* Release 2006-07-18 |
||||||
|
|
||||||
|
- Added an #if !defined __ASSEMBLER__ to the warning in usbdrv.h. This fixes |
||||||
|
an assembler error. |
||||||
|
- usbDeviceDisconnect() takes pull-up resistor to high impedance now. |
||||||
|
|
||||||
|
* Release 2007-02-01 |
||||||
|
|
||||||
|
- Merged in some code size improvements from usbtiny (thanks to Dick |
||||||
|
Streefland for these optimizations!) |
||||||
|
- Special alignment requirement for usbRxBuf not required any more. Thanks |
||||||
|
again to Dick Streefland for this hint! |
||||||
|
- Reverted to "#warning" instead of unused static variables -- new versions |
||||||
|
of IAR CC should handle this directive. |
||||||
|
- Changed Open Source license to GNU GPL v2 in order to make linking against |
||||||
|
other free libraries easier. We no longer require publication of the |
||||||
|
circuit diagrams, but we STRONGLY encourage it. If you improve the driver |
||||||
|
itself, PLEASE grant us a royalty free license to your changes for our |
||||||
|
commercial license. |
||||||
|
|
||||||
|
* Release 2007-03-29 |
||||||
|
|
||||||
|
- New configuration option "USB_PUBLIC" in usbconfig.h. |
||||||
|
- Set USB version number to 1.10 instead of 1.01. |
||||||
|
- Code used USB_CFG_DESCR_PROPS_STRING_DEVICE and |
||||||
|
USB_CFG_DESCR_PROPS_STRING_PRODUCT inconsistently. Changed all occurrences |
||||||
|
to USB_CFG_DESCR_PROPS_STRING_PRODUCT. |
||||||
|
- New assembler module for 16.5 MHz RC oscillator clock with PLL in receiver |
||||||
|
code. |
||||||
|
- New assembler module for 16 MHz crystal. |
||||||
|
- usbdrvasm.S contains common code only, clock-specific parts have been moved |
||||||
|
to usbdrvasm12.S, usbdrvasm16.S and usbdrvasm165.S respectively. |
||||||
|
|
||||||
|
* Release 2007-06-25 |
||||||
|
|
||||||
|
- 16 MHz module: Do SE0 check in stuffed bits as well. |
||||||
|
|
||||||
|
* Release 2007-07-07 |
||||||
|
|
||||||
|
- Define hi8(x) for IAR compiler to limit result to 8 bits. This is necessary |
||||||
|
for negative values. |
||||||
|
- Added 15 MHz module contributed by V. Bosch. |
||||||
|
- Interrupt vector name can now be configured. This is useful if somebody |
||||||
|
wants to use a different hardware interrupt than INT0. |
||||||
|
|
||||||
|
* Release 2007-08-07 |
||||||
|
|
||||||
|
- Moved handleIn3 routine in usbdrvasm16.S so that relative jump range is |
||||||
|
not exceeded. |
||||||
|
- More config options: USB_RX_USER_HOOK(), USB_INITIAL_DATATOKEN, |
||||||
|
USB_COUNT_SOF |
||||||
|
- USB_INTR_PENDING can now be a memory address, not just I/O |
||||||
|
|
||||||
|
* Release 2007-09-19 |
||||||
|
|
||||||
|
- Split out common parts of assembler modules into separate include file |
||||||
|
- Made endpoint numbers configurable so that given interface definitions |
||||||
|
can be matched. See USB_CFG_EP3_NUMBER in usbconfig-prototype.h. |
||||||
|
- Store endpoint number for interrupt/bulk-out so that usbFunctionWriteOut() |
||||||
|
can handle any number of endpoints. |
||||||
|
- Define usbDeviceConnect() and usbDeviceDisconnect() even if no |
||||||
|
USB_CFG_PULLUP_IOPORTNAME is defined. Directly set D+ and D- to 0 in this |
||||||
|
case. |
||||||
|
|
||||||
|
* Release 2007-12-01 |
||||||
|
|
||||||
|
- Optimize usbDeviceConnect() and usbDeviceDisconnect() for less code size |
||||||
|
when USB_CFG_PULLUP_IOPORTNAME is not defined. |
||||||
|
|
||||||
|
* Release 2007-12-13 |
||||||
|
|
||||||
|
- Renamed all include-only assembler modules from *.S to *.inc so that |
||||||
|
people don't add them to their project sources. |
||||||
|
- Distribute leap bits in tx loop more evenly for 16 MHz module. |
||||||
|
- Use "macro" and "endm" instead of ".macro" and ".endm" for IAR |
||||||
|
- Avoid compiler warnings for constant expr range by casting some values in |
||||||
|
USB descriptors. |
||||||
|
|
||||||
|
* Release 2008-01-21 |
||||||
|
|
||||||
|
- Fixed bug in 15 and 16 MHz module where the new address set with |
||||||
|
SET_ADDRESS was already accepted at the next NAK or ACK we send, not at |
||||||
|
the next data packet we send. This caused problems when the host polled |
||||||
|
too fast. Thanks to Alexander Neumann for his help and patience debugging |
||||||
|
this issue! |
||||||
|
|
||||||
|
* Release 2008-02-05 |
||||||
|
|
||||||
|
- Fixed bug in 16.5 MHz module where a register was used in the interrupt |
||||||
|
handler before it was pushed. This bug was introduced with version |
||||||
|
2007-09-19 when common parts were moved to a separate file. |
||||||
|
- Optimized CRC routine (thanks to Reimar Doeffinger). |
||||||
|
|
||||||
|
* Release 2008-02-16 |
||||||
|
|
||||||
|
- Removed outdated IAR compatibility stuff (code sections). |
||||||
|
- Added hook macros for USB_RESET_HOOK() and USB_SET_ADDRESS_HOOK(). |
||||||
|
- Added optional routine usbMeasureFrameLength() for calibration of the |
||||||
|
internal RC oscillator. |
||||||
|
|
||||||
|
* Release 2008-02-28 |
||||||
|
|
||||||
|
- USB_INITIAL_DATATOKEN defaults to USBPID_DATA1 now, which means that we |
||||||
|
start with sending USBPID_DATA0. |
||||||
|
- Changed defaults in usbconfig-prototype.h |
||||||
|
- Added free USB VID/PID pair for MIDI class devices |
||||||
|
- Restructured AVR-USB as separate package, not part of PowerSwitch any more. |
||||||
|
|
||||||
|
* Release 2008-04-18 |
||||||
|
|
||||||
|
- Restructured usbdrv.c so that it is easier to read and understand. |
||||||
|
- Better code optimization with gcc 4. |
||||||
|
- If a second interrupt in endpoint is enabled, also add it to config |
||||||
|
descriptor. |
||||||
|
- Added config option for long transfers (above 254 bytes), see |
||||||
|
USB_CFG_LONG_TRANSFERS in usbconfig.h. |
||||||
|
- Added 20 MHz module contributed by Jeroen Benschop. |
||||||
|
|
||||||
|
* Release 2008-05-13 |
||||||
|
|
||||||
|
- Fixed bug in libs-host/hiddata.c function usbhidGetReport(): length |
||||||
|
was not incremented, pointer to length was incremented instead. |
||||||
|
- Added code to command line tool(s) which claims an interface. This code |
||||||
|
is disabled by default, but may be necessary on newer Linux kernels. |
||||||
|
- Added usbconfig.h option "USB_CFG_CHECK_DATA_TOGGLING". |
||||||
|
- New header "usbportability.h" prepares ports to other development |
||||||
|
environments. |
||||||
|
- Long transfers (above 254 bytes) did not work when usbFunctionRead() was |
||||||
|
used to supply the data. Fixed this bug. [Thanks to Alexander Neumann!] |
||||||
|
- In hiddata.c (example code for sending/receiving data over HID), use |
||||||
|
USB_RECIP_DEVICE instead of USB_RECIP_INTERFACE for control transfers so |
||||||
|
that we need not claim the interface. |
||||||
|
- in usbPoll() loop 20 times polling for RESET state instead of 10 times. |
||||||
|
This accounts for the higher clock rates we now support. |
||||||
|
- Added a module for 12.8 MHz RC oscillator with PLL in receiver loop. |
||||||
|
- Added hook to SOF code so that oscillator can be tuned to USB frame clock. |
||||||
|
- Added timeout to waitForJ loop. Helps preventing unexpected hangs. |
||||||
|
- Added example code for oscillator tuning to libs-device (thanks to |
||||||
|
Henrik Haftmann for the idea to this routine). |
||||||
|
- Implemented option USB_CFG_SUPPRESS_INTR_CODE. |
||||||
|
|
||||||
|
* Release 2008-10-22 |
||||||
|
|
||||||
|
- Fixed libs-device/osctune.h: OSCCAL is memory address on ATMega88 and |
||||||
|
similar, not offset of 0x20 needs to be added. |
||||||
|
- Allow distribution under GPLv3 for those who have to link against other |
||||||
|
code distributed under GPLv3. |
||||||
|
|
||||||
|
* Release 2008-11-26 |
||||||
|
|
||||||
|
- Removed libusb-win32 dependency for hid-data example in Makefile.windows. |
||||||
|
It was never required and confused many people. |
||||||
|
- Added extern uchar usbRxToken to usbdrv.h. |
||||||
|
- Integrated a module with CRC checks at 18 MHz by Lukas Schrittwieser. |
||||||
|
|
||||||
|
* Release 2009-03-23 |
||||||
|
|
||||||
|
- Hid-mouse example used settings from hid-data example, fixed that. |
||||||
|
- Renamed project to V-USB due to a trademark issue with Atmel(r). |
||||||
|
- Changed CommercialLicense.txt and USBID-License.txt to make the |
||||||
|
background of USB ID registration clearer. |
||||||
|
|
||||||
|
* Release 2009-04-15 |
||||||
|
|
||||||
|
- Changed CommercialLicense.txt to reflect the new range of PIDs from |
||||||
|
Jason Kotzin. |
||||||
|
- Removed USBID-License.txt in favor of USB-IDs-for-free.txt and |
||||||
|
USB-ID-FAQ.txt |
||||||
|
- Fixed a bug in the 12.8 MHz module: End Of Packet decection was made in |
||||||
|
the center between bit 0 and 1 of each byte. This is where the data lines |
||||||
|
are expected to change and the sampled data may therefore be nonsense. |
||||||
|
We therefore check EOP ONLY if bits 0 AND 1 have both been read as 0 on D-. |
||||||
|
- Fixed a bitstuffing problem in the 16 MHz module: If bit 6 was stuffed, |
||||||
|
the unstuffing code in the receiver routine was 1 cycle too long. If |
||||||
|
multiple bytes had the unstuffing in bit 6, the error summed up until the |
||||||
|
receiver was out of sync. |
||||||
|
- Included option for faster CRC routine. |
||||||
|
Thanks to Slawomir Fras (BoskiDialer) for this code! |
||||||
|
- Updated bits in Configuration Descriptor's bmAttributes according to |
||||||
|
USB 1.1 (in particular bit 7, it is a must-be-set bit now). |
||||||
|
|
||||||
|
* Release 2009-08-22 |
||||||
|
|
||||||
|
- Moved first DBG1() after odDebugInit() in all examples. |
||||||
|
- Use vector INT0_vect instead of SIG_INTERRUPT0 if defined. This makes |
||||||
|
V-USB compatible with the new "p" suffix devices (e.g. ATMega328p). |
||||||
|
- USB_CFG_CLOCK_KHZ setting is now required in usbconfig.h (no default any |
||||||
|
more). |
||||||
|
- New option USB_CFG_DRIVER_FLASH_PAGE allows boot loaders on devices with |
||||||
|
more than 64 kB flash. |
||||||
|
- Built-in configuration descriptor allows custom definition for second |
||||||
|
endpoint now. |
||||||
|
|
||||||
|
* Release 2010-07-15 |
||||||
|
|
||||||
|
- Fixed bug in usbDriverSetup() which prevented descriptor sizes above 255 |
||||||
|
bytes. |
||||||
|
- Avoid a compiler warning for unused parameter in usbHandleResetHook() when |
||||||
|
compiler option -Wextra is enabled. |
||||||
|
- Fixed wrong hex value for some IDs in USB-IDs-for-free.txt. |
||||||
|
- Keep a define for USBATTR_BUSPOWER, although the flag does not exist |
||||||
|
in USB 1.1 any more. Set it to 0. This is for backward compatibility. |
||||||
|
|
||||||
|
* Release 2012-01-09 |
||||||
@ -0,0 +1,166 @@ |
|||||||
|
V-USB Driver Software License Agreement |
||||||
|
Version 2009-08-03 |
||||||
|
|
||||||
|
THIS LICENSE AGREEMENT GRANTS YOU CERTAIN RIGHTS IN A SOFTWARE. YOU CAN |
||||||
|
ENTER INTO THIS AGREEMENT AND ACQUIRE THE RIGHTS OUTLINED BELOW BY PAYING |
||||||
|
THE AMOUNT ACCORDING TO SECTION 4 ("PAYMENT") TO OBJECTIVE DEVELOPMENT. |
||||||
|
|
||||||
|
|
||||||
|
1 DEFINITIONS |
||||||
|
|
||||||
|
1.1 "OBJECTIVE DEVELOPMENT" shall mean OBJECTIVE DEVELOPMENT Software GmbH, |
||||||
|
Grosse Schiffgasse 1A/7, 1020 Wien, AUSTRIA. |
||||||
|
|
||||||
|
1.2 "You" shall mean the Licensee. |
||||||
|
|
||||||
|
1.3 "V-USB" shall mean all files included in the package distributed under |
||||||
|
the name "vusb" by OBJECTIVE DEVELOPMENT (http://www.obdev.at/vusb/) |
||||||
|
unless otherwise noted. This includes the firmware-only USB device |
||||||
|
implementation for Atmel AVR microcontrollers, some simple device examples |
||||||
|
and host side software examples and libraries. |
||||||
|
|
||||||
|
|
||||||
|
2 LICENSE GRANTS |
||||||
|
|
||||||
|
2.1 Source Code. OBJECTIVE DEVELOPMENT shall furnish you with the source |
||||||
|
code of V-USB. |
||||||
|
|
||||||
|
2.2 Distribution and Use. OBJECTIVE DEVELOPMENT grants you the |
||||||
|
non-exclusive right to use, copy and distribute V-USB with your hardware |
||||||
|
product(s), restricted by the limitations in section 3 below. |
||||||
|
|
||||||
|
2.3 Modifications. OBJECTIVE DEVELOPMENT grants you the right to modify |
||||||
|
the source code and your copy of V-USB according to your needs. |
||||||
|
|
||||||
|
2.4 USB IDs. OBJECTIVE DEVELOPMENT furnishes you with one or two USB |
||||||
|
Product ID(s), sent to you in e-mail. These Product IDs are reserved |
||||||
|
exclusively for you. OBJECTIVE DEVELOPMENT has obtained USB Product ID |
||||||
|
ranges under the Vendor ID 5824 from Wouter van Ooijen (Van Ooijen |
||||||
|
Technische Informatica, www.voti.nl) and under the Vendor ID 8352 from |
||||||
|
Jason Kotzin (Clay Logic, www.claylogic.com). Both owners of the Vendor IDs |
||||||
|
have obtained these IDs from the USB Implementers Forum, Inc. |
||||||
|
(www.usb.org). OBJECTIVE DEVELOPMENT disclaims all liability which might |
||||||
|
arise from the assignment of USB IDs. |
||||||
|
|
||||||
|
2.5 USB Certification. Although not part of this agreement, we want to make |
||||||
|
it clear that you cannot become USB certified when you use V-USB or a USB |
||||||
|
Product ID assigned by OBJECTIVE DEVELOPMENT. AVR microcontrollers don't |
||||||
|
meet the electrical specifications required by the USB specification and |
||||||
|
the USB Implementers Forum certifies only members who bought a Vendor ID of |
||||||
|
their own. |
||||||
|
|
||||||
|
|
||||||
|
3 LICENSE RESTRICTIONS |
||||||
|
|
||||||
|
3.1 Number of Units. Only one of the following three definitions is |
||||||
|
applicable. Which one is determined by the amount you pay to OBJECTIVE |
||||||
|
DEVELOPMENT, see section 4 ("Payment") below. |
||||||
|
|
||||||
|
Hobby License: You may use V-USB according to section 2 above in no more |
||||||
|
than 5 hardware units. These units must not be sold for profit. |
||||||
|
|
||||||
|
Entry Level License: You may use V-USB according to section 2 above in no |
||||||
|
more than 150 hardware units. |
||||||
|
|
||||||
|
Professional License: You may use V-USB according to section 2 above in |
||||||
|
any number of hardware units, except for large scale production ("unlimited |
||||||
|
fair use"). Quantities below 10,000 units are not considered large scale |
||||||
|
production. If your reach quantities which are obviously large scale |
||||||
|
production, you must pay a license fee of 0.10 EUR per unit for all units |
||||||
|
above 10,000. |
||||||
|
|
||||||
|
3.2 Rental. You may not rent, lease, or lend V-USB or otherwise encumber |
||||||
|
any copy of V-USB, or any of the rights granted herein. |
||||||
|
|
||||||
|
3.3 Transfer. You may not transfer your rights under this Agreement to |
||||||
|
another party without OBJECTIVE DEVELOPMENT's prior written consent. If |
||||||
|
such consent is obtained, you may permanently transfer this License to |
||||||
|
another party. The recipient of such transfer must agree to all terms and |
||||||
|
conditions of this Agreement. |
||||||
|
|
||||||
|
3.4 Reservation of Rights. OBJECTIVE DEVELOPMENT retains all rights not |
||||||
|
expressly granted. |
||||||
|
|
||||||
|
3.5 Non-Exclusive Rights. Your license rights under this Agreement are |
||||||
|
non-exclusive. |
||||||
|
|
||||||
|
3.6 Third Party Rights. This Agreement cannot grant you rights controlled |
||||||
|
by third parties. In particular, you are not allowed to use the USB logo or |
||||||
|
other trademarks owned by the USB Implementers Forum, Inc. without their |
||||||
|
consent. Since such consent depends on USB certification, it should be |
||||||
|
noted that V-USB will not pass certification because it does not |
||||||
|
implement checksum verification and the microcontroller ports do not meet |
||||||
|
the electrical specifications. |
||||||
|
|
||||||
|
|
||||||
|
4 PAYMENT |
||||||
|
|
||||||
|
The payment amount depends on the variation of this agreement (according to |
||||||
|
section 3.1) into which you want to enter. Concrete prices are listed on |
||||||
|
OBJECTIVE DEVELOPMENT's web site, usually at |
||||||
|
http://www.obdev.at/vusb/license.html. You agree to pay the amount listed |
||||||
|
there to OBJECTIVE DEVELOPMENT or OBJECTIVE DEVELOPMENT's payment processor |
||||||
|
or reseller. |
||||||
|
|
||||||
|
|
||||||
|
5 COPYRIGHT AND OWNERSHIP |
||||||
|
|
||||||
|
V-USB is protected by copyright laws and international copyright |
||||||
|
treaties, as well as other intellectual property laws and treaties. V-USB |
||||||
|
is licensed, not sold. |
||||||
|
|
||||||
|
|
||||||
|
6 TERM AND TERMINATION |
||||||
|
|
||||||
|
6.1 Term. This Agreement shall continue indefinitely. However, OBJECTIVE |
||||||
|
DEVELOPMENT may terminate this Agreement and revoke the granted license and |
||||||
|
USB-IDs if you fail to comply with any of its terms and conditions. |
||||||
|
|
||||||
|
6.2 Survival of Terms. All provisions regarding secrecy, confidentiality |
||||||
|
and limitation of liability shall survive termination of this agreement. |
||||||
|
|
||||||
|
|
||||||
|
7 DISCLAIMER OF WARRANTY AND LIABILITY |
||||||
|
|
||||||
|
LIMITED WARRANTY. V-USB IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
||||||
|
KIND. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, OBJECTIVE |
||||||
|
DEVELOPMENT AND ITS SUPPLIERS HEREBY DISCLAIM ALL WARRANTIES, EITHER |
||||||
|
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND |
||||||
|
NON-INFRINGEMENT, WITH REGARD TO V-USB, AND THE PROVISION OF OR FAILURE |
||||||
|
TO PROVIDE SUPPORT SERVICES. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL |
||||||
|
RIGHTS. YOU MAY HAVE OTHERS, WHICH VARY FROM STATE/JURISDICTION TO |
||||||
|
STATE/JURISDICTION. |
||||||
|
|
||||||
|
LIMITATION OF LIABILITY. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, |
||||||
|
IN NO EVENT SHALL OBJECTIVE DEVELOPMENT OR ITS SUPPLIERS BE LIABLE FOR ANY |
||||||
|
SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER |
||||||
|
(INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, |
||||||
|
BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY |
||||||
|
LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE V-USB OR THE |
||||||
|
PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF OBJECTIVE |
||||||
|
DEVELOPMENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY |
||||||
|
CASE, OBJECTIVE DEVELOPMENT'S ENTIRE LIABILITY UNDER ANY PROVISION OF THIS |
||||||
|
AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR V-USB. |
||||||
|
|
||||||
|
|
||||||
|
8 MISCELLANEOUS TERMS |
||||||
|
|
||||||
|
8.1 Marketing. OBJECTIVE DEVELOPMENT has the right to mention for marketing |
||||||
|
purposes that you entered into this agreement. |
||||||
|
|
||||||
|
8.2 Entire Agreement. This document represents the entire agreement between |
||||||
|
OBJECTIVE DEVELOPMENT and you. It may only be modified in writing signed by |
||||||
|
an authorized representative of both, OBJECTIVE DEVELOPMENT and you. |
||||||
|
|
||||||
|
8.3 Severability. In case a provision of these terms and conditions should |
||||||
|
be or become partly or entirely invalid, ineffective, or not executable, |
||||||
|
the validity of all other provisions shall not be affected. |
||||||
|
|
||||||
|
8.4 Applicable Law. This agreement is governed by the laws of the Republic |
||||||
|
of Austria. |
||||||
|
|
||||||
|
8.5 Responsible Courts. The responsible courts in Vienna/Austria will have |
||||||
|
exclusive jurisdiction regarding all disputes in connection with this |
||||||
|
agreement. |
||||||
|
|
||||||
@ -0,0 +1,172 @@ |
|||||||
|
This is the Readme file to Objective Development's firmware-only USB driver |
||||||
|
for Atmel AVR microcontrollers. For more information please visit |
||||||
|
http://www.obdev.at/vusb/ |
||||||
|
|
||||||
|
This directory contains the USB firmware only. Copy it as-is to your own |
||||||
|
project and add all .c and .S files to your project (these files are marked |
||||||
|
with an asterisk in the list below). Then copy usbconfig-prototype.h as |
||||||
|
usbconfig.h to your project and edit it according to your configuration. |
||||||
|
|
||||||
|
|
||||||
|
TECHNICAL DOCUMENTATION |
||||||
|
======================= |
||||||
|
The technical documentation (API) for the firmware driver is contained in the |
||||||
|
file "usbdrv.h". Please read all of it carefully! Configuration options are |
||||||
|
documented in "usbconfig-prototype.h". |
||||||
|
|
||||||
|
The driver consists of the following files: |
||||||
|
Readme.txt ............. The file you are currently reading. |
||||||
|
Changelog.txt .......... Release notes for all versions of the driver. |
||||||
|
usbdrv.h ............... Driver interface definitions and technical docs. |
||||||
|
* usbdrv.c ............... High level language part of the driver. Link this |
||||||
|
module to your code! |
||||||
|
* usbdrvasm.S ............ Assembler part of the driver. This module is mostly |
||||||
|
a stub and includes one of the usbdrvasm*.S files |
||||||
|
depending on processor clock. Link this module to |
||||||
|
your code! |
||||||
|
usbdrvasm*.inc ......... Assembler routines for particular clock frequencies. |
||||||
|
Included by usbdrvasm.S, don't link it directly! |
||||||
|
asmcommon.inc .......... Common assembler routines. Included by |
||||||
|
usbdrvasm*.inc, don't link it directly! |
||||||
|
usbconfig-prototype.h .. Prototype for your own usbdrv.h file. |
||||||
|
* oddebug.c .............. Debug functions. Only used when DEBUG_LEVEL is |
||||||
|
defined to a value greater than 0. Link this module |
||||||
|
to your code! |
||||||
|
oddebug.h .............. Interface definitions of the debug module. |
||||||
|
usbportability.h ....... Header with compiler-dependent stuff. |
||||||
|
usbdrvasm.asm .......... Compatibility stub for IAR-C-compiler. Use this |
||||||
|
module instead of usbdrvasm.S when you assembler |
||||||
|
with IAR's tools. |
||||||
|
License.txt ............ Open Source license for this driver. |
||||||
|
CommercialLicense.txt .. Optional commercial license for this driver. |
||||||
|
USB-ID-FAQ.txt ......... General infos about USB Product- and Vendor-IDs. |
||||||
|
USB-IDs-for-free.txt ... List and terms of use for free shared PIDs. |
||||||
|
|
||||||
|
(*) ... These files should be linked to your project. |
||||||
|
|
||||||
|
|
||||||
|
CPU CORE CLOCK FREQUENCY |
||||||
|
======================== |
||||||
|
We supply assembler modules for clock frequencies of 12 MHz, 12.8 MHz, 15 MHz, |
||||||
|
16 MHz, 16.5 MHz 18 MHz and 20 MHz. Other clock rates are not supported. The |
||||||
|
actual clock rate must be configured in usbconfig.h. |
||||||
|
|
||||||
|
12 MHz Clock |
||||||
|
This is the traditional clock rate of V-USB because it's the lowest clock |
||||||
|
rate where the timing constraints of the USB spec can be met. |
||||||
|
|
||||||
|
15 MHz Clock |
||||||
|
Similar to 12 MHz, but some NOPs inserted. On the other hand, the higher clock |
||||||
|
rate allows for some loops which make the resulting code size somewhat smaller |
||||||
|
than the 12 MHz version. |
||||||
|
|
||||||
|
16 MHz Clock |
||||||
|
This clock rate has been added for users of the Arduino board and other |
||||||
|
ready-made boards which come with a fixed 16 MHz crystal. It's also an option |
||||||
|
if you need the slightly higher clock rate for performance reasons. Since |
||||||
|
16 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code |
||||||
|
is somewhat tricky and has to insert a leap cycle every third byte. |
||||||
|
|
||||||
|
12.8 MHz and 16.5 MHz Clock |
||||||
|
The assembler modules for these clock rates differ from the other modules |
||||||
|
because they have been built for an RC oscillator with only 1% precision. The |
||||||
|
receiver code inserts leap cycles to compensate for clock deviations. 1% is |
||||||
|
also the precision which can be achieved by calibrating the internal RC |
||||||
|
oscillator of the AVR. Please note that only AVRs with internal 64 MHz PLL |
||||||
|
oscillator can reach 16.5 MHz with the RC oscillator. This includes the very |
||||||
|
popular ATTiny25, ATTiny45, ATTiny85 series as well as the ATTiny26. Almost |
||||||
|
all AVRs can reach 12.8 MHz, although this is outside the specified range. |
||||||
|
|
||||||
|
See the EasyLogger example at http://www.obdev.at/vusb/easylogger.html for |
||||||
|
code which calibrates the RC oscillator based on the USB frame clock. |
||||||
|
|
||||||
|
18 MHz Clock |
||||||
|
This module is closer to the USB specification because it performs an on the |
||||||
|
fly CRC check for incoming packets. Packets with invalid checksum are |
||||||
|
discarded as required by the spec. If you also implement checks for data |
||||||
|
PID toggling on application level (see option USB_CFG_CHECK_DATA_TOGGLING |
||||||
|
in usbconfig.h for more info), this ensures data integrity. Due to the CRC |
||||||
|
tables and alignment requirements, this code is bigger than modules for other |
||||||
|
clock rates. To activate this module, you must define USB_CFG_CHECK_CRC to 1 |
||||||
|
and USB_CFG_CLOCK_KHZ to 18000 in usbconfig.h. |
||||||
|
|
||||||
|
20 MHz Clock |
||||||
|
This module is for people who won't do it with less than the maximum. Since |
||||||
|
20 MHz is not divisible by the USB low speed bit clock of 1.5 MHz, the code |
||||||
|
uses similar tricks as the 16 MHz module to insert leap cycles. |
||||||
|
|
||||||
|
|
||||||
|
USB IDENTIFIERS |
||||||
|
=============== |
||||||
|
Every USB device needs a vendor- and a product-identifier (VID and PID). VIDs |
||||||
|
are obtained from usb.org for a price of 1,500 USD. Once you have a VID, you |
||||||
|
can assign PIDs at will. |
||||||
|
|
||||||
|
Since an entry level cost of 1,500 USD is too high for most small companies |
||||||
|
and hobbyists, we provide some VID/PID pairs for free. See the file |
||||||
|
USB-IDs-for-free.txt for details. |
||||||
|
|
||||||
|
Objective Development also has some license offerings which include product |
||||||
|
IDs. See http://www.obdev.at/vusb/ for details. |
||||||
|
|
||||||
|
|
||||||
|
DEVELOPMENT SYSTEM |
||||||
|
================== |
||||||
|
This driver has been developed and optimized for the GNU compiler version 3 |
||||||
|
and 4. We recommend that you use the GNU compiler suite because it is freely |
||||||
|
available. V-USB has also been ported to the IAR compiler and assembler. It |
||||||
|
has been tested with IAR 4.10B/W32 and 4.12A/W32 on an ATmega8 with the |
||||||
|
"small" and "tiny" memory model. Not every release is tested with IAR CC and |
||||||
|
the driver may therefore fail to compile with IAR. Please note that gcc is |
||||||
|
more efficient for usbdrv.c because this module has been deliberately |
||||||
|
optimized for gcc. |
||||||
|
|
||||||
|
Gcc version 3 produces smaller code than version 4 due to new optimizing |
||||||
|
capabilities which don't always improve things on 8 bit CPUs. The code size |
||||||
|
generated by gcc 4 can be reduced with the compiler options |
||||||
|
-fno-move-loop-invariants, -fno-tree-scev-cprop and |
||||||
|
-fno-inline-small-functions in addition to -Os. On devices with more than |
||||||
|
8k of flash memory, we also recommend the linker option --relax (written as |
||||||
|
-Wl,--relax for gcc) to convert absolute calls into relative where possible. |
||||||
|
|
||||||
|
For more information about optimizing options see: |
||||||
|
|
||||||
|
http://www.tty1.net/blog/2008-04-29-avr-gcc-optimisations_en.html |
||||||
|
|
||||||
|
These optimizations are good for gcc 4.x. Version 3.x of gcc does not support |
||||||
|
most of these options and produces good code anyway. |
||||||
|
|
||||||
|
|
||||||
|
USING V-USB FOR FREE |
||||||
|
==================== |
||||||
|
The AVR firmware driver is published under the GNU General Public License |
||||||
|
Version 2 (GPL2) and the GNU General Public License Version 3 (GPL3). It is |
||||||
|
your choice whether you apply the terms of version 2 or version 3. |
||||||
|
|
||||||
|
If you decide for the free GPL2 or GPL3, we STRONGLY ENCOURAGE you to do the |
||||||
|
following things IN ADDITION to the obligations from the GPL: |
||||||
|
|
||||||
|
(1) Publish your entire project on a web site and drop us a note with the URL. |
||||||
|
Use the form at http://www.obdev.at/vusb/feedback.html for your submission. |
||||||
|
If you don't have a web site, you can publish the project in obdev's |
||||||
|
documentation wiki at |
||||||
|
http://www.obdev.at/goto.php?t=vusb-wiki&p=hosted-projects. |
||||||
|
|
||||||
|
(2) Adhere to minimum publication standards. Please include AT LEAST: |
||||||
|
- a circuit diagram in PDF, PNG or GIF format |
||||||
|
- full source code for the host software |
||||||
|
- a Readme.txt file in ASCII format which describes the purpose of the |
||||||
|
project and what can be found in which directories and which files |
||||||
|
- a reference to http://www.obdev.at/vusb/ |
||||||
|
|
||||||
|
(3) If you improve the driver firmware itself, please give us a free license |
||||||
|
to your modifications for our commercial license offerings. |
||||||
|
|
||||||
|
|
||||||
|
COMMERCIAL LICENSES FOR V-USB |
||||||
|
============================= |
||||||
|
If you don't want to publish your source code under the terms of the GPL, |
||||||
|
you can simply pay money for V-USB. As an additional benefit you get |
||||||
|
USB PIDs for free, reserved exclusively to you. See the file |
||||||
|
"CommercialLicense.txt" for details. |
||||||
|
|
||||||
@ -0,0 +1,149 @@ |
|||||||
|
Version 2009-08-22 |
||||||
|
|
||||||
|
========================== |
||||||
|
WHY DO WE NEED THESE IDs? |
||||||
|
========================== |
||||||
|
|
||||||
|
USB is more than a low level protocol for data transport. It also defines a |
||||||
|
common set of requests which must be understood by all devices. And as part |
||||||
|
of these common requests, the specification defines data structures, the |
||||||
|
USB Descriptors, which are used to describe the properties of the device. |
||||||
|
|
||||||
|
From the perspective of an operating system, it is therefore possible to find |
||||||
|
out basic properties of a device (such as e.g. the manufacturer and the name |
||||||
|
of the device) without a device-specific driver. This is essential because |
||||||
|
the operating system can choose a driver to load based on this information |
||||||
|
(Plug-And-Play). |
||||||
|
|
||||||
|
Among the most important properties in the Device Descriptor are the USB |
||||||
|
Vendor- and Product-ID. Both are 16 bit integers. The most simple form of |
||||||
|
driver matching is based on these IDs. The driver announces the Vendor- and |
||||||
|
Product-IDs of the devices it can handle and the operating system loads the |
||||||
|
appropriate driver when the device is connected. |
||||||
|
|
||||||
|
It is obvious that this technique only works if the pair Vendor- plus |
||||||
|
Product-ID is unique: Only devices which require the same driver can have the |
||||||
|
same pair of IDs. |
||||||
|
|
||||||
|
|
||||||
|
===================================================== |
||||||
|
HOW DOES THE USB STANDARD ENSURE THAT IDs ARE UNIQUE? |
||||||
|
===================================================== |
||||||
|
|
||||||
|
Since it is so important that USB IDs are unique, the USB Implementers Forum, |
||||||
|
Inc. (usb.org) needs a way to enforce this legally. It is not forbidden by |
||||||
|
law to build a device and assign it any random numbers as IDs. Usb.org |
||||||
|
therefore needs an agreement to regulate the use of USB IDs. The agreement |
||||||
|
binds only parties who agreed to it, of course. Everybody else is free to use |
||||||
|
any numbers for their IDs. |
||||||
|
|
||||||
|
So how can usb.org ensure that every manufacturer of USB devices enters into |
||||||
|
an agreement with them? They do it via trademark licensing. Usb.org has |
||||||
|
registered the trademark "USB", all associated logos and related terms. If |
||||||
|
you want to put an USB logo on your product or claim that it is USB |
||||||
|
compliant, you must license these trademarks from usb.org. And this is where |
||||||
|
you enter into an agreement. See the "USB-IF Trademark License Agreement and |
||||||
|
Usage Guidelines for the USB-IF Logo" at |
||||||
|
http://www.usb.org/developers/logo_license/. |
||||||
|
|
||||||
|
Licensing the USB trademarks requires that you buy a USB Vendor-ID from |
||||||
|
usb.org (one-time fee of ca. 2,000 USD), that you become a member of usb.org |
||||||
|
(yearly fee of ca. 4,000 USD) and that you meet all the technical |
||||||
|
specifications from the USB spec. |
||||||
|
|
||||||
|
This means that most hobbyists and small companies will never be able to |
||||||
|
become USB compliant, just because membership is so expensive. And you can't |
||||||
|
be compliant with a driver based on V-USB anyway, because the AVR's port pins |
||||||
|
don't meet the electrical specifications for USB. So, in principle, all |
||||||
|
hobbyists and small companies are free to choose any random numbers for their |
||||||
|
IDs. They have nothing to lose... |
||||||
|
|
||||||
|
There is one exception worth noting, though: If you use a sub-component which |
||||||
|
implements USB, the vendor of the sub-components may guarantee USB |
||||||
|
compliance. This might apply to some or all of FTDI's solutions. |
||||||
|
|
||||||
|
|
||||||
|
======================================================================= |
||||||
|
WHY SHOULD YOU OBTAIN USB IDs EVEN IF YOU DON'T LICENSE USB TRADEMARKS? |
||||||
|
======================================================================= |
||||||
|
|
||||||
|
You have learned in the previous section that you are free to choose any |
||||||
|
numbers for your IDs anyway. So why not do exactly this? There is still the |
||||||
|
technical issue. If you choose IDs which are already in use by somebody else, |
||||||
|
operating systems will load the wrong drivers and your device won't work. |
||||||
|
Even if you choose IDs which are not currently in use, they may be in use in |
||||||
|
the next version of the operating system or even after an automatic update. |
||||||
|
|
||||||
|
So what you need is a pair of Vendor- and Product-IDs for which you have the |
||||||
|
guarantee that no USB compliant product uses them. This implies that no |
||||||
|
operating system will ever ship with drivers responsible for these IDs. |
||||||
|
|
||||||
|
|
||||||
|
============================================== |
||||||
|
HOW DOES OBJECTIVE DEVELOPMENT HANDLE USB IDs? |
||||||
|
============================================== |
||||||
|
|
||||||
|
Objective Development gives away pairs of USB-IDs with their V-USB licenses. |
||||||
|
In order to ensure that these IDs are unique, Objective Development has an |
||||||
|
agreement with the company/person who has bought the USB Vendor-ID from |
||||||
|
usb.org. This agreement ensures that a range of USB Product-IDs is reserved |
||||||
|
for assignment by Objective Development and that the owner of the Vendor-ID |
||||||
|
won't give it to anybody else. |
||||||
|
|
||||||
|
This means that you have to trust three parties to ensure uniqueness of |
||||||
|
your IDs: |
||||||
|
|
||||||
|
- Objective Development, that they don't give the same PID to more than |
||||||
|
one person. |
||||||
|
- The owner of the Vendor-ID that they don't assign PIDs from the range |
||||||
|
assigned to Objective Development to anybody else. |
||||||
|
- Usb.org that they don't assign the same Vendor-ID a second time. |
||||||
|
|
||||||
|
|
||||||
|
================================== |
||||||
|
WHO IS THE OWNER OF THE VENDOR-ID? |
||||||
|
================================== |
||||||
|
|
||||||
|
Objective Development has obtained ranges of USB Product-IDs under two |
||||||
|
Vendor-IDs: Under Vendor-ID 5824 from Wouter van Ooijen (Van Ooijen |
||||||
|
Technische Informatica, www.voti.nl) and under Vendor-ID 8352 from Jason |
||||||
|
Kotzin (Clay Logic, www.claylogic.com). Both VID owners have received their |
||||||
|
Vendor-ID directly from usb.org. |
||||||
|
|
||||||
|
|
||||||
|
========================================================================= |
||||||
|
CAN I USE USB-IDs FROM OBJECTIVE DEVELOPMENT WITH OTHER DRIVERS/HARDWARE? |
||||||
|
========================================================================= |
||||||
|
|
||||||
|
The short answer is: Yes. All you get is a guarantee that the IDs are never |
||||||
|
assigned to anybody else. What more do you need? |
||||||
|
|
||||||
|
|
||||||
|
============================ |
||||||
|
WHAT ABOUT SHARED ID PAIRS? |
||||||
|
============================ |
||||||
|
|
||||||
|
Objective Development has reserved some PID/VID pairs for shared use. You |
||||||
|
have no guarantee of uniqueness for them, except that no USB compliant device |
||||||
|
uses them. In order to avoid technical problems, we must ensure that all |
||||||
|
devices with the same pair of IDs use the same driver on kernel level. For |
||||||
|
details, see the file USB-IDs-for-free.txt. |
||||||
|
|
||||||
|
|
||||||
|
====================================================== |
||||||
|
I HAVE HEARD THAT SUB-LICENSING OF USB-IDs IS ILLEGAL? |
||||||
|
====================================================== |
||||||
|
|
||||||
|
A 16 bit integer number cannot be protected by copyright laws. It is not |
||||||
|
sufficiently complex. And since none of the parties involved entered into the |
||||||
|
USB-IF Trademark License Agreement, we are not bound by this agreement. So |
||||||
|
there is no reason why it should be illegal to sub-license USB-IDs. |
||||||
|
|
||||||
|
|
||||||
|
============================================= |
||||||
|
WHO IS LIABLE IF THERE ARE INCOMPATIBILITIES? |
||||||
|
============================================= |
||||||
|
|
||||||
|
Objective Development disclaims all liabilities which might arise from the |
||||||
|
assignment of IDs. If you guarantee product features to your customers |
||||||
|
without proper disclaimer, YOU are liable for that. |
||||||
@ -0,0 +1,154 @@ |
|||||||
|
Version 2009-08-22 |
||||||
|
|
||||||
|
=========================== |
||||||
|
FREE USB-IDs FOR SHARED USE |
||||||
|
=========================== |
||||||
|
|
||||||
|
Objective Development has reserved a set of USB Product-IDs for use according |
||||||
|
to the guidelines outlined below. For more information about the concept of |
||||||
|
USB IDs please see the file USB-ID-FAQ.txt. Objective Development guarantees |
||||||
|
that the IDs listed below are not used by any USB compliant devices. |
||||||
|
|
||||||
|
|
||||||
|
==================== |
||||||
|
MECHANISM OF SHARING |
||||||
|
==================== |
||||||
|
|
||||||
|
From a technical point of view, two different devices can share the same USB |
||||||
|
Vendor- and Product-ID if they require the same driver on operating system |
||||||
|
level. We make use of this fact by assigning separate IDs for various device |
||||||
|
classes. On application layer, devices must be distinguished by their textual |
||||||
|
name or serial number. We offer separate sets of IDs for discrimination by |
||||||
|
textual name and for serial number. |
||||||
|
|
||||||
|
Examples for shared use of USB IDs are included with V-USB in the "examples" |
||||||
|
subdirectory. |
||||||
|
|
||||||
|
|
||||||
|
====================================== |
||||||
|
IDs FOR DISCRIMINATION BY TEXTUAL NAME |
||||||
|
====================================== |
||||||
|
|
||||||
|
If you use one of the IDs listed below, your device and host-side software |
||||||
|
must conform to these rules: |
||||||
|
|
||||||
|
(1) The USB device MUST provide a textual representation of the manufacturer |
||||||
|
and product identification. The manufacturer identification MUST be available |
||||||
|
at least in USB language 0x0409 (English/US). |
||||||
|
|
||||||
|
(2) The textual manufacturer identification MUST contain either an Internet |
||||||
|
domain name (e.g. "mycompany.com") registered and owned by you, or an e-mail |
||||||
|
address under your control (e.g. "myname@gmx.net"). You can embed the domain |
||||||
|
name or e-mail address in any string you like, e.g. "Objective Development |
||||||
|
http://www.obdev.at/vusb/". |
||||||
|
|
||||||
|
(3) You are responsible for retaining ownership of the domain or e-mail |
||||||
|
address for as long as any of your products are in use. |
||||||
|
|
||||||
|
(4) You may choose any string for the textual product identification, as long |
||||||
|
as this string is unique within the scope of your textual manufacturer |
||||||
|
identification. |
||||||
|
|
||||||
|
(5) Application side device look-up MUST be based on the textual manufacturer |
||||||
|
and product identification in addition to VID/PID matching. The driver |
||||||
|
matching MUST be a comparison of the entire strings, NOT a sub-string match. |
||||||
|
|
||||||
|
(6) For devices which implement a particular USB device class (e.g. HID), the |
||||||
|
operating system's default class driver MUST be used. If an operating system |
||||||
|
driver for Vendor Class devices is needed, this driver must be libusb or |
||||||
|
libusb-win32 (see http://libusb.org/ and |
||||||
|
http://libusb-win32.sourceforge.net/). |
||||||
|
|
||||||
|
Table if IDs for discrimination by textual name: |
||||||
|
|
||||||
|
PID dec (hex) | VID dec (hex) | Description of use |
||||||
|
==============+===============+============================================ |
||||||
|
1500 (0x05dc) | 5824 (0x16c0) | For Vendor Class devices with libusb |
||||||
|
--------------+---------------+-------------------------------------------- |
||||||
|
1503 (0x05df) | 5824 (0x16c0) | For generic HID class devices (which are |
||||||
|
| | NOT mice, keyboards or joysticks) |
||||||
|
--------------+---------------+-------------------------------------------- |
||||||
|
1505 (0x05e1) | 5824 (0x16c0) | For CDC-ACM class devices (modems) |
||||||
|
--------------+---------------+-------------------------------------------- |
||||||
|
1508 (0x05e4) | 5824 (0x16c0) | For MIDI class devices |
||||||
|
--------------+---------------+-------------------------------------------- |
||||||
|
|
||||||
|
Note that Windows caches the textual product- and vendor-description for |
||||||
|
mice, keyboards and joysticks. Name-bsed discrimination is therefore not |
||||||
|
recommended for these device classes. |
||||||
|
|
||||||
|
|
||||||
|
======================================= |
||||||
|
IDs FOR DISCRIMINATION BY SERIAL NUMBER |
||||||
|
======================================= |
||||||
|
|
||||||
|
If you use one of the IDs listed below, your device and host-side software |
||||||
|
must conform to these rules: |
||||||
|
|
||||||
|
(1) The USB device MUST provide a textual representation of the serial |
||||||
|
number, unless ONLY the operating system's default class driver is used. |
||||||
|
The serial number string MUST be available at least in USB language 0x0409 |
||||||
|
(English/US). |
||||||
|
|
||||||
|
(2) The serial number MUST start with either an Internet domain name (e.g. |
||||||
|
"mycompany.com") registered and owned by you, or an e-mail address under your |
||||||
|
control (e.g. "myname@gmx.net"), both terminated with a colon (":") character. |
||||||
|
You MAY append any string you like for further discrimination of your devices. |
||||||
|
|
||||||
|
(3) You are responsible for retaining ownership of the domain or e-mail |
||||||
|
address for as long as any of your products are in use. |
||||||
|
|
||||||
|
(5) Application side device look-up MUST be based on the serial number string |
||||||
|
in addition to VID/PID matching. The matching must start at the first |
||||||
|
character of the serial number string and include the colon character |
||||||
|
terminating your domain or e-mail address. It MAY stop anywhere after that. |
||||||
|
|
||||||
|
(6) For devices which implement a particular USB device class (e.g. HID), the |
||||||
|
operating system's default class driver MUST be used. If an operating system |
||||||
|
driver for Vendor Class devices is needed, this driver must be libusb or |
||||||
|
libusb-win32 (see http://libusb.org/ and |
||||||
|
http://libusb-win32.sourceforge.net/). |
||||||
|
|
||||||
|
(7) If ONLY the operating system's default class driver is used, e.g. for |
||||||
|
mice, keyboards, joysticks, CDC or MIDI devices and no discrimination by an |
||||||
|
application is needed, the serial number may be omitted. |
||||||
|
|
||||||
|
|
||||||
|
Table if IDs for discrimination by serial number string: |
||||||
|
|
||||||
|
PID dec (hex) | VID dec (hex) | Description of use |
||||||
|
===============+===============+=========================================== |
||||||
|
10200 (0x27d8) | 5824 (0x16c0) | For Vendor Class devices with libusb |
||||||
|
---------------+---------------+------------------------------------------- |
||||||
|
10201 (0x27d9) | 5824 (0x16c0) | For generic HID class devices (which are |
||||||
|
| | NOT mice, keyboards or joysticks) |
||||||
|
---------------+---------------+------------------------------------------- |
||||||
|
10202 (0x27da) | 5824 (0x16c0) | For USB Mice |
||||||
|
---------------+---------------+------------------------------------------- |
||||||
|
10203 (0x27db) | 5824 (0x16c0) | For USB Keyboards |
||||||
|
---------------+---------------+------------------------------------------- |
||||||
|
10204 (0x27dc) | 5824 (0x16c0) | For USB Joysticks |
||||||
|
---------------+---------------+------------------------------------------- |
||||||
|
10205 (0x27dd) | 5824 (0x16c0) | For CDC-ACM class devices (modems) |
||||||
|
---------------+---------------+------------------------------------------- |
||||||
|
10206 (0x27de) | 5824 (0x16c0) | For MIDI class devices |
||||||
|
---------------+---------------+------------------------------------------- |
||||||
|
|
||||||
|
|
||||||
|
================= |
||||||
|
ORIGIN OF USB-IDs |
||||||
|
================= |
||||||
|
|
||||||
|
OBJECTIVE DEVELOPMENT Software GmbH has obtained all VID/PID pairs listed |
||||||
|
here from Wouter van Ooijen (see www.voti.nl) for exclusive disposition. |
||||||
|
Wouter van Ooijen has obtained the VID from the USB Implementers Forum, Inc. |
||||||
|
(see www.usb.org). The VID is registered for the company name "Van Ooijen |
||||||
|
Technische Informatica". |
||||||
|
|
||||||
|
|
||||||
|
========== |
||||||
|
DISCLAIMER |
||||||
|
========== |
||||||
|
|
||||||
|
OBJECTIVE DEVELOPMENT Software GmbH disclaims all liability for any |
||||||
|
problems which are caused by the shared use of these VID/PID pairs. |
||||||
@ -0,0 +1,188 @@ |
|||||||
|
/* Name: asmcommon.inc |
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2007-11-05 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||||
|
* appropriate implementation! |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
General Description: |
||||||
|
This file contains assembler code which is shared among the USB driver |
||||||
|
implementations for different CPU cocks. Since the code must be inserted |
||||||
|
in the middle of the module, it's split out into this file and #included. |
||||||
|
|
||||||
|
Jump destinations called from outside: |
||||||
|
sofError: Called when no start sequence was found. |
||||||
|
se0: Called when a package has been successfully received. |
||||||
|
overflow: Called when receive buffer overflows. |
||||||
|
doReturn: Called after sending data. |
||||||
|
|
||||||
|
Outside jump destinations used by this module: |
||||||
|
waitForJ: Called to receive an already arriving packet. |
||||||
|
sendAckAndReti: |
||||||
|
sendNakAndReti: |
||||||
|
sendCntAndReti: |
||||||
|
usbSendAndReti: |
||||||
|
|
||||||
|
The following macros must be defined before this file is included: |
||||||
|
.macro POP_STANDARD |
||||||
|
.endm |
||||||
|
.macro POP_RETI |
||||||
|
.endm |
||||||
|
*/ |
||||||
|
|
||||||
|
#define token x1 |
||||||
|
|
||||||
|
overflow: |
||||||
|
ldi x2, 1<<USB_INTR_PENDING_BIT |
||||||
|
USB_STORE_PENDING(x2) ; clear any pending interrupts |
||||||
|
ignorePacket: |
||||||
|
clr token |
||||||
|
rjmp storeTokenAndReturn |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Processing of received packet (numbers in brackets are cycles after center of SE0) |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
;This is the only non-error exit point for the software receiver loop |
||||||
|
;we don't check any CRCs here because there is no time left. |
||||||
|
se0: |
||||||
|
subi cnt, USB_BUFSIZE ;[5] |
||||||
|
neg cnt ;[6] |
||||||
|
sub YL, cnt ;[7] |
||||||
|
sbci YH, 0 ;[8] |
||||||
|
ldi x2, 1<<USB_INTR_PENDING_BIT ;[9] |
||||||
|
USB_STORE_PENDING(x2) ;[10] clear pending intr and check flag later. SE0 should be over. |
||||||
|
ld token, y ;[11] |
||||||
|
cpi token, USBPID_DATA0 ;[13] |
||||||
|
breq handleData ;[14] |
||||||
|
cpi token, USBPID_DATA1 ;[15] |
||||||
|
breq handleData ;[16] |
||||||
|
lds shift, usbDeviceAddr;[17] |
||||||
|
ldd x2, y+1 ;[19] ADDR and 1 bit endpoint number |
||||||
|
lsl x2 ;[21] shift out 1 bit endpoint number |
||||||
|
cpse x2, shift ;[22] |
||||||
|
rjmp ignorePacket ;[23] |
||||||
|
/* only compute endpoint number in x3 if required later */ |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT || USB_CFG_IMPLEMENT_FN_WRITEOUT |
||||||
|
ldd x3, y+2 ;[24] endpoint number + crc |
||||||
|
rol x3 ;[26] shift in LSB of endpoint |
||||||
|
#endif |
||||||
|
cpi token, USBPID_IN ;[27] |
||||||
|
breq handleIn ;[28] |
||||||
|
cpi token, USBPID_SETUP ;[29] |
||||||
|
breq handleSetupOrOut ;[30] |
||||||
|
cpi token, USBPID_OUT ;[31] |
||||||
|
brne ignorePacket ;[32] must be ack, nak or whatever |
||||||
|
; rjmp handleSetupOrOut ; fallthrough |
||||||
|
|
||||||
|
;Setup and Out are followed by a data packet two bit times (16 cycles) after |
||||||
|
;the end of SE0. The sync code allows up to 40 cycles delay from the start of |
||||||
|
;the sync pattern until the first bit is sampled. That's a total of 56 cycles. |
||||||
|
handleSetupOrOut: ;[32] |
||||||
|
#if USB_CFG_IMPLEMENT_FN_WRITEOUT /* if we have data for endpoint != 0, set usbCurrentTok to address */ |
||||||
|
andi x3, 0xf ;[32] |
||||||
|
breq storeTokenAndReturn ;[33] |
||||||
|
mov token, x3 ;[34] indicate that this is endpoint x OUT |
||||||
|
#endif |
||||||
|
storeTokenAndReturn: |
||||||
|
sts usbCurrentTok, token;[35] |
||||||
|
doReturn: |
||||||
|
POP_STANDARD ;[37] 12...16 cycles |
||||||
|
USB_LOAD_PENDING(YL) ;[49] |
||||||
|
sbrc YL, USB_INTR_PENDING_BIT;[50] check whether data is already arriving |
||||||
|
rjmp waitForJ ;[51] save the pops and pushes -- a new interrupt is already pending |
||||||
|
sofError: |
||||||
|
POP_RETI ;macro call |
||||||
|
reti |
||||||
|
|
||||||
|
handleData: |
||||||
|
#if USB_CFG_CHECK_CRC |
||||||
|
CRC_CLEANUP_AND_CHECK ; jumps to ignorePacket if CRC error |
||||||
|
#endif |
||||||
|
lds shift, usbCurrentTok;[18] |
||||||
|
tst shift ;[20] |
||||||
|
breq doReturn ;[21] |
||||||
|
lds x2, usbRxLen ;[22] |
||||||
|
tst x2 ;[24] |
||||||
|
brne sendNakAndReti ;[25] |
||||||
|
; 2006-03-11: The following two lines fix a problem where the device was not |
||||||
|
; recognized if usbPoll() was called less frequently than once every 4 ms. |
||||||
|
cpi cnt, 4 ;[26] zero sized data packets are status phase only -- ignore and ack |
||||||
|
brmi sendAckAndReti ;[27] keep rx buffer clean -- we must not NAK next SETUP |
||||||
|
#if USB_CFG_CHECK_DATA_TOGGLING |
||||||
|
sts usbCurrentDataToken, token ; store for checking by C code |
||||||
|
#endif |
||||||
|
sts usbRxLen, cnt ;[28] store received data, swap buffers |
||||||
|
sts usbRxToken, shift ;[30] |
||||||
|
lds x2, usbInputBufOffset;[32] swap buffers |
||||||
|
ldi cnt, USB_BUFSIZE ;[34] |
||||||
|
sub cnt, x2 ;[35] |
||||||
|
sts usbInputBufOffset, cnt;[36] buffers now swapped |
||||||
|
rjmp sendAckAndReti ;[38] 40 + 17 = 57 until SOP |
||||||
|
|
||||||
|
handleIn: |
||||||
|
;We don't send any data as long as the C code has not processed the current |
||||||
|
;input data and potentially updated the output data. That's more efficient |
||||||
|
;in terms of code size than clearing the tx buffers when a packet is received. |
||||||
|
lds x1, usbRxLen ;[30] |
||||||
|
cpi x1, 1 ;[32] negative values are flow control, 0 means "buffer free" |
||||||
|
brge sendNakAndReti ;[33] unprocessed input packet? |
||||||
|
ldi x1, USBPID_NAK ;[34] prepare value for usbTxLen |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
||||||
|
andi x3, 0xf ;[35] x3 contains endpoint |
||||||
|
#if USB_CFG_SUPPRESS_INTR_CODE |
||||||
|
brne sendNakAndReti ;[36] |
||||||
|
#else |
||||||
|
brne handleIn1 ;[36] |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
lds cnt, usbTxLen ;[37] |
||||||
|
sbrc cnt, 4 ;[39] all handshake tokens have bit 4 set |
||||||
|
rjmp sendCntAndReti ;[40] 42 + 16 = 58 until SOP |
||||||
|
sts usbTxLen, x1 ;[41] x1 == USBPID_NAK from above |
||||||
|
ldi YL, lo8(usbTxBuf) ;[43] |
||||||
|
ldi YH, hi8(usbTxBuf) ;[44] |
||||||
|
rjmp usbSendAndReti ;[45] 57 + 12 = 59 until SOP |
||||||
|
|
||||||
|
; Comment about when to set usbTxLen to USBPID_NAK: |
||||||
|
; We should set it back when we receive the ACK from the host. This would |
||||||
|
; be simple to implement: One static variable which stores whether the last |
||||||
|
; tx was for endpoint 0 or 1 and a compare in the receiver to distinguish the |
||||||
|
; ACK. However, we set it back immediately when we send the package, |
||||||
|
; assuming that no error occurs and the host sends an ACK. We save one byte |
||||||
|
; RAM this way and avoid potential problems with endless retries. The rest of |
||||||
|
; the driver assumes error-free transfers anyway. |
||||||
|
|
||||||
|
#if !USB_CFG_SUPPRESS_INTR_CODE && USB_CFG_HAVE_INTRIN_ENDPOINT /* placed here due to relative jump range */ |
||||||
|
handleIn1: ;[38] |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||||
|
; 2006-06-10 as suggested by O.Tamura: support second INTR IN / BULK IN endpoint |
||||||
|
cpi x3, USB_CFG_EP3_NUMBER;[38] |
||||||
|
breq handleIn3 ;[39] |
||||||
|
#endif |
||||||
|
lds cnt, usbTxLen1 ;[40] |
||||||
|
sbrc cnt, 4 ;[42] all handshake tokens have bit 4 set |
||||||
|
rjmp sendCntAndReti ;[43] 47 + 16 = 63 until SOP |
||||||
|
sts usbTxLen1, x1 ;[44] x1 == USBPID_NAK from above |
||||||
|
ldi YL, lo8(usbTxBuf1) ;[46] |
||||||
|
ldi YH, hi8(usbTxBuf1) ;[47] |
||||||
|
rjmp usbSendAndReti ;[48] 50 + 12 = 62 until SOP |
||||||
|
|
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||||
|
handleIn3: |
||||||
|
lds cnt, usbTxLen3 ;[41] |
||||||
|
sbrc cnt, 4 ;[43] |
||||||
|
rjmp sendCntAndReti ;[44] 49 + 16 = 65 until SOP |
||||||
|
sts usbTxLen3, x1 ;[45] x1 == USBPID_NAK from above |
||||||
|
ldi YL, lo8(usbTxBuf3) ;[47] |
||||||
|
ldi YH, hi8(usbTxBuf3) ;[48] |
||||||
|
rjmp usbSendAndReti ;[49] 51 + 12 = 63 until SOP |
||||||
|
#endif |
||||||
|
#endif |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
/* Name: oddebug.c
|
||||||
|
* Project: AVR library |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2005-01-16 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* This Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "oddebug.h" |
||||||
|
|
||||||
|
#if DEBUG_LEVEL > 0 |
||||||
|
|
||||||
|
#warning "Never compile production devices with debugging enabled" |
||||||
|
|
||||||
|
static void uartPutc(char c) |
||||||
|
{ |
||||||
|
while(!(ODDBG_USR & (1 << ODDBG_UDRE))); /* wait for data register empty */ |
||||||
|
ODDBG_UDR = c; |
||||||
|
} |
||||||
|
|
||||||
|
static uchar hexAscii(uchar h) |
||||||
|
{ |
||||||
|
h &= 0xf; |
||||||
|
if(h >= 10) |
||||||
|
h += 'a' - (uchar)10 - '0'; |
||||||
|
h += '0'; |
||||||
|
return h; |
||||||
|
} |
||||||
|
|
||||||
|
static void printHex(uchar c) |
||||||
|
{ |
||||||
|
uartPutc(hexAscii(c >> 4)); |
||||||
|
uartPutc(hexAscii(c)); |
||||||
|
} |
||||||
|
|
||||||
|
void odDebug(uchar prefix, uchar *data, uchar len) |
||||||
|
{ |
||||||
|
printHex(prefix); |
||||||
|
uartPutc(':'); |
||||||
|
while(len--){ |
||||||
|
uartPutc(' '); |
||||||
|
printHex(*data++); |
||||||
|
} |
||||||
|
uartPutc('\r'); |
||||||
|
uartPutc('\n'); |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,123 @@ |
|||||||
|
/* Name: oddebug.h
|
||||||
|
* Project: AVR library |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2005-01-16 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* This Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __oddebug_h_included__ |
||||||
|
#define __oddebug_h_included__ |
||||||
|
|
||||||
|
/*
|
||||||
|
General Description: |
||||||
|
This module implements a function for debug logs on the serial line of the |
||||||
|
AVR microcontroller. Debugging can be configured with the define |
||||||
|
'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging |
||||||
|
calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is |
||||||
|
2, DBG1 and DBG2 logs will be printed. |
||||||
|
|
||||||
|
A debug log consists of a label ('prefix') to indicate which debug log created |
||||||
|
the output and a memory block to dump in hex ('data' and 'len'). |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#ifndef F_CPU |
||||||
|
# define F_CPU 12000000 /* 12 MHz */ |
||||||
|
#endif |
||||||
|
|
||||||
|
/* make sure we have the UART defines: */ |
||||||
|
#include "usbportability.h" |
||||||
|
|
||||||
|
#ifndef uchar |
||||||
|
# define uchar unsigned char |
||||||
|
#endif |
||||||
|
|
||||||
|
#if DEBUG_LEVEL > 0 && !(defined TXEN || defined TXEN0) /* no UART in device */ |
||||||
|
# warning "Debugging disabled because device has no UART" |
||||||
|
# undef DEBUG_LEVEL |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef DEBUG_LEVEL |
||||||
|
# define DEBUG_LEVEL 0 |
||||||
|
#endif |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
#if DEBUG_LEVEL > 0 |
||||||
|
# define DBG1(prefix, data, len) odDebug(prefix, data, len) |
||||||
|
#else |
||||||
|
# define DBG1(prefix, data, len) |
||||||
|
#endif |
||||||
|
|
||||||
|
#if DEBUG_LEVEL > 1 |
||||||
|
# define DBG2(prefix, data, len) odDebug(prefix, data, len) |
||||||
|
#else |
||||||
|
# define DBG2(prefix, data, len) |
||||||
|
#endif |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
#if DEBUG_LEVEL > 0 |
||||||
|
extern void odDebug(uchar prefix, uchar *data, uchar len); |
||||||
|
|
||||||
|
/* Try to find our control registers; ATMEL likes to rename these */ |
||||||
|
|
||||||
|
#if defined UBRR |
||||||
|
# define ODDBG_UBRR UBRR |
||||||
|
#elif defined UBRRL |
||||||
|
# define ODDBG_UBRR UBRRL |
||||||
|
#elif defined UBRR0 |
||||||
|
# define ODDBG_UBRR UBRR0 |
||||||
|
#elif defined UBRR0L |
||||||
|
# define ODDBG_UBRR UBRR0L |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined UCR |
||||||
|
# define ODDBG_UCR UCR |
||||||
|
#elif defined UCSRB |
||||||
|
# define ODDBG_UCR UCSRB |
||||||
|
#elif defined UCSR0B |
||||||
|
# define ODDBG_UCR UCSR0B |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined TXEN |
||||||
|
# define ODDBG_TXEN TXEN |
||||||
|
#else |
||||||
|
# define ODDBG_TXEN TXEN0 |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined USR |
||||||
|
# define ODDBG_USR USR |
||||||
|
#elif defined UCSRA |
||||||
|
# define ODDBG_USR UCSRA |
||||||
|
#elif defined UCSR0A |
||||||
|
# define ODDBG_USR UCSR0A |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined UDRE |
||||||
|
# define ODDBG_UDRE UDRE |
||||||
|
#else |
||||||
|
# define ODDBG_UDRE UDRE0 |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined UDR |
||||||
|
# define ODDBG_UDR UDR |
||||||
|
#elif defined UDR0 |
||||||
|
# define ODDBG_UDR UDR0 |
||||||
|
#endif |
||||||
|
|
||||||
|
static inline void odDebugInit(void) |
||||||
|
{ |
||||||
|
ODDBG_UCR |= (1<<ODDBG_TXEN); |
||||||
|
ODDBG_UBRR = F_CPU / (19200 * 16L) - 1; |
||||||
|
} |
||||||
|
#else |
||||||
|
# define odDebugInit() |
||||||
|
#endif |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
#endif /* __oddebug_h_included__ */ |
||||||
@ -0,0 +1,376 @@ |
|||||||
|
/* Name: usbconfig.h
|
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2005-04-01 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* This Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __usbconfig_h_included__ |
||||||
|
#define __usbconfig_h_included__ |
||||||
|
|
||||||
|
/*
|
||||||
|
General Description: |
||||||
|
This file is an example configuration (with inline documentation) for the USB |
||||||
|
driver. It configures V-USB for USB D+ connected to Port D bit 2 (which is |
||||||
|
also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may |
||||||
|
wire the lines to any other port, as long as D+ is also wired to INT0 (or any |
||||||
|
other hardware interrupt, as long as it is the highest level interrupt, see |
||||||
|
section at the end of this file). |
||||||
|
+ To create your own usbconfig.h file, copy this file to your project's |
||||||
|
+ firmware source directory) and rename it to "usbconfig.h". |
||||||
|
+ Then edit it accordingly. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* ---------------------------- Hardware Config ---------------------------- */ |
||||||
|
|
||||||
|
#define USB_CFG_IOPORTNAME D |
||||||
|
/* This is the port where the USB bus is connected. When you configure it to
|
||||||
|
* "B", the registers PORTB, PINB and DDRB will be used. |
||||||
|
*/ |
||||||
|
#define USB_CFG_DMINUS_BIT 4 |
||||||
|
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
|
||||||
|
* This may be any bit in the port. |
||||||
|
*/ |
||||||
|
#define USB_CFG_DPLUS_BIT 2 |
||||||
|
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.
|
||||||
|
* This may be any bit in the port. Please note that D+ must also be connected |
||||||
|
* to interrupt pin INT0! [You can also use other interrupts, see section |
||||||
|
* "Optional MCU Description" below, or you can connect D- to the interrupt, as |
||||||
|
* it is required if you use the USB_COUNT_SOF feature. If you use D- for the |
||||||
|
* interrupt, the USB interrupt will also be triggered at Start-Of-Frame |
||||||
|
* markers every millisecond.] |
||||||
|
*/ |
||||||
|
#define USB_CFG_CLOCK_KHZ (F_CPU/1000) |
||||||
|
/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000,
|
||||||
|
* 16500, 18000 and 20000. The 12.8 MHz and 16.5 MHz versions of the code |
||||||
|
* require no crystal, they tolerate +/- 1% deviation from the nominal |
||||||
|
* frequency. All other rates require a precision of 2000 ppm and thus a |
||||||
|
* crystal! |
||||||
|
* Since F_CPU should be defined to your actual clock rate anyway, you should |
||||||
|
* not need to modify this setting. |
||||||
|
*/ |
||||||
|
#define USB_CFG_CHECK_CRC 0 |
||||||
|
/* Define this to 1 if you want that the driver checks integrity of incoming
|
||||||
|
* data packets (CRC checks). CRC checks cost quite a bit of code size and are |
||||||
|
* currently only available for 18 MHz crystal clock. You must choose |
||||||
|
* USB_CFG_CLOCK_KHZ = 18000 if you enable this option. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* ----------------------- Optional Hardware Config ------------------------ */ |
||||||
|
|
||||||
|
/* #define USB_CFG_PULLUP_IOPORTNAME D */ |
||||||
|
/* If you connect the 1.5k pullup resistor from D- to a port pin instead of
|
||||||
|
* V+, you can connect and disconnect the device from firmware by calling |
||||||
|
* the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h). |
||||||
|
* This constant defines the port on which the pullup resistor is connected. |
||||||
|
*/ |
||||||
|
/* #define USB_CFG_PULLUP_BIT 4 */ |
||||||
|
/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined
|
||||||
|
* above) where the 1.5k pullup resistor is connected. See description |
||||||
|
* above for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* --------------------------- Functional Range ---------------------------- */ |
||||||
|
|
||||||
|
#define USB_CFG_HAVE_INTRIN_ENDPOINT 0 |
||||||
|
/* Define this to 1 if you want to compile a version with two endpoints: The
|
||||||
|
* default control endpoint 0 and an interrupt-in endpoint (any other endpoint |
||||||
|
* number). |
||||||
|
*/ |
||||||
|
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 |
||||||
|
/* Define this to 1 if you want to compile a version with three endpoints: The
|
||||||
|
* default control endpoint 0, an interrupt-in endpoint 3 (or the number |
||||||
|
* configured below) and a catch-all default interrupt-in endpoint as above. |
||||||
|
* You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature. |
||||||
|
*/ |
||||||
|
#define USB_CFG_EP3_NUMBER 3 |
||||||
|
/* If the so-called endpoint 3 is used, it can now be configured to any other
|
||||||
|
* endpoint number (except 0) with this macro. Default if undefined is 3. |
||||||
|
*/ |
||||||
|
/* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */ |
||||||
|
/* The above macro defines the startup condition for data toggling on the
|
||||||
|
* interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1. |
||||||
|
* Since the token is toggled BEFORE sending any data, the first packet is |
||||||
|
* sent with the oposite value of this configuration! |
||||||
|
*/ |
||||||
|
#define USB_CFG_IMPLEMENT_HALT 0 |
||||||
|
/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature
|
||||||
|
* for endpoint 1 (interrupt endpoint). Although you may not need this feature, |
||||||
|
* it is required by the standard. We have made it a config option because it |
||||||
|
* bloats the code considerably. |
||||||
|
*/ |
||||||
|
#define USB_CFG_SUPPRESS_INTR_CODE 0 |
||||||
|
/* Define this to 1 if you want to declare interrupt-in endpoints, but don't
|
||||||
|
* want to send any data over them. If this macro is defined to 1, functions |
||||||
|
* usbSetInterrupt() and usbSetInterrupt3() are omitted. This is useful if |
||||||
|
* you need the interrupt-in endpoints in order to comply to an interface |
||||||
|
* (e.g. HID), but never want to send any data. This option saves a couple |
||||||
|
* of bytes in flash memory and the transmit buffers in RAM. |
||||||
|
*/ |
||||||
|
#define USB_CFG_INTR_POLL_INTERVAL 10 |
||||||
|
/* If you compile a version with endpoint 1 (interrupt-in), this is the poll
|
||||||
|
* interval. The value is in milliseconds and must not be less than 10 ms for |
||||||
|
* low speed devices. |
||||||
|
*/ |
||||||
|
#define USB_CFG_IS_SELF_POWERED 0 |
||||||
|
/* Define this to 1 if the device has its own power supply. Set it to 0 if the
|
||||||
|
* device is powered from the USB bus. |
||||||
|
*/ |
||||||
|
#define USB_CFG_MAX_BUS_POWER 100 |
||||||
|
/* Set this variable to the maximum USB bus power consumption of your device.
|
||||||
|
* The value is in milliamperes. [It will be divided by two since USB |
||||||
|
* communicates power requirements in units of 2 mA.] |
||||||
|
*/ |
||||||
|
#define USB_CFG_IMPLEMENT_FN_WRITE 0 |
||||||
|
/* Set this to 1 if you want usbFunctionWrite() to be called for control-out
|
||||||
|
* transfers. Set it to 0 if you don't need it and want to save a couple of |
||||||
|
* bytes. |
||||||
|
*/ |
||||||
|
#define USB_CFG_IMPLEMENT_FN_READ 0 |
||||||
|
/* Set this to 1 if you need to send control replies which are generated
|
||||||
|
* "on the fly" when usbFunctionRead() is called. If you only want to send |
||||||
|
* data from a static buffer, set it to 0 and return the data from |
||||||
|
* usbFunctionSetup(). This saves a couple of bytes. |
||||||
|
*/ |
||||||
|
#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0 |
||||||
|
/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints.
|
||||||
|
* You must implement the function usbFunctionWriteOut() which receives all |
||||||
|
* interrupt/bulk data sent to any endpoint other than 0. The endpoint number |
||||||
|
* can be found in 'usbRxToken'. |
||||||
|
*/ |
||||||
|
#define USB_CFG_HAVE_FLOWCONTROL 0 |
||||||
|
/* Define this to 1 if you want flowcontrol over USB data. See the definition
|
||||||
|
* of the macros usbDisableAllRequests() and usbEnableAllRequests() in |
||||||
|
* usbdrv.h. |
||||||
|
*/ |
||||||
|
#define USB_CFG_DRIVER_FLASH_PAGE 0 |
||||||
|
/* If the device has more than 64 kBytes of flash, define this to the 64 k page
|
||||||
|
* where the driver's constants (descriptors) are located. Or in other words: |
||||||
|
* Define this to 1 for boot loaders on the ATMega128. |
||||||
|
*/ |
||||||
|
#define USB_CFG_LONG_TRANSFERS 0 |
||||||
|
/* Define this to 1 if you want to send/receive blocks of more than 254 bytes
|
||||||
|
* in a single control-in or control-out transfer. Note that the capability |
||||||
|
* for long transfers increases the driver size. |
||||||
|
*/ |
||||||
|
/* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */ |
||||||
|
/* This macro is a hook if you want to do unconventional things. If it is
|
||||||
|
* defined, it's inserted at the beginning of received message processing. |
||||||
|
* If you eat the received message and don't want default processing to |
||||||
|
* proceed, do a return after doing your things. One possible application |
||||||
|
* (besides debugging) is to flash a status LED on each packet. |
||||||
|
*/ |
||||||
|
/* #define USB_RESET_HOOK(resetStarts) if(!resetStarts){hadUsbReset();} */ |
||||||
|
/* This macro is a hook if you need to know when an USB RESET occurs. It has
|
||||||
|
* one parameter which distinguishes between the start of RESET state and its |
||||||
|
* end. |
||||||
|
*/ |
||||||
|
/* #define USB_SET_ADDRESS_HOOK() hadAddressAssigned(); */ |
||||||
|
/* This macro (if defined) is executed when a USB SET_ADDRESS request was
|
||||||
|
* received. |
||||||
|
*/ |
||||||
|
#define USB_COUNT_SOF 0 |
||||||
|
/* define this macro to 1 if you need the global variable "usbSofCount" which
|
||||||
|
* counts SOF packets. This feature requires that the hardware interrupt is |
||||||
|
* connected to D- instead of D+. |
||||||
|
*/ |
||||||
|
/* #ifdef __ASSEMBLER__
|
||||||
|
* macro myAssemblerMacro |
||||||
|
* in YL, TCNT0 |
||||||
|
* sts timer0Snapshot, YL |
||||||
|
* endm |
||||||
|
* #endif |
||||||
|
* #define USB_SOF_HOOK myAssemblerMacro |
||||||
|
* This macro (if defined) is executed in the assembler module when a |
||||||
|
* Start Of Frame condition is detected. It is recommended to define it to |
||||||
|
* the name of an assembler macro which is defined here as well so that more |
||||||
|
* than one assembler instruction can be used. The macro may use the register |
||||||
|
* YL and modify SREG. If it lasts longer than a couple of cycles, USB messages |
||||||
|
* immediately after an SOF pulse may be lost and must be retried by the host. |
||||||
|
* What can you do with this hook? Since the SOF signal occurs exactly every |
||||||
|
* 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in |
||||||
|
* designs running on the internal RC oscillator. |
||||||
|
* Please note that Start Of Frame detection works only if D- is wired to the |
||||||
|
* interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES! |
||||||
|
*/ |
||||||
|
#define USB_CFG_CHECK_DATA_TOGGLING 0 |
||||||
|
/* define this macro to 1 if you want to filter out duplicate data packets
|
||||||
|
* sent by the host. Duplicates occur only as a consequence of communication |
||||||
|
* errors, when the host does not receive an ACK. Please note that you need to |
||||||
|
* implement the filtering yourself in usbFunctionWriteOut() and |
||||||
|
* usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable |
||||||
|
* for each control- and out-endpoint to check for duplicate packets. |
||||||
|
*/ |
||||||
|
#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0 |
||||||
|
/* define this macro to 1 if you want the function usbMeasureFrameLength()
|
||||||
|
* compiled in. This function can be used to calibrate the AVR's RC oscillator. |
||||||
|
*/ |
||||||
|
#define USB_USE_FAST_CRC 0 |
||||||
|
/* The assembler module has two implementations for the CRC algorithm. One is
|
||||||
|
* faster, the other is smaller. This CRC routine is only used for transmitted |
||||||
|
* messages where timing is not critical. The faster routine needs 31 cycles |
||||||
|
* per byte while the smaller one needs 61 to 69 cycles. The faster routine |
||||||
|
* may be worth the 32 bytes bigger code size if you transmit lots of data and |
||||||
|
* run the AVR close to its limit. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* -------------------------- Device Description --------------------------- */ |
||||||
|
|
||||||
|
#define USB_CFG_VENDOR_ID 0xc0, 0x16 /* = 0x16c0 = 5824 = voti.nl */ |
||||||
|
/* USB vendor ID for the device, low byte first. If you have registered your
|
||||||
|
* own Vendor ID, define it here. Otherwise you may use one of obdev's free |
||||||
|
* shared VID/PID pairs. Be sure to read USB-IDs-for-free.txt for rules! |
||||||
|
* *** IMPORTANT NOTE *** |
||||||
|
* This template uses obdev's shared VID/PID pair for Vendor Class devices |
||||||
|
* with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand |
||||||
|
* the implications! |
||||||
|
*/ |
||||||
|
#define USB_CFG_DEVICE_ID 0xdc, 0x05 /* = 0x05dc = 1500 */ |
||||||
|
/* This is the ID of the product, low byte first. It is interpreted in the
|
||||||
|
* scope of the vendor ID. If you have registered your own VID with usb.org |
||||||
|
* or if you have licensed a PID from somebody else, define it here. Otherwise |
||||||
|
* you may use one of obdev's free shared VID/PID pairs. See the file |
||||||
|
* USB-IDs-for-free.txt for details! |
||||||
|
* *** IMPORTANT NOTE *** |
||||||
|
* This template uses obdev's shared VID/PID pair for Vendor Class devices |
||||||
|
* with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand |
||||||
|
* the implications! |
||||||
|
*/ |
||||||
|
#define USB_CFG_DEVICE_VERSION 0x00, 0x01 |
||||||
|
/* Version number of the device: Minor number first, then major number.
|
||||||
|
*/ |
||||||
|
#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't' |
||||||
|
#define USB_CFG_VENDOR_NAME_LEN 8 |
||||||
|
/* These two values define the vendor name returned by the USB device. The name
|
||||||
|
* must be given as a list of characters under single quotes. The characters |
||||||
|
* are interpreted as Unicode (UTF-16) entities. |
||||||
|
* If you don't want a vendor name string, undefine these macros. |
||||||
|
* ALWAYS define a vendor name containing your Internet domain name if you use |
||||||
|
* obdev's free shared VID/PID pair. See the file USB-IDs-for-free.txt for |
||||||
|
* details. |
||||||
|
*/ |
||||||
|
#define USB_CFG_DEVICE_NAME 'T', 'e', 'm', 'p', 'l', 'a', 't', 'e' |
||||||
|
#define USB_CFG_DEVICE_NAME_LEN 8 |
||||||
|
/* Same as above for the device name. If you don't want a device name, undefine
|
||||||
|
* the macros. See the file USB-IDs-for-free.txt before you assign a name if |
||||||
|
* you use a shared VID/PID. |
||||||
|
*/ |
||||||
|
/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */ |
||||||
|
/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */ |
||||||
|
/* Same as above for the serial number. If you don't want a serial number,
|
||||||
|
* undefine the macros. |
||||||
|
* It may be useful to provide the serial number through other means than at |
||||||
|
* compile time. See the section about descriptor properties below for how |
||||||
|
* to fine tune control over USB descriptors such as the string descriptor |
||||||
|
* for the serial number. |
||||||
|
*/ |
||||||
|
#define USB_CFG_DEVICE_CLASS 0xff /* set to 0 if deferred to interface */ |
||||||
|
#define USB_CFG_DEVICE_SUBCLASS 0 |
||||||
|
/* See USB specification if you want to conform to an existing device class.
|
||||||
|
* Class 0xff is "vendor specific". |
||||||
|
*/ |
||||||
|
#define USB_CFG_INTERFACE_CLASS 0 /* define class here if not at device level */ |
||||||
|
#define USB_CFG_INTERFACE_SUBCLASS 0 |
||||||
|
#define USB_CFG_INTERFACE_PROTOCOL 0 |
||||||
|
/* See USB specification if you want to conform to an existing device class or
|
||||||
|
* protocol. The following classes must be set at interface level: |
||||||
|
* HID class is 3, no subclass and protocol required (but may be useful!) |
||||||
|
* CDC class is 2, use subclass 2 and protocol 1 for ACM |
||||||
|
*/ |
||||||
|
/* #define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 42 */ |
||||||
|
/* Define this to the length of the HID report descriptor, if you implement
|
||||||
|
* an HID device. Otherwise don't define it or define it to 0. |
||||||
|
* If you use this define, you must add a PROGMEM character array named |
||||||
|
* "usbHidReportDescriptor" to your code which contains the report descriptor. |
||||||
|
* Don't forget to keep the array and this define in sync! |
||||||
|
*/ |
||||||
|
|
||||||
|
/* #define USB_PUBLIC static */ |
||||||
|
/* Use the define above if you #include usbdrv.c instead of linking against it.
|
||||||
|
* This technique saves a couple of bytes in flash memory. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* ------------------- Fine Control over USB Descriptors ------------------- */ |
||||||
|
/* If you don't want to use the driver's default USB descriptors, you can
|
||||||
|
* provide our own. These can be provided as (1) fixed length static data in |
||||||
|
* flash memory, (2) fixed length static data in RAM or (3) dynamically at |
||||||
|
* runtime in the function usbFunctionDescriptor(). See usbdrv.h for more |
||||||
|
* information about this function. |
||||||
|
* Descriptor handling is configured through the descriptor's properties. If |
||||||
|
* no properties are defined or if they are 0, the default descriptor is used. |
||||||
|
* Possible properties are: |
||||||
|
* + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched |
||||||
|
* at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is |
||||||
|
* used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if |
||||||
|
* you want RAM pointers. |
||||||
|
* + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found |
||||||
|
* in static memory is in RAM, not in flash memory. |
||||||
|
* + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash), |
||||||
|
* the driver must know the descriptor's length. The descriptor itself is |
||||||
|
* found at the address of a well known identifier (see below). |
||||||
|
* List of static descriptor names (must be declared PROGMEM if in flash): |
||||||
|
* char usbDescriptorDevice[]; |
||||||
|
* char usbDescriptorConfiguration[]; |
||||||
|
* char usbDescriptorHidReport[]; |
||||||
|
* char usbDescriptorString0[]; |
||||||
|
* int usbDescriptorStringVendor[]; |
||||||
|
* int usbDescriptorStringDevice[]; |
||||||
|
* int usbDescriptorStringSerialNumber[]; |
||||||
|
* Other descriptors can't be provided statically, they must be provided |
||||||
|
* dynamically at runtime. |
||||||
|
* |
||||||
|
* Descriptor properties are or-ed or added together, e.g.: |
||||||
|
* #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18)) |
||||||
|
* |
||||||
|
* The following descriptors are defined: |
||||||
|
* USB_CFG_DESCR_PROPS_DEVICE |
||||||
|
* USB_CFG_DESCR_PROPS_CONFIGURATION |
||||||
|
* USB_CFG_DESCR_PROPS_STRINGS |
||||||
|
* USB_CFG_DESCR_PROPS_STRING_0 |
||||||
|
* USB_CFG_DESCR_PROPS_STRING_VENDOR |
||||||
|
* USB_CFG_DESCR_PROPS_STRING_PRODUCT |
||||||
|
* USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER |
||||||
|
* USB_CFG_DESCR_PROPS_HID |
||||||
|
* USB_CFG_DESCR_PROPS_HID_REPORT |
||||||
|
* USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver) |
||||||
|
* |
||||||
|
* Note about string descriptors: String descriptors are not just strings, they |
||||||
|
* are Unicode strings prefixed with a 2 byte header. Example: |
||||||
|
* int serialNumberDescriptor[] = { |
||||||
|
* USB_STRING_DESCRIPTOR_HEADER(6), |
||||||
|
* 'S', 'e', 'r', 'i', 'a', 'l' |
||||||
|
* }; |
||||||
|
*/ |
||||||
|
|
||||||
|
#define USB_CFG_DESCR_PROPS_DEVICE 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_STRINGS 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_0 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_HID 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_HID_REPORT 0 |
||||||
|
#define USB_CFG_DESCR_PROPS_UNKNOWN 0 |
||||||
|
|
||||||
|
/* ----------------------- Optional MCU Description ------------------------ */ |
||||||
|
|
||||||
|
/* The following configurations have working defaults in usbdrv.h. You
|
||||||
|
* usually don't need to set them explicitly. Only if you want to run |
||||||
|
* the driver on a device which is not yet supported or with a compiler |
||||||
|
* which is not fully supported (such as IAR C) or if you use a differnt |
||||||
|
* interrupt than INT0, you may have to define some of these. |
||||||
|
*/ |
||||||
|
/* #define USB_INTR_CFG MCUCR */ |
||||||
|
/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */ |
||||||
|
/* #define USB_INTR_CFG_CLR 0 */ |
||||||
|
/* #define USB_INTR_ENABLE GIMSK */ |
||||||
|
/* #define USB_INTR_ENABLE_BIT INT0 */ |
||||||
|
/* #define USB_INTR_PENDING GIFR */ |
||||||
|
/* #define USB_INTR_PENDING_BIT INTF0 */ |
||||||
|
/* #define USB_INTR_VECTOR INT0_vect */ |
||||||
|
|
||||||
|
#endif /* __usbconfig_h_included__ */ |
||||||
@ -0,0 +1,628 @@ |
|||||||
|
/* Name: usbdrv.c
|
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2004-12-29 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* This Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "usbportability.h" |
||||||
|
#include "usbdrv.h" |
||||||
|
#include "oddebug.h" |
||||||
|
|
||||||
|
/*
|
||||||
|
General Description: |
||||||
|
This module implements the C-part of the USB driver. See usbdrv.h for a |
||||||
|
documentation of the entire driver. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
/* raw USB registers / interface to assembler code: */ |
||||||
|
uchar usbRxBuf[2*USB_BUFSIZE]; /* raw RX buffer: PID, 8 bytes data, 2 bytes CRC */ |
||||||
|
uchar usbInputBufOffset; /* offset in usbRxBuf used for low level receiving */ |
||||||
|
uchar usbDeviceAddr; /* assigned during enumeration, defaults to 0 */ |
||||||
|
uchar usbNewDeviceAddr; /* device ID which should be set after status phase */ |
||||||
|
uchar usbConfiguration; /* currently selected configuration. Administered by driver, but not used */ |
||||||
|
volatile schar usbRxLen; /* = 0; number of bytes in usbRxBuf; 0 means free, -1 for flow control */ |
||||||
|
uchar usbCurrentTok; /* last token received or endpoint number for last OUT token if != 0 */ |
||||||
|
uchar usbRxToken; /* token for data we received; or endpont number for last OUT */ |
||||||
|
volatile uchar usbTxLen = USBPID_NAK; /* number of bytes to transmit with next IN token or handshake token */ |
||||||
|
uchar usbTxBuf[USB_BUFSIZE];/* data to transmit with next IN, free if usbTxLen contains handshake token */ |
||||||
|
#if USB_COUNT_SOF |
||||||
|
volatile uchar usbSofCount; /* incremented by assembler module every SOF */ |
||||||
|
#endif |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE |
||||||
|
usbTxStatus_t usbTxStatus1; |
||||||
|
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||||
|
usbTxStatus_t usbTxStatus3; |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
#if USB_CFG_CHECK_DATA_TOGGLING |
||||||
|
uchar usbCurrentDataToken;/* when we check data toggling to ignore duplicate packets */ |
||||||
|
#endif |
||||||
|
|
||||||
|
/* USB status registers / not shared with asm code */ |
||||||
|
uchar *usbMsgPtr; /* data to transmit next -- ROM or RAM address */ |
||||||
|
static usbMsgLen_t usbMsgLen = USB_NO_MSG; /* remaining number of bytes */ |
||||||
|
static uchar usbMsgFlags; /* flag values see below */ |
||||||
|
|
||||||
|
#define USB_FLG_MSGPTR_IS_ROM (1<<6) |
||||||
|
#define USB_FLG_USE_USER_RW (1<<7) |
||||||
|
|
||||||
|
/*
|
||||||
|
optimizing hints: |
||||||
|
- do not post/pre inc/dec integer values in operations |
||||||
|
- assign value of USB_READ_FLASH() 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 |
||||||
|
*/ |
||||||
|
|
||||||
|
/* -------------------------- String Descriptors --------------------------- */ |
||||||
|
|
||||||
|
#if USB_CFG_DESCR_PROPS_STRINGS == 0 |
||||||
|
|
||||||
|
#if USB_CFG_DESCR_PROPS_STRING_0 == 0 |
||||||
|
#undef USB_CFG_DESCR_PROPS_STRING_0 |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_0 sizeof(usbDescriptorString0) |
||||||
|
PROGMEM const char usbDescriptorString0[] = { /* language descriptor */ |
||||||
|
4, /* sizeof(usbDescriptorString0): length of descriptor in bytes */ |
||||||
|
3, /* descriptor type */ |
||||||
|
0x09, 0x04, /* language index (0x0409 = US-English) */ |
||||||
|
}; |
||||||
|
#endif |
||||||
|
|
||||||
|
#if USB_CFG_DESCR_PROPS_STRING_VENDOR == 0 && USB_CFG_VENDOR_NAME_LEN |
||||||
|
#undef USB_CFG_DESCR_PROPS_STRING_VENDOR |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_VENDOR sizeof(usbDescriptorStringVendor) |
||||||
|
PROGMEM const int usbDescriptorStringVendor[] = { |
||||||
|
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_VENDOR_NAME_LEN), |
||||||
|
USB_CFG_VENDOR_NAME |
||||||
|
}; |
||||||
|
#endif |
||||||
|
|
||||||
|
#if USB_CFG_DESCR_PROPS_STRING_PRODUCT == 0 && USB_CFG_DEVICE_NAME_LEN |
||||||
|
#undef USB_CFG_DESCR_PROPS_STRING_PRODUCT |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT sizeof(usbDescriptorStringDevice) |
||||||
|
PROGMEM const int usbDescriptorStringDevice[] = { |
||||||
|
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_DEVICE_NAME_LEN), |
||||||
|
USB_CFG_DEVICE_NAME |
||||||
|
}; |
||||||
|
#endif |
||||||
|
|
||||||
|
#if USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER == 0 && USB_CFG_SERIAL_NUMBER_LEN |
||||||
|
#undef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER sizeof(usbDescriptorStringSerialNumber) |
||||||
|
PROGMEM const int usbDescriptorStringSerialNumber[] = { |
||||||
|
USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LEN), |
||||||
|
USB_CFG_SERIAL_NUMBER |
||||||
|
}; |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* USB_CFG_DESCR_PROPS_STRINGS == 0 */ |
||||||
|
|
||||||
|
/* --------------------------- Device Descriptor --------------------------- */ |
||||||
|
|
||||||
|
#if USB_CFG_DESCR_PROPS_DEVICE == 0 |
||||||
|
#undef USB_CFG_DESCR_PROPS_DEVICE |
||||||
|
#define USB_CFG_DESCR_PROPS_DEVICE sizeof(usbDescriptorDevice) |
||||||
|
PROGMEM const char usbDescriptorDevice[] = { /* USB device descriptor */ |
||||||
|
18, /* sizeof(usbDescriptorDevice): length of descriptor in bytes */ |
||||||
|
USBDESCR_DEVICE, /* descriptor type */ |
||||||
|
0x10, 0x01, /* USB version supported */ |
||||||
|
USB_CFG_DEVICE_CLASS, |
||||||
|
USB_CFG_DEVICE_SUBCLASS, |
||||||
|
0, /* protocol */ |
||||||
|
8, /* max packet size */ |
||||||
|
/* the following two casts affect the first byte of the constant only, but
|
||||||
|
* that's sufficient to avoid a warning with the default values. |
||||||
|
*/ |
||||||
|
(char)USB_CFG_VENDOR_ID,/* 2 bytes */ |
||||||
|
(char)USB_CFG_DEVICE_ID,/* 2 bytes */ |
||||||
|
USB_CFG_DEVICE_VERSION, /* 2 bytes */ |
||||||
|
USB_CFG_DESCR_PROPS_STRING_VENDOR != 0 ? 1 : 0, /* manufacturer string index */ |
||||||
|
USB_CFG_DESCR_PROPS_STRING_PRODUCT != 0 ? 2 : 0, /* product string index */ |
||||||
|
USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER != 0 ? 3 : 0, /* serial number string index */ |
||||||
|
1, /* number of configurations */ |
||||||
|
}; |
||||||
|
#endif |
||||||
|
|
||||||
|
/* ----------------------- Configuration Descriptor ------------------------ */ |
||||||
|
|
||||||
|
#if USB_CFG_DESCR_PROPS_HID_REPORT != 0 && USB_CFG_DESCR_PROPS_HID == 0 |
||||||
|
#undef USB_CFG_DESCR_PROPS_HID |
||||||
|
#define USB_CFG_DESCR_PROPS_HID 9 /* length of HID descriptor in config descriptor below */ |
||||||
|
#endif |
||||||
|
|
||||||
|
#if USB_CFG_DESCR_PROPS_CONFIGURATION == 0 |
||||||
|
#undef USB_CFG_DESCR_PROPS_CONFIGURATION |
||||||
|
#define USB_CFG_DESCR_PROPS_CONFIGURATION sizeof(usbDescriptorConfiguration) |
||||||
|
PROGMEM const char usbDescriptorConfiguration[] = { /* USB configuration descriptor */ |
||||||
|
9, /* sizeof(usbDescriptorConfiguration): length of descriptor in bytes */ |
||||||
|
USBDESCR_CONFIG, /* descriptor type */ |
||||||
|
18 + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT + 7 * USB_CFG_HAVE_INTRIN_ENDPOINT3 + |
||||||
|
(USB_CFG_DESCR_PROPS_HID & 0xff), 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_CFG_IS_SELF_POWERED |
||||||
|
(1 << 7) | USBATTR_SELFPOWER, /* attributes */ |
||||||
|
#else |
||||||
|
(1 << 7), /* attributes */ |
||||||
|
#endif |
||||||
|
USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */ |
||||||
|
/* interface descriptor follows inline: */ |
||||||
|
9, /* sizeof(usbDescrInterface): length of descriptor in bytes */ |
||||||
|
USBDESCR_INTERFACE, /* descriptor type */ |
||||||
|
0, /* index of this interface */ |
||||||
|
0, /* alternate setting for this interface */ |
||||||
|
USB_CFG_HAVE_INTRIN_ENDPOINT + USB_CFG_HAVE_INTRIN_ENDPOINT3, /* endpoints excl 0: number of endpoint descriptors to follow */ |
||||||
|
USB_CFG_INTERFACE_CLASS, |
||||||
|
USB_CFG_INTERFACE_SUBCLASS, |
||||||
|
USB_CFG_INTERFACE_PROTOCOL, |
||||||
|
0, /* string index for interface */ |
||||||
|
#if (USB_CFG_DESCR_PROPS_HID & 0xff) /* HID descriptor */ |
||||||
|
9, /* sizeof(usbDescrHID): length of descriptor in bytes */ |
||||||
|
USBDESCR_HID, /* descriptor type: HID */ |
||||||
|
0x01, 0x01, /* BCD representation of HID version */ |
||||||
|
0x00, /* target country code */ |
||||||
|
0x01, /* number of HID Report (or other HID class) Descriptor infos to follow */ |
||||||
|
0x22, /* descriptor type: report */ |
||||||
|
USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH, 0, /* total length of report descriptor */ |
||||||
|
#endif |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT /* endpoint descriptor for endpoint 1 */ |
||||||
|
7, /* sizeof(usbDescrEndpoint) */ |
||||||
|
USBDESCR_ENDPOINT, /* descriptor type = endpoint */ |
||||||
|
(char)0x81, /* IN endpoint number 1 */ |
||||||
|
0x03, /* attrib: Interrupt endpoint */ |
||||||
|
8, 0, /* maximum packet size */ |
||||||
|
USB_CFG_INTR_POLL_INTERVAL, /* in ms */ |
||||||
|
#endif |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 /* endpoint descriptor for endpoint 3 */ |
||||||
|
7, /* sizeof(usbDescrEndpoint) */ |
||||||
|
USBDESCR_ENDPOINT, /* descriptor type = endpoint */ |
||||||
|
(char)(0x80 | USB_CFG_EP3_NUMBER), /* IN endpoint number 3 */ |
||||||
|
0x03, /* attrib: Interrupt endpoint */ |
||||||
|
8, 0, /* maximum packet size */ |
||||||
|
USB_CFG_INTR_POLL_INTERVAL, /* in ms */ |
||||||
|
#endif |
||||||
|
}; |
||||||
|
#endif |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
static inline void usbResetDataToggling(void) |
||||||
|
{ |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE |
||||||
|
USB_SET_DATATOKEN1(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ |
||||||
|
# if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||||
|
USB_SET_DATATOKEN3(USB_INITIAL_DATATOKEN); /* reset data toggling for interrupt endpoint */ |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
static inline void usbResetStall(void) |
||||||
|
{ |
||||||
|
#if USB_CFG_IMPLEMENT_HALT && USB_CFG_HAVE_INTRIN_ENDPOINT |
||||||
|
usbTxLen1 = USBPID_NAK; |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||||
|
usbTxLen3 = USBPID_NAK; |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
#if !USB_CFG_SUPPRESS_INTR_CODE |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
||||||
|
static void usbGenericSetInterrupt(uchar *data, uchar len, usbTxStatus_t *txStatus) |
||||||
|
{ |
||||||
|
uchar *p; |
||||||
|
char i; |
||||||
|
|
||||||
|
#if USB_CFG_IMPLEMENT_HALT |
||||||
|
if(usbTxLen1 == USBPID_STALL) |
||||||
|
return; |
||||||
|
#endif |
||||||
|
if(txStatus->len & 0x10){ /* packet buffer was empty */ |
||||||
|
txStatus->buffer[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* toggle token */ |
||||||
|
}else{ |
||||||
|
txStatus->len = USBPID_NAK; /* avoid sending outdated (overwritten) interrupt data */ |
||||||
|
} |
||||||
|
p = txStatus->buffer + 1; |
||||||
|
i = len; |
||||||
|
do{ /* if len == 0, we still copy 1 byte, but that's no problem */ |
||||||
|
*p++ = *data++; |
||||||
|
}while(--i > 0); /* loop control at the end is 2 bytes shorter than at beginning */ |
||||||
|
usbCrc16Append(&txStatus->buffer[1], len); |
||||||
|
txStatus->len = len + 4; /* len must be given including sync byte */ |
||||||
|
DBG2(0x21 + (((int)txStatus >> 3) & 3), txStatus->buffer, len + 3); |
||||||
|
} |
||||||
|
|
||||||
|
USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len) |
||||||
|
{ |
||||||
|
usbGenericSetInterrupt(data, len, &usbTxStatus1); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||||
|
USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len) |
||||||
|
{ |
||||||
|
usbGenericSetInterrupt(data, len, &usbTxStatus3); |
||||||
|
} |
||||||
|
#endif |
||||||
|
#endif /* USB_CFG_SUPPRESS_INTR_CODE */ |
||||||
|
|
||||||
|
/* ------------------ utilities for code following below ------------------- */ |
||||||
|
|
||||||
|
/* Use defines for the switch statement so that we can choose between an
|
||||||
|
* if()else if() and a switch/case based implementation. switch() is more |
||||||
|
* efficient for a LARGE set of sequential choices, if() is better in all other |
||||||
|
* cases. |
||||||
|
*/ |
||||||
|
#if USB_CFG_USE_SWITCH_STATEMENT |
||||||
|
# define SWITCH_START(cmd) switch(cmd){{ |
||||||
|
# define SWITCH_CASE(value) }break; case (value):{ |
||||||
|
# define SWITCH_CASE2(v1,v2) }break; case (v1): case(v2):{ |
||||||
|
# define SWITCH_CASE3(v1,v2,v3) }break; case (v1): case(v2): case(v3):{ |
||||||
|
# define SWITCH_DEFAULT }break; default:{ |
||||||
|
# define SWITCH_END }} |
||||||
|
#else |
||||||
|
# define SWITCH_START(cmd) {uchar _cmd = cmd; if(0){ |
||||||
|
# define SWITCH_CASE(value) }else if(_cmd == (value)){ |
||||||
|
# define SWITCH_CASE2(v1,v2) }else if(_cmd == (v1) || _cmd == (v2)){ |
||||||
|
# define SWITCH_CASE3(v1,v2,v3) }else if(_cmd == (v1) || _cmd == (v2) || (_cmd == v3)){ |
||||||
|
# define SWITCH_DEFAULT }else{ |
||||||
|
# define SWITCH_END }} |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef USB_RX_USER_HOOK |
||||||
|
#define USB_RX_USER_HOOK(data, len) |
||||||
|
#endif |
||||||
|
#ifndef USB_SET_ADDRESS_HOOK |
||||||
|
#define USB_SET_ADDRESS_HOOK() |
||||||
|
#endif |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
/* We use if() instead of #if in the macro below because #if can't be used
|
||||||
|
* in macros and the compiler optimizes constant conditions anyway. |
||||||
|
* This may cause problems with undefined symbols if compiled without |
||||||
|
* optimizing! |
||||||
|
*/ |
||||||
|
#define GET_DESCRIPTOR(cfgProp, staticName) \ |
||||||
|
if(cfgProp){ \
|
||||||
|
if((cfgProp) & USB_PROP_IS_RAM) \
|
||||||
|
flags = 0; \
|
||||||
|
if((cfgProp) & USB_PROP_IS_DYNAMIC){ \
|
||||||
|
len = usbFunctionDescriptor(rq); \
|
||||||
|
}else{ \
|
||||||
|
len = USB_PROP_LENGTH(cfgProp); \
|
||||||
|
usbMsgPtr = (uchar *)(staticName); \
|
||||||
|
} \
|
||||||
|
} |
||||||
|
|
||||||
|
/* usbDriverDescriptor() is similar to usbFunctionDescriptor(), but used
|
||||||
|
* internally for all types of descriptors. |
||||||
|
*/ |
||||||
|
static inline usbMsgLen_t usbDriverDescriptor(usbRequest_t *rq) |
||||||
|
{ |
||||||
|
usbMsgLen_t len = 0; |
||||||
|
uchar flags = USB_FLG_MSGPTR_IS_ROM; |
||||||
|
|
||||||
|
SWITCH_START(rq->wValue.bytes[1]) |
||||||
|
SWITCH_CASE(USBDESCR_DEVICE) /* 1 */ |
||||||
|
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice) |
||||||
|
SWITCH_CASE(USBDESCR_CONFIG) /* 2 */ |
||||||
|
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration) |
||||||
|
SWITCH_CASE(USBDESCR_STRING) /* 3 */ |
||||||
|
#if USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC |
||||||
|
if(USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_RAM) |
||||||
|
flags = 0; |
||||||
|
len = usbFunctionDescriptor(rq); |
||||||
|
#else /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ |
||||||
|
SWITCH_START(rq->wValue.bytes[0]) |
||||||
|
SWITCH_CASE(0) |
||||||
|
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0) |
||||||
|
SWITCH_CASE(1) |
||||||
|
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor) |
||||||
|
SWITCH_CASE(2) |
||||||
|
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_PRODUCT, usbDescriptorStringDevice) |
||||||
|
SWITCH_CASE(3) |
||||||
|
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber) |
||||||
|
SWITCH_DEFAULT |
||||||
|
if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ |
||||||
|
len = usbFunctionDescriptor(rq); |
||||||
|
} |
||||||
|
SWITCH_END |
||||||
|
#endif /* USB_CFG_DESCR_PROPS_STRINGS & USB_PROP_IS_DYNAMIC */ |
||||||
|
#if USB_CFG_DESCR_PROPS_HID_REPORT /* only support HID descriptors if enabled */ |
||||||
|
SWITCH_CASE(USBDESCR_HID) /* 0x21 */ |
||||||
|
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID, usbDescriptorConfiguration + 18) |
||||||
|
SWITCH_CASE(USBDESCR_HID_REPORT)/* 0x22 */ |
||||||
|
GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_HID_REPORT, usbDescriptorHidReport) |
||||||
|
#endif |
||||||
|
SWITCH_DEFAULT |
||||||
|
if(USB_CFG_DESCR_PROPS_UNKNOWN & USB_PROP_IS_DYNAMIC){ |
||||||
|
len = usbFunctionDescriptor(rq); |
||||||
|
} |
||||||
|
SWITCH_END |
||||||
|
usbMsgFlags = flags; |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
/* usbDriverSetup() is similar to usbFunctionSetup(), but it's used for
|
||||||
|
* standard requests instead of class and custom requests. |
||||||
|
*/ |
||||||
|
static inline usbMsgLen_t usbDriverSetup(usbRequest_t *rq) |
||||||
|
{ |
||||||
|
usbMsgLen_t len = 0; |
||||||
|
uchar *dataPtr = usbTxBuf + 9; /* there are 2 bytes free space at the end of the buffer */ |
||||||
|
uchar value = rq->wValue.bytes[0]; |
||||||
|
#if USB_CFG_IMPLEMENT_HALT |
||||||
|
uchar index = rq->wIndex.bytes[0]; |
||||||
|
#endif |
||||||
|
|
||||||
|
dataPtr[0] = 0; /* default reply common to USBRQ_GET_STATUS and USBRQ_GET_INTERFACE */ |
||||||
|
SWITCH_START(rq->bRequest) |
||||||
|
SWITCH_CASE(USBRQ_GET_STATUS) /* 0 */ |
||||||
|
uchar recipient = rq->bmRequestType & USBRQ_RCPT_MASK; /* assign arith ops to variables to enforce byte size */ |
||||||
|
if(USB_CFG_IS_SELF_POWERED && recipient == USBRQ_RCPT_DEVICE) |
||||||
|
dataPtr[0] = USB_CFG_IS_SELF_POWERED; |
||||||
|
#if USB_CFG_IMPLEMENT_HALT |
||||||
|
if(recipient == USBRQ_RCPT_ENDPOINT && index == 0x81) /* request status for endpoint 1 */ |
||||||
|
dataPtr[0] = usbTxLen1 == USBPID_STALL; |
||||||
|
#endif |
||||||
|
dataPtr[1] = 0; |
||||||
|
len = 2; |
||||||
|
#if USB_CFG_IMPLEMENT_HALT |
||||||
|
SWITCH_CASE2(USBRQ_CLEAR_FEATURE, USBRQ_SET_FEATURE) /* 1, 3 */ |
||||||
|
if(value == 0 && index == 0x81){ /* feature 0 == HALT for endpoint == 1 */ |
||||||
|
usbTxLen1 = rq->bRequest == USBRQ_CLEAR_FEATURE ? USBPID_NAK : USBPID_STALL; |
||||||
|
usbResetDataToggling(); |
||||||
|
} |
||||||
|
#endif |
||||||
|
SWITCH_CASE(USBRQ_SET_ADDRESS) /* 5 */ |
||||||
|
usbNewDeviceAddr = value; |
||||||
|
USB_SET_ADDRESS_HOOK(); |
||||||
|
SWITCH_CASE(USBRQ_GET_DESCRIPTOR) /* 6 */ |
||||||
|
len = usbDriverDescriptor(rq); |
||||||
|
goto skipMsgPtrAssignment; |
||||||
|
SWITCH_CASE(USBRQ_GET_CONFIGURATION) /* 8 */ |
||||||
|
dataPtr = &usbConfiguration; /* send current configuration value */ |
||||||
|
len = 1; |
||||||
|
SWITCH_CASE(USBRQ_SET_CONFIGURATION) /* 9 */ |
||||||
|
usbConfiguration = value; |
||||||
|
usbResetStall(); |
||||||
|
SWITCH_CASE(USBRQ_GET_INTERFACE) /* 10 */ |
||||||
|
len = 1; |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE |
||||||
|
SWITCH_CASE(USBRQ_SET_INTERFACE) /* 11 */ |
||||||
|
usbResetDataToggling(); |
||||||
|
usbResetStall(); |
||||||
|
#endif |
||||||
|
SWITCH_DEFAULT /* 7=SET_DESCRIPTOR, 12=SYNC_FRAME */ |
||||||
|
/* Should we add an optional hook here? */ |
||||||
|
SWITCH_END |
||||||
|
usbMsgPtr = dataPtr; |
||||||
|
skipMsgPtrAssignment: |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
/* usbProcessRx() is called for every message received by the interrupt
|
||||||
|
* routine. It distinguishes between SETUP and DATA packets and processes |
||||||
|
* them accordingly. |
||||||
|
*/ |
||||||
|
static inline void usbProcessRx(uchar *data, uchar len) |
||||||
|
{ |
||||||
|
usbRequest_t *rq = (void *)data; |
||||||
|
|
||||||
|
/* usbRxToken can be:
|
||||||
|
* 0x2d 00101101 (USBPID_SETUP for setup data) |
||||||
|
* 0xe1 11100001 (USBPID_OUT: data phase of setup transfer) |
||||||
|
* 0...0x0f for OUT on endpoint X |
||||||
|
*/ |
||||||
|
DBG2(0x10 + (usbRxToken & 0xf), data, len + 2); /* SETUP=1d, SETUP-DATA=11, OUTx=1x */ |
||||||
|
USB_RX_USER_HOOK(data, len) |
||||||
|
#if USB_CFG_IMPLEMENT_FN_WRITEOUT |
||||||
|
if(usbRxToken < 0x10){ /* OUT to endpoint != 0: endpoint number in usbRxToken */ |
||||||
|
usbFunctionWriteOut(data, len); |
||||||
|
return; |
||||||
|
} |
||||||
|
#endif |
||||||
|
if(usbRxToken == (uchar)USBPID_SETUP){ |
||||||
|
if(len != 8) /* Setup size must be always 8 bytes. Ignore otherwise. */ |
||||||
|
return; |
||||||
|
usbMsgLen_t replyLen; |
||||||
|
usbTxBuf[0] = USBPID_DATA0; /* initialize data toggling */ |
||||||
|
usbTxLen = USBPID_NAK; /* abort pending transmit */ |
||||||
|
usbMsgFlags = 0; |
||||||
|
uchar type = rq->bmRequestType & USBRQ_TYPE_MASK; |
||||||
|
if(type != USBRQ_TYPE_STANDARD){ /* standard requests are handled by driver */ |
||||||
|
replyLen = usbFunctionSetup(data); |
||||||
|
}else{ |
||||||
|
replyLen = usbDriverSetup(rq); |
||||||
|
} |
||||||
|
#if USB_CFG_IMPLEMENT_FN_READ || USB_CFG_IMPLEMENT_FN_WRITE |
||||||
|
if(replyLen == USB_NO_MSG){ /* use user-supplied read/write function */ |
||||||
|
/* do some conditioning on replyLen, but on IN transfers only */ |
||||||
|
if((rq->bmRequestType & USBRQ_DIR_MASK) != USBRQ_DIR_HOST_TO_DEVICE){ |
||||||
|
if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ |
||||||
|
replyLen = rq->wLength.bytes[0]; |
||||||
|
}else{ |
||||||
|
replyLen = rq->wLength.word; |
||||||
|
} |
||||||
|
} |
||||||
|
usbMsgFlags = USB_FLG_USE_USER_RW; |
||||||
|
}else /* The 'else' prevents that we limit a replyLen of USB_NO_MSG to the maximum transfer len. */ |
||||||
|
#endif |
||||||
|
if(sizeof(replyLen) < sizeof(rq->wLength.word)){ /* help compiler with optimizing */ |
||||||
|
if(!rq->wLength.bytes[1] && replyLen > rq->wLength.bytes[0]) /* limit length to max */ |
||||||
|
replyLen = rq->wLength.bytes[0]; |
||||||
|
}else{ |
||||||
|
if(replyLen > rq->wLength.word) /* limit length to max */ |
||||||
|
replyLen = rq->wLength.word; |
||||||
|
} |
||||||
|
usbMsgLen = replyLen; |
||||||
|
}else{ /* usbRxToken must be USBPID_OUT, which means data phase of setup (control-out) */ |
||||||
|
#if USB_CFG_IMPLEMENT_FN_WRITE |
||||||
|
if(usbMsgFlags & USB_FLG_USE_USER_RW){ |
||||||
|
uchar rval = usbFunctionWrite(data, len); |
||||||
|
if(rval == 0xff){ /* an error occurred */ |
||||||
|
usbTxLen = USBPID_STALL; |
||||||
|
}else if(rval != 0){ /* This was the final package */ |
||||||
|
usbMsgLen = 0; /* answer with a zero-sized data packet */ |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
/* This function is similar to usbFunctionRead(), but it's also called for
|
||||||
|
* data handled automatically by the driver (e.g. descriptor reads). |
||||||
|
*/ |
||||||
|
static uchar usbDeviceRead(uchar *data, uchar len) |
||||||
|
{ |
||||||
|
if(len > 0){ /* don't bother app with 0 sized reads */ |
||||||
|
#if USB_CFG_IMPLEMENT_FN_READ |
||||||
|
if(usbMsgFlags & USB_FLG_USE_USER_RW){ |
||||||
|
len = usbFunctionRead(data, len); |
||||||
|
}else |
||||||
|
#endif |
||||||
|
{ |
||||||
|
uchar i = len, *r = usbMsgPtr; |
||||||
|
if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){ /* ROM data */ |
||||||
|
do{ |
||||||
|
uchar c = USB_READ_FLASH(r); /* assign to char size variable to enforce byte ops */ |
||||||
|
*data++ = c; |
||||||
|
r++; |
||||||
|
}while(--i); |
||||||
|
}else{ /* RAM data */ |
||||||
|
do{ |
||||||
|
*data++ = *r++; |
||||||
|
}while(--i); |
||||||
|
} |
||||||
|
usbMsgPtr = r; |
||||||
|
} |
||||||
|
} |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
/* usbBuildTxBlock() is called when we have data to transmit and the
|
||||||
|
* interrupt routine's transmit buffer is empty. |
||||||
|
*/ |
||||||
|
static inline void usbBuildTxBlock(void) |
||||||
|
{ |
||||||
|
usbMsgLen_t wantLen; |
||||||
|
uchar len; |
||||||
|
|
||||||
|
wantLen = usbMsgLen; |
||||||
|
if(wantLen > 8) |
||||||
|
wantLen = 8; |
||||||
|
usbMsgLen -= wantLen; |
||||||
|
usbTxBuf[0] ^= USBPID_DATA0 ^ USBPID_DATA1; /* DATA toggling */ |
||||||
|
len = usbDeviceRead(usbTxBuf + 1, wantLen); |
||||||
|
if(len <= 8){ /* valid data packet */ |
||||||
|
usbCrc16Append(&usbTxBuf[1], len); |
||||||
|
len += 4; /* length including sync byte */ |
||||||
|
if(len < 12) /* a partial package identifies end of message */ |
||||||
|
usbMsgLen = USB_NO_MSG; |
||||||
|
}else{ |
||||||
|
len = USBPID_STALL; /* stall the endpoint */ |
||||||
|
usbMsgLen = USB_NO_MSG; |
||||||
|
} |
||||||
|
usbTxLen = len; |
||||||
|
DBG2(0x20, usbTxBuf, len-1); |
||||||
|
} |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
static inline void usbHandleResetHook(uchar notResetState) |
||||||
|
{ |
||||||
|
#ifdef USB_RESET_HOOK |
||||||
|
static uchar wasReset; |
||||||
|
uchar isReset = !notResetState; |
||||||
|
|
||||||
|
if(wasReset != isReset){ |
||||||
|
USB_RESET_HOOK(isReset); |
||||||
|
wasReset = isReset; |
||||||
|
} |
||||||
|
#else |
||||||
|
notResetState = notResetState; // avoid compiler warning
|
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
USB_PUBLIC void usbPoll(void) |
||||||
|
{ |
||||||
|
schar len; |
||||||
|
uchar i; |
||||||
|
|
||||||
|
len = usbRxLen - 3; |
||||||
|
if(len >= 0){ |
||||||
|
/* 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(buffer + 1, usbRxLen - 3); |
||||||
|
*/ |
||||||
|
usbProcessRx(usbRxBuf + USB_BUFSIZE + 1 - usbInputBufOffset, len); |
||||||
|
#if USB_CFG_HAVE_FLOWCONTROL |
||||||
|
if(usbRxLen > 0) /* only mark as available if not inactivated */ |
||||||
|
usbRxLen = 0; |
||||||
|
#else |
||||||
|
usbRxLen = 0; /* mark rx buffer as available */ |
||||||
|
#endif |
||||||
|
} |
||||||
|
if(usbTxLen & 0x10){ /* transmit system idle */ |
||||||
|
if(usbMsgLen != USB_NO_MSG){ /* transmit data pending? */ |
||||||
|
usbBuildTxBlock(); |
||||||
|
} |
||||||
|
} |
||||||
|
for(i = 20; i > 0; i--){ |
||||||
|
uchar usbLineStatus = USBIN & USBMASK; |
||||||
|
if(usbLineStatus != 0) /* SE0 has ended */ |
||||||
|
goto isNotReset; |
||||||
|
} |
||||||
|
/* RESET condition, called multiple times during reset */ |
||||||
|
usbNewDeviceAddr = 0; |
||||||
|
usbDeviceAddr = 0; |
||||||
|
usbResetStall(); |
||||||
|
DBG1(0xff, 0, 0); |
||||||
|
isNotReset: |
||||||
|
usbHandleResetHook(i); |
||||||
|
} |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
USB_PUBLIC void usbInit(void) |
||||||
|
{ |
||||||
|
#if USB_INTR_CFG_SET != 0 |
||||||
|
USB_INTR_CFG |= USB_INTR_CFG_SET; |
||||||
|
#endif |
||||||
|
#if USB_INTR_CFG_CLR != 0 |
||||||
|
USB_INTR_CFG &= ~(USB_INTR_CFG_CLR); |
||||||
|
#endif |
||||||
|
USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT); |
||||||
|
usbResetDataToggling(); |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT && !USB_CFG_SUPPRESS_INTR_CODE |
||||||
|
usbTxLen1 = USBPID_NAK; |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||||
|
usbTxLen3 = USBPID_NAK; |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
@ -0,0 +1,736 @@ |
|||||||
|
/* Name: usbdrv.h
|
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2004-12-29 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* This Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __usbdrv_h_included__ |
||||||
|
#define __usbdrv_h_included__ |
||||||
|
#include "usbconfig.h" |
||||||
|
#include "usbportability.h" |
||||||
|
|
||||||
|
/*
|
||||||
|
Hardware Prerequisites: |
||||||
|
======================= |
||||||
|
USB lines D+ and D- MUST be wired to the same I/O port. We recommend that D+ |
||||||
|
triggers the interrupt (best achieved by using INT0 for D+), but it is also |
||||||
|
possible to trigger the interrupt from D-. If D- is used, interrupts are also |
||||||
|
triggered by SOF packets. D- requires a pull-up of 1.5k to +3.5V (and the |
||||||
|
device must be powered at 3.5V) to identify as low-speed USB device. A |
||||||
|
pull-down or pull-up of 1M SHOULD be connected from D+ to +3.5V to prevent |
||||||
|
interference when no USB master is connected. If you use Zener diodes to limit |
||||||
|
the voltage on D+ and D-, you MUST use a pull-down resistor, not a pull-up. |
||||||
|
We use D+ as interrupt source and not D- because it does not trigger on |
||||||
|
keep-alive and RESET states. If you want to count keep-alive events with |
||||||
|
USB_COUNT_SOF, you MUST use D- as an interrupt source. |
||||||
|
|
||||||
|
As a compile time option, the 1.5k pull-up resistor on D- can be made |
||||||
|
switchable to allow the device to disconnect at will. See the definition of |
||||||
|
usbDeviceConnect() and usbDeviceDisconnect() further down in this file. |
||||||
|
|
||||||
|
Please adapt the values in usbconfig.h according to your hardware! |
||||||
|
|
||||||
|
The device MUST be clocked at exactly 12 MHz, 15 MHz, 16 MHz or 20 MHz |
||||||
|
or at 12.8 MHz resp. 16.5 MHz +/- 1%. See usbconfig-prototype.h for details. |
||||||
|
|
||||||
|
|
||||||
|
Limitations: |
||||||
|
============ |
||||||
|
Robustness with respect to communication errors: |
||||||
|
The driver assumes error-free communication. It DOES check for errors in |
||||||
|
the PID, but does NOT check bit stuffing errors, SE0 in middle of a byte, |
||||||
|
token CRC (5 bit) and data CRC (16 bit). CRC checks can not be performed due |
||||||
|
to timing constraints: We must start sending a reply within 7 bit times. |
||||||
|
Bit stuffing and misplaced SE0 would have to be checked in real-time, but CPU |
||||||
|
performance does not permit that. The driver does not check Data0/Data1 |
||||||
|
toggling, but application software can implement the check. |
||||||
|
|
||||||
|
Input characteristics: |
||||||
|
Since no differential receiver circuit is used, electrical interference |
||||||
|
robustness may suffer. The driver samples only one of the data lines with |
||||||
|
an ordinary I/O pin's input characteristics. However, since this is only a |
||||||
|
low speed USB implementation and the specification allows for 8 times the |
||||||
|
bit rate over the same hardware, we should be on the safe side. Even the spec |
||||||
|
requires detection of asymmetric states at high bit rate for SE0 detection. |
||||||
|
|
||||||
|
Number of endpoints: |
||||||
|
The driver supports the following endpoints: |
||||||
|
|
||||||
|
- Endpoint 0, the default control endpoint. |
||||||
|
- Any number of interrupt- or bulk-out endpoints. The data is sent to |
||||||
|
usbFunctionWriteOut() and USB_CFG_IMPLEMENT_FN_WRITEOUT must be defined |
||||||
|
to 1 to activate this feature. The endpoint number can be found in the |
||||||
|
global variable 'usbRxToken'. |
||||||
|
- One default interrupt- or bulk-in endpoint. This endpoint is used for |
||||||
|
interrupt- or bulk-in transfers which are not handled by any other endpoint. |
||||||
|
You must define USB_CFG_HAVE_INTRIN_ENDPOINT in order to activate this |
||||||
|
feature and call usbSetInterrupt() to send interrupt/bulk data. |
||||||
|
- One additional interrupt- or bulk-in endpoint. This was endpoint 3 in |
||||||
|
previous versions of this driver but can now be configured to any endpoint |
||||||
|
number. You must define USB_CFG_HAVE_INTRIN_ENDPOINT3 in order to activate |
||||||
|
this feature and call usbSetInterrupt3() to send interrupt/bulk data. The |
||||||
|
endpoint number can be set with USB_CFG_EP3_NUMBER. |
||||||
|
|
||||||
|
Please note that the USB standard forbids bulk endpoints for low speed devices! |
||||||
|
Most operating systems allow them anyway, but the AVR will spend 90% of the CPU |
||||||
|
time in the USB interrupt polling for bulk data. |
||||||
|
|
||||||
|
Maximum data payload: |
||||||
|
Data payload of control in and out transfers may be up to 254 bytes. In order |
||||||
|
to accept payload data of out transfers, you need to implement |
||||||
|
'usbFunctionWrite()'. |
||||||
|
|
||||||
|
USB Suspend Mode supply current: |
||||||
|
The USB standard limits power consumption to 500uA when the bus is in suspend |
||||||
|
mode. This is not a problem for self-powered devices since they don't need |
||||||
|
bus power anyway. Bus-powered devices can achieve this only by putting the |
||||||
|
CPU in sleep mode. The driver does not implement suspend handling by itself. |
||||||
|
However, the application may implement activity monitoring and wakeup from |
||||||
|
sleep. The host sends regular SE0 states on the bus to keep it active. These |
||||||
|
SE0 states can be detected by using D- as the interrupt source. Define |
||||||
|
USB_COUNT_SOF to 1 and use the global variable usbSofCount to check for bus |
||||||
|
activity. |
||||||
|
|
||||||
|
Operation without an USB master: |
||||||
|
The driver behaves neutral without connection to an USB master if D- reads |
||||||
|
as 1. To avoid spurious interrupts, we recommend a high impedance (e.g. 1M) |
||||||
|
pull-down or pull-up resistor on D+ (interrupt). If Zener diodes are used, |
||||||
|
use a pull-down. If D- becomes statically 0, the driver may block in the |
||||||
|
interrupt routine. |
||||||
|
|
||||||
|
Interrupt latency: |
||||||
|
The application must ensure that the USB interrupt is not disabled for more |
||||||
|
than 25 cycles (this is for 12 MHz, faster clocks allow longer latency). |
||||||
|
This implies that all interrupt routines must either have the "ISR_NOBLOCK" |
||||||
|
attribute set (see "avr/interrupt.h") or be written in assembler with "sei" |
||||||
|
as the first instruction. |
||||||
|
|
||||||
|
Maximum interrupt duration / CPU cycle consumption: |
||||||
|
The driver handles all USB communication during the interrupt service |
||||||
|
routine. The routine will not return before an entire USB message is received |
||||||
|
and the reply is sent. This may be up to ca. 1200 cycles @ 12 MHz (= 100us) if |
||||||
|
the host conforms to the standard. The driver will consume CPU cycles for all |
||||||
|
USB messages, even if they address another (low-speed) device on the same bus. |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
/* --------------------------- Module Interface ---------------------------- */ |
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
#define USBDRV_VERSION 20120109 |
||||||
|
/* This define uniquely identifies a driver version. It is a decimal number
|
||||||
|
* constructed from the driver's release date in the form YYYYMMDD. If the |
||||||
|
* driver's behavior or interface changes, you can use this constant to |
||||||
|
* distinguish versions. If it is not defined, the driver's release date is |
||||||
|
* older than 2006-01-25. |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#ifndef USB_PUBLIC |
||||||
|
#define USB_PUBLIC |
||||||
|
#endif |
||||||
|
/* USB_PUBLIC is used as declaration attribute for all functions exported by
|
||||||
|
* the USB driver. The default is no attribute (see above). You may define it |
||||||
|
* to static either in usbconfig.h or from the command line if you include |
||||||
|
* usbdrv.c instead of linking against it. Including the C module of the driver |
||||||
|
* directly in your code saves a couple of bytes in flash memory. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __ASSEMBLER__ |
||||||
|
#ifndef uchar |
||||||
|
#define uchar unsigned char |
||||||
|
#endif |
||||||
|
#ifndef schar |
||||||
|
#define schar signed char |
||||||
|
#endif |
||||||
|
/* shortcuts for well defined 8 bit integer types */ |
||||||
|
|
||||||
|
#if USB_CFG_LONG_TRANSFERS /* if more than 254 bytes transfer size required */ |
||||||
|
# define usbMsgLen_t unsigned |
||||||
|
#else |
||||||
|
# define usbMsgLen_t uchar |
||||||
|
#endif |
||||||
|
/* usbMsgLen_t is the data type used for transfer lengths. By default, it is
|
||||||
|
* defined to uchar, allowing a maximum of 254 bytes (255 is reserved for |
||||||
|
* USB_NO_MSG below). If the usbconfig.h defines USB_CFG_LONG_TRANSFERS to 1, |
||||||
|
* a 16 bit data type is used, allowing up to 16384 bytes (the rest is used |
||||||
|
* for flags in the descriptor configuration). |
||||||
|
*/ |
||||||
|
#define USB_NO_MSG ((usbMsgLen_t)-1) /* constant meaning "no message" */ |
||||||
|
|
||||||
|
struct usbRequest; /* forward declaration */ |
||||||
|
|
||||||
|
USB_PUBLIC void usbInit(void); |
||||||
|
/* This function must be called before interrupts are enabled and the main
|
||||||
|
* loop is entered. We exepct that the PORT and DDR bits for D+ and D- have |
||||||
|
* not been changed from their default status (which is 0). If you have changed |
||||||
|
* them, set both back to 0 (configure them as input with no internal pull-up). |
||||||
|
*/ |
||||||
|
USB_PUBLIC void usbPoll(void); |
||||||
|
/* This function must be called at regular intervals from the main loop.
|
||||||
|
* Maximum delay between calls is somewhat less than 50ms (USB timeout for |
||||||
|
* accepting a Setup message). Otherwise the device will not be recognized. |
||||||
|
* Please note that debug outputs through the UART take ~ 0.5ms per byte |
||||||
|
* at 19200 bps. |
||||||
|
*/ |
||||||
|
extern uchar *usbMsgPtr; |
||||||
|
/* This variable may be used to pass transmit data to the driver from the
|
||||||
|
* implementation of usbFunctionWrite(). It is also used internally by the |
||||||
|
* driver for standard control requests. |
||||||
|
*/ |
||||||
|
USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]); |
||||||
|
/* This function is called when the driver receives a SETUP transaction from
|
||||||
|
* the host which is not answered by the driver itself (in practice: class and |
||||||
|
* vendor requests). All control transfers start with a SETUP transaction where |
||||||
|
* the host communicates the parameters of the following (optional) data |
||||||
|
* transfer. The SETUP data is available in the 'data' parameter which can |
||||||
|
* (and should) be casted to 'usbRequest_t *' for a more user-friendly access |
||||||
|
* to parameters. |
||||||
|
* |
||||||
|
* If the SETUP indicates a control-in transfer, you should provide the |
||||||
|
* requested data to the driver. There are two ways to transfer this data: |
||||||
|
* (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data |
||||||
|
* block and return the length of the data in 'usbFunctionSetup()'. The driver |
||||||
|
* will handle the rest. Or (2) return USB_NO_MSG in 'usbFunctionSetup()'. The |
||||||
|
* driver will then call 'usbFunctionRead()' when data is needed. See the |
||||||
|
* documentation for usbFunctionRead() for details. |
||||||
|
* |
||||||
|
* If the SETUP indicates a control-out transfer, the only way to receive the |
||||||
|
* data from the host is through the 'usbFunctionWrite()' call. If you |
||||||
|
* implement this function, you must return USB_NO_MSG in 'usbFunctionSetup()' |
||||||
|
* to indicate that 'usbFunctionWrite()' should be used. See the documentation |
||||||
|
* of this function for more information. If you just want to ignore the data |
||||||
|
* sent by the host, return 0 in 'usbFunctionSetup()'. |
||||||
|
* |
||||||
|
* Note that calls to the functions usbFunctionRead() and usbFunctionWrite() |
||||||
|
* are only done if enabled by the configuration in usbconfig.h. |
||||||
|
*/ |
||||||
|
USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq); |
||||||
|
/* You need to implement this function ONLY if you provide USB descriptors at
|
||||||
|
* runtime (which is an expert feature). It is very similar to |
||||||
|
* usbFunctionSetup() above, but it is called only to request USB descriptor |
||||||
|
* data. See the documentation of usbFunctionSetup() above for more info. |
||||||
|
*/ |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT |
||||||
|
USB_PUBLIC void usbSetInterrupt(uchar *data, uchar len); |
||||||
|
/* This function sets the message which will be sent during the next interrupt
|
||||||
|
* IN transfer. The message is copied to an internal buffer and must not exceed |
||||||
|
* a length of 8 bytes. The message may be 0 bytes long just to indicate the |
||||||
|
* interrupt status to the host. |
||||||
|
* If you need to transfer more bytes, use a control read after the interrupt. |
||||||
|
*/ |
||||||
|
#define usbInterruptIsReady() (usbTxLen1 & 0x10) |
||||||
|
/* This macro indicates whether the last interrupt message has already been
|
||||||
|
* sent. If you set a new interrupt message before the old was sent, the |
||||||
|
* message already buffered will be lost. |
||||||
|
*/ |
||||||
|
#if USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||||
|
USB_PUBLIC void usbSetInterrupt3(uchar *data, uchar len); |
||||||
|
#define usbInterruptIsReady3() (usbTxLen3 & 0x10) |
||||||
|
/* Same as above for endpoint 3 */ |
||||||
|
#endif |
||||||
|
#endif /* USB_CFG_HAVE_INTRIN_ENDPOINT */ |
||||||
|
#if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* simplified interface for backward compatibility */ |
||||||
|
#define usbHidReportDescriptor usbDescriptorHidReport |
||||||
|
/* should be declared as: PROGMEM char usbHidReportDescriptor[]; */ |
||||||
|
/* If you implement an HID device, you need to provide a report descriptor.
|
||||||
|
* The HID report descriptor syntax is a bit complex. If you understand how |
||||||
|
* report descriptors are constructed, we recommend that you use the HID |
||||||
|
* Descriptor Tool from usb.org, see http://www.usb.org/developers/hidpage/.
|
||||||
|
* Otherwise you should probably start with a working example. |
||||||
|
*/ |
||||||
|
#endif /* USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH */ |
||||||
|
#if USB_CFG_IMPLEMENT_FN_WRITE |
||||||
|
USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len); |
||||||
|
/* This function is called by the driver to provide a control transfer's
|
||||||
|
* payload data (control-out). It is called in chunks of up to 8 bytes. The |
||||||
|
* total count provided in the current control transfer can be obtained from |
||||||
|
* the 'length' property in the setup data. If an error occurred during |
||||||
|
* processing, return 0xff (== -1). The driver will answer the entire transfer |
||||||
|
* with a STALL token in this case. If you have received the entire payload |
||||||
|
* successfully, return 1. If you expect more data, return 0. If you don't |
||||||
|
* know whether the host will send more data (you should know, the total is |
||||||
|
* provided in the usbFunctionSetup() call!), return 1. |
||||||
|
* NOTE: If you return 0xff for STALL, 'usbFunctionWrite()' may still be called |
||||||
|
* for the remaining data. You must continue to return 0xff for STALL in these |
||||||
|
* calls. |
||||||
|
* In order to get usbFunctionWrite() called, define USB_CFG_IMPLEMENT_FN_WRITE |
||||||
|
* to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. |
||||||
|
*/ |
||||||
|
#endif /* USB_CFG_IMPLEMENT_FN_WRITE */ |
||||||
|
#if USB_CFG_IMPLEMENT_FN_READ |
||||||
|
USB_PUBLIC uchar usbFunctionRead(uchar *data, uchar len); |
||||||
|
/* This function is called by the driver to ask the application for a control
|
||||||
|
* transfer's payload data (control-in). It is called in chunks of up to 8 |
||||||
|
* bytes each. You should copy the data to the location given by 'data' and |
||||||
|
* return the actual number of bytes copied. If you return less than requested, |
||||||
|
* the control-in transfer is terminated. If you return 0xff, the driver aborts |
||||||
|
* the transfer with a STALL token. |
||||||
|
* In order to get usbFunctionRead() called, define USB_CFG_IMPLEMENT_FN_READ |
||||||
|
* to 1 in usbconfig.h and return 0xff in usbFunctionSetup().. |
||||||
|
*/ |
||||||
|
#endif /* USB_CFG_IMPLEMENT_FN_READ */ |
||||||
|
|
||||||
|
extern uchar usbRxToken; /* may be used in usbFunctionWriteOut() below */ |
||||||
|
#if USB_CFG_IMPLEMENT_FN_WRITEOUT |
||||||
|
USB_PUBLIC void usbFunctionWriteOut(uchar *data, uchar len); |
||||||
|
/* This function is called by the driver when data is received on an interrupt-
|
||||||
|
* or bulk-out endpoint. The endpoint number can be found in the global |
||||||
|
* variable usbRxToken. You must define USB_CFG_IMPLEMENT_FN_WRITEOUT to 1 in |
||||||
|
* usbconfig.h to get this function called. |
||||||
|
*/ |
||||||
|
#endif /* USB_CFG_IMPLEMENT_FN_WRITEOUT */ |
||||||
|
#ifdef USB_CFG_PULLUP_IOPORTNAME |
||||||
|
#define usbDeviceConnect() ((USB_PULLUP_DDR |= (1<<USB_CFG_PULLUP_BIT)), \ |
||||||
|
(USB_PULLUP_OUT |= (1<<USB_CFG_PULLUP_BIT))) |
||||||
|
#define usbDeviceDisconnect() ((USB_PULLUP_DDR &= ~(1<<USB_CFG_PULLUP_BIT)), \ |
||||||
|
(USB_PULLUP_OUT &= ~(1<<USB_CFG_PULLUP_BIT))) |
||||||
|
#else /* USB_CFG_PULLUP_IOPORTNAME */ |
||||||
|
#define usbDeviceConnect() (USBDDR &= ~(1<<USBMINUS)) |
||||||
|
#define usbDeviceDisconnect() (USBDDR |= (1<<USBMINUS)) |
||||||
|
#endif /* USB_CFG_PULLUP_IOPORTNAME */ |
||||||
|
/* The macros usbDeviceConnect() and usbDeviceDisconnect() (intended to look
|
||||||
|
* like a function) connect resp. disconnect the device from the host's USB. |
||||||
|
* If the constants USB_CFG_PULLUP_IOPORT and USB_CFG_PULLUP_BIT are defined |
||||||
|
* in usbconfig.h, a disconnect consists of removing the pull-up resisitor |
||||||
|
* from D-, otherwise the disconnect is done by brute-force pulling D- to GND. |
||||||
|
* This does not conform to the spec, but it works. |
||||||
|
* Please note that the USB interrupt must be disabled while the device is |
||||||
|
* in disconnected state, or the interrupt handler will hang! You can either |
||||||
|
* turn off the USB interrupt selectively with |
||||||
|
* USB_INTR_ENABLE &= ~(1 << USB_INTR_ENABLE_BIT) |
||||||
|
* or use cli() to disable interrupts globally. |
||||||
|
*/ |
||||||
|
extern unsigned usbCrc16(unsigned data, uchar len); |
||||||
|
#define usbCrc16(data, len) usbCrc16((unsigned)(data), len) |
||||||
|
/* This function calculates the binary complement of the data CRC used in
|
||||||
|
* USB data packets. The value is used to build raw transmit packets. |
||||||
|
* You may want to use this function for data checksums or to verify received |
||||||
|
* data. We enforce 16 bit calling conventions for compatibility with IAR's |
||||||
|
* tiny memory model. |
||||||
|
*/ |
||||||
|
extern unsigned usbCrc16Append(unsigned data, uchar len); |
||||||
|
#define usbCrc16Append(data, len) usbCrc16Append((unsigned)(data), len) |
||||||
|
/* This function is equivalent to usbCrc16() above, except that it appends
|
||||||
|
* the 2 bytes CRC (lowbyte first) in the 'data' buffer after reading 'len' |
||||||
|
* bytes. |
||||||
|
*/ |
||||||
|
#if USB_CFG_HAVE_MEASURE_FRAME_LENGTH |
||||||
|
extern unsigned usbMeasureFrameLength(void); |
||||||
|
/* This function MUST be called IMMEDIATELY AFTER USB reset and measures 1/7 of
|
||||||
|
* the number of CPU cycles during one USB frame minus one low speed bit |
||||||
|
* length. In other words: return value = 1499 * (F_CPU / 10.5 MHz) |
||||||
|
* Since this is a busy wait, you MUST disable all interrupts with cli() before |
||||||
|
* calling this function. |
||||||
|
* This can be used to calibrate the AVR's RC oscillator. |
||||||
|
*/ |
||||||
|
#endif |
||||||
|
extern uchar usbConfiguration; |
||||||
|
/* This value contains the current configuration set by the host. The driver
|
||||||
|
* allows setting and querying of this variable with the USB SET_CONFIGURATION |
||||||
|
* and GET_CONFIGURATION requests, but does not use it otherwise. |
||||||
|
* You may want to reflect the "configured" status with a LED on the device or |
||||||
|
* switch on high power parts of the circuit only if the device is configured. |
||||||
|
*/ |
||||||
|
#if USB_COUNT_SOF |
||||||
|
extern volatile uchar usbSofCount; |
||||||
|
/* This variable is incremented on every SOF packet. It is only available if
|
||||||
|
* the macro USB_COUNT_SOF is defined to a value != 0. |
||||||
|
*/ |
||||||
|
#endif |
||||||
|
#if USB_CFG_CHECK_DATA_TOGGLING |
||||||
|
extern uchar usbCurrentDataToken; |
||||||
|
/* This variable can be checked in usbFunctionWrite() and usbFunctionWriteOut()
|
||||||
|
* to ignore duplicate packets. |
||||||
|
*/ |
||||||
|
#endif |
||||||
|
|
||||||
|
#define USB_STRING_DESCRIPTOR_HEADER(stringLength) ((2*(stringLength)+2) | (3<<8)) |
||||||
|
/* This macro builds a descriptor header for a string descriptor given the
|
||||||
|
* string's length. See usbdrv.c for an example how to use it. |
||||||
|
*/ |
||||||
|
#if USB_CFG_HAVE_FLOWCONTROL |
||||||
|
extern volatile schar usbRxLen; |
||||||
|
#define usbDisableAllRequests() usbRxLen = -1 |
||||||
|
/* Must be called from usbFunctionWrite(). This macro disables all data input
|
||||||
|
* from the USB interface. Requests from the host are answered with a NAK |
||||||
|
* while they are disabled. |
||||||
|
*/ |
||||||
|
#define usbEnableAllRequests() usbRxLen = 0 |
||||||
|
/* May only be called if requests are disabled. This macro enables input from
|
||||||
|
* the USB interface after it has been disabled with usbDisableAllRequests(). |
||||||
|
*/ |
||||||
|
#define usbAllRequestsAreDisabled() (usbRxLen < 0) |
||||||
|
/* Use this macro to find out whether requests are disabled. It may be needed
|
||||||
|
* to ensure that usbEnableAllRequests() is never called when requests are |
||||||
|
* enabled. |
||||||
|
*/ |
||||||
|
#endif |
||||||
|
|
||||||
|
#define USB_SET_DATATOKEN1(token) usbTxBuf1[0] = token |
||||||
|
#define USB_SET_DATATOKEN3(token) usbTxBuf3[0] = token |
||||||
|
/* These two macros can be used by application software to reset data toggling
|
||||||
|
* for interrupt-in endpoints 1 and 3. Since the token is toggled BEFORE |
||||||
|
* sending data, you must set the opposite value of the token which should come |
||||||
|
* first. |
||||||
|
*/ |
||||||
|
|
||||||
|
#endif /* __ASSEMBLER__ */ |
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
/* ----------------- Definitions for Descriptor Properties ----------------- */ |
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
/* This is advanced stuff. See usbconfig-prototype.h for more information
|
||||||
|
* about the various methods to define USB descriptors. If you do nothing, |
||||||
|
* the default descriptors will be used. |
||||||
|
*/ |
||||||
|
#define USB_PROP_IS_DYNAMIC (1 << 14) |
||||||
|
/* If this property is set for a descriptor, usbFunctionDescriptor() will be
|
||||||
|
* used to obtain the particular descriptor. Data directly returned via |
||||||
|
* usbMsgPtr are FLASH data by default, combine (OR) with USB_PROP_IS_RAM to |
||||||
|
* return RAM data. |
||||||
|
*/ |
||||||
|
#define USB_PROP_IS_RAM (1 << 15) |
||||||
|
/* If this property is set for a descriptor, the data is read from RAM
|
||||||
|
* memory instead of Flash. The property is used for all methods to provide |
||||||
|
* external descriptors. |
||||||
|
*/ |
||||||
|
#define USB_PROP_LENGTH(len) ((len) & 0x3fff) |
||||||
|
/* If a static external descriptor is used, this is the total length of the
|
||||||
|
* descriptor in bytes. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* all descriptors which may have properties: */ |
||||||
|
#ifndef USB_CFG_DESCR_PROPS_DEVICE |
||||||
|
#define USB_CFG_DESCR_PROPS_DEVICE 0 |
||||||
|
#endif |
||||||
|
#ifndef USB_CFG_DESCR_PROPS_CONFIGURATION |
||||||
|
#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 |
||||||
|
#endif |
||||||
|
#ifndef USB_CFG_DESCR_PROPS_STRINGS |
||||||
|
#define USB_CFG_DESCR_PROPS_STRINGS 0 |
||||||
|
#endif |
||||||
|
#ifndef USB_CFG_DESCR_PROPS_STRING_0 |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_0 0 |
||||||
|
#endif |
||||||
|
#ifndef USB_CFG_DESCR_PROPS_STRING_VENDOR |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 |
||||||
|
#endif |
||||||
|
#ifndef USB_CFG_DESCR_PROPS_STRING_PRODUCT |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 |
||||||
|
#endif |
||||||
|
#ifndef USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER |
||||||
|
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 |
||||||
|
#endif |
||||||
|
#ifndef USB_CFG_DESCR_PROPS_HID |
||||||
|
#define USB_CFG_DESCR_PROPS_HID 0 |
||||||
|
#endif |
||||||
|
#if !(USB_CFG_DESCR_PROPS_HID_REPORT) |
||||||
|
# undef USB_CFG_DESCR_PROPS_HID_REPORT |
||||||
|
# if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH /* do some backward compatibility tricks */ |
||||||
|
# define USB_CFG_DESCR_PROPS_HID_REPORT USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH |
||||||
|
# else |
||||||
|
# define USB_CFG_DESCR_PROPS_HID_REPORT 0 |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
#ifndef USB_CFG_DESCR_PROPS_UNKNOWN |
||||||
|
#define USB_CFG_DESCR_PROPS_UNKNOWN 0 |
||||||
|
#endif |
||||||
|
|
||||||
|
/* ------------------ forward declaration of descriptors ------------------- */ |
||||||
|
/* If you use external static descriptors, they must be stored in global
|
||||||
|
* arrays as declared below: |
||||||
|
*/ |
||||||
|
#ifndef __ASSEMBLER__ |
||||||
|
extern |
||||||
|
#if !(USB_CFG_DESCR_PROPS_DEVICE & USB_PROP_IS_RAM) |
||||||
|
PROGMEM |
||||||
|
#endif |
||||||
|
const char usbDescriptorDevice[]; |
||||||
|
|
||||||
|
extern |
||||||
|
#if !(USB_CFG_DESCR_PROPS_CONFIGURATION & USB_PROP_IS_RAM) |
||||||
|
PROGMEM |
||||||
|
#endif |
||||||
|
const char usbDescriptorConfiguration[]; |
||||||
|
|
||||||
|
extern |
||||||
|
#if !(USB_CFG_DESCR_PROPS_HID_REPORT & USB_PROP_IS_RAM) |
||||||
|
PROGMEM |
||||||
|
#endif |
||||||
|
const char usbDescriptorHidReport[]; |
||||||
|
|
||||||
|
extern |
||||||
|
#if !(USB_CFG_DESCR_PROPS_STRING_0 & USB_PROP_IS_RAM) |
||||||
|
PROGMEM |
||||||
|
#endif |
||||||
|
const char usbDescriptorString0[]; |
||||||
|
|
||||||
|
extern |
||||||
|
#if !(USB_CFG_DESCR_PROPS_STRING_VENDOR & USB_PROP_IS_RAM) |
||||||
|
PROGMEM |
||||||
|
#endif |
||||||
|
const int usbDescriptorStringVendor[]; |
||||||
|
|
||||||
|
extern |
||||||
|
#if !(USB_CFG_DESCR_PROPS_STRING_PRODUCT & USB_PROP_IS_RAM) |
||||||
|
PROGMEM |
||||||
|
#endif |
||||||
|
const int usbDescriptorStringDevice[]; |
||||||
|
|
||||||
|
extern |
||||||
|
#if !(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER & USB_PROP_IS_RAM) |
||||||
|
PROGMEM |
||||||
|
#endif |
||||||
|
const int usbDescriptorStringSerialNumber[]; |
||||||
|
|
||||||
|
#endif /* __ASSEMBLER__ */ |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
/* ------------------------ General Purpose Macros ------------------------- */ |
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
#define USB_CONCAT(a, b) a ## b |
||||||
|
#define USB_CONCAT_EXPANDED(a, b) USB_CONCAT(a, b) |
||||||
|
|
||||||
|
#define USB_OUTPORT(name) USB_CONCAT(PORT, name) |
||||||
|
#define USB_INPORT(name) USB_CONCAT(PIN, name) |
||||||
|
#define USB_DDRPORT(name) USB_CONCAT(DDR, name) |
||||||
|
/* The double-define trick above lets us concatenate strings which are
|
||||||
|
* defined by macros. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
/* ------------------------- Constant definitions -------------------------- */ |
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
#if !defined __ASSEMBLER__ && (!defined USB_CFG_VENDOR_ID || !defined USB_CFG_DEVICE_ID) |
||||||
|
#warning "You should define USB_CFG_VENDOR_ID and USB_CFG_DEVICE_ID in usbconfig.h" |
||||||
|
/* If the user has not defined IDs, we default to obdev's free IDs.
|
||||||
|
* See USB-IDs-for-free.txt for details. |
||||||
|
*/ |
||||||
|
#endif |
||||||
|
|
||||||
|
/* make sure we have a VID and PID defined, byte order is lowbyte, highbyte */ |
||||||
|
#ifndef USB_CFG_VENDOR_ID |
||||||
|
# define USB_CFG_VENDOR_ID 0xc0, 0x16 /* = 0x16c0 = 5824 = voti.nl */ |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef USB_CFG_DEVICE_ID |
||||||
|
# if USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH |
||||||
|
# define USB_CFG_DEVICE_ID 0xdf, 0x05 /* = 0x5df = 1503, shared PID for HIDs */ |
||||||
|
# elif USB_CFG_INTERFACE_CLASS == 2 |
||||||
|
# define USB_CFG_DEVICE_ID 0xe1, 0x05 /* = 0x5e1 = 1505, shared PID for CDC Modems */ |
||||||
|
# else |
||||||
|
# define USB_CFG_DEVICE_ID 0xdc, 0x05 /* = 0x5dc = 1500, obdev's free PID */ |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Derive Output, Input and DataDirection ports from port names */ |
||||||
|
#ifndef USB_CFG_IOPORTNAME |
||||||
|
#error "You must define USB_CFG_IOPORTNAME in usbconfig.h, see usbconfig-prototype.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#define USBOUT USB_OUTPORT(USB_CFG_IOPORTNAME) |
||||||
|
#define USB_PULLUP_OUT USB_OUTPORT(USB_CFG_PULLUP_IOPORTNAME) |
||||||
|
#define USBIN USB_INPORT(USB_CFG_IOPORTNAME) |
||||||
|
#define USBDDR USB_DDRPORT(USB_CFG_IOPORTNAME) |
||||||
|
#define USB_PULLUP_DDR USB_DDRPORT(USB_CFG_PULLUP_IOPORTNAME) |
||||||
|
|
||||||
|
#define USBMINUS USB_CFG_DMINUS_BIT |
||||||
|
#define USBPLUS USB_CFG_DPLUS_BIT |
||||||
|
#define USBIDLE (1<<USB_CFG_DMINUS_BIT) /* value representing J state */ |
||||||
|
#define USBMASK ((1<<USB_CFG_DPLUS_BIT) | (1<<USB_CFG_DMINUS_BIT)) /* mask for USB I/O bits */ |
||||||
|
|
||||||
|
/* defines for backward compatibility with older driver versions: */ |
||||||
|
#define USB_CFG_IOPORT USB_OUTPORT(USB_CFG_IOPORTNAME) |
||||||
|
#ifdef USB_CFG_PULLUP_IOPORTNAME |
||||||
|
#define USB_CFG_PULLUP_IOPORT USB_OUTPORT(USB_CFG_PULLUP_IOPORTNAME) |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef USB_CFG_EP3_NUMBER /* if not defined in usbconfig.h */ |
||||||
|
#define USB_CFG_EP3_NUMBER 3 |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef USB_CFG_HAVE_INTRIN_ENDPOINT3 |
||||||
|
#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 |
||||||
|
#endif |
||||||
|
|
||||||
|
#define USB_BUFSIZE 11 /* PID, 8 bytes data, 2 bytes CRC */ |
||||||
|
|
||||||
|
/* ----- Try to find registers and bits responsible for ext interrupt 0 ----- */ |
||||||
|
|
||||||
|
#ifndef USB_INTR_CFG /* allow user to override our default */ |
||||||
|
# if defined EICRA |
||||||
|
# define USB_INTR_CFG EICRA |
||||||
|
# else |
||||||
|
# define USB_INTR_CFG MCUCR |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
#ifndef USB_INTR_CFG_SET /* allow user to override our default */ |
||||||
|
# if defined(USB_COUNT_SOF) || defined(USB_SOF_HOOK) |
||||||
|
# define USB_INTR_CFG_SET (1 << ISC01) /* cfg for falling edge */ |
||||||
|
/* If any SOF logic is used, the interrupt must be wired to D- where
|
||||||
|
* we better trigger on falling edge |
||||||
|
*/ |
||||||
|
# else |
||||||
|
# define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) /* cfg for rising edge */ |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
#ifndef USB_INTR_CFG_CLR /* allow user to override our default */ |
||||||
|
# define USB_INTR_CFG_CLR 0 /* no bits to clear */ |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef USB_INTR_ENABLE /* allow user to override our default */ |
||||||
|
# if defined GIMSK |
||||||
|
# define USB_INTR_ENABLE GIMSK |
||||||
|
# elif defined EIMSK |
||||||
|
# define USB_INTR_ENABLE EIMSK |
||||||
|
# else |
||||||
|
# define USB_INTR_ENABLE GICR |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
#ifndef USB_INTR_ENABLE_BIT /* allow user to override our default */ |
||||||
|
# define USB_INTR_ENABLE_BIT INT0 |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef USB_INTR_PENDING /* allow user to override our default */ |
||||||
|
# if defined EIFR |
||||||
|
# define USB_INTR_PENDING EIFR |
||||||
|
# else |
||||||
|
# define USB_INTR_PENDING GIFR |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
#ifndef USB_INTR_PENDING_BIT /* allow user to override our default */ |
||||||
|
# define USB_INTR_PENDING_BIT INTF0 |
||||||
|
#endif |
||||||
|
|
||||||
|
/*
|
||||||
|
The defines above don't work for the following chips |
||||||
|
at90c8534: no ISC0?, no PORTB, can't find a data sheet |
||||||
|
at86rf401: no PORTB, no MCUCR etc, low clock rate |
||||||
|
atmega103: no ISC0? (maybe omission in header, can't find data sheet) |
||||||
|
atmega603: not defined in avr-libc |
||||||
|
at43usb320, at43usb355, at76c711: have USB anyway |
||||||
|
at94k: is different... |
||||||
|
|
||||||
|
at90s1200, attiny11, attiny12, attiny15, attiny28: these have no RAM |
||||||
|
*/ |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
/* ----------------- USB Specification Constants and Types ----------------- */ |
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
/* USB Token values */ |
||||||
|
#define USBPID_SETUP 0x2d |
||||||
|
#define USBPID_OUT 0xe1 |
||||||
|
#define USBPID_IN 0x69 |
||||||
|
#define USBPID_DATA0 0xc3 |
||||||
|
#define USBPID_DATA1 0x4b |
||||||
|
|
||||||
|
#define USBPID_ACK 0xd2 |
||||||
|
#define USBPID_NAK 0x5a |
||||||
|
#define USBPID_STALL 0x1e |
||||||
|
|
||||||
|
#ifndef USB_INITIAL_DATATOKEN |
||||||
|
#define USB_INITIAL_DATATOKEN USBPID_DATA1 |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef __ASSEMBLER__ |
||||||
|
|
||||||
|
typedef struct usbTxStatus{ |
||||||
|
volatile uchar len; |
||||||
|
uchar buffer[USB_BUFSIZE]; |
||||||
|
}usbTxStatus_t; |
||||||
|
|
||||||
|
extern usbTxStatus_t usbTxStatus1, usbTxStatus3; |
||||||
|
#define usbTxLen1 usbTxStatus1.len |
||||||
|
#define usbTxBuf1 usbTxStatus1.buffer |
||||||
|
#define usbTxLen3 usbTxStatus3.len |
||||||
|
#define usbTxBuf3 usbTxStatus3.buffer |
||||||
|
|
||||||
|
|
||||||
|
typedef union usbWord{ |
||||||
|
unsigned word; |
||||||
|
uchar bytes[2]; |
||||||
|
}usbWord_t; |
||||||
|
|
||||||
|
typedef struct usbRequest{ |
||||||
|
uchar bmRequestType; |
||||||
|
uchar bRequest; |
||||||
|
usbWord_t wValue; |
||||||
|
usbWord_t wIndex; |
||||||
|
usbWord_t wLength; |
||||||
|
}usbRequest_t; |
||||||
|
/* This structure matches the 8 byte setup request */ |
||||||
|
#endif |
||||||
|
|
||||||
|
/* bmRequestType field in USB setup:
|
||||||
|
* d t t r r r r r, where |
||||||
|
* d ..... direction: 0=host->device, 1=device->host |
||||||
|
* t ..... type: 0=standard, 1=class, 2=vendor, 3=reserved |
||||||
|
* r ..... recipient: 0=device, 1=interface, 2=endpoint, 3=other |
||||||
|
*/ |
||||||
|
|
||||||
|
/* USB setup recipient values */ |
||||||
|
#define USBRQ_RCPT_MASK 0x1f |
||||||
|
#define USBRQ_RCPT_DEVICE 0 |
||||||
|
#define USBRQ_RCPT_INTERFACE 1 |
||||||
|
#define USBRQ_RCPT_ENDPOINT 2 |
||||||
|
|
||||||
|
/* USB request type values */ |
||||||
|
#define USBRQ_TYPE_MASK 0x60 |
||||||
|
#define USBRQ_TYPE_STANDARD (0<<5) |
||||||
|
#define USBRQ_TYPE_CLASS (1<<5) |
||||||
|
#define USBRQ_TYPE_VENDOR (2<<5) |
||||||
|
|
||||||
|
/* USB direction values: */ |
||||||
|
#define USBRQ_DIR_MASK 0x80 |
||||||
|
#define USBRQ_DIR_HOST_TO_DEVICE (0<<7) |
||||||
|
#define USBRQ_DIR_DEVICE_TO_HOST (1<<7) |
||||||
|
|
||||||
|
/* USB Standard Requests */ |
||||||
|
#define USBRQ_GET_STATUS 0 |
||||||
|
#define USBRQ_CLEAR_FEATURE 1 |
||||||
|
#define USBRQ_SET_FEATURE 3 |
||||||
|
#define USBRQ_SET_ADDRESS 5 |
||||||
|
#define USBRQ_GET_DESCRIPTOR 6 |
||||||
|
#define USBRQ_SET_DESCRIPTOR 7 |
||||||
|
#define USBRQ_GET_CONFIGURATION 8 |
||||||
|
#define USBRQ_SET_CONFIGURATION 9 |
||||||
|
#define USBRQ_GET_INTERFACE 10 |
||||||
|
#define USBRQ_SET_INTERFACE 11 |
||||||
|
#define USBRQ_SYNCH_FRAME 12 |
||||||
|
|
||||||
|
/* USB descriptor constants */ |
||||||
|
#define USBDESCR_DEVICE 1 |
||||||
|
#define USBDESCR_CONFIG 2 |
||||||
|
#define USBDESCR_STRING 3 |
||||||
|
#define USBDESCR_INTERFACE 4 |
||||||
|
#define USBDESCR_ENDPOINT 5 |
||||||
|
#define USBDESCR_HID 0x21 |
||||||
|
#define USBDESCR_HID_REPORT 0x22 |
||||||
|
#define USBDESCR_HID_PHYS 0x23 |
||||||
|
|
||||||
|
//#define USBATTR_BUSPOWER 0x80 // USB 1.1 does not define this value any more
|
||||||
|
#define USBATTR_BUSPOWER 0 |
||||||
|
#define USBATTR_SELFPOWER 0x40 |
||||||
|
#define USBATTR_REMOTEWAKE 0x20 |
||||||
|
|
||||||
|
/* USB HID Requests */ |
||||||
|
#define USBRQ_HID_GET_REPORT 0x01 |
||||||
|
#define USBRQ_HID_GET_IDLE 0x02 |
||||||
|
#define USBRQ_HID_GET_PROTOCOL 0x03 |
||||||
|
#define USBRQ_HID_SET_REPORT 0x09 |
||||||
|
#define USBRQ_HID_SET_IDLE 0x0a |
||||||
|
#define USBRQ_HID_SET_PROTOCOL 0x0b |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
#endif /* __usbdrv_h_included__ */ |
||||||
@ -0,0 +1,393 @@ |
|||||||
|
/* Name: usbdrvasm.S |
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2007-06-13 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
General Description: |
||||||
|
This module is the assembler part of the USB driver. This file contains |
||||||
|
general code (preprocessor acrobatics and CRC computation) and then includes |
||||||
|
the file appropriate for the given clock rate. |
||||||
|
*/ |
||||||
|
|
||||||
|
#define __SFR_OFFSET 0 /* used by avr-libc's register definitions */ |
||||||
|
#include "usbportability.h" |
||||||
|
#include "usbdrv.h" /* for common defs */ |
||||||
|
|
||||||
|
/* register names */ |
||||||
|
#define x1 r16 |
||||||
|
#define x2 r17 |
||||||
|
#define shift r18 |
||||||
|
#define cnt r19 |
||||||
|
#define x3 r20 |
||||||
|
#define x4 r21 |
||||||
|
#define x5 r22 |
||||||
|
#define bitcnt x5 |
||||||
|
#define phase x4 |
||||||
|
#define leap x4 |
||||||
|
|
||||||
|
/* Some assembler dependent definitions and declarations: */ |
||||||
|
|
||||||
|
#ifdef __IAR_SYSTEMS_ASM__ |
||||||
|
extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBufOffset |
||||||
|
extern usbCurrentTok, usbRxLen, usbRxToken, usbTxLen |
||||||
|
extern usbTxBuf, usbTxStatus1, usbTxStatus3 |
||||||
|
# if USB_COUNT_SOF |
||||||
|
extern usbSofCount |
||||||
|
# endif |
||||||
|
public usbCrc16 |
||||||
|
public usbCrc16Append |
||||||
|
|
||||||
|
COMMON INTVEC |
||||||
|
# ifndef USB_INTR_VECTOR |
||||||
|
ORG INT0_vect |
||||||
|
# else /* USB_INTR_VECTOR */ |
||||||
|
ORG USB_INTR_VECTOR |
||||||
|
# undef USB_INTR_VECTOR |
||||||
|
# endif /* USB_INTR_VECTOR */ |
||||||
|
# define USB_INTR_VECTOR usbInterruptHandler |
||||||
|
rjmp USB_INTR_VECTOR |
||||||
|
RSEG CODE |
||||||
|
|
||||||
|
#else /* __IAR_SYSTEMS_ASM__ */ |
||||||
|
|
||||||
|
# ifndef USB_INTR_VECTOR /* default to hardware interrupt INT0 */ |
||||||
|
# ifdef INT0_vect |
||||||
|
# define USB_INTR_VECTOR INT0_vect // this is the "new" define for the vector |
||||||
|
# else |
||||||
|
# define USB_INTR_VECTOR SIG_INTERRUPT0 // this is the "old" vector |
||||||
|
# endif |
||||||
|
# endif |
||||||
|
.text |
||||||
|
.global USB_INTR_VECTOR
|
||||||
|
.type USB_INTR_VECTOR, @function
|
||||||
|
.global usbCrc16
|
||||||
|
.global usbCrc16Append
|
||||||
|
#endif /* __IAR_SYSTEMS_ASM__ */ |
||||||
|
|
||||||
|
|
||||||
|
#if USB_INTR_PENDING < 0x40 /* This is an I/O address, use in and out */ |
||||||
|
# define USB_LOAD_PENDING(reg) in reg, USB_INTR_PENDING |
||||||
|
# define USB_STORE_PENDING(reg) out USB_INTR_PENDING, reg |
||||||
|
#else /* It's a memory address, use lds and sts */ |
||||||
|
# define USB_LOAD_PENDING(reg) lds reg, USB_INTR_PENDING |
||||||
|
# define USB_STORE_PENDING(reg) sts USB_INTR_PENDING, reg |
||||||
|
#endif |
||||||
|
|
||||||
|
#define usbTxLen1 usbTxStatus1 |
||||||
|
#define usbTxBuf1 (usbTxStatus1 + 1) |
||||||
|
#define usbTxLen3 usbTxStatus3 |
||||||
|
#define usbTxBuf3 (usbTxStatus3 + 1) |
||||||
|
|
||||||
|
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
; Utility functions
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef __IAR_SYSTEMS_ASM__ |
||||||
|
/* Register assignments for usbCrc16 on IAR cc */ |
||||||
|
/* Calling conventions on IAR: |
||||||
|
* First parameter passed in r16/r17, second in r18/r19 and so on. |
||||||
|
* Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) |
||||||
|
* Result is passed in r16/r17 |
||||||
|
* In case of the "tiny" memory model, pointers are only 8 bit with no |
||||||
|
* padding. We therefore pass argument 1 as "16 bit unsigned". |
||||||
|
*/ |
||||||
|
RTMODEL "__rt_version", "3" |
||||||
|
/* The line above will generate an error if cc calling conventions change. |
||||||
|
* The value "3" above is valid for IAR 4.10B/W32 |
||||||
|
*/ |
||||||
|
# define argLen r18 /* argument 2 */ |
||||||
|
# define argPtrL r16 /* argument 1 */ |
||||||
|
# define argPtrH r17 /* argument 1 */ |
||||||
|
|
||||||
|
# define resCrcL r16 /* result */ |
||||||
|
# define resCrcH r17 /* result */ |
||||||
|
|
||||||
|
# define ptrL ZL |
||||||
|
# define ptrH ZH |
||||||
|
# define ptr Z |
||||||
|
# define byte r22 |
||||||
|
# define bitCnt r19 |
||||||
|
# define polyL r20 |
||||||
|
# define polyH r21 |
||||||
|
# define scratch r23 |
||||||
|
|
||||||
|
#else /* __IAR_SYSTEMS_ASM__ */
|
||||||
|
/* Register assignments for usbCrc16 on gcc */ |
||||||
|
/* Calling conventions on gcc: |
||||||
|
* First parameter passed in r24/r25, second in r22/23 and so on. |
||||||
|
* Callee must preserve r1-r17, r28/r29 |
||||||
|
* Result is passed in r24/r25 |
||||||
|
*/ |
||||||
|
# define argLen r22 /* argument 2 */ |
||||||
|
# define argPtrL r24 /* argument 1 */ |
||||||
|
# define argPtrH r25 /* argument 1 */ |
||||||
|
|
||||||
|
# define resCrcL r24 /* result */ |
||||||
|
# define resCrcH r25 /* result */ |
||||||
|
|
||||||
|
# define ptrL XL |
||||||
|
# define ptrH XH |
||||||
|
# define ptr x |
||||||
|
# define byte r18 |
||||||
|
# define bitCnt r19 |
||||||
|
# define polyL r20 |
||||||
|
# define polyH r21 |
||||||
|
# define scratch r23 |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
#if USB_USE_FAST_CRC |
||||||
|
|
||||||
|
; This implementation is faster, but has bigger code size
|
||||||
|
; Thanks to Slawomir Fras (BoskiDialer) for this code!
|
||||||
|
; It implements the following C pseudo-code:
|
||||||
|
; unsigned table(unsigned char x)
|
||||||
|
; {
|
||||||
|
; unsigned value;
|
||||||
|
;
|
||||||
|
; value = (unsigned)x << 6;
|
||||||
|
; value ^= (unsigned)x << 7;
|
||||||
|
; if(parity(x))
|
||||||
|
; value ^= 0xc001;
|
||||||
|
; return value;
|
||||||
|
; }
|
||||||
|
; unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen)
|
||||||
|
; {
|
||||||
|
; unsigned crc = 0xffff;
|
||||||
|
;
|
||||||
|
; while(argLen--)
|
||||||
|
; crc = table(lo8(crc) ^ *argPtr++) ^ hi8(crc);
|
||||||
|
; return ~crc;
|
||||||
|
; }
|
||||||
|
|
||||||
|
; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen);
|
||||||
|
; argPtr r24+25 / r16+r17
|
||||||
|
; argLen r22 / r18
|
||||||
|
; temp variables:
|
||||||
|
; byte r18 / r22
|
||||||
|
; scratch r23
|
||||||
|
; resCrc r24+r25 / r16+r17
|
||||||
|
; ptr X / Z
|
||||||
|
usbCrc16: |
||||||
|
mov ptrL, argPtrL |
||||||
|
mov ptrH, argPtrH |
||||||
|
ldi resCrcL, 0xFF |
||||||
|
ldi resCrcH, 0xFF |
||||||
|
rjmp usbCrc16LoopTest |
||||||
|
usbCrc16ByteLoop: |
||||||
|
ld byte, ptr+ |
||||||
|
eor resCrcL, byte ; resCrcL is now 'x' in table()
|
||||||
|
mov byte, resCrcL ; compute parity of 'x'
|
||||||
|
swap byte |
||||||
|
eor byte, resCrcL |
||||||
|
mov scratch, byte |
||||||
|
lsr byte |
||||||
|
lsr byte |
||||||
|
eor byte, scratch |
||||||
|
inc byte |
||||||
|
lsr byte |
||||||
|
andi byte, 1 ; byte is now parity(x)
|
||||||
|
mov scratch, resCrcL |
||||||
|
mov resCrcL, resCrcH |
||||||
|
eor resCrcL, byte ; low byte of if(parity(x)) value ^= 0xc001;
|
||||||
|
neg byte |
||||||
|
andi byte, 0xc0 |
||||||
|
mov resCrcH, byte ; high byte of if(parity(x)) value ^= 0xc001;
|
||||||
|
clr byte |
||||||
|
lsr scratch |
||||||
|
ror byte |
||||||
|
eor resCrcH, scratch |
||||||
|
eor resCrcL, byte |
||||||
|
lsr scratch |
||||||
|
ror byte |
||||||
|
eor resCrcH, scratch |
||||||
|
eor resCrcL, byte |
||||||
|
usbCrc16LoopTest: |
||||||
|
subi argLen, 1 |
||||||
|
brsh usbCrc16ByteLoop |
||||||
|
com resCrcL |
||||||
|
com resCrcH |
||||||
|
ret |
||||||
|
|
||||||
|
#else /* USB_USE_FAST_CRC */ |
||||||
|
|
||||||
|
; This implementation is slower, but has less code size
|
||||||
|
;
|
||||||
|
; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen);
|
||||||
|
; argPtr r24+25 / r16+r17
|
||||||
|
; argLen r22 / r18
|
||||||
|
; temp variables:
|
||||||
|
; byte r18 / r22
|
||||||
|
; bitCnt r19
|
||||||
|
; poly r20+r21
|
||||||
|
; scratch r23
|
||||||
|
; resCrc r24+r25 / r16+r17
|
||||||
|
; ptr X / Z
|
||||||
|
usbCrc16: |
||||||
|
mov ptrL, argPtrL |
||||||
|
mov ptrH, argPtrH |
||||||
|
ldi resCrcL, 0 |
||||||
|
ldi resCrcH, 0 |
||||||
|
ldi polyL, lo8(0xa001) |
||||||
|
ldi polyH, hi8(0xa001) |
||||||
|
com argLen ; argLen = -argLen - 1: modified loop to ensure that carry is set
|
||||||
|
ldi bitCnt, 0 ; loop counter with starnd condition = end condition
|
||||||
|
rjmp usbCrcLoopEntry |
||||||
|
usbCrcByteLoop: |
||||||
|
ld byte, ptr+ |
||||||
|
eor resCrcL, byte |
||||||
|
usbCrcBitLoop: |
||||||
|
ror resCrcH ; carry is always set here (see brcs jumps to here)
|
||||||
|
ror resCrcL |
||||||
|
brcs usbCrcNoXor |
||||||
|
eor resCrcL, polyL |
||||||
|
eor resCrcH, polyH |
||||||
|
usbCrcNoXor: |
||||||
|
subi bitCnt, 224 ; (8 * 224) % 256 = 0; this loop iterates 8 times
|
||||||
|
brcs usbCrcBitLoop |
||||||
|
usbCrcLoopEntry: |
||||||
|
subi argLen, -1 |
||||||
|
brcs usbCrcByteLoop |
||||||
|
usbCrcReady: |
||||||
|
ret |
||||||
|
; Thanks to Reimar Doeffinger for optimizing this CRC routine!
|
||||||
|
|
||||||
|
#endif /* USB_USE_FAST_CRC */ |
||||||
|
|
||||||
|
; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len);
|
||||||
|
usbCrc16Append: |
||||||
|
rcall usbCrc16 |
||||||
|
st ptr+, resCrcL |
||||||
|
st ptr+, resCrcH |
||||||
|
ret |
||||||
|
|
||||||
|
#undef argLen |
||||||
|
#undef argPtrL |
||||||
|
#undef argPtrH |
||||||
|
#undef resCrcL |
||||||
|
#undef resCrcH |
||||||
|
#undef ptrL |
||||||
|
#undef ptrH |
||||||
|
#undef ptr |
||||||
|
#undef byte |
||||||
|
#undef bitCnt |
||||||
|
#undef polyL |
||||||
|
#undef polyH |
||||||
|
#undef scratch |
||||||
|
|
||||||
|
|
||||||
|
#if USB_CFG_HAVE_MEASURE_FRAME_LENGTH |
||||||
|
#ifdef __IAR_SYSTEMS_ASM__ |
||||||
|
/* Register assignments for usbMeasureFrameLength on IAR cc */ |
||||||
|
/* Calling conventions on IAR: |
||||||
|
* First parameter passed in r16/r17, second in r18/r19 and so on. |
||||||
|
* Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) |
||||||
|
* Result is passed in r16/r17 |
||||||
|
* In case of the "tiny" memory model, pointers are only 8 bit with no |
||||||
|
* padding. We therefore pass argument 1 as "16 bit unsigned". |
||||||
|
*/ |
||||||
|
# define resL r16 |
||||||
|
# define resH r17 |
||||||
|
# define cnt16L r30 |
||||||
|
# define cnt16H r31 |
||||||
|
# define cntH r18 |
||||||
|
|
||||||
|
#else /* __IAR_SYSTEMS_ASM__ */
|
||||||
|
/* Register assignments for usbMeasureFrameLength on gcc */ |
||||||
|
/* Calling conventions on gcc: |
||||||
|
* First parameter passed in r24/r25, second in r22/23 and so on. |
||||||
|
* Callee must preserve r1-r17, r28/r29 |
||||||
|
* Result is passed in r24/r25 |
||||||
|
*/ |
||||||
|
# define resL r24 |
||||||
|
# define resH r25 |
||||||
|
# define cnt16L r24 |
||||||
|
# define cnt16H r25 |
||||||
|
# define cntH r26 |
||||||
|
#endif |
||||||
|
# define cnt16 cnt16L |
||||||
|
|
||||||
|
; extern unsigned usbMeasurePacketLength(void);
|
||||||
|
; returns time between two idle strobes in multiples of 7 CPU clocks
|
||||||
|
.global usbMeasureFrameLength
|
||||||
|
usbMeasureFrameLength: |
||||||
|
ldi cntH, 6 ; wait ~ 10 ms for D- == 0
|
||||||
|
clr cnt16L |
||||||
|
clr cnt16H |
||||||
|
usbMFTime16: |
||||||
|
dec cntH |
||||||
|
breq usbMFTimeout |
||||||
|
usbMFWaitStrobe: ; first wait for D- == 0 (idle strobe)
|
||||||
|
sbiw cnt16, 1 ;[0] [6]
|
||||||
|
breq usbMFTime16 ;[2]
|
||||||
|
sbic USBIN, USBMINUS ;[3]
|
||||||
|
rjmp usbMFWaitStrobe ;[4]
|
||||||
|
usbMFWaitIdle: ; then wait until idle again
|
||||||
|
sbis USBIN, USBMINUS ;1 wait for D- == 1
|
||||||
|
rjmp usbMFWaitIdle ;2
|
||||||
|
ldi cnt16L, 1 ;1 represents cycles so far
|
||||||
|
clr cnt16H ;1
|
||||||
|
usbMFWaitLoop: |
||||||
|
in cntH, USBIN ;[0] [7]
|
||||||
|
adiw cnt16, 1 ;[1]
|
||||||
|
breq usbMFTimeout ;[3]
|
||||||
|
andi cntH, USBMASK ;[4]
|
||||||
|
brne usbMFWaitLoop ;[5]
|
||||||
|
usbMFTimeout: |
||||||
|
#if resL != cnt16L |
||||||
|
mov resL, cnt16L |
||||||
|
mov resH, cnt16H |
||||||
|
#endif |
||||||
|
ret |
||||||
|
|
||||||
|
#undef resL |
||||||
|
#undef resH |
||||||
|
#undef cnt16 |
||||||
|
#undef cnt16L |
||||||
|
#undef cnt16H |
||||||
|
#undef cntH |
||||||
|
|
||||||
|
#endif /* USB_CFG_HAVE_MEASURE_FRAME_LENGTH */ |
||||||
|
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
; Now include the clock rate specific code
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef USB_CFG_CLOCK_KHZ |
||||||
|
# ifdef F_CPU |
||||||
|
# define USB_CFG_CLOCK_KHZ (F_CPU/1000) |
||||||
|
# else |
||||||
|
# error "USB_CFG_CLOCK_KHZ not defined in usbconfig.h and no F_CPU set!" |
||||||
|
# endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#if USB_CFG_CHECK_CRC /* separate dispatcher for CRC type modules */ |
||||||
|
# if USB_CFG_CLOCK_KHZ == 18000 |
||||||
|
# include "usbdrvasm18-crc.inc" |
||||||
|
# else |
||||||
|
# error "USB_CFG_CLOCK_KHZ is not one of the supported crc-rates!" |
||||||
|
# endif |
||||||
|
#else /* USB_CFG_CHECK_CRC */ |
||||||
|
# if USB_CFG_CLOCK_KHZ == 12000 |
||||||
|
# include "usbdrvasm12.inc" |
||||||
|
# elif USB_CFG_CLOCK_KHZ == 12800 |
||||||
|
# include "usbdrvasm128.inc" |
||||||
|
# elif USB_CFG_CLOCK_KHZ == 15000 |
||||||
|
# include "usbdrvasm15.inc" |
||||||
|
# elif USB_CFG_CLOCK_KHZ == 16000 |
||||||
|
# include "usbdrvasm16.inc" |
||||||
|
# elif USB_CFG_CLOCK_KHZ == 16500 |
||||||
|
# include "usbdrvasm165.inc" |
||||||
|
# elif USB_CFG_CLOCK_KHZ == 20000 |
||||||
|
# include "usbdrvasm20.inc" |
||||||
|
# else |
||||||
|
# error "USB_CFG_CLOCK_KHZ is not one of the supported non-crc-rates!" |
||||||
|
# endif |
||||||
|
#endif /* USB_CFG_CHECK_CRC */ |
||||||
@ -0,0 +1,21 @@ |
|||||||
|
/* Name: usbdrvasm.asm |
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2006-03-01 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* This Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
General Description: |
||||||
|
The IAR compiler/assembler system prefers assembler files with file extension |
||||||
|
".asm". We simply provide this file as an alias for usbdrvasm.S. |
||||||
|
|
||||||
|
Thanks to Oleg Semyonov for his help with the IAR tools port! |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "usbdrvasm.S" |
||||||
|
|
||||||
|
end |
||||||
@ -0,0 +1,393 @@ |
|||||||
|
/* Name: usbdrvasm12.inc |
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2004-12-29 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* This Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||||
|
* appropriate implementation! |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
General Description: |
||||||
|
This file is the 12 MHz version of the asssembler part of the USB driver. It |
||||||
|
requires a 12 MHz crystal (not a ceramic resonator and not a calibrated RC |
||||||
|
oscillator). |
||||||
|
|
||||||
|
See usbdrv.h for a description of the entire driver. |
||||||
|
|
||||||
|
Since almost all of this code is timing critical, don't change unless you |
||||||
|
really know what you are doing! Many parts require not only a maximum number |
||||||
|
of CPU cycles, but even an exact number of cycles! |
||||||
|
|
||||||
|
|
||||||
|
Timing constraints according to spec (in bit times): |
||||||
|
timing subject min max CPUcycles |
||||||
|
--------------------------------------------------------------------------- |
||||||
|
EOP of OUT/SETUP to sync pattern of DATA0 (both rx) 2 16 16-128 |
||||||
|
EOP of IN to sync pattern of DATA0 (rx, then tx) 2 7.5 16-60 |
||||||
|
DATAx (rx) to ACK/NAK/STALL (tx) 2 7.5 16-60 |
||||||
|
*/ |
||||||
|
|
||||||
|
;Software-receiver engine. Strict timing! Don't change unless you can preserve timing! |
||||||
|
;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled |
||||||
|
;max allowable interrupt latency: 34 cycles -> max 25 cycles interrupt disable |
||||||
|
;max stack usage: [ret(2), YL, SREG, YH, shift, x1, x2, x3, cnt, x4] = 11 bytes |
||||||
|
;Numbers in brackets are maximum cycles since SOF. |
||||||
|
USB_INTR_VECTOR: |
||||||
|
;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt |
||||||
|
push YL ;2 [35] push only what is necessary to sync with edge ASAP |
||||||
|
in YL, SREG ;1 [37] |
||||||
|
push YL ;2 [39] |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Synchronize with sync pattern: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] |
||||||
|
;sync up with J to K edge during sync pattern -- use fastest possible loops |
||||||
|
;The first part waits at most 1 bit long since we must be in sync pattern. |
||||||
|
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to |
||||||
|
;waitForJ, ensure that this prerequisite is met. |
||||||
|
waitForJ: |
||||||
|
inc YL |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
brne waitForJ ; just make sure we have ANY timeout |
||||||
|
waitForK: |
||||||
|
;The following code results in a sampling window of 1/4 bit which meets the spec. |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
#if USB_COUNT_SOF |
||||||
|
lds YL, usbSofCount |
||||||
|
inc YL |
||||||
|
sts usbSofCount, YL |
||||||
|
#endif /* USB_COUNT_SOF */ |
||||||
|
#ifdef USB_SOF_HOOK |
||||||
|
USB_SOF_HOOK |
||||||
|
#endif |
||||||
|
rjmp sofError |
||||||
|
foundK: |
||||||
|
;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] |
||||||
|
;we have 1 bit time for setup purposes, then sample again. Numbers in brackets |
||||||
|
;are cycles from center of first sync (double K) bit after the instruction |
||||||
|
push YH ;2 [2] |
||||||
|
lds YL, usbInputBufOffset;2 [4] |
||||||
|
clr YH ;1 [5] |
||||||
|
subi YL, lo8(-(usbRxBuf));1 [6] |
||||||
|
sbci YH, hi8(-(usbRxBuf));1 [7] |
||||||
|
|
||||||
|
sbis USBIN, USBMINUS ;1 [8] we want two bits K [sample 1 cycle too early] |
||||||
|
rjmp haveTwoBitsK ;2 [10] |
||||||
|
pop YH ;2 [11] undo the push from before |
||||||
|
rjmp waitForK ;2 [13] this was not the end of sync, retry |
||||||
|
haveTwoBitsK: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; push more registers and initialize values while we sample the first bits: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
push shift ;2 [16] |
||||||
|
push x1 ;2 [12] |
||||||
|
push x2 ;2 [14] |
||||||
|
|
||||||
|
in x1, USBIN ;1 [17] <-- sample bit 0 |
||||||
|
ldi shift, 0xff ;1 [18] |
||||||
|
bst x1, USBMINUS ;1 [19] |
||||||
|
bld shift, 0 ;1 [20] |
||||||
|
push x3 ;2 [22] |
||||||
|
push cnt ;2 [24] |
||||||
|
|
||||||
|
in x2, USBIN ;1 [25] <-- sample bit 1 |
||||||
|
ser x3 ;1 [26] [inserted init instruction] |
||||||
|
eor x1, x2 ;1 [27] |
||||||
|
bst x1, USBMINUS ;1 [28] |
||||||
|
bld shift, 1 ;1 [29] |
||||||
|
ldi cnt, USB_BUFSIZE;1 [30] [inserted init instruction] |
||||||
|
rjmp rxbit2 ;2 [32] |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Receiver loop (numbers in brackets are cycles within byte after instr) |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
unstuff0: ;1 (branch taken) |
||||||
|
andi x3, ~0x01 ;1 [15] |
||||||
|
mov x1, x2 ;1 [16] x2 contains last sampled (stuffed) bit |
||||||
|
in x2, USBIN ;1 [17] <-- sample bit 1 again |
||||||
|
ori shift, 0x01 ;1 [18] |
||||||
|
rjmp didUnstuff0 ;2 [20] |
||||||
|
|
||||||
|
unstuff1: ;1 (branch taken) |
||||||
|
mov x2, x1 ;1 [21] x1 contains last sampled (stuffed) bit |
||||||
|
andi x3, ~0x02 ;1 [22] |
||||||
|
ori shift, 0x02 ;1 [23] |
||||||
|
nop ;1 [24] |
||||||
|
in x1, USBIN ;1 [25] <-- sample bit 2 again |
||||||
|
rjmp didUnstuff1 ;2 [27] |
||||||
|
|
||||||
|
unstuff2: ;1 (branch taken) |
||||||
|
andi x3, ~0x04 ;1 [29] |
||||||
|
ori shift, 0x04 ;1 [30] |
||||||
|
mov x1, x2 ;1 [31] x2 contains last sampled (stuffed) bit |
||||||
|
nop ;1 [32] |
||||||
|
in x2, USBIN ;1 [33] <-- sample bit 3 |
||||||
|
rjmp didUnstuff2 ;2 [35] |
||||||
|
|
||||||
|
unstuff3: ;1 (branch taken) |
||||||
|
in x2, USBIN ;1 [34] <-- sample stuffed bit 3 [one cycle too late] |
||||||
|
andi x3, ~0x08 ;1 [35] |
||||||
|
ori shift, 0x08 ;1 [36] |
||||||
|
rjmp didUnstuff3 ;2 [38] |
||||||
|
|
||||||
|
unstuff4: ;1 (branch taken) |
||||||
|
andi x3, ~0x10 ;1 [40] |
||||||
|
in x1, USBIN ;1 [41] <-- sample stuffed bit 4 |
||||||
|
ori shift, 0x10 ;1 [42] |
||||||
|
rjmp didUnstuff4 ;2 [44] |
||||||
|
|
||||||
|
unstuff5: ;1 (branch taken) |
||||||
|
andi x3, ~0x20 ;1 [48] |
||||||
|
in x2, USBIN ;1 [49] <-- sample stuffed bit 5 |
||||||
|
ori shift, 0x20 ;1 [50] |
||||||
|
rjmp didUnstuff5 ;2 [52] |
||||||
|
|
||||||
|
unstuff6: ;1 (branch taken) |
||||||
|
andi x3, ~0x40 ;1 [56] |
||||||
|
in x1, USBIN ;1 [57] <-- sample stuffed bit 6 |
||||||
|
ori shift, 0x40 ;1 [58] |
||||||
|
rjmp didUnstuff6 ;2 [60] |
||||||
|
|
||||||
|
; extra jobs done during bit interval: |
||||||
|
; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs] |
||||||
|
; bit 1: se0 check |
||||||
|
; bit 2: overflow check |
||||||
|
; bit 3: recovery from delay [bit 0 tasks took too long] |
||||||
|
; bit 4: none |
||||||
|
; bit 5: none |
||||||
|
; bit 6: none |
||||||
|
; bit 7: jump, eor |
||||||
|
rxLoop: |
||||||
|
eor x3, shift ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others |
||||||
|
in x1, USBIN ;1 [1] <-- sample bit 0 |
||||||
|
st y+, x3 ;2 [3] store data |
||||||
|
ser x3 ;1 [4] |
||||||
|
nop ;1 [5] |
||||||
|
eor x2, x1 ;1 [6] |
||||||
|
bst x2, USBMINUS;1 [7] |
||||||
|
bld shift, 0 ;1 [8] |
||||||
|
in x2, USBIN ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed) |
||||||
|
andi x2, USBMASK ;1 [10] |
||||||
|
breq se0 ;1 [11] SE0 check for bit 1 |
||||||
|
andi shift, 0xf9 ;1 [12] |
||||||
|
didUnstuff0: |
||||||
|
breq unstuff0 ;1 [13] |
||||||
|
eor x1, x2 ;1 [14] |
||||||
|
bst x1, USBMINUS;1 [15] |
||||||
|
bld shift, 1 ;1 [16] |
||||||
|
rxbit2: |
||||||
|
in x1, USBIN ;1 [17] <-- sample bit 2 (or possibly bit 1 stuffed) |
||||||
|
andi shift, 0xf3 ;1 [18] |
||||||
|
breq unstuff1 ;1 [19] do remaining work for bit 1 |
||||||
|
didUnstuff1: |
||||||
|
subi cnt, 1 ;1 [20] |
||||||
|
brcs overflow ;1 [21] loop control |
||||||
|
eor x2, x1 ;1 [22] |
||||||
|
bst x2, USBMINUS;1 [23] |
||||||
|
bld shift, 2 ;1 [24] |
||||||
|
in x2, USBIN ;1 [25] <-- sample bit 3 (or possibly bit 2 stuffed) |
||||||
|
andi shift, 0xe7 ;1 [26] |
||||||
|
breq unstuff2 ;1 [27] |
||||||
|
didUnstuff2: |
||||||
|
eor x1, x2 ;1 [28] |
||||||
|
bst x1, USBMINUS;1 [29] |
||||||
|
bld shift, 3 ;1 [30] |
||||||
|
didUnstuff3: |
||||||
|
andi shift, 0xcf ;1 [31] |
||||||
|
breq unstuff3 ;1 [32] |
||||||
|
in x1, USBIN ;1 [33] <-- sample bit 4 |
||||||
|
eor x2, x1 ;1 [34] |
||||||
|
bst x2, USBMINUS;1 [35] |
||||||
|
bld shift, 4 ;1 [36] |
||||||
|
didUnstuff4: |
||||||
|
andi shift, 0x9f ;1 [37] |
||||||
|
breq unstuff4 ;1 [38] |
||||||
|
nop2 ;2 [40] |
||||||
|
in x2, USBIN ;1 [41] <-- sample bit 5 |
||||||
|
eor x1, x2 ;1 [42] |
||||||
|
bst x1, USBMINUS;1 [43] |
||||||
|
bld shift, 5 ;1 [44] |
||||||
|
didUnstuff5: |
||||||
|
andi shift, 0x3f ;1 [45] |
||||||
|
breq unstuff5 ;1 [46] |
||||||
|
nop2 ;2 [48] |
||||||
|
in x1, USBIN ;1 [49] <-- sample bit 6 |
||||||
|
eor x2, x1 ;1 [50] |
||||||
|
bst x2, USBMINUS;1 [51] |
||||||
|
bld shift, 6 ;1 [52] |
||||||
|
didUnstuff6: |
||||||
|
cpi shift, 0x02 ;1 [53] |
||||||
|
brlo unstuff6 ;1 [54] |
||||||
|
nop2 ;2 [56] |
||||||
|
in x2, USBIN ;1 [57] <-- sample bit 7 |
||||||
|
eor x1, x2 ;1 [58] |
||||||
|
bst x1, USBMINUS;1 [59] |
||||||
|
bld shift, 7 ;1 [60] |
||||||
|
didUnstuff7: |
||||||
|
cpi shift, 0x04 ;1 [61] |
||||||
|
brsh rxLoop ;2 [63] loop control |
||||||
|
unstuff7: |
||||||
|
andi x3, ~0x80 ;1 [63] |
||||||
|
ori shift, 0x80 ;1 [64] |
||||||
|
in x2, USBIN ;1 [65] <-- sample stuffed bit 7 |
||||||
|
nop ;1 [66] |
||||||
|
rjmp didUnstuff7 ;2 [68] |
||||||
|
|
||||||
|
macro POP_STANDARD ; 12 cycles |
||||||
|
pop cnt |
||||||
|
pop x3 |
||||||
|
pop x2 |
||||||
|
pop x1 |
||||||
|
pop shift |
||||||
|
pop YH |
||||||
|
endm |
||||||
|
macro POP_RETI ; 5 cycles |
||||||
|
pop YL |
||||||
|
out SREG, YL |
||||||
|
pop YL |
||||||
|
endm |
||||||
|
|
||||||
|
#include "asmcommon.inc" |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Transmitting data |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
txByteLoop: |
||||||
|
txBitloop: |
||||||
|
stuffN1Delay: ; [03] |
||||||
|
ror shift ;[-5] [11] [59] |
||||||
|
brcc doExorN1 ;[-4] [60] |
||||||
|
subi x4, 1 ;[-3] |
||||||
|
brne commonN1 ;[-2] |
||||||
|
lsl shift ;[-1] compensate ror after rjmp stuffDelay |
||||||
|
nop ;[00] stuffing consists of just waiting 8 cycles |
||||||
|
rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear |
||||||
|
|
||||||
|
sendNakAndReti: ;0 [-19] 19 cycles until SOP |
||||||
|
ldi x3, USBPID_NAK ;1 [-18] |
||||||
|
rjmp usbSendX3 ;2 [-16] |
||||||
|
sendAckAndReti: ;0 [-19] 19 cycles until SOP |
||||||
|
ldi x3, USBPID_ACK ;1 [-18] |
||||||
|
rjmp usbSendX3 ;2 [-16] |
||||||
|
sendCntAndReti: ;0 [-17] 17 cycles until SOP |
||||||
|
mov x3, cnt ;1 [-16] |
||||||
|
usbSendX3: ;0 [-16] |
||||||
|
ldi YL, 20 ;1 [-15] 'x3' is R20 |
||||||
|
ldi YH, 0 ;1 [-14] |
||||||
|
ldi cnt, 2 ;1 [-13] |
||||||
|
; rjmp usbSendAndReti fallthrough |
||||||
|
|
||||||
|
; USB spec says: |
||||||
|
; idle = J |
||||||
|
; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 |
||||||
|
; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 |
||||||
|
; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) |
||||||
|
|
||||||
|
;usbSend: |
||||||
|
;pointer to data in 'Y' |
||||||
|
;number of bytes in 'cnt' -- including sync byte |
||||||
|
;uses: x1...x2, x4, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x4 = bitstuff cnt] |
||||||
|
;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) |
||||||
|
usbSendAndReti: |
||||||
|
in x2, USBDDR ;[-12] 12 cycles until SOP |
||||||
|
ori x2, USBMASK ;[-11] |
||||||
|
sbi USBOUT, USBMINUS ;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) |
||||||
|
out USBDDR, x2 ;[-8] <--- acquire bus |
||||||
|
in x1, USBOUT ;[-7] port mirror for tx loop |
||||||
|
ldi shift, 0x40 ;[-6] sync byte is first byte sent (we enter loop after ror) |
||||||
|
ldi x2, USBMASK ;[-5] |
||||||
|
push x4 ;[-4] |
||||||
|
doExorN1: |
||||||
|
eor x1, x2 ;[-2] [06] [62] |
||||||
|
ldi x4, 6 ;[-1] [07] [63] |
||||||
|
commonN1: |
||||||
|
stuffN2Delay: |
||||||
|
out USBOUT, x1 ;[00] [08] [64] <--- set bit |
||||||
|
ror shift ;[01] |
||||||
|
brcc doExorN2 ;[02] |
||||||
|
subi x4, 1 ;[03] |
||||||
|
brne commonN2 ;[04] |
||||||
|
lsl shift ;[05] compensate ror after rjmp stuffDelay |
||||||
|
rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear |
||||||
|
doExorN2: |
||||||
|
eor x1, x2 ;[04] [12] |
||||||
|
ldi x4, 6 ;[05] [13] |
||||||
|
commonN2: |
||||||
|
nop ;[06] [14] |
||||||
|
subi cnt, 171 ;[07] [15] trick: (3 * 171) & 0xff = 1 |
||||||
|
out USBOUT, x1 ;[08] [16] <--- set bit |
||||||
|
brcs txBitloop ;[09] [25] [41] |
||||||
|
|
||||||
|
stuff6Delay: |
||||||
|
ror shift ;[42] [50] |
||||||
|
brcc doExor6 ;[43] |
||||||
|
subi x4, 1 ;[44] |
||||||
|
brne common6 ;[45] |
||||||
|
lsl shift ;[46] compensate ror after rjmp stuffDelay |
||||||
|
nop ;[47] stuffing consists of just waiting 8 cycles |
||||||
|
rjmp stuff6Delay ;[48] after ror, C bit is reliably clear |
||||||
|
doExor6: |
||||||
|
eor x1, x2 ;[45] [53] |
||||||
|
ldi x4, 6 ;[46] |
||||||
|
common6: |
||||||
|
stuff7Delay: |
||||||
|
ror shift ;[47] [55] |
||||||
|
out USBOUT, x1 ;[48] <--- set bit |
||||||
|
brcc doExor7 ;[49] |
||||||
|
subi x4, 1 ;[50] |
||||||
|
brne common7 ;[51] |
||||||
|
lsl shift ;[52] compensate ror after rjmp stuffDelay |
||||||
|
rjmp stuff7Delay ;[53] after ror, C bit is reliably clear |
||||||
|
doExor7: |
||||||
|
eor x1, x2 ;[51] [59] |
||||||
|
ldi x4, 6 ;[52] |
||||||
|
common7: |
||||||
|
ld shift, y+ ;[53] |
||||||
|
tst cnt ;[55] |
||||||
|
out USBOUT, x1 ;[56] <--- set bit |
||||||
|
brne txByteLoop ;[57] |
||||||
|
|
||||||
|
;make SE0: |
||||||
|
cbr x1, USBMASK ;[58] prepare SE0 [spec says EOP may be 15 to 18 cycles] |
||||||
|
lds x2, usbNewDeviceAddr;[59] |
||||||
|
lsl x2 ;[61] we compare with left shifted address |
||||||
|
subi YL, 2 + 20 ;[62] Only assign address on data packets, not ACK/NAK in x3 |
||||||
|
sbci YH, 0 ;[63] |
||||||
|
out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle |
||||||
|
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: |
||||||
|
;set address only after data packet was sent, not after handshake |
||||||
|
breq skipAddrAssign ;[01] |
||||||
|
sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer |
||||||
|
skipAddrAssign: |
||||||
|
;end of usbDeviceAddress transfer |
||||||
|
ldi x2, 1<<USB_INTR_PENDING_BIT;[03] int0 occurred during TX -- clear pending flag |
||||||
|
USB_STORE_PENDING(x2) ;[04] |
||||||
|
ori x1, USBIDLE ;[05] |
||||||
|
in x2, USBDDR ;[06] |
||||||
|
cbr x2, USBMASK ;[07] set both pins to input |
||||||
|
mov x3, x1 ;[08] |
||||||
|
cbr x3, USBMASK ;[09] configure no pullup on both pins |
||||||
|
pop x4 ;[10] |
||||||
|
nop2 ;[12] |
||||||
|
nop2 ;[14] |
||||||
|
out USBOUT, x1 ;[16] <-- out J (idle) -- end of SE0 (EOP signal) |
||||||
|
out USBDDR, x2 ;[17] <-- release bus now |
||||||
|
out USBOUT, x3 ;[18] <-- ensure no pull-up resistors are active |
||||||
|
rjmp doReturn |
||||||
@ -0,0 +1,750 @@ |
|||||||
|
/* Name: usbdrvasm128.inc |
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2008-10-11 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* This Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||||
|
* appropriate implementation! |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
General Description: |
||||||
|
This file is the 12.8 MHz version of the USB driver. It is intended for use |
||||||
|
with the internal RC oscillator. Although 12.8 MHz is outside the guaranteed |
||||||
|
calibration range of the oscillator, almost all AVRs can reach this frequency. |
||||||
|
This version contains a phase locked loop in the receiver routine to cope with |
||||||
|
slight clock rate deviations of up to +/- 1%. |
||||||
|
|
||||||
|
See usbdrv.h for a description of the entire driver. |
||||||
|
|
||||||
|
LIMITATIONS |
||||||
|
=========== |
||||||
|
Although it may seem very handy to save the crystal and use the internal |
||||||
|
RC oscillator of the CPU, this method (and this module) has some serious |
||||||
|
limitations: |
||||||
|
(1) The guaranteed calibration range of the oscillator is only 8.1 MHz. |
||||||
|
They typical range is 14.5 MHz and most AVRs can actually reach this rate. |
||||||
|
(2) Writing EEPROM and Flash may be unreliable (short data lifetime) since |
||||||
|
the write procedure is timed from the RC oscillator. |
||||||
|
(3) End Of Packet detection (SE0) should be in bit 1, bit it is only checked |
||||||
|
if bits 0 and 1 both read as 0 on D- and D+ read as 0 in the middle. This may |
||||||
|
cause problems with old hubs which delay SE0 by up to one cycle. |
||||||
|
(4) Code size is much larger than that of the other modules. |
||||||
|
|
||||||
|
Since almost all of this code is timing critical, don't change unless you |
||||||
|
really know what you are doing! Many parts require not only a maximum number |
||||||
|
of CPU cycles, but even an exact number of cycles! |
||||||
|
|
||||||
|
Implementation notes: |
||||||
|
====================== |
||||||
|
min frequency: 67 cycles for 8 bit -> 12.5625 MHz |
||||||
|
max frequency: 69.286 cycles for 8 bit -> 12.99 MHz |
||||||
|
nominal frequency: 12.77 MHz ( = sqrt(min * max)) |
||||||
|
|
||||||
|
sampling positions: (next even number in range [+/- 0.5]) |
||||||
|
cycle index range: 0 ... 66 |
||||||
|
bits: |
||||||
|
.5, 8.875, 17.25, 25.625, 34, 42.375, 50.75, 59.125 |
||||||
|
[0/1], [9], [17], [25/+26], [34], [+42/43], [51], [59] |
||||||
|
|
||||||
|
bit number: 0 1 2 3 4 5 6 7 |
||||||
|
spare cycles 1 2 1 2 1 1 1 0 |
||||||
|
|
||||||
|
operations to perform: duration cycle |
||||||
|
---------------- |
||||||
|
eor fix, shift 1 -> 00 |
||||||
|
andi phase, USBMASK 1 -> 08 |
||||||
|
breq se0 1 -> 16 (moved to 11) |
||||||
|
st y+, data 2 -> 24, 25 |
||||||
|
mov data, fix 1 -> 33 |
||||||
|
ser data 1 -> 41 |
||||||
|
subi cnt, 1 1 -> 49 |
||||||
|
brcs overflow 1 -> 50 |
||||||
|
|
||||||
|
layout of samples and operations: |
||||||
|
[##] = sample bit |
||||||
|
<##> = sample phase |
||||||
|
*##* = operation |
||||||
|
|
||||||
|
0: *00* [01] 02 03 04 <05> 06 07 |
||||||
|
1: *08* [09] 10 11 12 <13> 14 15 *16* |
||||||
|
2: [17] 18 19 20 <21> 22 23 |
||||||
|
3: *24* *25* [26] 27 28 29 <30> 31 32 |
||||||
|
4: *33* [34] 35 36 37 <38> 39 40 |
||||||
|
5: *41* [42] 43 44 45 <46> 47 48 |
||||||
|
6: *49* *50* [51] 52 53 54 <55> 56 57 58 |
||||||
|
7: [59] 60 61 62 <63> 64 65 66 |
||||||
|
*****************************************************************************/ |
||||||
|
|
||||||
|
/* we prefer positive expressions (do if condition) instead of negative |
||||||
|
* (skip if condition), therefore use defines for skip instructions: |
||||||
|
*/ |
||||||
|
#define ifioclr sbis |
||||||
|
#define ifioset sbic |
||||||
|
#define ifrclr sbrs |
||||||
|
#define ifrset sbrc |
||||||
|
|
||||||
|
/* The registers "fix" and "data" swap their meaning during the loop. Use |
||||||
|
* defines to keep their name constant. |
||||||
|
*/ |
||||||
|
#define fix x2 |
||||||
|
#define data x1 |
||||||
|
#undef phase /* phase has a default definition to x4 */ |
||||||
|
#define phase x3 |
||||||
|
|
||||||
|
|
||||||
|
USB_INTR_VECTOR: |
||||||
|
;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt, r0 |
||||||
|
push YL ;2 push only what is necessary to sync with edge ASAP |
||||||
|
in YL, SREG ;1 |
||||||
|
push YL ;2 |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Synchronize with sync pattern: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] |
||||||
|
;sync up with J to K edge during sync pattern -- use fastest possible loops |
||||||
|
;The first part waits at most 1 bit long since we must be in sync pattern. |
||||||
|
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to |
||||||
|
;waitForJ, ensure that this prerequisite is met. |
||||||
|
waitForJ: |
||||||
|
inc YL |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
brne waitForJ ; just make sure we have ANY timeout |
||||||
|
waitForK: |
||||||
|
;The following code results in a sampling window of 1/4 bit which meets the spec. |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS ;[0] |
||||||
|
rjmp foundK ;[1] |
||||||
|
#if USB_COUNT_SOF |
||||||
|
lds YL, usbSofCount |
||||||
|
inc YL |
||||||
|
sts usbSofCount, YL |
||||||
|
#endif /* USB_COUNT_SOF */ |
||||||
|
#ifdef USB_SOF_HOOK |
||||||
|
USB_SOF_HOOK |
||||||
|
#endif |
||||||
|
rjmp sofError |
||||||
|
|
||||||
|
foundK: |
||||||
|
;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] |
||||||
|
;we have 1 bit time for setup purposes, then sample again. Numbers in brackets |
||||||
|
;are cycles from center of first sync (double K) bit after the instruction |
||||||
|
push YH ;[2] |
||||||
|
lds YL, usbInputBufOffset;[4] |
||||||
|
clr YH ;[6] |
||||||
|
subi YL, lo8(-(usbRxBuf));[7] |
||||||
|
sbci YH, hi8(-(usbRxBuf));[8] |
||||||
|
|
||||||
|
sbis USBIN, USBMINUS ;[9] we want two bits K [we want to sample at 8 + 4 - 1.5 = 10.5] |
||||||
|
rjmp haveTwoBitsK ;[10] |
||||||
|
pop YH ;[11] undo the push from before |
||||||
|
rjmp waitForK ;[13] this was not the end of sync, retry |
||||||
|
haveTwoBitsK: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; push more registers and initialize values while we sample the first bits: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
#define fix x2 |
||||||
|
#define data x1 |
||||||
|
|
||||||
|
push shift ;[12] |
||||||
|
push x1 ;[14] |
||||||
|
push x2 ;[16] |
||||||
|
ldi shift, 0x80 ;[18] prevent bit-unstuffing but init low bits to 0 |
||||||
|
ifioset USBIN, USBMINUS ;[19] [01] <--- bit 0 [10.5 + 8 = 18.5] |
||||||
|
ori shift, 1<<0 ;[02] |
||||||
|
push x3 ;[03] |
||||||
|
push cnt ;[05] |
||||||
|
push r0 ;[07] |
||||||
|
ifioset USBIN, USBMINUS ;[09] <--- bit 1 |
||||||
|
ori shift, 1<<1 ;[10] |
||||||
|
ser fix ;[11] |
||||||
|
ldi cnt, USB_BUFSIZE ;[12] |
||||||
|
mov data, shift ;[13] |
||||||
|
lsl shift ;[14] |
||||||
|
nop2 ;[15] |
||||||
|
ifioset USBIN, USBMINUS ;[17] <--- bit 2 |
||||||
|
ori data, 3<<2 ;[18] store in bit 2 AND bit 3 |
||||||
|
eor shift, data ;[19] do nrzi decoding |
||||||
|
andi data, 1<<3 ;[20] |
||||||
|
in phase, USBIN ;[21] <- phase |
||||||
|
brne jumpToEntryAfterSet ;[22] if USBMINS at bit 3 was 1 |
||||||
|
nop ;[23] |
||||||
|
rjmp entryAfterClr ;[24] |
||||||
|
jumpToEntryAfterSet: |
||||||
|
rjmp entryAfterSet ;[24] |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Receiver loop (numbers in brackets are cycles within byte after instr) |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
#undef fix |
||||||
|
#define fix x1 |
||||||
|
#undef data |
||||||
|
#define data x2 |
||||||
|
|
||||||
|
bit7IsSet: |
||||||
|
ifrclr phase, USBMINUS ;[62] check phase only if D- changed |
||||||
|
lpm ;[63] |
||||||
|
in phase, USBIN ;[64] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 7 ;[65] |
||||||
|
nop ;[66] |
||||||
|
;;;;rjmp bit0AfterSet ; -> [00] == [67] moved block up to save jump |
||||||
|
bit0AfterSet: |
||||||
|
eor fix, shift ;[00] |
||||||
|
#undef fix |
||||||
|
#define fix x2 |
||||||
|
#undef data |
||||||
|
#define data x1 /* we now have result in data, fix is reset to 0xff */ |
||||||
|
ifioclr USBIN, USBMINUS ;[01] <--- sample 0 |
||||||
|
rjmp bit0IsClr ;[02] |
||||||
|
andi shift, ~(7 << 0) ;[03] |
||||||
|
breq unstuff0s ;[04] |
||||||
|
in phase, USBIN ;[05] <- phase |
||||||
|
rjmp bit1AfterSet ;[06] |
||||||
|
unstuff0s: |
||||||
|
in phase, USBIN ;[06] <- phase (one cycle too late) |
||||||
|
andi fix, ~(1 << 0) ;[07] |
||||||
|
ifioclr USBIN, USBMINUS ;[00] |
||||||
|
ifioset USBIN, USBPLUS ;[01] |
||||||
|
rjmp bit0IsClr ;[02] executed if first expr false or second true |
||||||
|
se0AndStore: ; executed only if both bits 0 |
||||||
|
st y+, x1 ;[15/17] cycles after start of byte |
||||||
|
rjmp se0 ;[17/19] |
||||||
|
|
||||||
|
bit0IsClr: |
||||||
|
ifrset phase, USBMINUS ;[04] check phase only if D- changed |
||||||
|
lpm ;[05] |
||||||
|
in phase, USBIN ;[06] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 0 ;[07] |
||||||
|
bit1AfterClr: |
||||||
|
andi phase, USBMASK ;[08] |
||||||
|
ifioset USBIN, USBMINUS ;[09] <--- sample 1 |
||||||
|
rjmp bit1IsSet ;[10] |
||||||
|
breq se0AndStore ;[11] if D- was 0 in bits 0 AND 1 and D+ was 0 in between, we have SE0 |
||||||
|
andi shift, ~(7 << 1) ;[12] |
||||||
|
in phase, USBIN ;[13] <- phase |
||||||
|
breq unstuff1c ;[14] |
||||||
|
rjmp bit2AfterClr ;[15] |
||||||
|
unstuff1c: |
||||||
|
andi fix, ~(1 << 1) ;[16] |
||||||
|
nop2 ;[08] |
||||||
|
nop2 ;[10] |
||||||
|
bit1IsSet: |
||||||
|
ifrclr phase, USBMINUS ;[12] check phase only if D- changed |
||||||
|
lpm ;[13] |
||||||
|
in phase, USBIN ;[14] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 1 ;[15] |
||||||
|
nop ;[16] |
||||||
|
bit2AfterSet: |
||||||
|
ifioclr USBIN, USBMINUS ;[17] <--- sample 2 |
||||||
|
rjmp bit2IsClr ;[18] |
||||||
|
andi shift, ~(7 << 2) ;[19] |
||||||
|
breq unstuff2s ;[20] |
||||||
|
in phase, USBIN ;[21] <- phase |
||||||
|
rjmp bit3AfterSet ;[22] |
||||||
|
unstuff2s: |
||||||
|
in phase, USBIN ;[22] <- phase (one cycle too late) |
||||||
|
andi fix, ~(1 << 2) ;[23] |
||||||
|
nop2 ;[16] |
||||||
|
nop2 ;[18] |
||||||
|
bit2IsClr: |
||||||
|
ifrset phase, USBMINUS ;[20] check phase only if D- changed |
||||||
|
lpm ;[21] |
||||||
|
in phase, USBIN ;[22] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 2 ;[23] |
||||||
|
bit3AfterClr: |
||||||
|
st y+, data ;[24] |
||||||
|
entryAfterClr: |
||||||
|
ifioset USBIN, USBMINUS ;[26] <--- sample 3 |
||||||
|
rjmp bit3IsSet ;[27] |
||||||
|
andi shift, ~(7 << 3) ;[28] |
||||||
|
breq unstuff3c ;[29] |
||||||
|
in phase, USBIN ;[30] <- phase |
||||||
|
rjmp bit4AfterClr ;[31] |
||||||
|
unstuff3c: |
||||||
|
in phase, USBIN ;[31] <- phase (one cycle too late) |
||||||
|
andi fix, ~(1 << 3) ;[32] |
||||||
|
nop2 ;[25] |
||||||
|
nop2 ;[27] |
||||||
|
bit3IsSet: |
||||||
|
ifrclr phase, USBMINUS ;[29] check phase only if D- changed |
||||||
|
lpm ;[30] |
||||||
|
in phase, USBIN ;[31] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 3 ;[32] |
||||||
|
bit4AfterSet: |
||||||
|
mov data, fix ;[33] undo this move by swapping defines |
||||||
|
#undef fix |
||||||
|
#define fix x1 |
||||||
|
#undef data |
||||||
|
#define data x2 |
||||||
|
ifioclr USBIN, USBMINUS ;[34] <--- sample 4 |
||||||
|
rjmp bit4IsClr ;[35] |
||||||
|
andi shift, ~(7 << 4) ;[36] |
||||||
|
breq unstuff4s ;[37] |
||||||
|
in phase, USBIN ;[38] <- phase |
||||||
|
rjmp bit5AfterSet ;[39] |
||||||
|
unstuff4s: |
||||||
|
in phase, USBIN ;[39] <- phase (one cycle too late) |
||||||
|
andi fix, ~(1 << 4) ;[40] |
||||||
|
nop2 ;[33] |
||||||
|
nop2 ;[35] |
||||||
|
bit4IsClr: |
||||||
|
ifrset phase, USBMINUS ;[37] check phase only if D- changed |
||||||
|
lpm ;[38] |
||||||
|
in phase, USBIN ;[39] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 4 ;[40] |
||||||
|
bit5AfterClr: |
||||||
|
ser data ;[41] |
||||||
|
ifioset USBIN, USBMINUS ;[42] <--- sample 5 |
||||||
|
rjmp bit5IsSet ;[43] |
||||||
|
andi shift, ~(7 << 5) ;[44] |
||||||
|
breq unstuff5c ;[45] |
||||||
|
in phase, USBIN ;[46] <- phase |
||||||
|
rjmp bit6AfterClr ;[47] |
||||||
|
unstuff5c: |
||||||
|
in phase, USBIN ;[47] <- phase (one cycle too late) |
||||||
|
andi fix, ~(1 << 5) ;[48] |
||||||
|
nop2 ;[41] |
||||||
|
nop2 ;[43] |
||||||
|
bit5IsSet: |
||||||
|
ifrclr phase, USBMINUS ;[45] check phase only if D- changed |
||||||
|
lpm ;[46] |
||||||
|
in phase, USBIN ;[47] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 5 ;[48] |
||||||
|
bit6AfterSet: |
||||||
|
subi cnt, 1 ;[49] |
||||||
|
brcs jumpToOverflow ;[50] |
||||||
|
ifioclr USBIN, USBMINUS ;[51] <--- sample 6 |
||||||
|
rjmp bit6IsClr ;[52] |
||||||
|
andi shift, ~(3 << 6) ;[53] |
||||||
|
cpi shift, 2 ;[54] |
||||||
|
in phase, USBIN ;[55] <- phase |
||||||
|
brlt unstuff6s ;[56] |
||||||
|
rjmp bit7AfterSet ;[57] |
||||||
|
|
||||||
|
jumpToOverflow: |
||||||
|
rjmp overflow |
||||||
|
|
||||||
|
unstuff6s: |
||||||
|
andi fix, ~(1 << 6) ;[50] |
||||||
|
lpm ;[51] |
||||||
|
bit6IsClr: |
||||||
|
ifrset phase, USBMINUS ;[54] check phase only if D- changed |
||||||
|
lpm ;[55] |
||||||
|
in phase, USBIN ;[56] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 6 ;[57] |
||||||
|
nop ;[58] |
||||||
|
bit7AfterClr: |
||||||
|
ifioset USBIN, USBMINUS ;[59] <--- sample 7 |
||||||
|
rjmp bit7IsSet ;[60] |
||||||
|
andi shift, ~(1 << 7) ;[61] |
||||||
|
cpi shift, 4 ;[62] |
||||||
|
in phase, USBIN ;[63] <- phase |
||||||
|
brlt unstuff7c ;[64] |
||||||
|
rjmp bit0AfterClr ;[65] -> [00] == [67] |
||||||
|
unstuff7c: |
||||||
|
andi fix, ~(1 << 7) ;[58] |
||||||
|
nop ;[59] |
||||||
|
rjmp bit7IsSet ;[60] |
||||||
|
|
||||||
|
bit7IsClr: |
||||||
|
ifrset phase, USBMINUS ;[62] check phase only if D- changed |
||||||
|
lpm ;[63] |
||||||
|
in phase, USBIN ;[64] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 7 ;[65] |
||||||
|
nop ;[66] |
||||||
|
;;;;rjmp bit0AfterClr ; -> [00] == [67] moved block up to save jump |
||||||
|
bit0AfterClr: |
||||||
|
eor fix, shift ;[00] |
||||||
|
#undef fix |
||||||
|
#define fix x2 |
||||||
|
#undef data |
||||||
|
#define data x1 /* we now have result in data, fix is reset to 0xff */ |
||||||
|
ifioset USBIN, USBMINUS ;[01] <--- sample 0 |
||||||
|
rjmp bit0IsSet ;[02] |
||||||
|
andi shift, ~(7 << 0) ;[03] |
||||||
|
breq unstuff0c ;[04] |
||||||
|
in phase, USBIN ;[05] <- phase |
||||||
|
rjmp bit1AfterClr ;[06] |
||||||
|
unstuff0c: |
||||||
|
in phase, USBIN ;[06] <- phase (one cycle too late) |
||||||
|
andi fix, ~(1 << 0) ;[07] |
||||||
|
ifioclr USBIN, USBMINUS ;[00] |
||||||
|
ifioset USBIN, USBPLUS ;[01] |
||||||
|
rjmp bit0IsSet ;[02] executed if first expr false or second true |
||||||
|
rjmp se0AndStore ;[03] executed only if both bits 0 |
||||||
|
bit0IsSet: |
||||||
|
ifrclr phase, USBMINUS ;[04] check phase only if D- changed |
||||||
|
lpm ;[05] |
||||||
|
in phase, USBIN ;[06] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 0 ;[07] |
||||||
|
bit1AfterSet: |
||||||
|
andi shift, ~(7 << 1) ;[08] compensated by "ori shift, 1<<1" if bit1IsClr |
||||||
|
ifioclr USBIN, USBMINUS ;[09] <--- sample 1 |
||||||
|
rjmp bit1IsClr ;[10] |
||||||
|
breq unstuff1s ;[11] |
||||||
|
nop2 ;[12] do not check for SE0 if bit 0 was 1 |
||||||
|
in phase, USBIN ;[14] <- phase (one cycle too late) |
||||||
|
rjmp bit2AfterSet ;[15] |
||||||
|
unstuff1s: |
||||||
|
in phase, USBIN ;[13] <- phase |
||||||
|
andi fix, ~(1 << 1) ;[14] |
||||||
|
lpm ;[07] |
||||||
|
nop2 ;[10] |
||||||
|
bit1IsClr: |
||||||
|
ifrset phase, USBMINUS ;[12] check phase only if D- changed |
||||||
|
lpm ;[13] |
||||||
|
in phase, USBIN ;[14] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 1 ;[15] |
||||||
|
nop ;[16] |
||||||
|
bit2AfterClr: |
||||||
|
ifioset USBIN, USBMINUS ;[17] <--- sample 2 |
||||||
|
rjmp bit2IsSet ;[18] |
||||||
|
andi shift, ~(7 << 2) ;[19] |
||||||
|
breq unstuff2c ;[20] |
||||||
|
in phase, USBIN ;[21] <- phase |
||||||
|
rjmp bit3AfterClr ;[22] |
||||||
|
unstuff2c: |
||||||
|
in phase, USBIN ;[22] <- phase (one cycle too late) |
||||||
|
andi fix, ~(1 << 2) ;[23] |
||||||
|
nop2 ;[16] |
||||||
|
nop2 ;[18] |
||||||
|
bit2IsSet: |
||||||
|
ifrclr phase, USBMINUS ;[20] check phase only if D- changed |
||||||
|
lpm ;[21] |
||||||
|
in phase, USBIN ;[22] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 2 ;[23] |
||||||
|
bit3AfterSet: |
||||||
|
st y+, data ;[24] |
||||||
|
entryAfterSet: |
||||||
|
ifioclr USBIN, USBMINUS ;[26] <--- sample 3 |
||||||
|
rjmp bit3IsClr ;[27] |
||||||
|
andi shift, ~(7 << 3) ;[28] |
||||||
|
breq unstuff3s ;[29] |
||||||
|
in phase, USBIN ;[30] <- phase |
||||||
|
rjmp bit4AfterSet ;[31] |
||||||
|
unstuff3s: |
||||||
|
in phase, USBIN ;[31] <- phase (one cycle too late) |
||||||
|
andi fix, ~(1 << 3) ;[32] |
||||||
|
nop2 ;[25] |
||||||
|
nop2 ;[27] |
||||||
|
bit3IsClr: |
||||||
|
ifrset phase, USBMINUS ;[29] check phase only if D- changed |
||||||
|
lpm ;[30] |
||||||
|
in phase, USBIN ;[31] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 3 ;[32] |
||||||
|
bit4AfterClr: |
||||||
|
mov data, fix ;[33] undo this move by swapping defines |
||||||
|
#undef fix |
||||||
|
#define fix x1 |
||||||
|
#undef data |
||||||
|
#define data x2 |
||||||
|
ifioset USBIN, USBMINUS ;[34] <--- sample 4 |
||||||
|
rjmp bit4IsSet ;[35] |
||||||
|
andi shift, ~(7 << 4) ;[36] |
||||||
|
breq unstuff4c ;[37] |
||||||
|
in phase, USBIN ;[38] <- phase |
||||||
|
rjmp bit5AfterClr ;[39] |
||||||
|
unstuff4c: |
||||||
|
in phase, USBIN ;[39] <- phase (one cycle too late) |
||||||
|
andi fix, ~(1 << 4) ;[40] |
||||||
|
nop2 ;[33] |
||||||
|
nop2 ;[35] |
||||||
|
bit4IsSet: |
||||||
|
ifrclr phase, USBMINUS ;[37] check phase only if D- changed |
||||||
|
lpm ;[38] |
||||||
|
in phase, USBIN ;[39] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 4 ;[40] |
||||||
|
bit5AfterSet: |
||||||
|
ser data ;[41] |
||||||
|
ifioclr USBIN, USBMINUS ;[42] <--- sample 5 |
||||||
|
rjmp bit5IsClr ;[43] |
||||||
|
andi shift, ~(7 << 5) ;[44] |
||||||
|
breq unstuff5s ;[45] |
||||||
|
in phase, USBIN ;[46] <- phase |
||||||
|
rjmp bit6AfterSet ;[47] |
||||||
|
unstuff5s: |
||||||
|
in phase, USBIN ;[47] <- phase (one cycle too late) |
||||||
|
andi fix, ~(1 << 5) ;[48] |
||||||
|
nop2 ;[41] |
||||||
|
nop2 ;[43] |
||||||
|
bit5IsClr: |
||||||
|
ifrset phase, USBMINUS ;[45] check phase only if D- changed |
||||||
|
lpm ;[46] |
||||||
|
in phase, USBIN ;[47] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 5 ;[48] |
||||||
|
bit6AfterClr: |
||||||
|
subi cnt, 1 ;[49] |
||||||
|
brcs overflow ;[50] |
||||||
|
ifioset USBIN, USBMINUS ;[51] <--- sample 6 |
||||||
|
rjmp bit6IsSet ;[52] |
||||||
|
andi shift, ~(3 << 6) ;[53] |
||||||
|
cpi shift, 2 ;[54] |
||||||
|
in phase, USBIN ;[55] <- phase |
||||||
|
brlt unstuff6c ;[56] |
||||||
|
rjmp bit7AfterClr ;[57] |
||||||
|
unstuff6c: |
||||||
|
andi fix, ~(1 << 6) ;[50] |
||||||
|
lpm ;[51] |
||||||
|
bit6IsSet: |
||||||
|
ifrclr phase, USBMINUS ;[54] check phase only if D- changed |
||||||
|
lpm ;[55] |
||||||
|
in phase, USBIN ;[56] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << 6 ;[57] |
||||||
|
bit7AfterSet: |
||||||
|
ifioclr USBIN, USBMINUS ;[59] <--- sample 7 |
||||||
|
rjmp bit7IsClr ;[60] |
||||||
|
andi shift, ~(1 << 7) ;[61] |
||||||
|
cpi shift, 4 ;[62] |
||||||
|
in phase, USBIN ;[63] <- phase |
||||||
|
brlt unstuff7s ;[64] |
||||||
|
rjmp bit0AfterSet ;[65] -> [00] == [67] |
||||||
|
unstuff7s: |
||||||
|
andi fix, ~(1 << 7) ;[58] |
||||||
|
nop ;[59] |
||||||
|
rjmp bit7IsClr ;[60] |
||||||
|
|
||||||
|
macro POP_STANDARD ; 14 cycles |
||||||
|
pop r0 |
||||||
|
pop cnt |
||||||
|
pop x3 |
||||||
|
pop x2 |
||||||
|
pop x1 |
||||||
|
pop shift |
||||||
|
pop YH |
||||||
|
endm |
||||||
|
macro POP_RETI ; 5 cycles |
||||||
|
pop YL |
||||||
|
out SREG, YL |
||||||
|
pop YL |
||||||
|
endm |
||||||
|
|
||||||
|
#include "asmcommon.inc" |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Transmitting data |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
txByteLoop: |
||||||
|
txBitloop: |
||||||
|
stuffN1Delay: ; [03] |
||||||
|
ror shift ;[-5] [11] [63] |
||||||
|
brcc doExorN1 ;[-4] [64] |
||||||
|
subi x3, 1 ;[-3] |
||||||
|
brne commonN1 ;[-2] |
||||||
|
lsl shift ;[-1] compensate ror after rjmp stuffDelay |
||||||
|
nop ;[00] stuffing consists of just waiting 8 cycles |
||||||
|
rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear |
||||||
|
|
||||||
|
sendNakAndReti: |
||||||
|
ldi cnt, USBPID_NAK ;[-19] |
||||||
|
rjmp sendCntAndReti ;[-18] |
||||||
|
sendAckAndReti: |
||||||
|
ldi cnt, USBPID_ACK ;[-17] |
||||||
|
sendCntAndReti: |
||||||
|
mov r0, cnt ;[-16] |
||||||
|
ldi YL, 0 ;[-15] R0 address is 0 |
||||||
|
ldi YH, 0 ;[-14] |
||||||
|
ldi cnt, 2 ;[-13] |
||||||
|
; rjmp usbSendAndReti fallthrough |
||||||
|
|
||||||
|
; USB spec says: |
||||||
|
; idle = J |
||||||
|
; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 |
||||||
|
; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 |
||||||
|
; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) |
||||||
|
|
||||||
|
;usbSend: |
||||||
|
;pointer to data in 'Y' |
||||||
|
;number of bytes in 'cnt' -- including sync byte |
||||||
|
;uses: x1...x3, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x3 = bitstuff cnt] |
||||||
|
;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) |
||||||
|
usbSendAndReti: |
||||||
|
in x2, USBDDR ;[-10] 10 cycles until SOP |
||||||
|
ori x2, USBMASK ;[-9] |
||||||
|
sbi USBOUT, USBMINUS ;[-8] prepare idle state; D+ and D- must have been 0 (no pullups) |
||||||
|
out USBDDR, x2 ;[-6] <--- acquire bus |
||||||
|
in x1, USBOUT ;[-5] port mirror for tx loop |
||||||
|
ldi shift, 0x40 ;[-4] sync byte is first byte sent (we enter loop after ror) |
||||||
|
ldi x2, USBMASK ;[-3] |
||||||
|
doExorN1: |
||||||
|
eor x1, x2 ;[-2] [06] [62] |
||||||
|
ldi x3, 6 ;[-1] [07] [63] |
||||||
|
commonN1: |
||||||
|
stuffN2Delay: |
||||||
|
out USBOUT, x1 ;[00] [08] [64] <--- set bit |
||||||
|
ror shift ;[01] |
||||||
|
brcc doExorN2 ;[02] |
||||||
|
subi x3, 1 ;[03] |
||||||
|
brne commonN2 ;[04] |
||||||
|
lsl shift ;[05] compensate ror after rjmp stuffDelay |
||||||
|
rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear |
||||||
|
doExorN2: |
||||||
|
eor x1, x2 ;[04] [12] |
||||||
|
ldi x3, 6 ;[05] [13] |
||||||
|
commonN2: |
||||||
|
nop2 ;[06] [14] |
||||||
|
subi cnt, 171 ;[08] [16] trick: (3 * 171) & 0xff = 1 |
||||||
|
out USBOUT, x1 ;[09] [17] <--- set bit |
||||||
|
brcs txBitloop ;[10] [27] [44] |
||||||
|
|
||||||
|
stuff6Delay: |
||||||
|
ror shift ;[45] [53] |
||||||
|
brcc doExor6 ;[46] |
||||||
|
subi x3, 1 ;[47] |
||||||
|
brne common6 ;[48] |
||||||
|
lsl shift ;[49] compensate ror after rjmp stuffDelay |
||||||
|
nop ;[50] stuffing consists of just waiting 8 cycles |
||||||
|
rjmp stuff6Delay ;[51] after ror, C bit is reliably clear |
||||||
|
doExor6: |
||||||
|
eor x1, x2 ;[48] [56] |
||||||
|
ldi x3, 6 ;[49] |
||||||
|
common6: |
||||||
|
stuff7Delay: |
||||||
|
ror shift ;[50] [58] |
||||||
|
out USBOUT, x1 ;[51] <--- set bit |
||||||
|
brcc doExor7 ;[52] |
||||||
|
subi x3, 1 ;[53] |
||||||
|
brne common7 ;[54] |
||||||
|
lsl shift ;[55] compensate ror after rjmp stuffDelay |
||||||
|
rjmp stuff7Delay ;[56] after ror, C bit is reliably clear |
||||||
|
doExor7: |
||||||
|
eor x1, x2 ;[54] [62] |
||||||
|
ldi x3, 6 ;[55] |
||||||
|
common7: |
||||||
|
ld shift, y+ ;[56] |
||||||
|
nop ;[58] |
||||||
|
tst cnt ;[59] |
||||||
|
out USBOUT, x1 ;[60] [00]<--- set bit |
||||||
|
brne txByteLoop ;[61] [01] |
||||||
|
;make SE0: |
||||||
|
cbr x1, USBMASK ;[02] prepare SE0 [spec says EOP may be 15 to 18 cycles] |
||||||
|
lds x2, usbNewDeviceAddr;[03] |
||||||
|
lsl x2 ;[05] we compare with left shifted address |
||||||
|
subi YL, 2 + 0 ;[06] Only assign address on data packets, not ACK/NAK in r0 |
||||||
|
sbci YH, 0 ;[07] |
||||||
|
out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle |
||||||
|
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: |
||||||
|
;set address only after data packet was sent, not after handshake |
||||||
|
breq skipAddrAssign ;[01] |
||||||
|
sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer |
||||||
|
skipAddrAssign: |
||||||
|
;end of usbDeviceAddress transfer |
||||||
|
ldi x2, 1<<USB_INTR_PENDING_BIT;[03] int0 occurred during TX -- clear pending flag |
||||||
|
USB_STORE_PENDING(x2) ;[04] |
||||||
|
ori x1, USBIDLE ;[05] |
||||||
|
in x2, USBDDR ;[06] |
||||||
|
cbr x2, USBMASK ;[07] set both pins to input |
||||||
|
mov x3, x1 ;[08] |
||||||
|
cbr x3, USBMASK ;[09] configure no pullup on both pins |
||||||
|
lpm ;[10] |
||||||
|
lpm ;[13] |
||||||
|
out USBOUT, x1 ;[16] <-- out J (idle) -- end of SE0 (EOP signal) |
||||||
|
out USBDDR, x2 ;[17] <-- release bus now |
||||||
|
out USBOUT, x3 ;[18] <-- ensure no pull-up resistors are active |
||||||
|
rjmp doReturn |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************** |
||||||
|
The following PHP script generates a code skeleton for the receiver routine: |
||||||
|
|
||||||
|
<?php |
||||||
|
|
||||||
|
function printCmdBuffer($thisBit) |
||||||
|
{ |
||||||
|
global $cycle; |
||||||
|
|
||||||
|
$nextBit = ($thisBit + 1) % 8; |
||||||
|
$s = ob_get_contents(); |
||||||
|
ob_end_clean(); |
||||||
|
$s = str_replace("#", $thisBit, $s); |
||||||
|
$s = str_replace("@", $nextBit, $s); |
||||||
|
$lines = explode("\n", $s); |
||||||
|
for($i = 0; $i < count($lines); $i++){ |
||||||
|
$s = $lines[$i]; |
||||||
|
if(ereg("\\[([0-9-][0-9])\\]", $s, $regs)){ |
||||||
|
$c = $cycle + (int)$regs[1]; |
||||||
|
$s = ereg_replace("\\[[0-9-][0-9]\\]", sprintf("[%02d]", $c), $s); |
||||||
|
} |
||||||
|
if(strlen($s) > 0) |
||||||
|
echo "$s\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function printBit($isAfterSet, $bitNum) |
||||||
|
{ |
||||||
|
ob_start(); |
||||||
|
if($isAfterSet){ |
||||||
|
?> |
||||||
|
ifioclr USBIN, USBMINUS ;[00] <--- sample |
||||||
|
rjmp bit#IsClr ;[01] |
||||||
|
andi shift, ~(7 << #) ;[02] |
||||||
|
breq unstuff#s ;[03] |
||||||
|
in phase, USBIN ;[04] <- phase |
||||||
|
rjmp bit@AfterSet ;[05] |
||||||
|
unstuff#s: |
||||||
|
in phase, USBIN ;[05] <- phase (one cycle too late) |
||||||
|
andi fix, ~(1 << #) ;[06] |
||||||
|
nop2 ;[-1] |
||||||
|
nop2 ;[01] |
||||||
|
bit#IsClr: |
||||||
|
ifrset phase, USBMINUS ;[03] check phase only if D- changed |
||||||
|
lpm ;[04] |
||||||
|
in phase, USBIN ;[05] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << # ;[06] |
||||||
|
<?php |
||||||
|
}else{ |
||||||
|
?> |
||||||
|
ifioset USBIN, USBMINUS ;[00] <--- sample |
||||||
|
rjmp bit#IsSet ;[01] |
||||||
|
andi shift, ~(7 << #) ;[02] |
||||||
|
breq unstuff#c ;[03] |
||||||
|
in phase, USBIN ;[04] <- phase |
||||||
|
rjmp bit@AfterClr ;[05] |
||||||
|
unstuff#c: |
||||||
|
in phase, USBIN ;[05] <- phase (one cycle too late) |
||||||
|
andi fix, ~(1 << #) ;[06] |
||||||
|
nop2 ;[-1] |
||||||
|
nop2 ;[01] |
||||||
|
bit#IsSet: |
||||||
|
ifrclr phase, USBMINUS ;[03] check phase only if D- changed |
||||||
|
lpm ;[04] |
||||||
|
in phase, USBIN ;[05] <- phase (one cycle too late) |
||||||
|
ori shift, 1 << # ;[06] |
||||||
|
<?php |
||||||
|
} |
||||||
|
printCmdBuffer($bitNum); |
||||||
|
} |
||||||
|
|
||||||
|
$bitStartCycles = array(1, 9, 17, 26, 34, 42, 51, 59); |
||||||
|
for($i = 0; $i < 16; $i++){ |
||||||
|
$bit = $i % 8; |
||||||
|
$emitClrCode = ($i + (int)($i / 8)) % 2; |
||||||
|
$cycle = $bitStartCycles[$bit]; |
||||||
|
if($emitClrCode){ |
||||||
|
printf("bit%dAfterClr:\n", $bit); |
||||||
|
}else{ |
||||||
|
printf("bit%dAfterSet:\n", $bit); |
||||||
|
} |
||||||
|
ob_start(); |
||||||
|
echo " ***** ;[-1]\n"; |
||||||
|
printCmdBuffer($bit); |
||||||
|
printBit(!$emitClrCode, $bit); |
||||||
|
if($i == 7) |
||||||
|
echo "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
*****************************************************************************/ |
||||||
@ -0,0 +1,423 @@ |
|||||||
|
/* Name: usbdrvasm15.inc |
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: contributed by V. Bosch |
||||||
|
* Creation Date: 2007-08-06 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||||
|
* appropriate implementation! |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
General Description: |
||||||
|
This file is the 15 MHz version of the asssembler part of the USB driver. It |
||||||
|
requires a 15 MHz crystal (not a ceramic resonator and not a calibrated RC |
||||||
|
oscillator). |
||||||
|
|
||||||
|
See usbdrv.h for a description of the entire driver. |
||||||
|
|
||||||
|
Since almost all of this code is timing critical, don't change unless you |
||||||
|
really know what you are doing! Many parts require not only a maximum number |
||||||
|
of CPU cycles, but even an exact number of cycles! |
||||||
|
*/ |
||||||
|
|
||||||
|
;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes |
||||||
|
;nominal frequency: 15 MHz -> 10.0 cycles per bit, 80.0 cycles per byte |
||||||
|
; Numbers in brackets are clocks counted from center of last sync bit |
||||||
|
; when instruction starts |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; order of registers pushed: |
||||||
|
; YL, SREG [sofError] YH, shift, x1, x2, x3, bitcnt, cnt, x4 |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
USB_INTR_VECTOR: |
||||||
|
push YL ;2 push only what is necessary to sync with edge ASAP |
||||||
|
in YL, SREG ;1 |
||||||
|
push YL ;2 |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Synchronize with sync pattern: |
||||||
|
; |
||||||
|
; sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] |
||||||
|
; sync up with J to K edge during sync pattern -- use fastest possible loops |
||||||
|
;The first part waits at most 1 bit long since we must be in sync pattern. |
||||||
|
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to |
||||||
|
;waitForJ, ensure that this prerequisite is met. |
||||||
|
waitForJ: |
||||||
|
inc YL |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
brne waitForJ ; just make sure we have ANY timeout |
||||||
|
;------------------------------------------------------------------------------- |
||||||
|
; The following code results in a sampling window of < 1/4 bit |
||||||
|
; which meets the spec. |
||||||
|
;------------------------------------------------------------------------------- |
||||||
|
waitForK: ;- |
||||||
|
sbis USBIN, USBMINUS ;1 [00] <-- sample |
||||||
|
rjmp foundK ;2 [01] |
||||||
|
sbis USBIN, USBMINUS ; <-- sample |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS ; <-- sample |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS ; <-- sample |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS ; <-- sample |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS ; <-- sample |
||||||
|
rjmp foundK |
||||||
|
#if USB_COUNT_SOF |
||||||
|
lds YL, usbSofCount |
||||||
|
inc YL |
||||||
|
sts usbSofCount, YL |
||||||
|
#endif /* USB_COUNT_SOF */ |
||||||
|
#ifdef USB_SOF_HOOK |
||||||
|
USB_SOF_HOOK |
||||||
|
#endif |
||||||
|
rjmp sofError |
||||||
|
;------------------------------------------------------------------------------ |
||||||
|
; {3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for |
||||||
|
; center sampling] |
||||||
|
; we have 1 bit time for setup purposes, then sample again. |
||||||
|
; Numbers in brackets are cycles from center of first sync (double K) |
||||||
|
; bit after the instruction |
||||||
|
;------------------------------------------------------------------------------ |
||||||
|
foundK: ;- [02] |
||||||
|
lds YL, usbInputBufOffset;2 [03+04] tx loop |
||||||
|
push YH ;2 [05+06] |
||||||
|
clr YH ;1 [07] |
||||||
|
subi YL, lo8(-(usbRxBuf)) ;1 [08] [rx loop init] |
||||||
|
sbci YH, hi8(-(usbRxBuf)) ;1 [09] [rx loop init] |
||||||
|
push shift ;2 [10+11] |
||||||
|
ser shift ;1 [12] |
||||||
|
sbis USBIN, USBMINUS ;1 [-1] [13] <--sample:we want two bits K (sample 1 cycle too early) |
||||||
|
rjmp haveTwoBitsK ;2 [00] [14] |
||||||
|
pop shift ;2 [15+16] undo the push from before |
||||||
|
pop YH ;2 [17+18] undo the push from before |
||||||
|
rjmp waitForK ;2 [19+20] this was not the end of sync, retry |
||||||
|
; The entire loop from waitForK until rjmp waitForK above must not exceed two |
||||||
|
; bit times (= 20 cycles). |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; push more registers and initialize values while we sample the first bits: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
haveTwoBitsK: ;- [01] |
||||||
|
push x1 ;2 [02+03] |
||||||
|
push x2 ;2 [04+05] |
||||||
|
push x3 ;2 [06+07] |
||||||
|
push bitcnt ;2 [08+09] |
||||||
|
in x1, USBIN ;1 [00] [10] <-- sample bit 0 |
||||||
|
bst x1, USBMINUS ;1 [01] |
||||||
|
bld shift, 0 ;1 [02] |
||||||
|
push cnt ;2 [03+04] |
||||||
|
ldi cnt, USB_BUFSIZE ;1 [05] |
||||||
|
push x4 ;2 [06+07] tx loop |
||||||
|
rjmp rxLoop ;2 [08] |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Receiver loop (numbers in brackets are cycles within byte after instr) |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
unstuff0: ;- [07] (branch taken) |
||||||
|
andi x3, ~0x01 ;1 [08] |
||||||
|
mov x1, x2 ;1 [09] x2 contains last sampled (stuffed) bit |
||||||
|
in x2, USBIN ;1 [00] [10] <-- sample bit 1 again |
||||||
|
andi x2, USBMASK ;1 [01] |
||||||
|
breq se0Hop ;1 [02] SE0 check for bit 1 |
||||||
|
ori shift, 0x01 ;1 [03] 0b00000001 |
||||||
|
nop ;1 [04] |
||||||
|
rjmp didUnstuff0 ;2 [05] |
||||||
|
;----------------------------------------------------- |
||||||
|
unstuff1: ;- [05] (branch taken) |
||||||
|
mov x2, x1 ;1 [06] x1 contains last sampled (stuffed) bit |
||||||
|
andi x3, ~0x02 ;1 [07] |
||||||
|
ori shift, 0x02 ;1 [08] 0b00000010 |
||||||
|
nop ;1 [09] |
||||||
|
in x1, USBIN ;1 [00] [10] <-- sample bit 2 again |
||||||
|
andi x1, USBMASK ;1 [01] |
||||||
|
breq se0Hop ;1 [02] SE0 check for bit 2 |
||||||
|
rjmp didUnstuff1 ;2 [03] |
||||||
|
;----------------------------------------------------- |
||||||
|
unstuff2: ;- [05] (branch taken) |
||||||
|
andi x3, ~0x04 ;1 [06] |
||||||
|
ori shift, 0x04 ;1 [07] 0b00000100 |
||||||
|
mov x1, x2 ;1 [08] x2 contains last sampled (stuffed) bit |
||||||
|
nop ;1 [09] |
||||||
|
in x2, USBIN ;1 [00] [10] <-- sample bit 3 |
||||||
|
andi x2, USBMASK ;1 [01] |
||||||
|
breq se0Hop ;1 [02] SE0 check for bit 3 |
||||||
|
rjmp didUnstuff2 ;2 [03] |
||||||
|
;----------------------------------------------------- |
||||||
|
unstuff3: ;- [00] [10] (branch taken) |
||||||
|
in x2, USBIN ;1 [01] [11] <-- sample stuffed bit 3 one cycle too late |
||||||
|
andi x2, USBMASK ;1 [02] |
||||||
|
breq se0Hop ;1 [03] SE0 check for stuffed bit 3 |
||||||
|
andi x3, ~0x08 ;1 [04] |
||||||
|
ori shift, 0x08 ;1 [05] 0b00001000 |
||||||
|
rjmp didUnstuff3 ;2 [06] |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; extra jobs done during bit interval: |
||||||
|
; |
||||||
|
; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs], |
||||||
|
; overflow check, jump to the head of rxLoop |
||||||
|
; bit 1: SE0 check |
||||||
|
; bit 2: SE0 check, recovery from delay [bit 0 tasks took too long] |
||||||
|
; bit 3: SE0 check, recovery from delay [bit 0 tasks took too long] |
||||||
|
; bit 4: SE0 check, none |
||||||
|
; bit 5: SE0 check, none |
||||||
|
; bit 6: SE0 check, none |
||||||
|
; bit 7: SE0 check, reconstruct: x3 is 0 at bit locations we changed, 1 at others |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
rxLoop: ;- [09] |
||||||
|
in x2, USBIN ;1 [00] [10] <-- sample bit 1 (or possibly bit 0 stuffed) |
||||||
|
andi x2, USBMASK ;1 [01] |
||||||
|
brne SkipSe0Hop ;1 [02] |
||||||
|
se0Hop: ;- [02] |
||||||
|
rjmp se0 ;2 [03] SE0 check for bit 1 |
||||||
|
SkipSe0Hop: ;- [03] |
||||||
|
ser x3 ;1 [04] |
||||||
|
andi shift, 0xf9 ;1 [05] 0b11111001 |
||||||
|
breq unstuff0 ;1 [06] |
||||||
|
didUnstuff0: ;- [06] |
||||||
|
eor x1, x2 ;1 [07] |
||||||
|
bst x1, USBMINUS ;1 [08] |
||||||
|
bld shift, 1 ;1 [09] |
||||||
|
in x1, USBIN ;1 [00] [10] <-- sample bit 2 (or possibly bit 1 stuffed) |
||||||
|
andi x1, USBMASK ;1 [01] |
||||||
|
breq se0Hop ;1 [02] SE0 check for bit 2 |
||||||
|
andi shift, 0xf3 ;1 [03] 0b11110011 |
||||||
|
breq unstuff1 ;1 [04] do remaining work for bit 1 |
||||||
|
didUnstuff1: ;- [04] |
||||||
|
eor x2, x1 ;1 [05] |
||||||
|
bst x2, USBMINUS ;1 [06] |
||||||
|
bld shift, 2 ;1 [07] |
||||||
|
nop2 ;2 [08+09] |
||||||
|
in x2, USBIN ;1 [00] [10] <-- sample bit 3 (or possibly bit 2 stuffed) |
||||||
|
andi x2, USBMASK ;1 [01] |
||||||
|
breq se0Hop ;1 [02] SE0 check for bit 3 |
||||||
|
andi shift, 0xe7 ;1 [03] 0b11100111 |
||||||
|
breq unstuff2 ;1 [04] |
||||||
|
didUnstuff2: ;- [04] |
||||||
|
eor x1, x2 ;1 [05] |
||||||
|
bst x1, USBMINUS ;1 [06] |
||||||
|
bld shift, 3 ;1 [07] |
||||||
|
didUnstuff3: ;- [07] |
||||||
|
andi shift, 0xcf ;1 [08] 0b11001111 |
||||||
|
breq unstuff3 ;1 [09] |
||||||
|
in x1, USBIN ;1 [00] [10] <-- sample bit 4 |
||||||
|
andi x1, USBMASK ;1 [01] |
||||||
|
breq se0Hop ;1 [02] SE0 check for bit 4 |
||||||
|
eor x2, x1 ;1 [03] |
||||||
|
bst x2, USBMINUS ;1 [04] |
||||||
|
bld shift, 4 ;1 [05] |
||||||
|
didUnstuff4: ;- [05] |
||||||
|
andi shift, 0x9f ;1 [06] 0b10011111 |
||||||
|
breq unstuff4 ;1 [07] |
||||||
|
nop2 ;2 [08+09] |
||||||
|
in x2, USBIN ;1 [00] [10] <-- sample bit 5 |
||||||
|
andi x2, USBMASK ;1 [01] |
||||||
|
breq se0 ;1 [02] SE0 check for bit 5 |
||||||
|
eor x1, x2 ;1 [03] |
||||||
|
bst x1, USBMINUS ;1 [04] |
||||||
|
bld shift, 5 ;1 [05] |
||||||
|
didUnstuff5: ;- [05] |
||||||
|
andi shift, 0x3f ;1 [06] 0b00111111 |
||||||
|
breq unstuff5 ;1 [07] |
||||||
|
nop2 ;2 [08+09] |
||||||
|
in x1, USBIN ;1 [00] [10] <-- sample bit 6 |
||||||
|
andi x1, USBMASK ;1 [01] |
||||||
|
breq se0 ;1 [02] SE0 check for bit 6 |
||||||
|
eor x2, x1 ;1 [03] |
||||||
|
bst x2, USBMINUS ;1 [04] |
||||||
|
bld shift, 6 ;1 [05] |
||||||
|
didUnstuff6: ;- [05] |
||||||
|
cpi shift, 0x02 ;1 [06] 0b00000010 |
||||||
|
brlo unstuff6 ;1 [07] |
||||||
|
nop2 ;2 [08+09] |
||||||
|
in x2, USBIN ;1 [00] [10] <-- sample bit 7 |
||||||
|
andi x2, USBMASK ;1 [01] |
||||||
|
breq se0 ;1 [02] SE0 check for bit 7 |
||||||
|
eor x1, x2 ;1 [03] |
||||||
|
bst x1, USBMINUS ;1 [04] |
||||||
|
bld shift, 7 ;1 [05] |
||||||
|
didUnstuff7: ;- [05] |
||||||
|
cpi shift, 0x04 ;1 [06] 0b00000100 |
||||||
|
brlo unstuff7 ;1 [07] |
||||||
|
eor x3, shift ;1 [08] reconstruct: x3 is 0 at bit locations we changed, 1 at others |
||||||
|
nop ;1 [09] |
||||||
|
in x1, USBIN ;1 [00] [10] <-- sample bit 0 |
||||||
|
st y+, x3 ;2 [01+02] store data |
||||||
|
eor x2, x1 ;1 [03] |
||||||
|
bst x2, USBMINUS ;1 [04] |
||||||
|
bld shift, 0 ;1 [05] |
||||||
|
subi cnt, 1 ;1 [06] |
||||||
|
brcs overflow ;1 [07] |
||||||
|
rjmp rxLoop ;2 [08] |
||||||
|
;----------------------------------------------------- |
||||||
|
unstuff4: ;- [08] |
||||||
|
andi x3, ~0x10 ;1 [09] |
||||||
|
in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 4 |
||||||
|
andi x1, USBMASK ;1 [01] |
||||||
|
breq se0 ;1 [02] SE0 check for stuffed bit 4 |
||||||
|
ori shift, 0x10 ;1 [03] |
||||||
|
rjmp didUnstuff4 ;2 [04] |
||||||
|
;----------------------------------------------------- |
||||||
|
unstuff5: ;- [08] |
||||||
|
ori shift, 0x20 ;1 [09] |
||||||
|
in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 5 |
||||||
|
andi x2, USBMASK ;1 [01] |
||||||
|
breq se0 ;1 [02] SE0 check for stuffed bit 5 |
||||||
|
andi x3, ~0x20 ;1 [03] |
||||||
|
rjmp didUnstuff5 ;2 [04] |
||||||
|
;----------------------------------------------------- |
||||||
|
unstuff6: ;- [08] |
||||||
|
andi x3, ~0x40 ;1 [09] |
||||||
|
in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 6 |
||||||
|
andi x1, USBMASK ;1 [01] |
||||||
|
breq se0 ;1 [02] SE0 check for stuffed bit 6 |
||||||
|
ori shift, 0x40 ;1 [03] |
||||||
|
rjmp didUnstuff6 ;2 [04] |
||||||
|
;----------------------------------------------------- |
||||||
|
unstuff7: ;- [08] |
||||||
|
andi x3, ~0x80 ;1 [09] |
||||||
|
in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 7 |
||||||
|
andi x2, USBMASK ;1 [01] |
||||||
|
breq se0 ;1 [02] SE0 check for stuffed bit 7 |
||||||
|
ori shift, 0x80 ;1 [03] |
||||||
|
rjmp didUnstuff7 ;2 [04] |
||||||
|
|
||||||
|
macro POP_STANDARD ; 16 cycles |
||||||
|
pop x4 |
||||||
|
pop cnt |
||||||
|
pop bitcnt |
||||||
|
pop x3 |
||||||
|
pop x2 |
||||||
|
pop x1 |
||||||
|
pop shift |
||||||
|
pop YH |
||||||
|
endm |
||||||
|
macro POP_RETI ; 5 cycles |
||||||
|
pop YL |
||||||
|
out SREG, YL |
||||||
|
pop YL |
||||||
|
endm |
||||||
|
|
||||||
|
#include "asmcommon.inc" |
||||||
|
|
||||||
|
;--------------------------------------------------------------------------- |
||||||
|
; USB spec says: |
||||||
|
; idle = J |
||||||
|
; J = (D+ = 0), (D- = 1) |
||||||
|
; K = (D+ = 1), (D- = 0) |
||||||
|
; Spec allows 7.5 bit times from EOP to SOP for replies |
||||||
|
;--------------------------------------------------------------------------- |
||||||
|
bitstuffN: ;- [04] |
||||||
|
eor x1, x4 ;1 [05] |
||||||
|
clr x2 ;1 [06] |
||||||
|
nop ;1 [07] |
||||||
|
rjmp didStuffN ;1 [08] |
||||||
|
;--------------------------------------------------------------------------- |
||||||
|
bitstuff6: ;- [04] |
||||||
|
eor x1, x4 ;1 [05] |
||||||
|
clr x2 ;1 [06] |
||||||
|
rjmp didStuff6 ;1 [07] |
||||||
|
;--------------------------------------------------------------------------- |
||||||
|
bitstuff7: ;- [02] |
||||||
|
eor x1, x4 ;1 [03] |
||||||
|
clr x2 ;1 [06] |
||||||
|
nop ;1 [05] |
||||||
|
rjmp didStuff7 ;1 [06] |
||||||
|
;--------------------------------------------------------------------------- |
||||||
|
sendNakAndReti: ;- [-19] |
||||||
|
ldi x3, USBPID_NAK ;1 [-18] |
||||||
|
rjmp sendX3AndReti ;1 [-17] |
||||||
|
;--------------------------------------------------------------------------- |
||||||
|
sendAckAndReti: ;- [-17] |
||||||
|
ldi cnt, USBPID_ACK ;1 [-16] |
||||||
|
sendCntAndReti: ;- [-16] |
||||||
|
mov x3, cnt ;1 [-15] |
||||||
|
sendX3AndReti: ;- [-15] |
||||||
|
ldi YL, 20 ;1 [-14] x3==r20 address is 20 |
||||||
|
ldi YH, 0 ;1 [-13] |
||||||
|
ldi cnt, 2 ;1 [-12] |
||||||
|
; rjmp usbSendAndReti fallthrough |
||||||
|
;--------------------------------------------------------------------------- |
||||||
|
;usbSend: |
||||||
|
;pointer to data in 'Y' |
||||||
|
;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] |
||||||
|
;uses: x1...x4, btcnt, shift, cnt, Y |
||||||
|
;Numbers in brackets are time since first bit of sync pattern is sent |
||||||
|
;We need not to match the transfer rate exactly because the spec demands |
||||||
|
;only 1.5% precision anyway. |
||||||
|
usbSendAndReti: ;- [-13] 13 cycles until SOP |
||||||
|
in x2, USBDDR ;1 [-12] |
||||||
|
ori x2, USBMASK ;1 [-11] |
||||||
|
sbi USBOUT, USBMINUS ;2 [-09-10] prepare idle state; D+ and D- must have been 0 (no pullups) |
||||||
|
in x1, USBOUT ;1 [-08] port mirror for tx loop |
||||||
|
out USBDDR, x2 ;1 [-07] <- acquire bus |
||||||
|
; need not init x2 (bitstuff history) because sync starts with 0 |
||||||
|
ldi x4, USBMASK ;1 [-06] exor mask |
||||||
|
ldi shift, 0x80 ;1 [-05] sync byte is first byte sent |
||||||
|
ldi bitcnt, 6 ;1 [-04] |
||||||
|
txBitLoop: ;- [-04] [06] |
||||||
|
sbrs shift, 0 ;1 [-03] [07] |
||||||
|
eor x1, x4 ;1 [-02] [08] |
||||||
|
ror shift ;1 [-01] [09] |
||||||
|
didStuffN: ;- [09] |
||||||
|
out USBOUT, x1 ;1 [00] [10] <-- out N |
||||||
|
ror x2 ;1 [01] |
||||||
|
cpi x2, 0xfc ;1 [02] |
||||||
|
brcc bitstuffN ;1 [03] |
||||||
|
dec bitcnt ;1 [04] |
||||||
|
brne txBitLoop ;1 [05] |
||||||
|
sbrs shift, 0 ;1 [06] |
||||||
|
eor x1, x4 ;1 [07] |
||||||
|
ror shift ;1 [08] |
||||||
|
didStuff6: ;- [08] |
||||||
|
nop ;1 [09] |
||||||
|
out USBOUT, x1 ;1 [00] [10] <-- out 6 |
||||||
|
ror x2 ;1 [01] |
||||||
|
cpi x2, 0xfc ;1 [02] |
||||||
|
brcc bitstuff6 ;1 [03] |
||||||
|
sbrs shift, 0 ;1 [04] |
||||||
|
eor x1, x4 ;1 [05] |
||||||
|
ror shift ;1 [06] |
||||||
|
ror x2 ;1 [07] |
||||||
|
didStuff7: ;- [07] |
||||||
|
ldi bitcnt, 6 ;1 [08] |
||||||
|
cpi x2, 0xfc ;1 [09] |
||||||
|
out USBOUT, x1 ;1 [00] [10] <-- out 7 |
||||||
|
brcc bitstuff7 ;1 [01] |
||||||
|
ld shift, y+ ;2 [02+03] |
||||||
|
dec cnt ;1 [04] |
||||||
|
brne txBitLoop ;1 [05] |
||||||
|
makeSE0: |
||||||
|
cbr x1, USBMASK ;1 [06] prepare SE0 [spec says EOP may be 19 to 23 cycles] |
||||||
|
lds x2, usbNewDeviceAddr;2 [07+08] |
||||||
|
lsl x2 ;1 [09] we compare with left shifted address |
||||||
|
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: |
||||||
|
;set address only after data packet was sent, not after handshake |
||||||
|
out USBOUT, x1 ;1 [00] [10] <-- out SE0-- from now 2 bits==20 cycl. until bus idle |
||||||
|
subi YL, 20 + 2 ;1 [01] Only assign address on data packets, not ACK/NAK in x3 |
||||||
|
sbci YH, 0 ;1 [02] |
||||||
|
breq skipAddrAssign ;1 [03] |
||||||
|
sts usbDeviceAddr, x2 ;2 [04+05] if not skipped: SE0 is one cycle longer |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
;end of usbDeviceAddress transfer |
||||||
|
skipAddrAssign: ;- [03/04] |
||||||
|
ldi x2, 1<<USB_INTR_PENDING_BIT ;1 [05] int0 occurred during TX -- clear pending flag |
||||||
|
USB_STORE_PENDING(x2) ;1 [06] |
||||||
|
ori x1, USBIDLE ;1 [07] |
||||||
|
in x2, USBDDR ;1 [08] |
||||||
|
cbr x2, USBMASK ;1 [09] set both pins to input |
||||||
|
mov x3, x1 ;1 [10] |
||||||
|
cbr x3, USBMASK ;1 [11] configure no pullup on both pins |
||||||
|
ldi x4, 3 ;1 [12] |
||||||
|
se0Delay: ;- [12] [15] |
||||||
|
dec x4 ;1 [13] [16] |
||||||
|
brne se0Delay ;1 [14] [17] |
||||||
|
nop2 ;2 [18+19] |
||||||
|
out USBOUT, x1 ;1 [20] <--out J (idle) -- end of SE0 (EOP sig.) |
||||||
|
out USBDDR, x2 ;1 [21] <--release bus now |
||||||
|
out USBOUT, x3 ;1 [22] <--ensure no pull-up resistors are active |
||||||
|
rjmp doReturn ;1 [23] |
||||||
|
;--------------------------------------------------------------------------- |
||||||
@ -0,0 +1,346 @@ |
|||||||
|
/* Name: usbdrvasm16.inc |
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2007-06-15 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||||
|
* appropriate implementation! |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
General Description: |
||||||
|
This file is the 16 MHz version of the asssembler part of the USB driver. It |
||||||
|
requires a 16 MHz crystal (not a ceramic resonator and not a calibrated RC |
||||||
|
oscillator). |
||||||
|
|
||||||
|
See usbdrv.h for a description of the entire driver. |
||||||
|
|
||||||
|
Since almost all of this code is timing critical, don't change unless you |
||||||
|
really know what you are doing! Many parts require not only a maximum number |
||||||
|
of CPU cycles, but even an exact number of cycles! |
||||||
|
*/ |
||||||
|
|
||||||
|
;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes |
||||||
|
;nominal frequency: 16 MHz -> 10.6666666 cycles per bit, 85.333333333 cycles per byte |
||||||
|
; Numbers in brackets are clocks counted from center of last sync bit |
||||||
|
; when instruction starts |
||||||
|
|
||||||
|
USB_INTR_VECTOR: |
||||||
|
;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt |
||||||
|
push YL ;[-25] push only what is necessary to sync with edge ASAP |
||||||
|
in YL, SREG ;[-23] |
||||||
|
push YL ;[-22] |
||||||
|
push YH ;[-20] |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Synchronize with sync pattern: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] |
||||||
|
;sync up with J to K edge during sync pattern -- use fastest possible loops |
||||||
|
;The first part waits at most 1 bit long since we must be in sync pattern. |
||||||
|
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to |
||||||
|
;waitForJ, ensure that this prerequisite is met. |
||||||
|
waitForJ: |
||||||
|
inc YL |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
brne waitForJ ; just make sure we have ANY timeout |
||||||
|
waitForK: |
||||||
|
;The following code results in a sampling window of < 1/4 bit which meets the spec. |
||||||
|
sbis USBIN, USBMINUS ;[-15] |
||||||
|
rjmp foundK ;[-14] |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
#if USB_COUNT_SOF |
||||||
|
lds YL, usbSofCount |
||||||
|
inc YL |
||||||
|
sts usbSofCount, YL |
||||||
|
#endif /* USB_COUNT_SOF */ |
||||||
|
#ifdef USB_SOF_HOOK |
||||||
|
USB_SOF_HOOK |
||||||
|
#endif |
||||||
|
rjmp sofError |
||||||
|
foundK: ;[-12] |
||||||
|
;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] |
||||||
|
;we have 1 bit time for setup purposes, then sample again. Numbers in brackets |
||||||
|
;are cycles from center of first sync (double K) bit after the instruction |
||||||
|
push bitcnt ;[-12] |
||||||
|
; [---] ;[-11] |
||||||
|
lds YL, usbInputBufOffset;[-10] |
||||||
|
; [---] ;[-9] |
||||||
|
clr YH ;[-8] |
||||||
|
subi YL, lo8(-(usbRxBuf));[-7] [rx loop init] |
||||||
|
sbci YH, hi8(-(usbRxBuf));[-6] [rx loop init] |
||||||
|
push shift ;[-5] |
||||||
|
; [---] ;[-4] |
||||||
|
ldi bitcnt, 0x55 ;[-3] [rx loop init] |
||||||
|
sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) |
||||||
|
rjmp haveTwoBitsK ;[-1] |
||||||
|
pop shift ;[0] undo the push from before |
||||||
|
pop bitcnt ;[2] undo the push from before |
||||||
|
rjmp waitForK ;[4] this was not the end of sync, retry |
||||||
|
; The entire loop from waitForK until rjmp waitForK above must not exceed two |
||||||
|
; bit times (= 21 cycles). |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; push more registers and initialize values while we sample the first bits: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
haveTwoBitsK: |
||||||
|
push x1 ;[1] |
||||||
|
push x2 ;[3] |
||||||
|
push x3 ;[5] |
||||||
|
ldi shift, 0 ;[7] |
||||||
|
ldi x3, 1<<4 ;[8] [rx loop init] first sample is inverse bit, compensate that |
||||||
|
push x4 ;[9] == leap |
||||||
|
|
||||||
|
in x1, USBIN ;[11] <-- sample bit 0 |
||||||
|
andi x1, USBMASK ;[12] |
||||||
|
bst x1, USBMINUS ;[13] |
||||||
|
bld shift, 7 ;[14] |
||||||
|
push cnt ;[15] |
||||||
|
ldi leap, 0 ;[17] [rx loop init] |
||||||
|
ldi cnt, USB_BUFSIZE;[18] [rx loop init] |
||||||
|
rjmp rxbit1 ;[19] arrives at [21] |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Receiver loop (numbers in brackets are cycles within byte after instr) |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
; duration of unstuffing code should be 10.66666667 cycles. We adjust "leap" |
||||||
|
; accordingly to approximate this value in the long run. |
||||||
|
|
||||||
|
unstuff6: |
||||||
|
andi x2, USBMASK ;[03] |
||||||
|
ori x3, 1<<6 ;[04] will not be shifted any more |
||||||
|
andi shift, ~0x80;[05] |
||||||
|
mov x1, x2 ;[06] sampled bit 7 is actually re-sampled bit 6 |
||||||
|
subi leap, -1 ;[07] total duration = 11 bits -> subtract 1/3 |
||||||
|
rjmp didUnstuff6 ;[08] |
||||||
|
|
||||||
|
unstuff7: |
||||||
|
ori x3, 1<<7 ;[09] will not be shifted any more |
||||||
|
in x2, USBIN ;[00] [10] re-sample bit 7 |
||||||
|
andi x2, USBMASK ;[01] |
||||||
|
andi shift, ~0x80;[02] |
||||||
|
subi leap, 2 ;[03] total duration = 10 bits -> add 1/3 |
||||||
|
rjmp didUnstuff7 ;[04] |
||||||
|
|
||||||
|
unstuffEven: |
||||||
|
ori x3, 1<<6 ;[09] will be shifted right 6 times for bit 0 |
||||||
|
in x1, USBIN ;[00] [10] |
||||||
|
andi shift, ~0x80;[01] |
||||||
|
andi x1, USBMASK ;[02] |
||||||
|
breq se0 ;[03] |
||||||
|
subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 |
||||||
|
nop2 ;[05] |
||||||
|
rjmp didUnstuffE ;[06] |
||||||
|
|
||||||
|
unstuffOdd: |
||||||
|
ori x3, 1<<5 ;[09] will be shifted right 4 times for bit 1 |
||||||
|
in x2, USBIN ;[00] [10] |
||||||
|
andi shift, ~0x80;[01] |
||||||
|
andi x2, USBMASK ;[02] |
||||||
|
breq se0 ;[03] |
||||||
|
subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 |
||||||
|
nop2 ;[05] |
||||||
|
rjmp didUnstuffO ;[06] |
||||||
|
|
||||||
|
rxByteLoop: |
||||||
|
andi x1, USBMASK ;[03] |
||||||
|
eor x2, x1 ;[04] |
||||||
|
subi leap, 1 ;[05] |
||||||
|
brpl skipLeap ;[06] |
||||||
|
subi leap, -3 ;1 one leap cycle every 3rd byte -> 85 + 1/3 cycles per byte |
||||||
|
nop ;1 |
||||||
|
skipLeap: |
||||||
|
subi x2, 1 ;[08] |
||||||
|
ror shift ;[09] |
||||||
|
didUnstuff6: |
||||||
|
cpi shift, 0xfc ;[10] |
||||||
|
in x2, USBIN ;[00] [11] <-- sample bit 7 |
||||||
|
brcc unstuff6 ;[01] |
||||||
|
andi x2, USBMASK ;[02] |
||||||
|
eor x1, x2 ;[03] |
||||||
|
subi x1, 1 ;[04] |
||||||
|
ror shift ;[05] |
||||||
|
didUnstuff7: |
||||||
|
cpi shift, 0xfc ;[06] |
||||||
|
brcc unstuff7 ;[07] |
||||||
|
eor x3, shift ;[08] reconstruct: x3 is 1 at bit locations we changed, 0 at others |
||||||
|
st y+, x3 ;[09] store data |
||||||
|
rxBitLoop: |
||||||
|
in x1, USBIN ;[00] [11] <-- sample bit 0/2/4 |
||||||
|
andi x1, USBMASK ;[01] |
||||||
|
eor x2, x1 ;[02] |
||||||
|
andi x3, 0x3f ;[03] topmost two bits reserved for 6 and 7 |
||||||
|
subi x2, 1 ;[04] |
||||||
|
ror shift ;[05] |
||||||
|
cpi shift, 0xfc ;[06] |
||||||
|
brcc unstuffEven ;[07] |
||||||
|
didUnstuffE: |
||||||
|
lsr x3 ;[08] |
||||||
|
lsr x3 ;[09] |
||||||
|
rxbit1: |
||||||
|
in x2, USBIN ;[00] [10] <-- sample bit 1/3/5 |
||||||
|
andi x2, USBMASK ;[01] |
||||||
|
breq se0 ;[02] |
||||||
|
eor x1, x2 ;[03] |
||||||
|
subi x1, 1 ;[04] |
||||||
|
ror shift ;[05] |
||||||
|
cpi shift, 0xfc ;[06] |
||||||
|
brcc unstuffOdd ;[07] |
||||||
|
didUnstuffO: |
||||||
|
subi bitcnt, 0xab;[08] == addi 0x55, 0x55 = 0x100/3 |
||||||
|
brcs rxBitLoop ;[09] |
||||||
|
|
||||||
|
subi cnt, 1 ;[10] |
||||||
|
in x1, USBIN ;[00] [11] <-- sample bit 6 |
||||||
|
brcc rxByteLoop ;[01] |
||||||
|
rjmp overflow |
||||||
|
|
||||||
|
macro POP_STANDARD ; 14 cycles |
||||||
|
pop cnt |
||||||
|
pop x4 |
||||||
|
pop x3 |
||||||
|
pop x2 |
||||||
|
pop x1 |
||||||
|
pop shift |
||||||
|
pop bitcnt |
||||||
|
endm |
||||||
|
macro POP_RETI ; 7 cycles |
||||||
|
pop YH |
||||||
|
pop YL |
||||||
|
out SREG, YL |
||||||
|
pop YL |
||||||
|
endm |
||||||
|
|
||||||
|
#include "asmcommon.inc" |
||||||
|
|
||||||
|
; USB spec says: |
||||||
|
; idle = J |
||||||
|
; J = (D+ = 0), (D- = 1) |
||||||
|
; K = (D+ = 1), (D- = 0) |
||||||
|
; Spec allows 7.5 bit times from EOP to SOP for replies |
||||||
|
|
||||||
|
bitstuffN: |
||||||
|
eor x1, x4 ;[5] |
||||||
|
ldi x2, 0 ;[6] |
||||||
|
nop2 ;[7] |
||||||
|
nop ;[9] |
||||||
|
out USBOUT, x1 ;[10] <-- out |
||||||
|
rjmp didStuffN ;[0] |
||||||
|
|
||||||
|
bitstuff6: |
||||||
|
eor x1, x4 ;[5] |
||||||
|
ldi x2, 0 ;[6] Carry is zero due to brcc |
||||||
|
rol shift ;[7] compensate for ror shift at branch destination |
||||||
|
rjmp didStuff6 ;[8] |
||||||
|
|
||||||
|
bitstuff7: |
||||||
|
ldi x2, 0 ;[2] Carry is zero due to brcc |
||||||
|
rjmp didStuff7 ;[3] |
||||||
|
|
||||||
|
|
||||||
|
sendNakAndReti: |
||||||
|
ldi x3, USBPID_NAK ;[-18] |
||||||
|
rjmp sendX3AndReti ;[-17] |
||||||
|
sendAckAndReti: |
||||||
|
ldi cnt, USBPID_ACK ;[-17] |
||||||
|
sendCntAndReti: |
||||||
|
mov x3, cnt ;[-16] |
||||||
|
sendX3AndReti: |
||||||
|
ldi YL, 20 ;[-15] x3==r20 address is 20 |
||||||
|
ldi YH, 0 ;[-14] |
||||||
|
ldi cnt, 2 ;[-13] |
||||||
|
; rjmp usbSendAndReti fallthrough |
||||||
|
|
||||||
|
;usbSend: |
||||||
|
;pointer to data in 'Y' |
||||||
|
;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] |
||||||
|
;uses: x1...x4, btcnt, shift, cnt, Y |
||||||
|
;Numbers in brackets are time since first bit of sync pattern is sent |
||||||
|
;We don't match the transfer rate exactly (don't insert leap cycles every third |
||||||
|
;byte) because the spec demands only 1.5% precision anyway. |
||||||
|
usbSendAndReti: ; 12 cycles until SOP |
||||||
|
in x2, USBDDR ;[-12] |
||||||
|
ori x2, USBMASK ;[-11] |
||||||
|
sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) |
||||||
|
in x1, USBOUT ;[-8] port mirror for tx loop |
||||||
|
out USBDDR, x2 ;[-7] <- acquire bus |
||||||
|
; need not init x2 (bitstuff history) because sync starts with 0 |
||||||
|
ldi x4, USBMASK ;[-6] exor mask |
||||||
|
ldi shift, 0x80 ;[-5] sync byte is first byte sent |
||||||
|
txByteLoop: |
||||||
|
ldi bitcnt, 0x35 ;[-4] [6] binary 0011 0101 |
||||||
|
txBitLoop: |
||||||
|
sbrs shift, 0 ;[-3] [7] |
||||||
|
eor x1, x4 ;[-2] [8] |
||||||
|
out USBOUT, x1 ;[-1] [9] <-- out N |
||||||
|
ror shift ;[0] [10] |
||||||
|
ror x2 ;[1] |
||||||
|
didStuffN: |
||||||
|
cpi x2, 0xfc ;[2] |
||||||
|
brcc bitstuffN ;[3] |
||||||
|
lsr bitcnt ;[4] |
||||||
|
brcc txBitLoop ;[5] |
||||||
|
brne txBitLoop ;[6] |
||||||
|
|
||||||
|
sbrs shift, 0 ;[7] |
||||||
|
eor x1, x4 ;[8] |
||||||
|
didStuff6: |
||||||
|
out USBOUT, x1 ;[-1] [9] <-- out 6 |
||||||
|
ror shift ;[0] [10] |
||||||
|
ror x2 ;[1] |
||||||
|
cpi x2, 0xfc ;[2] |
||||||
|
brcc bitstuff6 ;[3] |
||||||
|
ror shift ;[4] |
||||||
|
didStuff7: |
||||||
|
ror x2 ;[5] |
||||||
|
sbrs x2, 7 ;[6] |
||||||
|
eor x1, x4 ;[7] |
||||||
|
nop ;[8] |
||||||
|
cpi x2, 0xfc ;[9] |
||||||
|
out USBOUT, x1 ;[-1][10] <-- out 7 |
||||||
|
brcc bitstuff7 ;[0] [11] |
||||||
|
ld shift, y+ ;[1] |
||||||
|
dec cnt ;[3] |
||||||
|
brne txByteLoop ;[4] |
||||||
|
;make SE0: |
||||||
|
cbr x1, USBMASK ;[5] prepare SE0 [spec says EOP may be 21 to 25 cycles] |
||||||
|
lds x2, usbNewDeviceAddr;[6] |
||||||
|
lsl x2 ;[8] we compare with left shifted address |
||||||
|
subi YL, 20 + 2 ;[9] Only assign address on data packets, not ACK/NAK in x3 |
||||||
|
sbci YH, 0 ;[10] |
||||||
|
out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle |
||||||
|
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: |
||||||
|
;set address only after data packet was sent, not after handshake |
||||||
|
breq skipAddrAssign ;[0] |
||||||
|
sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer |
||||||
|
skipAddrAssign: |
||||||
|
;end of usbDeviceAddress transfer |
||||||
|
ldi x2, 1<<USB_INTR_PENDING_BIT;[2] int0 occurred during TX -- clear pending flag |
||||||
|
USB_STORE_PENDING(x2) ;[3] |
||||||
|
ori x1, USBIDLE ;[4] |
||||||
|
in x2, USBDDR ;[5] |
||||||
|
cbr x2, USBMASK ;[6] set both pins to input |
||||||
|
mov x3, x1 ;[7] |
||||||
|
cbr x3, USBMASK ;[8] configure no pullup on both pins |
||||||
|
ldi x4, 4 ;[9] |
||||||
|
se0Delay: |
||||||
|
dec x4 ;[10] [13] [16] [19] |
||||||
|
brne se0Delay ;[11] [14] [17] [20] |
||||||
|
out USBOUT, x1 ;[21] <-- out J (idle) -- end of SE0 (EOP signal) |
||||||
|
out USBDDR, x2 ;[22] <-- release bus now |
||||||
|
out USBOUT, x3 ;[23] <-- ensure no pull-up resistors are active |
||||||
|
rjmp doReturn |
||||||
@ -0,0 +1,453 @@ |
|||||||
|
/* Name: usbdrvasm165.inc |
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2007-04-22 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||||
|
* appropriate implementation! |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
General Description: |
||||||
|
This file is the 16.5 MHz version of the USB driver. It is intended for the |
||||||
|
ATTiny45 and similar controllers running on 16.5 MHz internal RC oscillator. |
||||||
|
This version contains a phase locked loop in the receiver routine to cope with |
||||||
|
slight clock rate deviations of up to +/- 1%. |
||||||
|
|
||||||
|
See usbdrv.h for a description of the entire driver. |
||||||
|
|
||||||
|
Since almost all of this code is timing critical, don't change unless you |
||||||
|
really know what you are doing! Many parts require not only a maximum number |
||||||
|
of CPU cycles, but even an exact number of cycles! |
||||||
|
*/ |
||||||
|
|
||||||
|
;Software-receiver engine. Strict timing! Don't change unless you can preserve timing! |
||||||
|
;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled |
||||||
|
;max allowable interrupt latency: 59 cycles -> max 52 cycles interrupt disable |
||||||
|
;max stack usage: [ret(2), r0, SREG, YL, YH, shift, x1, x2, x3, x4, cnt] = 12 bytes |
||||||
|
;nominal frequency: 16.5 MHz -> 11 cycles per bit |
||||||
|
; 16.3125 MHz < F_CPU < 16.6875 MHz (+/- 1.1%) |
||||||
|
; Numbers in brackets are clocks counted from center of last sync bit |
||||||
|
; when instruction starts |
||||||
|
|
||||||
|
|
||||||
|
USB_INTR_VECTOR: |
||||||
|
;order of registers pushed: YL, SREG [sofError], r0, YH, shift, x1, x2, x3, x4, cnt |
||||||
|
push YL ;[-23] push only what is necessary to sync with edge ASAP |
||||||
|
in YL, SREG ;[-21] |
||||||
|
push YL ;[-20] |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Synchronize with sync pattern: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] |
||||||
|
;sync up with J to K edge during sync pattern -- use fastest possible loops |
||||||
|
;The first part waits at most 1 bit long since we must be in sync pattern. |
||||||
|
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to |
||||||
|
;waitForJ, ensure that this prerequisite is met. |
||||||
|
waitForJ: |
||||||
|
inc YL |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
brne waitForJ ; just make sure we have ANY timeout |
||||||
|
waitForK: |
||||||
|
;The following code results in a sampling window of < 1/4 bit which meets the spec. |
||||||
|
sbis USBIN, USBMINUS ;[-15] |
||||||
|
rjmp foundK ;[-14] |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
#if USB_COUNT_SOF |
||||||
|
lds YL, usbSofCount |
||||||
|
inc YL |
||||||
|
sts usbSofCount, YL |
||||||
|
#endif /* USB_COUNT_SOF */ |
||||||
|
#ifdef USB_SOF_HOOK |
||||||
|
USB_SOF_HOOK |
||||||
|
#endif |
||||||
|
rjmp sofError |
||||||
|
foundK: ;[-12] |
||||||
|
;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] |
||||||
|
;we have 1 bit time for setup purposes, then sample again. Numbers in brackets |
||||||
|
;are cycles from center of first sync (double K) bit after the instruction |
||||||
|
push r0 ;[-12] |
||||||
|
; [---] ;[-11] |
||||||
|
push YH ;[-10] |
||||||
|
; [---] ;[-9] |
||||||
|
lds YL, usbInputBufOffset;[-8] |
||||||
|
; [---] ;[-7] |
||||||
|
clr YH ;[-6] |
||||||
|
subi YL, lo8(-(usbRxBuf));[-5] [rx loop init] |
||||||
|
sbci YH, hi8(-(usbRxBuf));[-4] [rx loop init] |
||||||
|
mov r0, x2 ;[-3] [rx loop init] |
||||||
|
sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) |
||||||
|
rjmp haveTwoBitsK ;[-1] |
||||||
|
pop YH ;[0] undo the pushes from before |
||||||
|
pop r0 ;[2] |
||||||
|
rjmp waitForK ;[4] this was not the end of sync, retry |
||||||
|
; The entire loop from waitForK until rjmp waitForK above must not exceed two |
||||||
|
; bit times (= 22 cycles). |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; push more registers and initialize values while we sample the first bits: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
haveTwoBitsK: ;[1] |
||||||
|
push shift ;[1] |
||||||
|
push x1 ;[3] |
||||||
|
push x2 ;[5] |
||||||
|
push x3 ;[7] |
||||||
|
ldi shift, 0xff ;[9] [rx loop init] |
||||||
|
ori x3, 0xff ;[10] [rx loop init] == ser x3, clear zero flag |
||||||
|
|
||||||
|
in x1, USBIN ;[11] <-- sample bit 0 |
||||||
|
bst x1, USBMINUS ;[12] |
||||||
|
bld shift, 0 ;[13] |
||||||
|
push x4 ;[14] == phase |
||||||
|
; [---] ;[15] |
||||||
|
push cnt ;[16] |
||||||
|
; [---] ;[17] |
||||||
|
ldi phase, 0 ;[18] [rx loop init] |
||||||
|
ldi cnt, USB_BUFSIZE;[19] [rx loop init] |
||||||
|
rjmp rxbit1 ;[20] |
||||||
|
; [---] ;[21] |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Receiver loop (numbers in brackets are cycles within byte after instr) |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
/* |
||||||
|
byte oriented operations done during loop: |
||||||
|
bit 0: store data |
||||||
|
bit 1: SE0 check |
||||||
|
bit 2: overflow check |
||||||
|
bit 3: catch up |
||||||
|
bit 4: rjmp to achieve conditional jump range |
||||||
|
bit 5: PLL |
||||||
|
bit 6: catch up |
||||||
|
bit 7: jump, fixup bitstuff |
||||||
|
; 87 [+ 2] cycles |
||||||
|
------------------------------------------------------------------ |
||||||
|
*/ |
||||||
|
continueWithBit5: |
||||||
|
in x2, USBIN ;[055] <-- bit 5 |
||||||
|
eor r0, x2 ;[056] |
||||||
|
or phase, r0 ;[057] |
||||||
|
sbrc phase, USBMINUS ;[058] |
||||||
|
lpm ;[059] optional nop3; modifies r0 |
||||||
|
in phase, USBIN ;[060] <-- phase |
||||||
|
eor x1, x2 ;[061] |
||||||
|
bst x1, USBMINUS ;[062] |
||||||
|
bld shift, 5 ;[063] |
||||||
|
andi shift, 0x3f ;[064] |
||||||
|
in x1, USBIN ;[065] <-- bit 6 |
||||||
|
breq unstuff5 ;[066] *** unstuff escape |
||||||
|
eor phase, x1 ;[067] |
||||||
|
eor x2, x1 ;[068] |
||||||
|
bst x2, USBMINUS ;[069] |
||||||
|
bld shift, 6 ;[070] |
||||||
|
didUnstuff6: ;[ ] |
||||||
|
in r0, USBIN ;[071] <-- phase |
||||||
|
cpi shift, 0x02 ;[072] |
||||||
|
brlo unstuff6 ;[073] *** unstuff escape |
||||||
|
didUnstuff5: ;[ ] |
||||||
|
nop2 ;[074] |
||||||
|
; [---] ;[075] |
||||||
|
in x2, USBIN ;[076] <-- bit 7 |
||||||
|
eor x1, x2 ;[077] |
||||||
|
bst x1, USBMINUS ;[078] |
||||||
|
bld shift, 7 ;[079] |
||||||
|
didUnstuff7: ;[ ] |
||||||
|
eor r0, x2 ;[080] |
||||||
|
or phase, r0 ;[081] |
||||||
|
in r0, USBIN ;[082] <-- phase |
||||||
|
cpi shift, 0x04 ;[083] |
||||||
|
brsh rxLoop ;[084] |
||||||
|
; [---] ;[085] |
||||||
|
unstuff7: ;[ ] |
||||||
|
andi x3, ~0x80 ;[085] |
||||||
|
ori shift, 0x80 ;[086] |
||||||
|
in x2, USBIN ;[087] <-- sample stuffed bit 7 |
||||||
|
nop ;[088] |
||||||
|
rjmp didUnstuff7 ;[089] |
||||||
|
; [---] ;[090] |
||||||
|
;[080] |
||||||
|
|
||||||
|
unstuff5: ;[067] |
||||||
|
eor phase, x1 ;[068] |
||||||
|
andi x3, ~0x20 ;[069] |
||||||
|
ori shift, 0x20 ;[070] |
||||||
|
in r0, USBIN ;[071] <-- phase |
||||||
|
mov x2, x1 ;[072] |
||||||
|
nop ;[073] |
||||||
|
nop2 ;[074] |
||||||
|
; [---] ;[075] |
||||||
|
in x1, USBIN ;[076] <-- bit 6 |
||||||
|
eor r0, x1 ;[077] |
||||||
|
or phase, r0 ;[078] |
||||||
|
eor x2, x1 ;[079] |
||||||
|
bst x2, USBMINUS ;[080] |
||||||
|
bld shift, 6 ;[081] no need to check bitstuffing, we just had one |
||||||
|
in r0, USBIN ;[082] <-- phase |
||||||
|
rjmp didUnstuff5 ;[083] |
||||||
|
; [---] ;[084] |
||||||
|
;[074] |
||||||
|
|
||||||
|
unstuff6: ;[074] |
||||||
|
andi x3, ~0x40 ;[075] |
||||||
|
in x1, USBIN ;[076] <-- bit 6 again |
||||||
|
ori shift, 0x40 ;[077] |
||||||
|
nop2 ;[078] |
||||||
|
; [---] ;[079] |
||||||
|
rjmp didUnstuff6 ;[080] |
||||||
|
; [---] ;[081] |
||||||
|
;[071] |
||||||
|
|
||||||
|
unstuff0: ;[013] |
||||||
|
eor r0, x2 ;[014] |
||||||
|
or phase, r0 ;[015] |
||||||
|
andi x2, USBMASK ;[016] check for SE0 |
||||||
|
in r0, USBIN ;[017] <-- phase |
||||||
|
breq didUnstuff0 ;[018] direct jump to se0 would be too long |
||||||
|
andi x3, ~0x01 ;[019] |
||||||
|
ori shift, 0x01 ;[020] |
||||||
|
mov x1, x2 ;[021] mov existing sample |
||||||
|
in x2, USBIN ;[022] <-- bit 1 again |
||||||
|
rjmp didUnstuff0 ;[023] |
||||||
|
; [---] ;[024] |
||||||
|
;[014] |
||||||
|
|
||||||
|
unstuff1: ;[024] |
||||||
|
eor r0, x1 ;[025] |
||||||
|
or phase, r0 ;[026] |
||||||
|
andi x3, ~0x02 ;[027] |
||||||
|
in r0, USBIN ;[028] <-- phase |
||||||
|
ori shift, 0x02 ;[029] |
||||||
|
mov x2, x1 ;[030] |
||||||
|
rjmp didUnstuff1 ;[031] |
||||||
|
; [---] ;[032] |
||||||
|
;[022] |
||||||
|
|
||||||
|
unstuff2: ;[035] |
||||||
|
eor r0, x2 ;[036] |
||||||
|
or phase, r0 ;[037] |
||||||
|
andi x3, ~0x04 ;[038] |
||||||
|
in r0, USBIN ;[039] <-- phase |
||||||
|
ori shift, 0x04 ;[040] |
||||||
|
mov x1, x2 ;[041] |
||||||
|
rjmp didUnstuff2 ;[042] |
||||||
|
; [---] ;[043] |
||||||
|
;[033] |
||||||
|
|
||||||
|
unstuff3: ;[043] |
||||||
|
in x2, USBIN ;[044] <-- bit 3 again |
||||||
|
eor r0, x2 ;[045] |
||||||
|
or phase, r0 ;[046] |
||||||
|
andi x3, ~0x08 ;[047] |
||||||
|
ori shift, 0x08 ;[048] |
||||||
|
nop ;[049] |
||||||
|
in r0, USBIN ;[050] <-- phase |
||||||
|
rjmp didUnstuff3 ;[051] |
||||||
|
; [---] ;[052] |
||||||
|
;[042] |
||||||
|
|
||||||
|
unstuff4: ;[053] |
||||||
|
andi x3, ~0x10 ;[054] |
||||||
|
in x1, USBIN ;[055] <-- bit 4 again |
||||||
|
ori shift, 0x10 ;[056] |
||||||
|
rjmp didUnstuff4 ;[057] |
||||||
|
; [---] ;[058] |
||||||
|
;[048] |
||||||
|
|
||||||
|
rxLoop: ;[085] |
||||||
|
eor x3, shift ;[086] reconstruct: x3 is 0 at bit locations we changed, 1 at others |
||||||
|
in x1, USBIN ;[000] <-- bit 0 |
||||||
|
st y+, x3 ;[001] |
||||||
|
; [---] ;[002] |
||||||
|
eor r0, x1 ;[003] |
||||||
|
or phase, r0 ;[004] |
||||||
|
eor x2, x1 ;[005] |
||||||
|
in r0, USBIN ;[006] <-- phase |
||||||
|
ser x3 ;[007] |
||||||
|
bst x2, USBMINUS ;[008] |
||||||
|
bld shift, 0 ;[009] |
||||||
|
andi shift, 0xf9 ;[010] |
||||||
|
rxbit1: ;[ ] |
||||||
|
in x2, USBIN ;[011] <-- bit 1 |
||||||
|
breq unstuff0 ;[012] *** unstuff escape |
||||||
|
andi x2, USBMASK ;[013] SE0 check for bit 1 |
||||||
|
didUnstuff0: ;[ ] Z only set if we detected SE0 in bitstuff |
||||||
|
breq se0 ;[014] |
||||||
|
eor r0, x2 ;[015] |
||||||
|
or phase, r0 ;[016] |
||||||
|
in r0, USBIN ;[017] <-- phase |
||||||
|
eor x1, x2 ;[018] |
||||||
|
bst x1, USBMINUS ;[019] |
||||||
|
bld shift, 1 ;[020] |
||||||
|
andi shift, 0xf3 ;[021] |
||||||
|
didUnstuff1: ;[ ] |
||||||
|
in x1, USBIN ;[022] <-- bit 2 |
||||||
|
breq unstuff1 ;[023] *** unstuff escape |
||||||
|
eor r0, x1 ;[024] |
||||||
|
or phase, r0 ;[025] |
||||||
|
subi cnt, 1 ;[026] overflow check |
||||||
|
brcs overflow ;[027] |
||||||
|
in r0, USBIN ;[028] <-- phase |
||||||
|
eor x2, x1 ;[029] |
||||||
|
bst x2, USBMINUS ;[030] |
||||||
|
bld shift, 2 ;[031] |
||||||
|
andi shift, 0xe7 ;[032] |
||||||
|
didUnstuff2: ;[ ] |
||||||
|
in x2, USBIN ;[033] <-- bit 3 |
||||||
|
breq unstuff2 ;[034] *** unstuff escape |
||||||
|
eor r0, x2 ;[035] |
||||||
|
or phase, r0 ;[036] |
||||||
|
eor x1, x2 ;[037] |
||||||
|
bst x1, USBMINUS ;[038] |
||||||
|
in r0, USBIN ;[039] <-- phase |
||||||
|
bld shift, 3 ;[040] |
||||||
|
andi shift, 0xcf ;[041] |
||||||
|
didUnstuff3: ;[ ] |
||||||
|
breq unstuff3 ;[042] *** unstuff escape |
||||||
|
nop ;[043] |
||||||
|
in x1, USBIN ;[044] <-- bit 4 |
||||||
|
eor x2, x1 ;[045] |
||||||
|
bst x2, USBMINUS ;[046] |
||||||
|
bld shift, 4 ;[047] |
||||||
|
didUnstuff4: ;[ ] |
||||||
|
eor r0, x1 ;[048] |
||||||
|
or phase, r0 ;[049] |
||||||
|
in r0, USBIN ;[050] <-- phase |
||||||
|
andi shift, 0x9f ;[051] |
||||||
|
breq unstuff4 ;[052] *** unstuff escape |
||||||
|
rjmp continueWithBit5;[053] |
||||||
|
; [---] ;[054] |
||||||
|
|
||||||
|
macro POP_STANDARD ; 16 cycles |
||||||
|
pop cnt |
||||||
|
pop x4 |
||||||
|
pop x3 |
||||||
|
pop x2 |
||||||
|
pop x1 |
||||||
|
pop shift |
||||||
|
pop YH |
||||||
|
pop r0 |
||||||
|
endm |
||||||
|
macro POP_RETI ; 5 cycles |
||||||
|
pop YL |
||||||
|
out SREG, YL |
||||||
|
pop YL |
||||||
|
endm |
||||||
|
|
||||||
|
#include "asmcommon.inc" |
||||||
|
|
||||||
|
|
||||||
|
; USB spec says: |
||||||
|
; idle = J |
||||||
|
; J = (D+ = 0), (D- = 1) |
||||||
|
; K = (D+ = 1), (D- = 0) |
||||||
|
; Spec allows 7.5 bit times from EOP to SOP for replies |
||||||
|
|
||||||
|
bitstuff7: |
||||||
|
eor x1, x4 ;[4] |
||||||
|
ldi x2, 0 ;[5] |
||||||
|
nop2 ;[6] C is zero (brcc) |
||||||
|
rjmp didStuff7 ;[8] |
||||||
|
|
||||||
|
bitstuffN: |
||||||
|
eor x1, x4 ;[5] |
||||||
|
ldi x2, 0 ;[6] |
||||||
|
lpm ;[7] 3 cycle NOP, modifies r0 |
||||||
|
out USBOUT, x1 ;[10] <-- out |
||||||
|
rjmp didStuffN ;[0] |
||||||
|
|
||||||
|
#define bitStatus x3 |
||||||
|
|
||||||
|
sendNakAndReti: |
||||||
|
ldi cnt, USBPID_NAK ;[-19] |
||||||
|
rjmp sendCntAndReti ;[-18] |
||||||
|
sendAckAndReti: |
||||||
|
ldi cnt, USBPID_ACK ;[-17] |
||||||
|
sendCntAndReti: |
||||||
|
mov r0, cnt ;[-16] |
||||||
|
ldi YL, 0 ;[-15] R0 address is 0 |
||||||
|
ldi YH, 0 ;[-14] |
||||||
|
ldi cnt, 2 ;[-13] |
||||||
|
; rjmp usbSendAndReti fallthrough |
||||||
|
|
||||||
|
;usbSend: |
||||||
|
;pointer to data in 'Y' |
||||||
|
;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] |
||||||
|
;uses: x1...x4, shift, cnt, Y |
||||||
|
;Numbers in brackets are time since first bit of sync pattern is sent |
||||||
|
usbSendAndReti: ; 12 cycles until SOP |
||||||
|
in x2, USBDDR ;[-12] |
||||||
|
ori x2, USBMASK ;[-11] |
||||||
|
sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) |
||||||
|
in x1, USBOUT ;[-8] port mirror for tx loop |
||||||
|
out USBDDR, x2 ;[-7] <- acquire bus |
||||||
|
; need not init x2 (bitstuff history) because sync starts with 0 |
||||||
|
ldi x4, USBMASK ;[-6] exor mask |
||||||
|
ldi shift, 0x80 ;[-5] sync byte is first byte sent |
||||||
|
ldi bitStatus, 0xff ;[-4] init bit loop counter, works for up to 12 bytes |
||||||
|
byteloop: |
||||||
|
bitloop: |
||||||
|
sbrs shift, 0 ;[8] [-3] |
||||||
|
eor x1, x4 ;[9] [-2] |
||||||
|
out USBOUT, x1 ;[10] [-1] <-- out |
||||||
|
ror shift ;[0] |
||||||
|
ror x2 ;[1] |
||||||
|
didStuffN: |
||||||
|
cpi x2, 0xfc ;[2] |
||||||
|
brcc bitstuffN ;[3] |
||||||
|
nop ;[4] |
||||||
|
subi bitStatus, 37 ;[5] 256 / 7 ~=~ 37 |
||||||
|
brcc bitloop ;[6] when we leave the loop, bitStatus has almost the initial value |
||||||
|
sbrs shift, 0 ;[7] |
||||||
|
eor x1, x4 ;[8] |
||||||
|
ror shift ;[9] |
||||||
|
didStuff7: |
||||||
|
out USBOUT, x1 ;[10] <-- out |
||||||
|
ror x2 ;[0] |
||||||
|
cpi x2, 0xfc ;[1] |
||||||
|
brcc bitstuff7 ;[2] |
||||||
|
ld shift, y+ ;[3] |
||||||
|
dec cnt ;[5] |
||||||
|
brne byteloop ;[6] |
||||||
|
;make SE0: |
||||||
|
cbr x1, USBMASK ;[7] prepare SE0 [spec says EOP may be 21 to 25 cycles] |
||||||
|
lds x2, usbNewDeviceAddr;[8] |
||||||
|
lsl x2 ;[10] we compare with left shifted address |
||||||
|
out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle |
||||||
|
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: |
||||||
|
;set address only after data packet was sent, not after handshake |
||||||
|
subi YL, 2 ;[0] Only assign address on data packets, not ACK/NAK in r0 |
||||||
|
sbci YH, 0 ;[1] |
||||||
|
breq skipAddrAssign ;[2] |
||||||
|
sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer |
||||||
|
skipAddrAssign: |
||||||
|
;end of usbDeviceAddress transfer |
||||||
|
ldi x2, 1<<USB_INTR_PENDING_BIT;[4] int0 occurred during TX -- clear pending flag |
||||||
|
USB_STORE_PENDING(x2) ;[5] |
||||||
|
ori x1, USBIDLE ;[6] |
||||||
|
in x2, USBDDR ;[7] |
||||||
|
cbr x2, USBMASK ;[8] set both pins to input |
||||||
|
mov x3, x1 ;[9] |
||||||
|
cbr x3, USBMASK ;[10] configure no pullup on both pins |
||||||
|
ldi x4, 4 ;[11] |
||||||
|
se0Delay: |
||||||
|
dec x4 ;[12] [15] [18] [21] |
||||||
|
brne se0Delay ;[13] [16] [19] [22] |
||||||
|
out USBOUT, x1 ;[23] <-- out J (idle) -- end of SE0 (EOP signal) |
||||||
|
out USBDDR, x2 ;[24] <-- release bus now |
||||||
|
out USBOUT, x3 ;[25] <-- ensure no pull-up resistors are active |
||||||
|
rjmp doReturn |
||||||
|
|
||||||
@ -0,0 +1,707 @@ |
|||||||
|
/* Name: usbdrvasm18.inc |
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Lukas Schrittwieser (based on 20 MHz usbdrvasm20.inc by Jeroen Benschop) |
||||||
|
* Creation Date: 2009-01-20 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2008 by Lukas Schrittwieser and OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||||
|
* appropriate implementation! |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
General Description: |
||||||
|
This file is the 18 MHz version of the asssembler part of the USB driver. It |
||||||
|
requires a 18 MHz crystal (not a ceramic resonator and not a calibrated RC |
||||||
|
oscillator). |
||||||
|
|
||||||
|
See usbdrv.h for a description of the entire driver. |
||||||
|
|
||||||
|
Since almost all of this code is timing critical, don't change unless you |
||||||
|
really know what you are doing! Many parts require not only a maximum number |
||||||
|
of CPU cycles, but even an exact number of cycles! |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
;max stack usage: [ret(2), YL, SREG, YH, [sofError], bitcnt(x5), shift, x1, x2, x3, x4, cnt, ZL, ZH] = 14 bytes |
||||||
|
;nominal frequency: 18 MHz -> 12 cycles per bit |
||||||
|
; Numbers in brackets are clocks counted from center of last sync bit |
||||||
|
; when instruction starts |
||||||
|
;register use in receive loop to receive the data bytes: |
||||||
|
; shift assembles the byte currently being received |
||||||
|
; x1 holds the D+ and D- line state |
||||||
|
; x2 holds the previous line state |
||||||
|
; cnt holds the number of bytes left in the receive buffer |
||||||
|
; x3 holds the higher crc byte (see algorithm below) |
||||||
|
; x4 is used as temporary register for the crc algorithm |
||||||
|
; x5 is used for unstuffing: when unstuffing the last received bit is inverted in shift (to prevent further |
||||||
|
; unstuffing calls. In the same time the corresponding bit in x5 is cleared to mark the bit as beening iverted |
||||||
|
; zl lower crc value and crc table index |
||||||
|
; zh used for crc table accesses |
||||||
|
|
||||||
|
;-------------------------------------------------------------------------------------------------------------- |
||||||
|
; CRC mods: |
||||||
|
; table driven crc checker, Z points to table in prog space |
||||||
|
; ZL is the lower crc byte, x3 is the higher crc byte |
||||||
|
; x4 is used as temp register to store different results |
||||||
|
; the initialization of the crc register is not 0xFFFF but 0xFE54. This is because during the receipt of the |
||||||
|
; first data byte an virtual zero data byte is added to the crc register, this results in the correct initial |
||||||
|
; value of 0xFFFF at beginning of the second data byte before the first data byte is added to the crc. |
||||||
|
; The magic number 0xFE54 results form the crc table: At tabH[0x54] = 0xFF = crcH (required) and |
||||||
|
; tabL[0x54] = 0x01 -> crcL = 0x01 xor 0xFE = 0xFF |
||||||
|
; bitcnt is renamed to x5 and is used for unstuffing purposes, the unstuffing works like in the 12MHz version |
||||||
|
;-------------------------------------------------------------------------------------------------------------- |
||||||
|
; CRC algorithm: |
||||||
|
; The crc register is formed by x3 (higher byte) and ZL (lower byte). The algorithm uses a 'reversed' form |
||||||
|
; i.e. that it takes the least significant bit first and shifts to the right. So in fact the highest order |
||||||
|
; bit seen from the polynomial devision point of view is the lsb of ZL. (If this sounds strange to you i |
||||||
|
; propose a research on CRC :-) ) |
||||||
|
; Each data byte received is xored to ZL, the lower crc byte. This byte now builds the crc |
||||||
|
; table index. Next the new high byte is loaded from the table and stored in x4 until we have space in x3 |
||||||
|
; (its destination). |
||||||
|
; Afterwards the lower table is loaded from the table and stored in ZL (the old index is overwritten as |
||||||
|
; we don't need it anymore. In fact this is a right shift by 8 bits.) Now the old crc high value is xored |
||||||
|
; to ZL, this is the second shift of the old crc value. Now x4 (the temp reg) is moved to x3 and the crc |
||||||
|
; calculation is done. |
||||||
|
; Prior to the first byte the two CRC register have to be initialized to 0xFFFF (as defined in usb spec) |
||||||
|
; however the crc engine also runs during the receipt of the first byte, therefore x3 and zl are initialized |
||||||
|
; to a magic number which results in a crc value of 0xFFFF after the first complete byte. |
||||||
|
; |
||||||
|
; This algorithm is split into the extra cycles of the different bits: |
||||||
|
; bit7: XOR the received byte to ZL |
||||||
|
; bit5: load the new high byte to x4 |
||||||
|
; bit6: load the lower xor byte from the table, xor zl and x3, store result in zl (=the new crc low value) |
||||||
|
; move x4 (the new high byte) to x3, the crc value is ready |
||||||
|
; |
||||||
|
|
||||||
|
|
||||||
|
macro POP_STANDARD ; 18 cycles |
||||||
|
pop ZH |
||||||
|
pop ZL |
||||||
|
pop cnt |
||||||
|
pop x5 |
||||||
|
pop x3 |
||||||
|
pop x2 |
||||||
|
pop x1 |
||||||
|
pop shift |
||||||
|
pop x4 |
||||||
|
endm |
||||||
|
macro POP_RETI ; 7 cycles |
||||||
|
pop YH |
||||||
|
pop YL |
||||||
|
out SREG, YL |
||||||
|
pop YL |
||||||
|
endm |
||||||
|
|
||||||
|
macro CRC_CLEANUP_AND_CHECK |
||||||
|
; the last byte has already been xored with the lower crc byte, we have to do the table lookup and xor |
||||||
|
; x3 is the higher crc byte, zl the lower one |
||||||
|
ldi ZH, hi8(usbCrcTableHigh);[+1] get the new high byte from the table |
||||||
|
lpm x2, Z ;[+2][+3][+4] |
||||||
|
ldi ZH, hi8(usbCrcTableLow);[+5] get the new low xor byte from the table |
||||||
|
lpm ZL, Z ;[+6][+7][+8] |
||||||
|
eor ZL, x3 ;[+7] xor the old high byte with the value from the table, x2:ZL now holds the crc value |
||||||
|
cpi ZL, 0x01 ;[+8] if the crc is ok we have a fixed remainder value of 0xb001 in x2:ZL (see usb spec) |
||||||
|
brne ignorePacket ;[+9] detected a crc fault -> paket is ignored and retransmitted by the host |
||||||
|
cpi x2, 0xb0 ;[+10] |
||||||
|
brne ignorePacket ;[+11] detected a crc fault -> paket is ignored and retransmitted by the host |
||||||
|
endm |
||||||
|
|
||||||
|
|
||||||
|
USB_INTR_VECTOR: |
||||||
|
;order of registers pushed: YL, SREG, YH, [sofError], x4, shift, x1, x2, x3, x5, cnt, ZL, ZH |
||||||
|
push YL ;[-28] push only what is necessary to sync with edge ASAP |
||||||
|
in YL, SREG ;[-26] |
||||||
|
push YL ;[-25] |
||||||
|
push YH ;[-23] |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Synchronize with sync pattern: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] |
||||||
|
;sync up with J to K edge during sync pattern -- use fastest possible loops |
||||||
|
;The first part waits at most 1 bit long since we must be in sync pattern. |
||||||
|
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to |
||||||
|
;waitForJ, ensure that this prerequisite is met. |
||||||
|
waitForJ: |
||||||
|
inc YL |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
brne waitForJ ; just make sure we have ANY timeout |
||||||
|
waitForK: |
||||||
|
;The following code results in a sampling window of < 1/4 bit which meets the spec. |
||||||
|
sbis USBIN, USBMINUS ;[-17] |
||||||
|
rjmp foundK ;[-16] |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
#if USB_COUNT_SOF |
||||||
|
lds YL, usbSofCount |
||||||
|
inc YL |
||||||
|
sts usbSofCount, YL |
||||||
|
#endif /* USB_COUNT_SOF */ |
||||||
|
#ifdef USB_SOF_HOOK |
||||||
|
USB_SOF_HOOK |
||||||
|
#endif |
||||||
|
rjmp sofError |
||||||
|
foundK: ;[-15] |
||||||
|
;{3, 5} after falling D- edge, average delay: 4 cycles |
||||||
|
;bit0 should be at 30 (2.5 bits) for center sampling. Currently at 4 so 26 cylces till bit 0 sample |
||||||
|
;use 1 bit time for setup purposes, then sample again. Numbers in brackets |
||||||
|
;are cycles from center of first sync (double K) bit after the instruction |
||||||
|
push x4 ;[-14] |
||||||
|
; [---] ;[-13] |
||||||
|
lds YL, usbInputBufOffset;[-12] used to toggle the two usb receive buffers |
||||||
|
; [---] ;[-11] |
||||||
|
clr YH ;[-10] |
||||||
|
subi YL, lo8(-(usbRxBuf));[-9] [rx loop init] |
||||||
|
sbci YH, hi8(-(usbRxBuf));[-8] [rx loop init] |
||||||
|
push shift ;[-7] |
||||||
|
; [---] ;[-6] |
||||||
|
ldi shift, 0x80 ;[-5] the last bit is the end of byte marker for the pid receiver loop |
||||||
|
clc ;[-4] the carry has to be clear for receipt of pid bit 0 |
||||||
|
sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) |
||||||
|
rjmp haveTwoBitsK ;[-2] |
||||||
|
pop shift ;[-1] undo the push from before |
||||||
|
pop x4 ;[1] |
||||||
|
rjmp waitForK ;[3] this was not the end of sync, retry |
||||||
|
; The entire loop from waitForK until rjmp waitForK above must not exceed two |
||||||
|
; bit times (= 24 cycles). |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; push more registers and initialize values while we sample the first bits: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
haveTwoBitsK: |
||||||
|
push x1 ;[0] |
||||||
|
push x2 ;[2] |
||||||
|
push x3 ;[4] crc high byte |
||||||
|
ldi x2, 1<<USBPLUS ;[6] [rx loop init] current line state is K state. D+=="1", D-=="0" |
||||||
|
push x5 ;[7] |
||||||
|
push cnt ;[9] |
||||||
|
ldi cnt, USB_BUFSIZE ;[11] |
||||||
|
|
||||||
|
|
||||||
|
;-------------------------------------------------------------------------------------------------------------- |
||||||
|
; receives the pid byte |
||||||
|
; there is no real unstuffing algorithm implemented here as a stuffing bit is impossible in the pid byte. |
||||||
|
; That's because the last four bits of the byte are the inverted of the first four bits. If we detect a |
||||||
|
; unstuffing condition something went wrong and abort |
||||||
|
; shift has to be initialized to 0x80 |
||||||
|
;-------------------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
; pid bit 0 - used for even more register saving (we need the z pointer) |
||||||
|
in x1, USBIN ;[0] sample line state |
||||||
|
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||||
|
eor x2, x1 ;[2] generate inverted of actual bit |
||||||
|
sbrc x2, USBMINUS ;[3] if the bit is set we received a zero |
||||||
|
sec ;[4] |
||||||
|
ror shift ;[5] we perform no unstuffing check here as this is the first bit |
||||||
|
mov x2, x1 ;[6] |
||||||
|
push ZL ;[7] |
||||||
|
;[8] |
||||||
|
push ZH ;[9] |
||||||
|
;[10] |
||||||
|
ldi x3, 0xFE ;[11] x3 is the high order crc value |
||||||
|
|
||||||
|
|
||||||
|
bitloopPid: |
||||||
|
in x1, USBIN ;[0] sample line state |
||||||
|
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||||
|
breq nse0 ;[2] both lines are low so handle se0 |
||||||
|
eor x2, x1 ;[3] generate inverted of actual bit |
||||||
|
sbrc x2, USBMINUS ;[4] set the carry if we received a zero |
||||||
|
sec ;[5] |
||||||
|
ror shift ;[6] |
||||||
|
ldi ZL, 0x54 ;[7] ZL is the low order crc value |
||||||
|
ser x4 ;[8] the is no bit stuffing check here as the pid bit can't be stuffed. if so |
||||||
|
; some error occured. In this case the paket is discarded later on anyway. |
||||||
|
mov x2, x1 ;[9] prepare for the next cycle |
||||||
|
brcc bitloopPid ;[10] while 0s drop out of shift we get the next bit |
||||||
|
eor x4, shift ;[11] invert all bits in shift and store result in x4 |
||||||
|
|
||||||
|
;-------------------------------------------------------------------------------------------------------------- |
||||||
|
; receives data bytes and calculates the crc |
||||||
|
; the last USBIN state has to be in x2 |
||||||
|
; this is only the first half, due to branch distanc limitations the second half of the loop is near the end |
||||||
|
; of this asm file |
||||||
|
;-------------------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
rxDataStart: |
||||||
|
in x1, USBIN ;[0] sample line state (note: a se0 check is not useful due to bit dribbling) |
||||||
|
ser x5 ;[1] prepare the unstuff marker register |
||||||
|
eor x2, x1 ;[2] generates the inverted of the actual bit |
||||||
|
bst x2, USBMINUS ;[3] copy the bit from x2 |
||||||
|
bld shift, 0 ;[4] and store it in shift |
||||||
|
mov x2, shift ;[5] make a copy of shift for unstuffing check |
||||||
|
andi x2, 0xF9 ;[6] mask the last six bits, if we got six zeros (which are six ones in fact) |
||||||
|
breq unstuff0 ;[7] then Z is set now and we branch to the unstuffing handler |
||||||
|
didunstuff0: |
||||||
|
subi cnt, 1 ;[8] cannot use dec because it doesn't affect the carry flag |
||||||
|
brcs nOverflow ;[9] Too many bytes received. Ignore packet |
||||||
|
st Y+, x4 ;[10] store the last received byte |
||||||
|
;[11] st needs two cycles |
||||||
|
|
||||||
|
; bit1 |
||||||
|
in x2, USBIN ;[0] sample line state |
||||||
|
andi x1, USBMASK ;[1] check for se0 during bit 0 |
||||||
|
breq nse0 ;[2] |
||||||
|
andi x2, USBMASK ;[3] check se0 during bit 1 |
||||||
|
breq nse0 ;[4] |
||||||
|
eor x1, x2 ;[5] |
||||||
|
bst x1, USBMINUS ;[6] |
||||||
|
bld shift, 1 ;[7] |
||||||
|
mov x1, shift ;[8] |
||||||
|
andi x1, 0xF3 ;[9] |
||||||
|
breq unstuff1 ;[10] |
||||||
|
didunstuff1: |
||||||
|
nop ;[11] |
||||||
|
|
||||||
|
; bit2 |
||||||
|
in x1, USBIN ;[0] sample line state |
||||||
|
andi x1, USBMASK ;[1] check for se0 (as there is nothing else to do here |
||||||
|
breq nOverflow ;[2] |
||||||
|
eor x2, x1 ;[3] generates the inverted of the actual bit |
||||||
|
bst x2, USBMINUS ;[4] |
||||||
|
bld shift, 2 ;[5] store the bit |
||||||
|
mov x2, shift ;[6] |
||||||
|
andi x2, 0xE7 ;[7] if we have six zeros here (which means six 1 in the stream) |
||||||
|
breq unstuff2 ;[8] the next bit is a stuffing bit |
||||||
|
didunstuff2: |
||||||
|
nop2 ;[9] |
||||||
|
;[10] |
||||||
|
nop ;[11] |
||||||
|
|
||||||
|
; bit3 |
||||||
|
in x2, USBIN ;[0] sample line state |
||||||
|
andi x2, USBMASK ;[1] check for se0 |
||||||
|
breq nOverflow ;[2] |
||||||
|
eor x1, x2 ;[3] |
||||||
|
bst x1, USBMINUS ;[4] |
||||||
|
bld shift, 3 ;[5] |
||||||
|
mov x1, shift ;[6] |
||||||
|
andi x1, 0xCF ;[7] |
||||||
|
breq unstuff3 ;[8] |
||||||
|
didunstuff3: |
||||||
|
nop ;[9] |
||||||
|
rjmp rxDataBit4 ;[10] |
||||||
|
;[11] |
||||||
|
|
||||||
|
; the avr branch instructions allow an offset of +63 insturction only, so we need this |
||||||
|
; 'local copy' of se0 |
||||||
|
nse0: |
||||||
|
rjmp se0 ;[4] |
||||||
|
;[5] |
||||||
|
; the same same as for se0 is needed for overflow and StuffErr |
||||||
|
nOverflow: |
||||||
|
stuffErr: |
||||||
|
rjmp overflow |
||||||
|
|
||||||
|
|
||||||
|
unstuff0: ;[8] this is the branch delay of breq unstuffX |
||||||
|
andi x1, USBMASK ;[9] do an se0 check here (if the last crc byte ends with 5 one's we might end up here |
||||||
|
breq didunstuff0 ;[10] event tough the message is complete -> jump back and store the byte |
||||||
|
ori shift, 0x01 ;[11] invert the last received bit to prevent furhter unstuffing |
||||||
|
in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||||
|
andi x5, 0xFE ;[1] mark this bit as inverted (will be corrected before storing shift) |
||||||
|
eor x1, x2 ;[2] x1 and x2 have to be different because the stuff bit is always a zero |
||||||
|
andi x1, USBMASK ;[3] mask the interesting bits |
||||||
|
breq stuffErr ;[4] if the stuff bit is a 1-bit something went wrong |
||||||
|
mov x1, x2 ;[5] the next bit expects the last state to be in x1 |
||||||
|
rjmp didunstuff0 ;[6] |
||||||
|
;[7] jump delay of rjmp didunstuffX |
||||||
|
|
||||||
|
unstuff1: ;[11] this is the jump delay of breq unstuffX |
||||||
|
in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||||
|
ori shift, 0x02 ;[1] invert the last received bit to prevent furhter unstuffing |
||||||
|
andi x5, 0xFD ;[2] mark this bit as inverted (will be corrected before storing shift) |
||||||
|
eor x2, x1 ;[3] x1 and x2 have to be different because the stuff bit is always a zero |
||||||
|
andi x2, USBMASK ;[4] mask the interesting bits |
||||||
|
breq stuffErr ;[5] if the stuff bit is a 1-bit something went wrong |
||||||
|
mov x2, x1 ;[6] the next bit expects the last state to be in x2 |
||||||
|
nop2 ;[7] |
||||||
|
;[8] |
||||||
|
rjmp didunstuff1 ;[9] |
||||||
|
;[10] jump delay of rjmp didunstuffX |
||||||
|
|
||||||
|
unstuff2: ;[9] this is the jump delay of breq unstuffX |
||||||
|
ori shift, 0x04 ;[10] invert the last received bit to prevent furhter unstuffing |
||||||
|
andi x5, 0xFB ;[11] mark this bit as inverted (will be corrected before storing shift) |
||||||
|
in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||||
|
eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero |
||||||
|
andi x1, USBMASK ;[2] mask the interesting bits |
||||||
|
breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong |
||||||
|
mov x1, x2 ;[4] the next bit expects the last state to be in x1 |
||||||
|
nop2 ;[5] |
||||||
|
;[6] |
||||||
|
rjmp didunstuff2 ;[7] |
||||||
|
;[8] jump delay of rjmp didunstuffX |
||||||
|
|
||||||
|
unstuff3: ;[9] this is the jump delay of breq unstuffX |
||||||
|
ori shift, 0x08 ;[10] invert the last received bit to prevent furhter unstuffing |
||||||
|
andi x5, 0xF7 ;[11] mark this bit as inverted (will be corrected before storing shift) |
||||||
|
in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||||
|
eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero |
||||||
|
andi x2, USBMASK ;[2] mask the interesting bits |
||||||
|
breq stuffErr ;[3] if the stuff bit is a 1-bit something went wrong |
||||||
|
mov x2, x1 ;[4] the next bit expects the last state to be in x2 |
||||||
|
nop2 ;[5] |
||||||
|
;[6] |
||||||
|
rjmp didunstuff3 ;[7] |
||||||
|
;[8] jump delay of rjmp didunstuffX |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; the include has to be here due to branch distance restirctions |
||||||
|
#define __USE_CRC__ |
||||||
|
#include "asmcommon.inc" |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; USB spec says: |
||||||
|
; idle = J |
||||||
|
; J = (D+ = 0), (D- = 1) |
||||||
|
; K = (D+ = 1), (D- = 0) |
||||||
|
; Spec allows 7.5 bit times from EOP to SOP for replies |
||||||
|
; 7.5 bit times is 90 cycles. ...there is plenty of time |
||||||
|
|
||||||
|
|
||||||
|
sendNakAndReti: |
||||||
|
ldi x3, USBPID_NAK ;[-18] |
||||||
|
rjmp sendX3AndReti ;[-17] |
||||||
|
sendAckAndReti: |
||||||
|
ldi cnt, USBPID_ACK ;[-17] |
||||||
|
sendCntAndReti: |
||||||
|
mov x3, cnt ;[-16] |
||||||
|
sendX3AndReti: |
||||||
|
ldi YL, 20 ;[-15] x3==r20 address is 20 |
||||||
|
ldi YH, 0 ;[-14] |
||||||
|
ldi cnt, 2 ;[-13] |
||||||
|
; rjmp usbSendAndReti fallthrough |
||||||
|
|
||||||
|
;usbSend: |
||||||
|
;pointer to data in 'Y' |
||||||
|
;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] |
||||||
|
;uses: x1...x4, btcnt, shift, cnt, Y |
||||||
|
;Numbers in brackets are time since first bit of sync pattern is sent |
||||||
|
|
||||||
|
usbSendAndReti: ; 12 cycles until SOP |
||||||
|
in x2, USBDDR ;[-12] |
||||||
|
ori x2, USBMASK ;[-11] |
||||||
|
sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) |
||||||
|
in x1, USBOUT ;[-8] port mirror for tx loop |
||||||
|
out USBDDR, x2 ;[-6] <- acquire bus |
||||||
|
ldi x2, 0 ;[-6] init x2 (bitstuff history) because sync starts with 0 |
||||||
|
ldi x4, USBMASK ;[-5] exor mask |
||||||
|
ldi shift, 0x80 ;[-4] sync byte is first byte sent |
||||||
|
txByteLoop: |
||||||
|
ldi bitcnt, 0x40 ;[-3]=[9] binary 01000000 |
||||||
|
txBitLoop: ; the loop sends the first 7 bits of the byte |
||||||
|
sbrs shift, 0 ;[-2]=[10] if we have to send a 1 don't change the line state |
||||||
|
eor x1, x4 ;[-1]=[11] |
||||||
|
out USBOUT, x1 ;[0] |
||||||
|
ror shift ;[1] |
||||||
|
ror x2 ;[2] transfers the last sent bit to the stuffing history |
||||||
|
didStuffN: |
||||||
|
nop ;[3] |
||||||
|
nop ;[4] |
||||||
|
cpi x2, 0xfc ;[5] if we sent six consecutive ones |
||||||
|
brcc bitstuffN ;[6] |
||||||
|
lsr bitcnt ;[7] |
||||||
|
brne txBitLoop ;[8] restart the loop while the 1 is still in the bitcount |
||||||
|
|
||||||
|
; transmit bit 7 |
||||||
|
sbrs shift, 0 ;[9] |
||||||
|
eor x1, x4 ;[10] |
||||||
|
didStuff7: |
||||||
|
ror shift ;[11] |
||||||
|
out USBOUT, x1 ;[0] transfer bit 7 to the pins |
||||||
|
ror x2 ;[1] move the bit into the stuffing history |
||||||
|
cpi x2, 0xfc ;[2] |
||||||
|
brcc bitstuff7 ;[3] |
||||||
|
ld shift, y+ ;[4] get next byte to transmit |
||||||
|
dec cnt ;[5] decrement byte counter |
||||||
|
brne txByteLoop ;[7] if we have more bytes start next one |
||||||
|
;[8] branch delay |
||||||
|
|
||||||
|
;make SE0: |
||||||
|
cbr x1, USBMASK ;[8] prepare SE0 [spec says EOP may be 25 to 30 cycles] |
||||||
|
lds x2, usbNewDeviceAddr;[9] |
||||||
|
lsl x2 ;[11] we compare with left shifted address |
||||||
|
out USBOUT, x1 ;[0] <-- out SE0 -- from now 2 bits = 24 cycles until bus idle |
||||||
|
subi YL, 20 + 2 ;[1] Only assign address on data packets, not ACK/NAK in x3 |
||||||
|
sbci YH, 0 ;[2] |
||||||
|
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: |
||||||
|
;set address only after data packet was sent, not after handshake |
||||||
|
breq skipAddrAssign ;[3] |
||||||
|
sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer |
||||||
|
skipAddrAssign: |
||||||
|
;end of usbDeviceAddress transfer |
||||||
|
ldi x2, 1<<USB_INTR_PENDING_BIT;[5] int0 occurred during TX -- clear pending flag |
||||||
|
USB_STORE_PENDING(x2) ;[6] |
||||||
|
ori x1, USBIDLE ;[7] |
||||||
|
in x2, USBDDR ;[8] |
||||||
|
cbr x2, USBMASK ;[9] set both pins to input |
||||||
|
mov x3, x1 ;[10] |
||||||
|
cbr x3, USBMASK ;[11] configure no pullup on both pins |
||||||
|
ldi x4, 4 ;[12] |
||||||
|
se0Delay: |
||||||
|
dec x4 ;[13] [16] [19] [22] |
||||||
|
brne se0Delay ;[14] [17] [20] [23] |
||||||
|
out USBOUT, x1 ;[24] <-- out J (idle) -- end of SE0 (EOP signal) |
||||||
|
out USBDDR, x2 ;[25] <-- release bus now |
||||||
|
out USBOUT, x3 ;[26] <-- ensure no pull-up resistors are active |
||||||
|
rjmp doReturn |
||||||
|
|
||||||
|
bitstuffN: |
||||||
|
eor x1, x4 ;[8] generate a zero |
||||||
|
ldi x2, 0 ;[9] reset the bit stuffing history |
||||||
|
nop2 ;[10] |
||||||
|
out USBOUT, x1 ;[0] <-- send the stuffing bit |
||||||
|
rjmp didStuffN ;[1] |
||||||
|
|
||||||
|
bitstuff7: |
||||||
|
eor x1, x4 ;[5] |
||||||
|
ldi x2, 0 ;[6] reset bit stuffing history |
||||||
|
clc ;[7] fill a zero into the shift register |
||||||
|
rol shift ;[8] compensate for ror shift at branch destination |
||||||
|
rjmp didStuff7 ;[9] |
||||||
|
;[10] jump delay |
||||||
|
|
||||||
|
;-------------------------------------------------------------------------------------------------------------- |
||||||
|
; receives data bytes and calculates the crc |
||||||
|
; second half of the data byte receiver loop |
||||||
|
; most parts of the crc algorithm are here |
||||||
|
;-------------------------------------------------------------------------------------------------------------- |
||||||
|
|
||||||
|
nOverflow2: |
||||||
|
rjmp overflow |
||||||
|
|
||||||
|
rxDataBit4: |
||||||
|
in x1, USBIN ;[0] sample line state |
||||||
|
andi x1, USBMASK ;[1] check for se0 |
||||||
|
breq nOverflow2 ;[2] |
||||||
|
eor x2, x1 ;[3] |
||||||
|
bst x2, USBMINUS ;[4] |
||||||
|
bld shift, 4 ;[5] |
||||||
|
mov x2, shift ;[6] |
||||||
|
andi x2, 0x9F ;[7] |
||||||
|
breq unstuff4 ;[8] |
||||||
|
didunstuff4: |
||||||
|
nop2 ;[9][10] |
||||||
|
nop ;[11] |
||||||
|
|
||||||
|
; bit5 |
||||||
|
in x2, USBIN ;[0] sample line state |
||||||
|
ldi ZH, hi8(usbCrcTableHigh);[1] use the table for the higher byte |
||||||
|
eor x1, x2 ;[2] |
||||||
|
bst x1, USBMINUS ;[3] |
||||||
|
bld shift, 5 ;[4] |
||||||
|
mov x1, shift ;[5] |
||||||
|
andi x1, 0x3F ;[6] |
||||||
|
breq unstuff5 ;[7] |
||||||
|
didunstuff5: |
||||||
|
lpm x4, Z ;[8] load the higher crc xor-byte and store it for later use |
||||||
|
;[9] lpm needs 3 cycles |
||||||
|
;[10] |
||||||
|
ldi ZH, hi8(usbCrcTableLow);[11] load the lower crc xor byte adress |
||||||
|
|
||||||
|
; bit6 |
||||||
|
in x1, USBIN ;[0] sample line state |
||||||
|
eor x2, x1 ;[1] |
||||||
|
bst x2, USBMINUS ;[2] |
||||||
|
bld shift, 6 ;[3] |
||||||
|
mov x2, shift ;[4] |
||||||
|
andi x2, 0x7E ;[5] |
||||||
|
breq unstuff6 ;[6] |
||||||
|
didunstuff6: |
||||||
|
lpm ZL, Z ;[7] load the lower xor crc byte |
||||||
|
;[8] lpm needs 3 cycles |
||||||
|
;[9] |
||||||
|
eor ZL, x3 ;[10] xor the old high crc byte with the low xor-byte |
||||||
|
mov x3, x4 ;[11] move the new high order crc value from temp to its destination |
||||||
|
|
||||||
|
; bit7 |
||||||
|
in x2, USBIN ;[0] sample line state |
||||||
|
eor x1, x2 ;[1] |
||||||
|
bst x1, USBMINUS ;[2] |
||||||
|
bld shift, 7 ;[3] now shift holds the complete but inverted data byte |
||||||
|
mov x1, shift ;[4] |
||||||
|
andi x1, 0xFC ;[5] |
||||||
|
breq unstuff7 ;[6] |
||||||
|
didunstuff7: |
||||||
|
eor x5, shift ;[7] x5 marks all bits which have not been inverted by the unstuffing subs |
||||||
|
mov x4, x5 ;[8] keep a copy of the data byte it will be stored during next bit0 |
||||||
|
eor ZL, x4 ;[9] feed the actual byte into the crc algorithm |
||||||
|
rjmp rxDataStart ;[10] next byte |
||||||
|
;[11] during the reception of the next byte this one will be fed int the crc algorithm |
||||||
|
|
||||||
|
unstuff4: ;[9] this is the jump delay of rjmp unstuffX |
||||||
|
ori shift, 0x10 ;[10] invert the last received bit to prevent furhter unstuffing |
||||||
|
andi x5, 0xEF ;[11] mark this bit as inverted (will be corrected before storing shift) |
||||||
|
in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||||
|
eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero |
||||||
|
andi x1, USBMASK ;[2] mask the interesting bits |
||||||
|
breq stuffErr2 ;[3] if the stuff bit is a 1-bit something went wrong |
||||||
|
mov x1, x2 ;[4] the next bit expects the last state to be in x1 |
||||||
|
nop2 ;[5] |
||||||
|
;[6] |
||||||
|
rjmp didunstuff4 ;[7] |
||||||
|
;[8] jump delay of rjmp didunstuffX |
||||||
|
|
||||||
|
unstuff5: ;[8] this is the jump delay of rjmp unstuffX |
||||||
|
nop ;[9] |
||||||
|
ori shift, 0x20 ;[10] invert the last received bit to prevent furhter unstuffing |
||||||
|
andi x5, 0xDF ;[11] mark this bit as inverted (will be corrected before storing shift) |
||||||
|
in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||||
|
eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero |
||||||
|
andi x2, USBMASK ;[2] mask the interesting bits |
||||||
|
breq stuffErr2 ;[3] if the stuff bit is a 1-bit something went wrong |
||||||
|
mov x2, x1 ;[4] the next bit expects the last state to be in x2 |
||||||
|
nop ;[5] |
||||||
|
rjmp didunstuff5 ;[6] |
||||||
|
;[7] jump delay of rjmp didunstuffX |
||||||
|
|
||||||
|
unstuff6: ;[7] this is the jump delay of rjmp unstuffX |
||||||
|
nop2 ;[8] |
||||||
|
;[9] |
||||||
|
ori shift, 0x40 ;[10] invert the last received bit to prevent furhter unstuffing |
||||||
|
andi x5, 0xBF ;[11] mark this bit as inverted (will be corrected before storing shift) |
||||||
|
in x2, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||||
|
eor x1, x2 ;[1] x1 and x2 have to be different because the stuff bit is always a zero |
||||||
|
andi x1, USBMASK ;[2] mask the interesting bits |
||||||
|
breq stuffErr2 ;[3] if the stuff bit is a 1-bit something went wrong |
||||||
|
mov x1, x2 ;[4] the next bit expects the last state to be in x1 |
||||||
|
rjmp didunstuff6 ;[5] |
||||||
|
;[6] jump delay of rjmp didunstuffX |
||||||
|
|
||||||
|
unstuff7: ;[7] this is the jump delay of rjmp unstuffX |
||||||
|
nop ;[8] |
||||||
|
nop ;[9] |
||||||
|
ori shift, 0x80 ;[10] invert the last received bit to prevent furhter unstuffing |
||||||
|
andi x5, 0x7F ;[11] mark this bit as inverted (will be corrected before storing shift) |
||||||
|
in x1, USBIN ;[0] we have some free cycles so we could check for bit stuffing errors |
||||||
|
eor x2, x1 ;[1] x1 and x2 have to be different because the stuff bit is always a zero |
||||||
|
andi x2, USBMASK ;[2] mask the interesting bits |
||||||
|
breq stuffErr2 ;[3] if the stuff bit is a 1-bit something went wrong |
||||||
|
mov x2, x1 ;[4] the next bit expects the last state to be in x2 |
||||||
|
rjmp didunstuff7 ;[5] |
||||||
|
;[6] jump delay of rjmp didunstuff7 |
||||||
|
|
||||||
|
; local copy of the stuffErr desitnation for the second half of the receiver loop |
||||||
|
stuffErr2: |
||||||
|
rjmp stuffErr |
||||||
|
|
||||||
|
;-------------------------------------------------------------------------------------------------------------- |
||||||
|
; The crc table follows. It has to be aligned to enable a fast loading of the needed bytes. |
||||||
|
; There are two tables of 256 entries each, the low and the high byte table. |
||||||
|
; Table values were generated with the following C code: |
||||||
|
/* |
||||||
|
#include <stdio.h> |
||||||
|
int main (int argc, char **argv) |
||||||
|
{ |
||||||
|
int i, j; |
||||||
|
for (i=0; i<512; i++){ |
||||||
|
unsigned short crc = i & 0xff; |
||||||
|
for(j=0; j<8; j++) crc = (crc >> 1) ^ ((crc & 1) ? 0xa001 : 0); |
||||||
|
if((i & 7) == 0) printf("\n.byte "); |
||||||
|
printf("0x%02x, ", (i > 0xff ? (crc >> 8) : crc) & 0xff); |
||||||
|
if(i == 255) printf("\n"); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Use the following algorithm to compute CRC values: |
||||||
|
ushort computeCrc(uchar *msg, uchar msgLen) |
||||||
|
{ |
||||||
|
uchar i; |
||||||
|
ushort crc = 0xffff; |
||||||
|
for(i = 0; i < msgLen; i++) |
||||||
|
crc = usbCrcTable16[lo8(crc) ^ msg[i]] ^ hi8(crc); |
||||||
|
return crc; |
||||||
|
} |
||||||
|
*/ |
||||||
|
|
||||||
|
.balign 256 |
||||||
|
usbCrcTableLow: |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41 |
||||||
|
.byte 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 |
||||||
|
|
||||||
|
; .balign 256 |
||||||
|
usbCrcTableHigh: |
||||||
|
.byte 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2 |
||||||
|
.byte 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04 |
||||||
|
.byte 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E |
||||||
|
.byte 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8 |
||||||
|
.byte 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A |
||||||
|
.byte 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC |
||||||
|
.byte 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6 |
||||||
|
.byte 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10 |
||||||
|
.byte 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32 |
||||||
|
.byte 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4 |
||||||
|
.byte 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE |
||||||
|
.byte 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38 |
||||||
|
.byte 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA |
||||||
|
.byte 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C |
||||||
|
.byte 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26 |
||||||
|
.byte 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0 |
||||||
|
.byte 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62 |
||||||
|
.byte 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4 |
||||||
|
.byte 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE |
||||||
|
.byte 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68 |
||||||
|
.byte 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA |
||||||
|
.byte 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C |
||||||
|
.byte 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76 |
||||||
|
.byte 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0 |
||||||
|
.byte 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92 |
||||||
|
.byte 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54 |
||||||
|
.byte 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E |
||||||
|
.byte 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98 |
||||||
|
.byte 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A |
||||||
|
.byte 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C |
||||||
|
.byte 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86 |
||||||
|
.byte 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 |
||||||
|
|
||||||
@ -0,0 +1,360 @@ |
|||||||
|
/* Name: usbdrvasm20.inc |
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Jeroen Benschop |
||||||
|
* Based on usbdrvasm16.inc from Christian Starkjohann |
||||||
|
* Creation Date: 2008-03-05 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2008 by Jeroen Benschop and OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Do not link this file! Link usbdrvasm.S instead, which includes the |
||||||
|
* appropriate implementation! |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
General Description: |
||||||
|
This file is the 20 MHz version of the asssembler part of the USB driver. It |
||||||
|
requires a 20 MHz crystal (not a ceramic resonator and not a calibrated RC |
||||||
|
oscillator). |
||||||
|
|
||||||
|
See usbdrv.h for a description of the entire driver. |
||||||
|
|
||||||
|
Since almost all of this code is timing critical, don't change unless you |
||||||
|
really know what you are doing! Many parts require not only a maximum number |
||||||
|
of CPU cycles, but even an exact number of cycles! |
||||||
|
*/ |
||||||
|
|
||||||
|
#define leap2 x3 |
||||||
|
#ifdef __IAR_SYSTEMS_ASM__ |
||||||
|
#define nextInst $+2 |
||||||
|
#else |
||||||
|
#define nextInst .+0 |
||||||
|
#endif |
||||||
|
|
||||||
|
;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes |
||||||
|
;nominal frequency: 20 MHz -> 13.333333 cycles per bit, 106.666667 cycles per byte |
||||||
|
; Numbers in brackets are clocks counted from center of last sync bit |
||||||
|
; when instruction starts |
||||||
|
;register use in receive loop: |
||||||
|
; shift assembles the byte currently being received |
||||||
|
; x1 holds the D+ and D- line state |
||||||
|
; x2 holds the previous line state |
||||||
|
; x4 (leap) is used to add a leap cycle once every three bytes received |
||||||
|
; X3 (leap2) is used to add a leap cycle once every three stuff bits received |
||||||
|
; bitcnt is used to determine when a stuff bit is due |
||||||
|
; cnt holds the number of bytes left in the receive buffer |
||||||
|
|
||||||
|
USB_INTR_VECTOR: |
||||||
|
;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt |
||||||
|
push YL ;[-28] push only what is necessary to sync with edge ASAP |
||||||
|
in YL, SREG ;[-26] |
||||||
|
push YL ;[-25] |
||||||
|
push YH ;[-23] |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Synchronize with sync pattern: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] |
||||||
|
;sync up with J to K edge during sync pattern -- use fastest possible loops |
||||||
|
;The first part waits at most 1 bit long since we must be in sync pattern. |
||||||
|
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to |
||||||
|
;waitForJ, ensure that this prerequisite is met. |
||||||
|
waitForJ: |
||||||
|
inc YL |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
brne waitForJ ; just make sure we have ANY timeout |
||||||
|
waitForK: |
||||||
|
;The following code results in a sampling window of < 1/4 bit which meets the spec. |
||||||
|
sbis USBIN, USBMINUS ;[-19] |
||||||
|
rjmp foundK ;[-18] |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
sbis USBIN, USBMINUS |
||||||
|
rjmp foundK |
||||||
|
#if USB_COUNT_SOF |
||||||
|
lds YL, usbSofCount |
||||||
|
inc YL |
||||||
|
sts usbSofCount, YL |
||||||
|
#endif /* USB_COUNT_SOF */ |
||||||
|
#ifdef USB_SOF_HOOK |
||||||
|
USB_SOF_HOOK |
||||||
|
#endif |
||||||
|
rjmp sofError |
||||||
|
foundK: ;[-16] |
||||||
|
;{3, 5} after falling D- edge, average delay: 4 cycles |
||||||
|
;bit0 should be at 34 for center sampling. Currently at 4 so 30 cylces till bit 0 sample |
||||||
|
;use 1 bit time for setup purposes, then sample again. Numbers in brackets |
||||||
|
;are cycles from center of first sync (double K) bit after the instruction |
||||||
|
push bitcnt ;[-16] |
||||||
|
; [---] ;[-15] |
||||||
|
lds YL, usbInputBufOffset;[-14] |
||||||
|
; [---] ;[-13] |
||||||
|
clr YH ;[-12] |
||||||
|
subi YL, lo8(-(usbRxBuf));[-11] [rx loop init] |
||||||
|
sbci YH, hi8(-(usbRxBuf));[-10] [rx loop init] |
||||||
|
push shift ;[-9] |
||||||
|
; [---] ;[-8] |
||||||
|
ldi shift,0x40 ;[-7] set msb to "1" so processing bit7 can be detected |
||||||
|
nop2 ;[-6] |
||||||
|
; [---] ;[-5] |
||||||
|
ldi bitcnt, 5 ;[-4] [rx loop init] |
||||||
|
sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) |
||||||
|
rjmp haveTwoBitsK ;[-2] |
||||||
|
pop shift ;[-1] undo the push from before |
||||||
|
pop bitcnt ;[1] |
||||||
|
rjmp waitForK ;[3] this was not the end of sync, retry |
||||||
|
; The entire loop from waitForK until rjmp waitForK above must not exceed two |
||||||
|
; bit times (= 27 cycles). |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; push more registers and initialize values while we sample the first bits: |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
haveTwoBitsK: |
||||||
|
push x1 ;[0] |
||||||
|
push x2 ;[2] |
||||||
|
push x3 ;[4] (leap2) |
||||||
|
ldi leap2, 0x55 ;[6] add leap cycle on 2nd,5th,8th,... stuff bit |
||||||
|
push x4 ;[7] == leap |
||||||
|
ldi leap, 0x55 ;[9] skip leap cycle on 2nd,5th,8th,... byte received |
||||||
|
push cnt ;[10] |
||||||
|
ldi cnt, USB_BUFSIZE ;[12] [rx loop init] |
||||||
|
ldi x2, 1<<USBPLUS ;[13] current line state is K state. D+=="1", D-=="0" |
||||||
|
bit0: |
||||||
|
in x1, USBIN ;[0] sample line state |
||||||
|
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||||
|
rjmp handleBit ;[2] make bit0 14 cycles long |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Process bit7. However, bit 6 still may need unstuffing. |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
b6checkUnstuff: |
||||||
|
dec bitcnt ;[9] |
||||||
|
breq unstuff6 ;[10] |
||||||
|
bit7: |
||||||
|
subi cnt, 1 ;[11] cannot use dec becaus it does not affect the carry flag |
||||||
|
brcs overflow ;[12] Too many bytes received. Ignore packet |
||||||
|
in x1, USBIN ;[0] sample line state |
||||||
|
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||||
|
cpse x1, x2 ;[2] when previous line state equals current line state, handle "1" |
||||||
|
rjmp b7handle0 ;[3] when line state differs, handle "0" |
||||||
|
sec ;[4] |
||||||
|
ror shift ;[5] shift "1" into the data |
||||||
|
st y+, shift ;[6] store the data into the buffer |
||||||
|
ldi shift, 0x40 ;[7] reset data for receiving the next byte |
||||||
|
subi leap, 0x55 ;[9] trick to introduce a leap cycle every 3 bytes |
||||||
|
brcc nextInst ;[10 or 11] it will fail after 85 bytes. However low speed can only receive 11 |
||||||
|
dec bitcnt ;[11 or 12] |
||||||
|
brne bit0 ;[12 or 13] |
||||||
|
ldi x1, 1 ;[13 or 14] unstuffing bit 7 |
||||||
|
in bitcnt, USBIN ;[0] sample stuff bit |
||||||
|
rjmp unstuff ;[1] |
||||||
|
|
||||||
|
b7handle0: |
||||||
|
mov x2,x1 ;[5] Set x2 to current line state |
||||||
|
ldi bitcnt, 6 ;[6] |
||||||
|
lsr shift ;[7] shift "0" into the data |
||||||
|
st y+, shift ;[8] store data into the buffer |
||||||
|
ldi shift, 0x40 ;[10] reset data for receiving the next byte |
||||||
|
subi leap, 0x55 ;[11] trick to introduce a leap cycle every 3 bytes |
||||||
|
brcs bit0 ;[12] it will fail after 85 bytes. However low speed can only receive 11 |
||||||
|
rjmp bit0 ;[13] |
||||||
|
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Handle unstuff |
||||||
|
; x1==0xFF indicate unstuffing bit6 |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
unstuff6: |
||||||
|
ldi x1,0xFF ;[12] indicate unstuffing bit 6 |
||||||
|
in bitcnt, USBIN ;[0] sample stuff bit |
||||||
|
nop ;[1] fix timing |
||||||
|
unstuff: ;b0-5 b6 b7 |
||||||
|
mov x2,bitcnt ;[3] [2] [3] Set x2 to match line state |
||||||
|
subi leap2, 0x55 ;[4] [3] [4] delay loop |
||||||
|
brcs nextInst ;[5] [4] [5] add one cycle every three stuff bits |
||||||
|
sbci leap2,0 ;[6] [5] [6] |
||||||
|
ldi bitcnt,6 ;[7] [6] [7] reset bit stuff counter |
||||||
|
andi x2, USBMASK ;[8] [7] [8] only keep D+ and D- |
||||||
|
cpi x1,0 ;[9] [8] [9] |
||||||
|
brmi bit7 ;[10] [9] [10] finished unstuffing bit6 When x1<0 |
||||||
|
breq bitloop ;[11] --- [11] finished unstuffing bit0-5 when x1=0 |
||||||
|
nop ;--- --- [12] |
||||||
|
in x1, USBIN ;--- --- [0] sample line state for bit0 |
||||||
|
andi x1, USBMASK ;--- --- [1] filter only D+ and D- bits |
||||||
|
rjmp handleBit ;--- --- [2] make bit0 14 cycles long |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; Receiver loop (numbers in brackets are cycles within byte after instr) |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
bitloop: |
||||||
|
in x1, USBIN ;[0] sample line state |
||||||
|
andi x1, USBMASK ;[1] filter only D+ and D- bits |
||||||
|
breq se0 ;[2] both lines are low so handle se0 |
||||||
|
handleBit: |
||||||
|
cpse x1, x2 ;[3] when previous line state equals current line state, handle "1" |
||||||
|
rjmp handle0 ;[4] when line state differs, handle "0" |
||||||
|
sec ;[5] |
||||||
|
ror shift ;[6] shift "1" into the data |
||||||
|
brcs b6checkUnstuff ;[7] When after shift C is set, next bit is bit7 |
||||||
|
nop2 ;[8] |
||||||
|
dec bitcnt ;[10] |
||||||
|
brne bitloop ;[11] |
||||||
|
ldi x1,0 ;[12] indicate unstuff for bit other than bit6 or bit7 |
||||||
|
in bitcnt, USBIN ;[0] sample stuff bit |
||||||
|
rjmp unstuff ;[1] |
||||||
|
|
||||||
|
handle0: |
||||||
|
mov x2, x1 ;[6] Set x2 to current line state |
||||||
|
ldi bitcnt, 6 ;[7] reset unstuff counter. |
||||||
|
lsr shift ;[8] shift "0" into the data |
||||||
|
brcs bit7 ;[9] When after shift C is set, next bit is bit7 |
||||||
|
nop ;[10] |
||||||
|
rjmp bitloop ;[11] |
||||||
|
|
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
; End of receive loop. Now start handling EOP |
||||||
|
;---------------------------------------------------------------------------- |
||||||
|
|
||||||
|
macro POP_STANDARD ; 14 cycles |
||||||
|
pop cnt |
||||||
|
pop x4 |
||||||
|
pop x3 |
||||||
|
pop x2 |
||||||
|
pop x1 |
||||||
|
pop shift |
||||||
|
pop bitcnt |
||||||
|
endm |
||||||
|
macro POP_RETI ; 7 cycles |
||||||
|
pop YH |
||||||
|
pop YL |
||||||
|
out SREG, YL |
||||||
|
pop YL |
||||||
|
endm |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "asmcommon.inc" |
||||||
|
|
||||||
|
; USB spec says: |
||||||
|
; idle = J |
||||||
|
; J = (D+ = 0), (D- = 1) |
||||||
|
; K = (D+ = 1), (D- = 0) |
||||||
|
; Spec allows 7.5 bit times from EOP to SOP for replies |
||||||
|
; 7.5 bit times is 100 cycles. This implementation arrives a bit later at se0 |
||||||
|
; then specified in the include file but there is plenty of time |
||||||
|
|
||||||
|
bitstuffN: |
||||||
|
eor x1, x4 ;[8] |
||||||
|
ldi x2, 0 ;[9] |
||||||
|
nop2 ;[10] |
||||||
|
out USBOUT, x1 ;[12] <-- out |
||||||
|
rjmp didStuffN ;[0] |
||||||
|
|
||||||
|
bitstuff7: |
||||||
|
eor x1, x4 ;[6] |
||||||
|
ldi x2, 0 ;[7] Carry is zero due to brcc |
||||||
|
rol shift ;[8] compensate for ror shift at branch destination |
||||||
|
nop2 ;[9] |
||||||
|
rjmp didStuff7 ;[11] |
||||||
|
|
||||||
|
sendNakAndReti: |
||||||
|
ldi x3, USBPID_NAK ;[-18] |
||||||
|
rjmp sendX3AndReti ;[-17] |
||||||
|
sendAckAndReti: |
||||||
|
ldi cnt, USBPID_ACK ;[-17] |
||||||
|
sendCntAndReti: |
||||||
|
mov x3, cnt ;[-16] |
||||||
|
sendX3AndReti: |
||||||
|
ldi YL, 20 ;[-15] x3==r20 address is 20 |
||||||
|
ldi YH, 0 ;[-14] |
||||||
|
ldi cnt, 2 ;[-13] |
||||||
|
; rjmp usbSendAndReti fallthrough |
||||||
|
|
||||||
|
;usbSend: |
||||||
|
;pointer to data in 'Y' |
||||||
|
;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] |
||||||
|
;uses: x1...x4, btcnt, shift, cnt, Y |
||||||
|
;Numbers in brackets are time since first bit of sync pattern is sent |
||||||
|
;We don't match the transfer rate exactly (don't insert leap cycles every third |
||||||
|
;byte) because the spec demands only 1.5% precision anyway. |
||||||
|
usbSendAndReti: ; 12 cycles until SOP |
||||||
|
in x2, USBDDR ;[-12] |
||||||
|
ori x2, USBMASK ;[-11] |
||||||
|
sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) |
||||||
|
in x1, USBOUT ;[-8] port mirror for tx loop |
||||||
|
out USBDDR, x2 ;[-7] <- acquire bus |
||||||
|
; need not init x2 (bitstuff history) because sync starts with 0 |
||||||
|
ldi x4, USBMASK ;[-6] exor mask |
||||||
|
ldi shift, 0x80 ;[-5] sync byte is first byte sent |
||||||
|
txByteLoop: |
||||||
|
ldi bitcnt, 0x49 ;[-4] [10] binary 01001001 |
||||||
|
txBitLoop: |
||||||
|
sbrs shift, 0 ;[-3] [10] [11] |
||||||
|
eor x1, x4 ;[-2] [11] [12] |
||||||
|
out USBOUT, x1 ;[-1] [12] [13] <-- out N |
||||||
|
ror shift ;[0] [13] [14] |
||||||
|
ror x2 ;[1] |
||||||
|
didStuffN: |
||||||
|
nop2 ;[2] |
||||||
|
nop ;[4] |
||||||
|
cpi x2, 0xfc ;[5] |
||||||
|
brcc bitstuffN ;[6] |
||||||
|
lsr bitcnt ;[7] |
||||||
|
brcc txBitLoop ;[8] |
||||||
|
brne txBitLoop ;[9] |
||||||
|
|
||||||
|
sbrs shift, 0 ;[10] |
||||||
|
eor x1, x4 ;[11] |
||||||
|
didStuff7: |
||||||
|
out USBOUT, x1 ;[-1] [13] <-- out 7 |
||||||
|
ror shift ;[0] [14] |
||||||
|
ror x2 ;[1] |
||||||
|
nop ;[2] |
||||||
|
cpi x2, 0xfc ;[3] |
||||||
|
brcc bitstuff7 ;[4] |
||||||
|
ld shift, y+ ;[5] |
||||||
|
dec cnt ;[7] |
||||||
|
brne txByteLoop ;[8] |
||||||
|
;make SE0: |
||||||
|
cbr x1, USBMASK ;[9] prepare SE0 [spec says EOP may be 25 to 30 cycles] |
||||||
|
lds x2, usbNewDeviceAddr;[10] |
||||||
|
lsl x2 ;[12] we compare with left shifted address |
||||||
|
out USBOUT, x1 ;[13] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle |
||||||
|
subi YL, 20 + 2 ;[0] Only assign address on data packets, not ACK/NAK in x3 |
||||||
|
sbci YH, 0 ;[1] |
||||||
|
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: |
||||||
|
;set address only after data packet was sent, not after handshake |
||||||
|
breq skipAddrAssign ;[2] |
||||||
|
sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer |
||||||
|
skipAddrAssign: |
||||||
|
;end of usbDeviceAddress transfer |
||||||
|
ldi x2, 1<<USB_INTR_PENDING_BIT;[4] int0 occurred during TX -- clear pending flag |
||||||
|
USB_STORE_PENDING(x2) ;[5] |
||||||
|
ori x1, USBIDLE ;[6] |
||||||
|
in x2, USBDDR ;[7] |
||||||
|
cbr x2, USBMASK ;[8] set both pins to input |
||||||
|
mov x3, x1 ;[9] |
||||||
|
cbr x3, USBMASK ;[10] configure no pullup on both pins |
||||||
|
ldi x4, 5 ;[11] |
||||||
|
se0Delay: |
||||||
|
dec x4 ;[12] [15] [18] [21] [24] |
||||||
|
brne se0Delay ;[13] [16] [19] [22] [25] |
||||||
|
out USBOUT, x1 ;[26] <-- out J (idle) -- end of SE0 (EOP signal) |
||||||
|
out USBDDR, x2 ;[27] <-- release bus now |
||||||
|
out USBOUT, x3 ;[28] <-- ensure no pull-up resistors are active |
||||||
|
rjmp doReturn |
||||||
@ -0,0 +1,144 @@ |
|||||||
|
/* Name: usbportability.h
|
||||||
|
* Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
||||||
|
* Author: Christian Starkjohann |
||||||
|
* Creation Date: 2008-06-17 |
||||||
|
* Tabsize: 4 |
||||||
|
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH |
||||||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) |
||||||
|
* This Revision: $Id$ |
||||||
|
*/ |
||||||
|
|
||||||
|
/*
|
||||||
|
General Description: |
||||||
|
This header is intended to contain all (or at least most of) the compiler |
||||||
|
and library dependent stuff. The C code is written for avr-gcc and avr-libc. |
||||||
|
The API of other development environments is converted to gcc's and avr-libc's |
||||||
|
API by means of defines. |
||||||
|
|
||||||
|
This header also contains all system includes since they depend on the |
||||||
|
development environment. |
||||||
|
|
||||||
|
Thanks to Oleg Semyonov for his help with the IAR tools port! |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __usbportability_h_INCLUDED__ |
||||||
|
#define __usbportability_h_INCLUDED__ |
||||||
|
|
||||||
|
/* We check explicitly for IAR and CodeVision. Default is avr-gcc/avr-libc. */ |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
#if defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ /* check for IAR */ |
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
#ifndef ENABLE_BIT_DEFINITIONS |
||||||
|
# define ENABLE_BIT_DEFINITIONS 1 /* Enable bit definitions */ |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Include IAR headers */ |
||||||
|
#include <ioavr.h> |
||||||
|
#ifndef __IAR_SYSTEMS_ASM__ |
||||||
|
# include <inavr.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#define __attribute__(arg) /* not supported on IAR */ |
||||||
|
|
||||||
|
#ifdef __IAR_SYSTEMS_ASM__ |
||||||
|
# define __ASSEMBLER__ /* IAR does not define standard macro for asm */ |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef __HAS_ELPM__ |
||||||
|
# define PROGMEM __farflash |
||||||
|
#else |
||||||
|
# define PROGMEM __flash |
||||||
|
#endif |
||||||
|
|
||||||
|
#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) |
||||||
|
|
||||||
|
/* The following definitions are not needed by the driver, but may be of some
|
||||||
|
* help if you port a gcc based project to IAR. |
||||||
|
*/ |
||||||
|
#define cli() __disable_interrupt() |
||||||
|
#define sei() __enable_interrupt() |
||||||
|
#define wdt_reset() __watchdog_reset() |
||||||
|
#define _BV(x) (1 << (x)) |
||||||
|
|
||||||
|
/* assembler compatibility macros */ |
||||||
|
#define nop2 rjmp $+2 /* jump to next instruction */ |
||||||
|
#define XL r26 |
||||||
|
#define XH r27 |
||||||
|
#define YL r28 |
||||||
|
#define YH r29 |
||||||
|
#define ZL r30 |
||||||
|
#define ZH r31 |
||||||
|
#define lo8(x) LOW(x) |
||||||
|
#define hi8(x) (((x)>>8) & 0xff) /* not HIGH to allow XLINK to make a proper range check */ |
||||||
|
|
||||||
|
/* Depending on the device you use, you may get problems with the way usbdrv.h
|
||||||
|
* handles the differences between devices. Since IAR does not use #defines |
||||||
|
* for MCU registers, we can't check for the existence of a particular |
||||||
|
* register with an #ifdef. If the autodetection mechanism fails, include |
||||||
|
* definitions for the required USB_INTR_* macros in your usbconfig.h. See |
||||||
|
* usbconfig-prototype.h and usbdrv.h for details. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
#elif __CODEVISIONAVR__ /* check for CodeVision AVR */ |
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
/* This port is not working (yet) */ |
||||||
|
|
||||||
|
/* #define F_CPU _MCU_CLOCK_FREQUENCY_ seems to be defined automatically */ |
||||||
|
|
||||||
|
#include <io.h> |
||||||
|
#include <delay.h> |
||||||
|
|
||||||
|
#define __attribute__(arg) /* not supported on IAR */ |
||||||
|
|
||||||
|
#define PROGMEM __flash |
||||||
|
#define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) |
||||||
|
|
||||||
|
#ifndef __ASSEMBLER__ |
||||||
|
static inline void cli(void) |
||||||
|
{ |
||||||
|
#asm("cli"); |
||||||
|
} |
||||||
|
static inline void sei(void) |
||||||
|
{ |
||||||
|
#asm("sei"); |
||||||
|
} |
||||||
|
#endif |
||||||
|
#define _delay_ms(t) delay_ms(t) |
||||||
|
#define _BV(x) (1 << (x)) |
||||||
|
#define USB_CFG_USE_SWITCH_STATEMENT 1 /* macro for if() cascase fails for unknown reason */ |
||||||
|
|
||||||
|
#define macro .macro |
||||||
|
#define endm .endmacro |
||||||
|
#define nop2 rjmp .+0 /* jump to next instruction */ |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
#else /* default development environment is avr-gcc/avr-libc */ |
||||||
|
/* ------------------------------------------------------------------------- */ |
||||||
|
|
||||||
|
#include <avr/io.h> |
||||||
|
#ifdef __ASSEMBLER__ |
||||||
|
# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ |
||||||
|
#else |
||||||
|
# include <avr/pgmspace.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#if USB_CFG_DRIVER_FLASH_PAGE |
||||||
|
# define USB_READ_FLASH(addr) pgm_read_byte_far(((long)USB_CFG_DRIVER_FLASH_PAGE << 16) | (long)(addr)) |
||||||
|
#else |
||||||
|
# define USB_READ_FLASH(addr) pgm_read_byte(addr) |
||||||
|
#endif |
||||||
|
|
||||||
|
#define macro .macro |
||||||
|
#define endm .endm |
||||||
|
#define nop2 rjmp .+0 /* jump to next instruction */ |
||||||
|
|
||||||
|
#endif /* development environment */ |
||||||
|
|
||||||
|
/* for conveniecne, ensure that PRG_RDB exists */ |
||||||
|
#ifndef PRG_RDB |
||||||
|
# define PRG_RDB(addr) USB_READ_FLASH(addr) |
||||||
|
#endif |
||||||
|
#endif /* __usbportability_h_INCLUDED__ */ |
||||||
Loading…
Reference in new issue