From c9b4165645e2bd47485d01f9448151ccc64635b0 Mon Sep 17 00:00:00 2001 From: eddyem Date: Sun, 5 Apr 2020 19:09:54 +0300 Subject: [PATCH] Fix USB HID for F103 --- .gitignore | 6 + F1-nolib/CDC_ACM/cdcacm.bin | Bin 5008 -> 5120 bytes F1-nolib/CDC_ACM/main.c | 2 +- F1-nolib/CDC_ACM/usb.c | 10 +- F1-nolib/CDC_ACM/usb_lib.c | 5 +- F1-nolib/PL2303/pl2303.bin | Bin 5672 -> 5776 bytes F1-nolib/PL2303/usb_lib.c | 2 +- F1-nolib/USB_HID/hardware.h | 4 +- F1-nolib/USB_HID/main.c | 10 +- F1-nolib/USB_HID/usart.c | 10 +- F1-nolib/USB_HID/usart.h | 2 +- F1-nolib/USB_HID/usb.c | 35 +++--- F1-nolib/USB_HID/usb.h | 1 - F1-nolib/USB_HID/usb_lib.c | 209 ++++++++++++++------------------- F1-nolib/USB_HID/usb_lib.h | 97 +++++---------- F1-nolib/USB_HID/usbhid103.bin | Bin 5352 -> 5448 bytes 16 files changed, 159 insertions(+), 234 deletions(-) diff --git a/.gitignore b/.gitignore index a08af4e..8a76b48 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,9 @@ F1/client-term/client *.files *.includes +*.cflags +*.config +*.creator* +*.cxxflags +*.files +*.includes diff --git a/F1-nolib/CDC_ACM/cdcacm.bin b/F1-nolib/CDC_ACM/cdcacm.bin index ccafebaaacf9f631a7606406ef5cc8dc8986c641..46c7c54005b2b465abd82bdbcc70aa9048b0e9d4 100755 GIT binary patch delta 1561 zcmbVMeQXnD9R9s`$JWu>wHqI6S8T5*(&dI$N|dj#TUpPw$LMfsGCrg-5(hR|AZp`3 z*SQ!gj4v-HhOtBw63Ni8wId{iIZ#CT?`~sBBdEh@4xPrUZtS*U=X(SF12iFC^1S!F z_x?Vf=gs@xktHdg2#w%*rwmMQVYJ-?xaToq3>cqhIl4dR-WvZk&zoj*#rv9WwEh30 ze>Xc?{Gc_W4;uJ@aoGk`OSmpg2U>v*E5u3Athd2eqqOp%9n>#opi1f{ciZ88&_dH+ zsBIY|q+;@%R7LG`P~~8%z_4Iw#y;B+m&5NLvS%okA|5H1zBgSWT(`3)O9-^BLa0nm z_vU*o{DOuezAAY!Uqc=bF1%h-U715`PGsKG;lg%TfbXgJc(kb4y5Va_xsECaGws3N zXLZGrN0uFR)d{`dR6jGp%>ctDZm2Zx=lgH!+{{Fmgv?9nH>SYr5bQii9-d=B+dBo! zf{25McS#TD1#OoUHLoTfot8SxtI(6um*&M{eProRT68e|v~V=&8?f_Pof>KHITVWL znzPnCG7stS!FRgtX!Pfo$(jvZ6iM!>_US_*_aN&0aMX(2Q*F1m^1w!9jo-rq)19XX zzh2Ivz_mIf_$7mecnPJBKn5D>e%})km`{FKY?$yYxZrUxZj1ePfH6G=?@iJ<1cwgK>4Gmuo znFfG)kv9$CmVT%@rCd=l8%fk@{Gh>Xrn)rWukH!?2KMj=Gdo>t!!~DYM<(1047CdD zwi&vQ8@k;Sxnf~!?2gtr0iF&79@qjL_<>s+ECq-GC+U{!;_HzWXUbb0l&$rvV%Z4sZ&Y{Y&W*Hf#O-*w!6$MH}-cKnq(SkjVQ zsLWygatZsXLU?0oK3pT163)a~Tp6)vm8=Thpx{i*Oo)?WOOj?!DW9lCxSfezgmr(U zJT==Au_2?S3CTd)V^2hyV#ieET`t7ZM(UQymgEg}d%8;4lscdmFQzW-jom zm9}G6RH&8ON|yxU_>!kEj$m|P_%M1fu45GYEV&F8af>Eq#=C7zif)P@*^Ln6Rbf3o()xBz|6nm8Uh>@g z{NCU5d*0uB_uhpaC#!kz2(X%qfp!OkaSOnfE33&OJDnYmBlp&9_4uzisl8Ln-?QkF z{{Nf)v)HNJ?Rs==SGY}GtOIFAE%O^LKOnslIsuft&6lH~rb+TMKp9qnLpX;$XG9oq z1}Lo?m*lLZZ0$3l1|Kqy!e=@GF$NKa00xBe!|{dKG*05^RI%{0_7Zw?BQ~K!k=|0) z68d#=VX~=)NR)JHnA0klFJLYwo3OWN<8_ij_K!!AeMMe}`NL&R?ssOBiu7NJ9;09H zQKh4;`-1B1Q%r7KCPinu6i{$ep1q|~nbf<2F2A1etpl5xH8Mc3GL#)C^g6JY`pgV? z6SGn_ptyzJ{6=(TT^P-GBdG9g{%(FhTfd}q#nha|#*Pt>V{U|*R`HWbPszfI4AtP6 zqwXKF^c%*awSIOI*m1X0H5m}s_t7z$R`IG-qJcfkeVzpRI?;FgTy z!9X#-79R@ShyN5$dawmhcSC%SxgY~ObakfW01B+b2(SXK2vFcj2q&Np(0U*^3$OFf z_=3|WzZth&tH{ZZKizgU&>X=O9(#?(Ma15$gc0c|7CgdM-0dg`J9_vwKX}MnPQ9{1 z9n9l`v*nu7cz?Ssgu*(Mq(y?Xh=6p8z}Cz5aozvcW_Od_#Sij9w&K-`f9f#6lfR1{ zY=6i~E=!&8MSr;_%L5@B=@#vz4I15$atW`GVZ}&=e>Cz~q%=bND!GeGNKb-$l#}Eu zx`EJ6Nkh6s9r=}n+PKI8?}My1SRR~*J_tE4&B(y!X3K+_)cD#!h#;q=3vv!wC2C0n zY?41Op~RlHdLQs((C~d3vF%~GNLGFV%4HbWven+Qh|a%@)pC8@8Tm-W%Jzj%$_6oE~EW8?9oqwKR34NSLl)+qA zdQh%(5<4OFrJt4Sh#io5(tf#!D1nSh56C!C3|V9Pu-rlzA$uVGgs`Nqc5T8B&R_ys zD+C1*fUrP(0_Srb1&JtZ$2q0B(-xX=&SX*}LW^M)HKhf&@$t%dA<)*rQ$f|jBX0{% zbz2JC!E#vBZ|G5XQ$p0(Q2KXDfvFR~*#1!2-PF4A3*#@Dv USB_TXBUFSZ-1){ - USB_send_blk(buf, len); - return; - } if(len > USB_TXBUFSZ-1 - buflen){ usbwr(usbbuff, buflen); buflen = 0; } + if(len > USB_TXBUFSZ-1){ + USB_send_blk(buf, len); + return; + } while(len--) usbbuff[buflen++] = *buf++; } @@ -128,7 +127,6 @@ void USB_send_blk(const uint8_t *buf, uint16_t len){ } } - void usb_proc(){ switch(USB_Dev.USB_Status){ case USB_STATE_CONFIGURED: diff --git a/F1-nolib/CDC_ACM/usb_lib.c b/F1-nolib/CDC_ACM/usb_lib.c index 943fde7..a25a31d 100644 --- a/F1-nolib/CDC_ACM/usb_lib.c +++ b/F1-nolib/CDC_ACM/usb_lib.c @@ -198,6 +198,7 @@ static void wr0(const uint8_t *buf, uint16_t size){ if(setup_packet.wLength < size) size = setup_packet.wLength; // shortened request if(size < endpoints[0].txbufsz){ EP_WriteIRQ(0, buf, size); + return; } while(size){ uint16_t l = size; @@ -305,8 +306,6 @@ bmRequestType: 76543210 */ /** * Endpoint0 (control) handler - * @param ep - endpoint state - * @return data written to EP0R */ static void EP0_Handler(){ uint8_t reqtype = setup_packet.bmRequestType & 0x7f; @@ -487,7 +486,7 @@ void usb_lp_can_rx0_isr(){ */ void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){ uint8_t i; - if(size > USB_TXBUFSZ) size = USB_TXBUFSZ; + if(size > endpoints[number].txbufsz) size = endpoints[number].txbufsz; uint16_t N2 = (size + 1) >> 1; // the buffer is 16-bit, so we should copy data as it would be uint16_t uint16_t *buf16 = (uint16_t *)buf; diff --git a/F1-nolib/PL2303/pl2303.bin b/F1-nolib/PL2303/pl2303.bin index 6805c73a1e1c3b8553b690a59e915d95b3db2a2f..81a55a6b2736a7146fc653eebfc844ede572a4f9 100755 GIT binary patch delta 1516 zcmbVMe@q)y9Dmoljw>*@wv5mMX|ER|8(Q!^BIk2o1SL?fO8pAXpUh;Y0 z&wKCtqv*K# z|0n&cm>*wjj?T3@uF)^nfkn#q87u4IU#oj(tv z+jlUXh~!uq`TcOKV;UKKcfJI1tUP~nFAL~^tg%C^RKMOP5KPONMNnF*oNq0X(Xq*& zv?E;A9^&mNpc`b7?bJZ=tg5%3ekv7drZff}jZ!H$WD#^kbE6)9f3ONGycoD~oOE#a z^M+ZZ5w`Mh(1QJz*dMfEGl}!9cpOlVv3PtcgSkQ7$I6rn*=(rgVU3;EDS@|$?4&x> zS{ol?4@C#6{Di&#VAMYjC~S*6V+-W56k*zzH4!3~&Qx zo3|7I9xzgB{hUzF?dC&mzNHtu&EkPE$y+Y_mR{N?dF^t9E|URuC{y4r2}q))A9yO_ z2xT3g%oGr29uIW$hOtnaA@8xEB6)jVJ)XDMR1nuo=w+V^Y(8b}w_#NmE7uhzp7qTH zn?zI-&{w^fj0jYd7D1H9(NKu1ye(b`ir0K&`|zOO&KyWF4;J#9C+)_%#6Y_%Y=D6V zdZ$cWBI!*sL)#T6n@bcCfcYNT)f!p@8R%}Jp^34 z;KqMYg&wZ*l`noZp@6WY3kvUV$F|&iTl$e(($6$O=}*IGgpMg+sle65*+aQ>ORg_W(&v$y(`%+6(cu5_FcAJy^FL$mdLiL^`r$dIqQY91Y}=l zACTsW8jl9=HC7JrG6(|V0_^*seFmbSwm2Dui*ipG^NTYi-Dh&?tx~f&jhvMlEf2Y# zC^Y5HY(Qlz6tNLo$ZFVPC8-PWY;EM24x*vEBbrVZW^cn delta 1412 zcmbVMZ%7+w7=JEzsfmB{M}yJK<}SMG&T7yVy4uPaYm9ld&Q;lr9X+ZaGPK*R-3LFE zUDkHpbzA?i^UKge#~4T{)Fv&ZP|2X|gAB%9tq`eFVWT%!3aO1v*0E%7;4}$PHs|=S`J6EqE51~; z&;0+B{!^^Wu+<#BTNQ3m7wI8nvW_nWLpgx+1h%I!CNQQjd}Uy8V_e332{%%Zjrtb- z2%c3(y7Z$IaY(*toFf_{V*Uk!KK}w3J2HDgc6vD%#Lv6{m(6eY!dIk|V<7&q+*sH` z?3PCh5z#8&Dy-|?hjy;Udml)9{a<@(qjqmNMXj8gIo~lDYl{>=CCDKY$oEokc{2Pk zwQ}+G0}oS6GtIq({9#As;_@o&`sQ&laW|Jdm2kJa;vw=>+)Xy~$H!iw^6&efU#YgEy}WLPXu+d$ zY|KLazHofZK`kyMI;beP=2Da!>TanUnUQ12WC>mhqwC6gVXkoNx#@a0A!sDFNVti$*gU*-`vREJXoc==80f_OweUXJn5>@vWUX zB71B~h&3tTI+x0G6HEb`P33_-Xg~%&!0Tp&PF>FPR3mcsiFPGtS2c)c(`QQ_^$Rzx zK|AHRU%9PdVbnK2)+Uj0k^I6#DTJ#n9wO8cQWQo|Wzdxf;id?u{is?Nm&QG~1wXY=!FdW|C8x{(qx69Gz|IvNnACo^g1*Rn@Zr z#mMSa9m7jKT?iS33IUjI{KG$M2vvT5_nDCtfn8QQ(9pP(T~s@LndH&Tf;K$Pu=R?I zEmdgtk(x(){2WrG%HV`FAldMFsp9Xh68$m0kB?$4!QN5lHDLCj3L%s1%M6dx?2l?# z+s1BJ4A~yCS5+O|=xgvpki;?HX#~@VcqyyiEn>Nb7hXj>y2>SEuqM~w3GIlq9R)(u z8n;F?4NJ1ep%ld;>mb%Ek#$b2#$!nQM77XWc*{)s1lEyFvd5|v%}3V1|3FN9bSlCzo>otnVV0jB(J0!AnLb^17*h+TkPc`&XcK0C{5c>ab4{~^@7@%~ zRg4vkLK_&SvE?!HY71jwydC!qeQsO2nEOs9`HuXpIY}(Zla_(1T_s?s#fOi3tv1Rr z!i|HDqHcH`(hr&XQ#G7yu{PK?TygCB49wm8%Kvr+tUsCCGieQ;zzTjlo!Pg+6nUfc GZQWmS%Ff6D diff --git a/F1-nolib/PL2303/usb_lib.c b/F1-nolib/PL2303/usb_lib.c index 86436e6..db9635b 100644 --- a/F1-nolib/PL2303/usb_lib.c +++ b/F1-nolib/PL2303/usb_lib.c @@ -438,7 +438,7 @@ void usb_lp_can_rx0_isr(){ */ void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){ uint8_t i; - if(size > USB_TXBUFSZ) size = USB_TXBUFSZ; + if(size > endpoints[number].txbufsz) size = endpoints[number].txbufsz; uint16_t N2 = (size + 1) >> 1; // the buffer is 16-bit, so we should copy data as it would be uint16_t uint16_t *buf16 = (uint16_t *)buf; diff --git a/F1-nolib/USB_HID/hardware.h b/F1-nolib/USB_HID/hardware.h index 0f3c11f..961e042 100644 --- a/F1-nolib/USB_HID/hardware.h +++ b/F1-nolib/USB_HID/hardware.h @@ -27,8 +27,8 @@ #include "stm32f1.h" // LED - blinking each second -#define LED_port GPIOA -#define LED_pin (1<<0) +#define LED_port GPIOC +#define LED_pin (1<<13) // USB pullup (not used in STM32F0x2!) - PA13 #define USBPU_port GPIOA diff --git a/F1-nolib/USB_HID/main.c b/F1-nolib/USB_HID/main.c index 1384434..602f474 100644 --- a/F1-nolib/USB_HID/main.c +++ b/F1-nolib/USB_HID/main.c @@ -34,13 +34,13 @@ void sys_tick_handler(void){ static void hw_setup(){ // Enable clocks to the GPIO subsystems (PB for ADC), turn on AFIO clocking to disable SWD/JTAG - RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN; + RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN; // turn off SWJ/JTAG - AFIO->MAPR = AFIO_MAPR_SWJ_CFG_DISABLE; + //AFIO->MAPR = AFIO_MAPR_SWJ_CFG_DISABLE; // turn off USB pullup GPIOA->ODR = (1<<13); - // Set led (PA0) as opendrain output - GPIOA->CRL = CRL(0, CNF_ODOUTPUT|MODE_SLOW); + // Set led (PC13) as opendrain output + GPIOC->CRH = CRH(13, CNF_ODOUTPUT|MODE_SLOW); // Set USB pullup (PA13) as opendrain output GPIOA->CRH = CRH(13, CNF_ODOUTPUT|MODE_SLOW); } @@ -111,7 +111,7 @@ static char *parse_cmd(char *buf){ break; case 'C': SEND("USB "); - if(!USB_configured()) SEND("dis"); + if(!usbON) SEND("dis"); SEND("connected\n"); break; case 'K': diff --git a/F1-nolib/USB_HID/usart.c b/F1-nolib/USB_HID/usart.c index 2c0f730..c16ab3f 100644 --- a/F1-nolib/USB_HID/usart.c +++ b/F1-nolib/USB_HID/usart.c @@ -53,7 +53,10 @@ int usart_getline(char **line){ // transmit current tbuf and swap buffers void transmit_tbuf(){ uint32_t tmout = 72000; - while(!txrdy){if(--tmout == 0) return;}; // wait for previos buffer transmission + while(!txrdy){ // wait for previos buffer transmission + IWDG->KR = IWDG_REFRESH; + if(--tmout == 0) return; + } register int l = odatalen[tbufno]; if(!l) return; txrdy = 0; @@ -66,7 +69,10 @@ void transmit_tbuf(){ } void usart_putchar(const char ch){ - for(int i = 0; odatalen[tbufno] == UARTBUFSZO && i < 1024; ++i) transmit_tbuf(); + for(int i = 0; odatalen[tbufno] == UARTBUFSZO && i < 1024; ++i){ + IWDG->KR = IWDG_REFRESH; + transmit_tbuf(); + } tbuf[tbufno][odatalen[tbufno]++] = ch; } diff --git a/F1-nolib/USB_HID/usart.h b/F1-nolib/USB_HID/usart.h index a48b92c..eb722cc 100644 --- a/F1-nolib/USB_HID/usart.h +++ b/F1-nolib/USB_HID/usart.h @@ -38,7 +38,7 @@ #ifdef EBUG #define MSG(str) do{SEND(__FILE__ " (L" STR(__LINE__) "): " str);}while(0) -#define DBG(str) do{SEND(str); usart_putchar('\n'); }while(0) +#define DBG(str) do{SEND(str); usart_putchar('\n'); transmit_tbuf();}while(0) #else #define MSG(str) #define DBG(str) diff --git a/F1-nolib/USB_HID/usb.c b/F1-nolib/USB_HID/usb.c index e3a5f70..67bf72b 100644 --- a/F1-nolib/USB_HID/usb.c +++ b/F1-nolib/USB_HID/usb.c @@ -25,24 +25,25 @@ #include "usb_lib.h" #include "usart.h" -// incoming buffer size -#define IDATASZ (256) -static volatile uint8_t tx_succesfull = 0; -static int8_t usbON = 0; // ==1 when USB fully configured +static volatile uint8_t tx_succesfull = 1; // interrupt IN handler -static uint16_t EP1_Handler(ep_t ep){ - if(ep.tx_flag){ +static void EP1_Handler(){ + uint16_t epstatus = KEEP_DTOG(USB->EPnR[1]); + if(RX_FLAG(epstatus)) epstatus = (epstatus & ~USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_RX; // set valid RX + else{ tx_succesfull = 1; - ep.status = SET_VALID_RX(ep.status); - ep.status = SET_VALID_TX(ep.status); + epstatus = epstatus & ~(USB_EPnR_STAT_TX|USB_EPnR_STAT_RX); } - return ep.status; + // clear CTR + epstatus = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX)); + USB->EPnR[1] = epstatus; } void USB_setup(){ NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn); NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn); + DBG("USB setup"); RCC->APB1ENR |= RCC_APB1ENR_USBEN; USB->CNTR = USB_CNTR_FRES; // Force USB Reset for(uint32_t ctr = 0; ctr < 72000; ++ctr) nop(); // wait >1ms @@ -52,11 +53,10 @@ void USB_setup(){ USB->ISTR = 0; USB->CNTR = USB_CNTR_RESETM | USB_CNTR_WKUPM; // allow only wakeup & reset interrupts NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn); - NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn ); } void usb_proc(){ - if(USB_GetState() == USB_CONFIGURE_STATE){ // USB configured - activate other endpoints + if(USB_Dev.USB_Status == USB_STATE_CONFIGURED){ // USB configured - activate other endpoints if(!usbON){ // endpoints not activated EP_Init(1, EP_TYPE_INTERRUPT, USB_TXBUFSZ, 0, EP1_Handler); // IN1 - transmit usbON = 1; @@ -67,14 +67,16 @@ void usb_proc(){ } void USB_send(uint8_t *buf, uint16_t size){ - if(!usbON) return; + if(!usbON || !size) return; uint16_t ctr = 0; while(size){ uint16_t s = (size > USB_KEYBOARD_REPORT_SIZE) ? USB_KEYBOARD_REPORT_SIZE : size; tx_succesfull = 0; EP_Write(1, (uint8_t*)&buf[ctr], s); uint32_t ctra = 1000000; - while(--ctra && tx_succesfull == 0); + while(--ctra && tx_succesfull == 0){ + IWDG->KR = IWDG_REFRESH; + } if(!tx_succesfull){ DBG("Error sending data!"); } @@ -83,10 +85,3 @@ void USB_send(uint8_t *buf, uint16_t size){ } } -/** - * @brief USB_configured - * @return 1 if USB is in configured state - */ -int USB_configured(){ - return usbON; -} diff --git a/F1-nolib/USB_HID/usb.h b/F1-nolib/USB_HID/usb.h index a8655aa..9847331 100644 --- a/F1-nolib/USB_HID/usb.h +++ b/F1-nolib/USB_HID/usb.h @@ -31,6 +31,5 @@ void USB_setup(); void usb_proc(); void USB_send(uint8_t *buf, uint16_t size); -int USB_configured(); #endif // __USB_H__ diff --git a/F1-nolib/USB_HID/usb_lib.c b/F1-nolib/USB_HID/usb_lib.c index 6e66720..3ce80c5 100644 --- a/F1-nolib/USB_HID/usb_lib.c +++ b/F1-nolib/USB_HID/usb_lib.c @@ -27,8 +27,10 @@ ep_t endpoints[STM32ENDPOINTS]; -static usb_dev_t USB_Dev; -config_pack_t setup_packet; +usb_dev_t USB_Dev; +static config_pack_t setup_packet; + +uint8_t usbON = 0; // device disconnected from terminal // definition of parts common for USB_DeviceDescriptor & USB_DeviceQualifierDescriptor #define bcdUSB_L 0x00 @@ -175,14 +177,37 @@ static const uint8_t USB_ConfigDescriptor[] = { 0x01, /* bInterval: */ }; -_USB_LANG_ID_(USB_StringLangDescriptor, LANG_US); -_USB_STRING_(USB_StringSerialDescriptor, u"0"); -_USB_STRING_(USB_StringManufacturingDescriptor, u"SAO RAS"); -_USB_STRING_(USB_StringProdDescriptor, u"USB HID mouse+keyboard"); +USB_LANG_ID(USB_StringLangDescriptor, LANG_US); +USB_STRING(USB_StringSerialDescriptor, u"01"); +USB_STRING(USB_StringManufacturingDescriptor, u"Eddy @ SAO RAS"); +USB_STRING(USB_StringProdDescriptor, u"USB HID mouse+keyboard"); static void wr0(const uint8_t *buf, uint16_t size){ - if(setup_packet.wLength < size) size = setup_packet.wLength; - EP_WriteIRQ(0, buf, size); + if(setup_packet.wLength < size) size = setup_packet.wLength; // shortened request + if(size < endpoints[0].txbufsz){ + EP_WriteIRQ(0, buf, size); + return; + } + while(size){ + uint16_t l = size; + if(l > endpoints[0].txbufsz) l = endpoints[0].txbufsz; + EP_WriteIRQ(0, buf, l); + buf += l; + size -= l; + uint8_t needzlp = (l == endpoints[0].txbufsz) ? 1 : 0; + if(size || needzlp){ // send last data buffer + uint16_t status = KEEP_DTOG(USB->EPnR[0]); + // keep DTOGs, clear CTR_RX,TX, set TX VALID, leave stat_Rx + USB->EPnR[0] = (status & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX|USB_EPnR_STAT_RX)) + ^ USB_EPnR_STAT_TX; + uint32_t ctr = 1000000; + while(--ctr && (USB->ISTR & USB_ISTR_CTR) == 0){IWDG->KR = IWDG_REFRESH;}; + if((USB->ISTR & USB_ISTR_CTR) == 0){ + return; + } + if(needzlp) EP_WriteIRQ(0, (uint8_t*)0, 0); + } + } } static inline void get_descriptor(){ @@ -216,6 +241,11 @@ static inline void get_descriptor(){ wr0(USB_DeviceQualifierDescriptor, USB_DeviceQualifierDescriptor[0]); break; default: + MSG("WTF?"); +#ifdef EBUG + printuhex(setup_packet.wValue); + newline(); +#endif break; } } @@ -246,7 +276,7 @@ static inline void std_h2d_req(){ break; case SET_CONFIGURATION: // Now device configured - USB_Dev.USB_Status = USB_CONFIGURE_STATE; + USB_Dev.USB_Status = USB_STATE_CONFIGURED; configuration = setup_packet.wValue; break; default: @@ -254,46 +284,17 @@ static inline void std_h2d_req(){ } } -static uint16_t WriteHID_descriptor(uint16_t status){ - uint16_t rest = sizeof(HID_ReportDescriptor); - uint8_t *ptr = (uint8_t*)HID_ReportDescriptor; - while(rest){ - uint16_t l = rest; - if(l > endpoints[0].txbufsz) l = endpoints[0].txbufsz; - EP_WriteIRQ(0, ptr, l); - ptr += l; - rest -= l; - uint8_t needzlp = (l == endpoints[0].txbufsz) ? 1 : 0; - if(rest || needzlp){ // send last data buffer - status = SET_NAK_RX(status); - status = SET_VALID_TX(status); - status = KEEP_DTOG_TX(status); - status = KEEP_DTOG_RX(status); - status = CLEAR_CTR_RX(status); - status = CLEAR_CTR_TX(status); - USB->ISTR = 0; - USB->EPnR[0] = status; - uint32_t ctr = 1000000; - while(--ctr && (USB->ISTR & USB_ISTR_CTR) == 0); - if((USB->ISTR & USB_ISTR_CTR) == 0){MSG("ERR\n");}; - USB->ISTR = 0; - status = USB->EPnR[0]; - if(needzlp) EP_WriteIRQ(0, (uint8_t*)0, 0); - } - } - return status; -} - /** * Endpoint0 (control) handler * @param ep - endpoint state * @return data written to EP0R */ -static uint16_t EP0_Handler(ep_t ep){ - uint16_t epstatus = ep.status; // EP0R on input -> return this value after modifications +static void EP0_Handler(){ + uint16_t epstatus = USB->EPnR[0]; // EP0R on input -> return this value after modifications uint8_t reqtype = setup_packet.bmRequestType & 0x7f; uint8_t dev2host = (setup_packet.bmRequestType & 0x80) ? 1 : 0; - if ((ep.rx_flag) && (ep.setup_flag)){ + int rxflag = RX_FLAG(epstatus); + if(rxflag && SETUP_FLAG(epstatus)){ switch(reqtype){ case STANDARD_DEVICE_REQUEST_TYPE: // standard device request if(dev2host){ @@ -302,62 +303,44 @@ static uint16_t EP0_Handler(ep_t ep){ std_h2d_req(); EP_WriteIRQ(0, (uint8_t *)0, 0); } - epstatus = SET_NAK_RX(epstatus); - epstatus = SET_VALID_TX(epstatus); break; case STANDARD_INTERFACE_REQUEST_TYPE: if(dev2host && setup_packet.bRequest == GET_DESCRIPTOR){ if(setup_packet.wValue == HID_REPORT_DESCRIPTOR){ - epstatus = WriteHID_descriptor(epstatus); + DBG("HID_REPORT_DESCRIPTOR"); + wr0(HID_ReportDescriptor, sizeof(HID_ReportDescriptor)); } } - epstatus = SET_NAK_RX(epstatus); - epstatus = SET_VALID_TX(epstatus); break; case STANDARD_ENDPOINT_REQUEST_TYPE: // standard endpoint request if(setup_packet.bRequest == CLEAR_FEATURE){ EP_WriteIRQ(0, (uint8_t *)0, 0); - epstatus = SET_NAK_RX(epstatus); - epstatus = SET_VALID_TX(epstatus); } break; case CONTROL_REQUEST_TYPE: if(setup_packet.bRequest == SET_IDLE_REQUEST){ EP_WriteIRQ(0, (uint8_t *)0, 0); - epstatus = SET_NAK_RX(epstatus); - epstatus = SET_VALID_TX(epstatus); }else if (setup_packet.bRequest == SET_FEAUTRE){ //set_featuring = 1; - epstatus = SET_VALID_RX(epstatus); - epstatus = KEEP_STAT_TX(epstatus); } break; default: EP_WriteIRQ(0, (uint8_t *)0, 0); - epstatus = SET_NAK_RX(epstatus); - epstatus = SET_VALID_TX(epstatus); + MSG("WTF?"); } - }else if (ep.rx_flag || ep.tx_flag){ // got data over EP0 or host acknowlegement || package transmitted - if(ep.rx_flag){ - /*if (set_featuring){ - set_featuring = 0; - // here we can do something with ep.rx_buf - set_feature - }*/ - }else{ // tx - // now we can change address after enumeration - if ((USB->DADDR & USB_DADDR_ADD) != USB_Dev.USB_Addr){ - USB->DADDR = USB_DADDR_EF | USB_Dev.USB_Addr; - // change state to ADRESSED - USB_Dev.USB_Status = USB_ADRESSED_STATE; - } + }else if(TX_FLAG(epstatus)){ + // now we can change address after enumeration + if ((USB->DADDR & USB_DADDR_ADD) != USB_Dev.USB_Addr){ + USB->DADDR = USB_DADDR_EF | USB_Dev.USB_Addr; + // change state to ADRESSED + USB_Dev.USB_Status = USB_STATE_ADDRESSED; } - // end of transaction - epstatus = CLEAR_DTOG_RX(epstatus); - epstatus = CLEAR_DTOG_TX(epstatus); - epstatus = SET_VALID_RX(epstatus); - epstatus = SET_VALID_TX(epstatus); } - return epstatus; + epstatus = KEEP_DTOG(USB->EPnR[0]); + if(rxflag) epstatus ^= USB_EPnR_STAT_TX; // start ZLP/data transmission + else epstatus &= ~USB_EPnR_STAT_TX; // or leave unchanged + // keep DTOGs, clear CTR_RX,TX, set RX VALID + USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX)) ^ USB_EPnR_STAT_RX; } static uint16_t lastaddr = LASTADDR_DEFAULT; @@ -370,7 +353,7 @@ static uint16_t lastaddr = LASTADDR_DEFAULT; * @param uint16_t (*func)(ep_t *ep) - EP handler function * @return 0 if all OK */ -int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, uint16_t (*func)(ep_t ep)){ +int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)(ep_t ep)){ if(number >= STM32ENDPOINTS) return 4; // out of configured amount if(txsz > USB_BTABLE_SIZE || rxsz > USB_BTABLE_SIZE) return 1; // buffer too large if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE) return 2; // out of btable @@ -397,32 +380,28 @@ int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, uint16_t } // standard IRQ handler -void usb_isr(){ - // disallow interrupts - //USB->CNTR = 0; - if (USB->ISTR & USB_ISTR_RESET){ - USB->ISTR = 0; - USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM; +void usb_lp_can_rx0_isr(){ + if(USB->ISTR & USB_ISTR_RESET){ + usbON = 0; + DBG("RESET"); + USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM; // Endpoint 0 - CONTROL // ON USB LS size of EP0 may be 8 bytes, but on FS it should be 64 bytes! lastaddr = LASTADDR_DEFAULT; - if(EP_Init(0, EP_TYPE_CONTROL, USB_EP0_BUFSZ, USB_EP0_BUFSZ, EP0_Handler)){ - DBG("Err init EP0"); - } // clear address, leave only enable bit USB->DADDR = USB_DADDR_EF; // state is default - wait for enumeration - USB_Dev.USB_Status = USB_DEFAULT_STATE; + USB_Dev.USB_Status = USB_STATE_DEFAULT; + USB->ISTR = ~USB_ISTR_RESET; + if(EP_Init(0, EP_TYPE_CONTROL, USB_EP0_BUFSZ, USB_EP0_BUFSZ, EP0_Handler)){ + return; + } } if(USB->ISTR & USB_ISTR_CTR){ // EP number uint8_t n = USB->ISTR & USB_ISTR_EPID; // copy status register uint16_t epstatus = USB->EPnR[n]; - // Calculate flags - endpoints[n].rx_flag = (epstatus & USB_EPnR_CTR_RX) ? 1 : 0; - endpoints[n].setup_flag = (epstatus & USB_EPnR_SETUP) ? 1 : 0; - endpoints[n].tx_flag = (epstatus & USB_EPnR_CTR_TX) ? 1 : 0; // copy received bytes amount endpoints[n].rx_cnt = USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter // check direction @@ -436,29 +415,17 @@ void usb_isr(){ }else{ // IN interrupt - transmit data, only CTR_TX == 1 // enumeration end could be here (if EP0) } - // prepare status field for EP handler - endpoints[n].status = epstatus; - // call EP handler (even if it will change EPnR, it should return new status) - epstatus = endpoints[n].func(endpoints[n]); - // keep DTOG state - epstatus = KEEP_DTOG_TX(epstatus); - epstatus = KEEP_DTOG_RX(epstatus); - // clear all RX/TX flags - epstatus = CLEAR_CTR_RX(epstatus); - epstatus = CLEAR_CTR_TX(epstatus); - // refresh EPnR - USB->EPnR[n] = epstatus; + if(endpoints[n].func) endpoints[n].func(endpoints[n]); + } + if(USB->ISTR & USB_ISTR_SUSP){ // suspend -> still no connection, may sleep + usbON = 0; + USB->CNTR |= USB_CNTR_FSUSP | USB_CNTR_LP_MODE; + USB->ISTR = ~USB_ISTR_SUSP; + } + if(USB->ISTR & USB_ISTR_WKUP){ // wakeup + USB->CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LP_MODE); // clear suspend flags + USB->ISTR = ~USB_ISTR_WKUP; } - // allow interrupts again - // USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM; -} - -void usb_lp_can_rx0_isr(){ - usb_isr(); -} - -void usb_hp_can_tx_isr(){ - usb_isr(); } /** @@ -487,13 +454,10 @@ void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){ * @param size - its size */ void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size){ - uint16_t status = USB->EPnR[number]; EP_WriteIRQ(number, buf, size); - status = SET_NAK_RX(status); - status = SET_VALID_TX(status); - status = KEEP_DTOG_TX(status); - status = KEEP_DTOG_RX(status); - USB->EPnR[number] = status; + uint16_t status = KEEP_DTOG(USB->EPnR[number]); + // keep DTOGs, clear CTR_TX & set TX VALID to start transmission + USB->EPnR[number] = (status & ~(USB_EPnR_CTR_TX)) ^ USB_EPnR_STAT_TX; } /* @@ -502,16 +466,15 @@ void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size){ * @return amount of data read */ int EP_Read(uint8_t number, uint16_t *buf){ - int n = (endpoints[number].rx_cnt + 1) >> 1; + int sz = endpoints[number].rx_cnt; + if(!sz) return 0; + endpoints[number].rx_cnt = 0; + int n = (sz + 1) >> 1; uint32_t *in = (uint32_t *)endpoints[number].rx_buf; if(n){ for(int i = 0; i < n; ++i, ++in) buf[i] = *(uint16_t*)in; } - return endpoints[number].rx_cnt; + return sz; } -// USB status -uint8_t USB_GetState(){ - return USB_Dev.USB_Status; -} diff --git a/F1-nolib/USB_HID/usb_lib.h b/F1-nolib/USB_HID/usb_lib.h index 8a29d62..06b0972 100644 --- a/F1-nolib/USB_HID/usb_lib.h +++ b/F1-nolib/USB_HID/usb_lib.h @@ -71,41 +71,31 @@ #define CONTROL_DTR 0x01 #define CONTROL_RTS 0x02 -// wValue -#define DEVICE_DESCRIPTOR 0x100 -#define CONFIGURATION_DESCRIPTOR 0x200 -#define STRING_LANG_DESCRIPTOR 0x300 -#define STRING_MAN_DESCRIPTOR 0x301 -#define STRING_PROD_DESCRIPTOR 0x302 -#define STRING_SN_DESCRIPTOR 0x303 -#define DEVICE_QUALIFIER_DESCRIPTOR 0x600 +// wValue = DESCR_TYPE<<8 | DESCR_INDEX +#define DEVICE_DESCRIPTOR 0x0100 +#define CONFIGURATION_DESCRIPTOR 0x0200 +#define STRING_LANG_DESCRIPTOR 0x0300 +#define STRING_MAN_DESCRIPTOR 0x0301 +#define STRING_PROD_DESCRIPTOR 0x0302 +#define STRING_SN_DESCRIPTOR 0x0303 +#define DEVICE_QUALIFIER_DESCRIPTOR 0x0600 #define HID_REPORT_DESCRIPTOR 0x2200 -// EPnR bits manipulation -#define CLEAR_DTOG_RX(R) (R & USB_EPnR_DTOG_RX) ? R : (R & (~USB_EPnR_DTOG_RX)) -#define SET_DTOG_RX(R) (R & USB_EPnR_DTOG_RX) ? (R & (~USB_EPnR_DTOG_RX)) : R -#define TOGGLE_DTOG_RX(R) (R | USB_EPnR_DTOG_RX) -#define KEEP_DTOG_RX(R) (R & (~USB_EPnR_DTOG_RX)) -#define CLEAR_DTOG_TX(R) (R & USB_EPnR_DTOG_TX) ? R : (R & (~USB_EPnR_DTOG_TX)) -#define SET_DTOG_TX(R) (R & USB_EPnR_DTOG_TX) ? (R & (~USB_EPnR_DTOG_TX)) : R -#define TOGGLE_DTOG_TX(R) (R | USB_EPnR_DTOG_TX) -#define KEEP_DTOG_TX(R) (R & (~USB_EPnR_DTOG_TX)) -#define SET_VALID_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX) | (R & (~USB_EPnR_STAT_RX)) -#define SET_NAK_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX_1) | (R & (~USB_EPnR_STAT_RX)) -#define SET_STALL_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX_0) | (R & (~USB_EPnR_STAT_RX)) -#define KEEP_STAT_RX(R) (R & (~USB_EPnR_STAT_RX)) -#define SET_VALID_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX) | (R & (~USB_EPnR_STAT_TX)) -#define SET_NAK_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX_1) | (R & (~USB_EPnR_STAT_TX)) -#define SET_STALL_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX_0) | (R & (~USB_EPnR_STAT_TX)) -#define KEEP_STAT_TX(R) (R & (~USB_EPnR_STAT_TX)) -#define CLEAR_CTR_RX(R) (R & (~USB_EPnR_CTR_RX)) -#define CLEAR_CTR_TX(R) (R & (~USB_EPnR_CTR_TX)) -#define CLEAR_CTR_RX_TX(R) (R & (~(USB_EPnR_CTR_TX | USB_EPnR_CTR_RX))) +#define RX_FLAG(epstat) (epstat & USB_EPnR_CTR_RX) +#define TX_FLAG(epstat) (epstat & USB_EPnR_CTR_TX) +#define SETUP_FLAG(epstat) (epstat & USB_EPnR_SETUP) + +// keep all DTOGs and STATs +#define KEEP_DTOG_STAT(EPnR) (EPnR & ~(USB_EPnR_STAT_RX|USB_EPnR_STAT_TX|USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX)) +#define KEEP_DTOG(EPnR) (EPnR & ~(USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX)) // USB state: uninitialized, addressed, ready for use -#define USB_DEFAULT_STATE 0 -#define USB_ADRESSED_STATE 1 -#define USB_CONFIGURE_STATE 2 +typedef enum{ + USB_STATE_DEFAULT, + USB_STATE_ADDRESSED, + USB_STATE_CONFIGURED, + USB_STATE_CONNECTED +} USB_state; // EP types #define EP_TYPE_BULK 0x00 @@ -115,7 +105,7 @@ #define LANG_US (uint16_t)0x0409 -#define _USB_STRING_(name, str) \ +#define USB_STRING(name, str) \ static const struct name \ { \ uint8_t bLength; \ @@ -125,7 +115,7 @@ static const struct name \ } \ name = {sizeof(name), 0x03, str} -#define _USB_LANG_ID_(name, lng_id) \ +#define USB_LANG_ID(name, lng_id) \ \ static const struct name \ { \ @@ -151,12 +141,8 @@ typedef struct __ep_t{ uint16_t *tx_buf; // transmission buffer address uint16_t txbufsz; // transmission buffer size uint16_t *rx_buf; // reception buffer address - uint16_t (*func)(); // endpoint action function - uint16_t status; // status flags + void (*func)(); // endpoint action function unsigned rx_cnt : 10; // received data counter - unsigned tx_flag : 1; // transmission flag - unsigned rx_flag : 1; // reception flag - unsigned setup_flag : 1; // this is setup packet (only for EP0) } ep_t; // USB status & its address @@ -165,42 +151,15 @@ typedef struct { uint16_t USB_Addr; }usb_dev_t; -typedef struct { - uint32_t dwDTERate; - uint8_t bCharFormat; - #define USB_CDC_1_STOP_BITS 0 - #define USB_CDC_1_5_STOP_BITS 1 - #define USB_CDC_2_STOP_BITS 2 - uint8_t bParityType; - #define USB_CDC_NO_PARITY 0 - #define USB_CDC_ODD_PARITY 1 - #define USB_CDC_EVEN_PARITY 2 - #define USB_CDC_MARK_PARITY 3 - #define USB_CDC_SPACE_PARITY 4 - uint8_t bDataBits; -} __attribute__ ((packed)) usb_LineCoding; - -typedef struct { - uint8_t bmRequestType; - uint8_t bNotificationType; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; -} __attribute__ ((packed)) usb_cdc_notification; - extern ep_t endpoints[]; +extern usb_dev_t USB_Dev; +extern uint8_t usbON; void USB_Init(); -uint8_t USB_GetState(); -int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, uint16_t (*func)(ep_t ep)); +void USB_ResetState(); +int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)(ep_t ep)); void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size); void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size); int EP_Read(uint8_t number, uint16_t *buf); -usb_LineCoding getLineCoding(); - -void WEAK linecoding_handler(usb_LineCoding *lc); -void WEAK clstate_handler(uint16_t val); -void WEAK break_handler(); -void WEAK vendor_handler(config_pack_t *packet); #endif // __USB_LIB_H__ diff --git a/F1-nolib/USB_HID/usbhid103.bin b/F1-nolib/USB_HID/usbhid103.bin index 580dfda12bbef92d3912b80fb465e2b713ef8294..eebdad408f99183849e13543467c4ee427c17bae 100755 GIT binary patch delta 3432 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