From d858aaa8569ddd87174de6b736bdf382b8d24408 Mon Sep 17 00:00:00 2001 From: Frank Voorburg Date: Fri, 28 Jul 2017 16:37:42 +0000 Subject: [PATCH] Refs #316. Completed the XCP on CAN transport layer implementation. git-svn-id: https://svn.code.sf.net/p/openblt/code/trunk@317 5dc33758-31d5-4daf-9ae8-b24bf3d40d73 --- Host/Source/LibOpenBLT/port/linux/critutil.c | 4 +- .../Source/LibOpenBLT/port/windows/critutil.c | 6 +- Host/Source/LibOpenBLT/xcptpcan.c | 65 +++++++++++++++++- Host/libopenblt.dll | Bin 102400 -> 102400 bytes 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/Host/Source/LibOpenBLT/port/linux/critutil.c b/Host/Source/LibOpenBLT/port/linux/critutil.c index 1f446b9d..705432ab 100644 --- a/Host/Source/LibOpenBLT/port/linux/critutil.c +++ b/Host/Source/LibOpenBLT/port/linux/critutil.c @@ -40,10 +40,10 @@ * Local data declarations ****************************************************************************************/ /** \brief Flag to determine if the critical section object was already initialized. */ -static volatile bool criticalSectionInitialized = false; +static bool criticalSectionInitialized = false; /** \brief Crital section nesting counter. ***/ -static volatile uint32_t criticalSectionNesting; +static uint32_t criticalSectionNesting; /** \brief Critical section object. */ static pthread_mutex_t mtxCritSect; diff --git a/Host/Source/LibOpenBLT/port/windows/critutil.c b/Host/Source/LibOpenBLT/port/windows/critutil.c index 7b57ba12..9e0130c2 100644 --- a/Host/Source/LibOpenBLT/port/windows/critutil.c +++ b/Host/Source/LibOpenBLT/port/windows/critutil.c @@ -40,13 +40,13 @@ * Local data declarations ****************************************************************************************/ /** \brief Flag to determine if the critical section object was already initialized. */ -static volatile bool criticalSectionInitialized = false; +static bool criticalSectionInitialized = false; /** \brief Crital section nesting counter. ***/ -static volatile uint32_t criticalSectionNesting; +static uint32_t criticalSectionNesting; /** \brief Critical section object. */ -static volatile CRITICAL_SECTION criticalSection; +static CRITICAL_SECTION criticalSection; /************************************************************************************//** diff --git a/Host/Source/LibOpenBLT/xcptpcan.c b/Host/Source/LibOpenBLT/xcptpcan.c index 0a042cd2..69947371 100644 --- a/Host/Source/LibOpenBLT/xcptpcan.c +++ b/Host/Source/LibOpenBLT/xcptpcan.c @@ -87,6 +87,18 @@ static const tCanEvents canEvents = /** \brief The settings to use in this transport layer. */ static tXcpTpCanSettings tpCanSettings; +/** \brief Flag to indicate that a response packet was received via CAN. Made volatile + * because it is shared with an event callback function that could be called from + * a different thread. + */ +static volatile bool tpCanResponseMessageReceived; + +/** \brief Buffer for storing the CAN message with response packet data. Made volatile + * because it is shared with an event callback function that could be called from + * a different thread. + */ +static volatile tCanMsg tpCanResponseMessage; + /***********************************************************************************//** ** \brief Obtains a pointer to the transport layer structure, so that it can be @@ -306,6 +318,14 @@ static bool XcpTpCanSendPacket(tXcpTransportPacket const * txPacket, { canMsg.data[idx] = txPacket->data[idx]; } + /* Enter critical section. */ + UtilCriticalSectionEnter(); + /* Reset packet received flag before transmitting the packet, to be able to detect + * its response packet. + */ + tpCanResponseMessageReceived = false; + /* Exit critical section. */ + UtilCriticalSectionExit(); /* Submit the packet for transmission on the CAN bus. */ if (!CanTransmit(&canMsg)) { @@ -321,9 +341,36 @@ static bool XcpTpCanSendPacket(tXcpTransportPacket const * txPacket, */ while (UtilTimeGetSystemTimeMs() < responseTimeoutTime) { - /* ##Vg TODO Implement packet reception. */ - break; + /* Enter critical section. */ + UtilCriticalSectionEnter(); + /* Response received? */ + if (tpCanResponseMessageReceived) + { + /* Copy the response packet. */ + rxPacket->len = tpCanResponseMessage.dlc; + for (uint8_t idx = 0; idx < rxPacket->len; idx++) + { + rxPacket->data[idx] = tpCanResponseMessage.data[idx]; + } + /* Exit critical section. */ + UtilCriticalSectionExit(); + /* Response packet receive so no need to continue loop. */ + break; + } + /* Exit critical section. */ + UtilCriticalSectionExit(); + /* Wait a little bit to not starve the CPU. */ + UtilTimeDelayMs(1); } + /* Enter critical section. */ + UtilCriticalSectionEnter(); + /* Check if a timeout occurred and no response was received. */ + if (!tpCanResponseMessageReceived) + { + result = false; + } + /* Exit critical section. */ + UtilCriticalSectionExit(); } } } @@ -364,7 +411,19 @@ static void XcpTpCanEventMessageReceived(tCanMsg const * msg) /* Check if the identifier matches the one for XCP on CAN. */ if (msg->id == tpCanRxId) { - /* ##Vg TODO process CAN message reception. */ + /* Enter critical section. */ + UtilCriticalSectionEnter(); + /* Copy to the packet response message buffer. */ + tpCanResponseMessage.id = msg->id; + tpCanResponseMessage.dlc = msg->dlc; + for (uint8_t idx = 0; idx < msg->dlc; idx++) + { + tpCanResponseMessage.data[idx] = msg->data[idx]; + } + /* Set packet received flag. */ + tpCanResponseMessageReceived = true; + /* Exit critical section. */ + UtilCriticalSectionExit(); } } /*** end of XcpTpCanEventMessageReceived ***/ diff --git a/Host/libopenblt.dll b/Host/libopenblt.dll index 7f9b29fb82884d1ba1fa8e9d74d1bc2d4063f313..a8e61e10b54ad8193f345f9c77e84385a4d9e1cf 100644 GIT binary patch delta 9938 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 delta 9737 zcmch64OCQB*YLduP*9LjK>_7g20;Pm&d;4Ycm4puUsOaHRjK2MM_D{yU!g$>v`Vwu6M0(y|dQYXWzZ| z*=O&4&fe$TI~9%HDjK`hJ&0(Y{%jW0`Mbf8@SdzU!<4N z4!&o{vqwQkmb01m*GM5td@3Q_y?@x`wG4CnD-(jxy%21zL{PUHfqnpjmer8yJ~a2> zY{aXB5rl>z_}mkL2SLD4Y-|OB^Ku0Lj6#sw6Tzce1WBO?f+@`riC|C&f)J`@$9x2H zse-rM5cCK@@M108cGnCTPn9jGK@drS)g8eY1A=3fcAdvAmJD7GpLL5MZtLN3x40hH zaGsg^yN=7XpzN+8TWeuK_b|U=q+akiv6hw5U{L<-TG-uv1PQE#TiwG3JX*&v>r$NS zQ=I!wp%C^ntIoNj*q7@d%u^)$*1-(VsNO44?7Rn+kaQHR1Mr+g`mFtVo&t;`6`&>lR&bn;6)!fQC7iapHy;lH3ap_BDJfbq*puH zR0)fHt4NPZX!Fe@jTJCc78Ov6Yh-J)WwfU|{TrHC8I4_=Bc}p($pXp93OFuHB<>aP zt4vP5utIq6D00jSqkBh3*sLx?e5>EbbwL#=-f2$%`Ysg(R)F5|p?Vavjq9jd;mKH5 z*)qJ-Z7H%eB-kc;raNA+!j0a=(|hm6LDE>;wGXWy!#2?O*$MV~O8N_`aWwtX%=i`^S^1E8sx?IC6LeeA<5o zxl{`3fJ{jIg`rnq=({b2_K@M^?ImCgO(d(9Kt<>PGJ6T^4joPU zEdfWUg4`^EpfCg3Uj&(9a$+q4YnVue7fHTc3FFDb#o!q(Cm$|`Vc{Zy#S)_;oF^j| z!-?=gB%D(E!h`Uhgk223g^wmJg)k~YL8=R3WrTr@D1;Lcv4H`F7`j`}ymJ|U8MeC) z@2{OsG>|QX-y)`vgNqk+q@3(s2(uzZGH0P= zz>!Fv^jipyNQM8+0+(j0b?sM@){hDxPCkmPEr8{6Ims@7J@Poh6-b8Okn`l*dw|jzBhCKk}V(SN@gbv8Q7x{c~nP3F60g|lB`^KaY#g8&0j}E z=ECPgbmaCt@E-cMdefMUbR9a4IgsKV%NW)TXpnmbbcN@hm2HJRFbb z!cTFF!*}K|43@EU+xux7{Wpk=%n^;0%xSpYBGIL1bD%2zdGh6K2pgs#ugr$@VZ+J% z*|2?B7V(}9KMq?>PRxa6is58F|Xn zhCG}Go_r-Ko(494A@P_7zwj@RSy`}GJB_$y!DDS4xt0k-gyH1m3|K4#SohAr!g0>? ze9G^#+k3He~OD4}R0XZ31CM1$JGtDviDe}}j zzr;>G#x8QeorJsqN~TJpBrYCVTOdb!4JCc|3R}4IvlDgC^)FF9lR% zvq({j{kgG8Z1Cmbu9A*Nlj?oPs!WW5`>K(8_Xp= zE&t7kr{tFPmi{npR#a- z9!_NJA^SzhoPvj$2-YcYkau*TpE@SEzwYT_7tw?+$7bm4`=+ui$r2=&-pPy$entDV z^i0E3CjOwc`(!0|3l6chUbr5~Uq6|?AF z8t{gd=IE}Gk_97Q>oWuQXx=UyW%H+Ky2IPWtT|KS>BzAcWpnJbwtY?~mbkpudzkFy z&Gfc1m~CDgZNwUg4?J$=_6q6Xe>bUJ-o z-mAIjTeFh^>zvuvjTtx79TiAMk9}LtU{LU&dCd5>jC)*j(K68SH63w0L*{lC&pyXB zeA1`17Q5qJ_Q&-pk6kfJEy5TT$3DF^gC4K42{=hl)Z`ZqAv&8MU0#V zY+UhtDONOI^cv*44@pnb^F#=qHyDP+zK!SfIVhSR(zCR)c3to&gB|nD-RN_38GJc^ zM|X-kdhUi*dHt1N>_)$glCUMf*49KZyz@S}Hm`}Hl~NKwd#JVxc_LSaVij_RvFIv| z6lbv3S>ihJZHR|=;{Qb4A8}AF(6XhFzaYJzq|3J&Fa0!zzhexawfUF9)dhK;lr_;Y z@p(wfk08$HAveE;t$^SH-P}s#+_{X|;M=lJTClg-iFp-Bbll%5ZA{;mD>YZ?$~aML zinHd7CEKwbS-!`|$=u$qO~Lm8@AJ}HGY%`#^(k$|vH2`s@ja-%G{>7e;EjUe{p6jz zqWP4t&bDb9#Jgz{`!olimUC-CMu+}c@Mi+Lo3v<*#`=Ws{ zCy;Oy+XatPU92;Rb(2DTu8Z~_tSB@OqSCfR_hu@K?-;fIRMeK}-An~-qh+-|@4mX!l1S(5hI>huI75A+U z!3?tb)4&Y!r=&`JU2ulhW;mMY)1xarJt$8u5mRp3d?8v|r}3e=2@MXzoCrRYnTC?57*DWC?Ef z4&3hjZT_dAm!;oy$wf;T4JSo0oQzA2Gng)v#FiF-`pZay{uITx$WFmeB|(Xef?YSV zOes>?N^*1OEoD-kWn^jCren*+wgg)lwzb%{VB3wYv$oJ; zGo{lR%Wy0Z)F|9k?k+=3I6t0dPMZ&P7aj0)(W5Bc+Gg{oX^2?JeF?$jehUr zCna<_y@{&CK-5WRw##*Cm~qFY@W6>_a;iUX(Y>1 z&$ri3KHqVt2dykQr3pQ$Z?!3R>ArwhOGl#xk4h`p%TT%^n7snqR!kt5tKqv9F;Vz= zMRpgLm|E4nhLK%CoM!FsNI%huw^swdGA#Q#H3xqeJX*W3e_x~ikh|L@_n;HQ3_P-k zp0{DHb>7k+eZGdwD&sY1Ss7!cIHuv3I1kD;6rzhhl!l8J;2NT*L#Qpte~I$_DIfPp zCw~CtQwx!Q3i(Zxje+iDcl1LxH5=KN|3$sE&CyB)Zn^|o|Be1rl<#oy9sjC-H)T`D zQGeUN=x=kBQ$7s`y`~i5!=H{~_~KcwdH+8Gjq$`e7%59S1K{V1U%NkD!fP<#!<5|yor36i$OzZ1OylU60MH(}cf>_Qd^y(xj1NrS>FVnU@I zX^H$}&;UBfJKgsNFLO|feJdym5P{XY2*IMJl~d1`)p6ldr)T&uk7a$ zBiqc%uu#i!?E?^X`bWBAeh24<9x}*Y(?8fHZIRO<-YC7TS5Zjr_c*5^Na#<3*rfgM{k2%o1!P3%4zYJ z%xtptMFWo64k66mFoewA4jVQM?6q(^BWuG^^yDku4lNrFQvOEdH^7FC$ZvRx-vBKe z6~xg1j*SU@4&vi2*R_7Oh=PR*2rx^6A>+d~sFhsl~#@$rI30zxl$=FA0; zWQk4pa9SsJl18TBBj&>D$?!eqKl=@Ubv|7T0_+^j`o*{!<&TVrqhi?Lo^ z#=_aHTpt?J6H++eqi>Y8nkT)y3lWrs zUs&*s_BvDIr8;x60tsu`D&tGinDD%pKx`N=fIj83PXB?@RmI)rBTe%*R*!5bY8Zy| zG&T$ll$2R~ah_MP9N`7(wt?QIeO`bY4S{Su{MxXoH}wV!LIWnbbUUesojWok>XF(+ zK{Mr}aR|{WH0v@o>*5;~;J0%sd2kdK?HtQy+fVLX%F?H}U3;*zzrQPrW#>ZM?mg^W zJM8%mAMDRI@@~EJP>=N8-*p#lS_o&GlN6MS-!W=^D00DLVdrGhF^n~@i4r6}MJ{;c zf%=6t84dJqqAlJN(I zB74UHK}H!^HeF%3*;EhzJitptTgDBf@51mj*V_lbbdP0=!2i%NlHCL&4h;(`lV+Ex z&bYpJ1>nVvzW1N^D6?-mblg3rTrvtz=u!z8_Cjz?3eeevH^ zLH4~D7rEKbTr{%o6-|t+`6I%*7rlnn>$QtA60)K54_LZCQm|z{#&(M?3&U)fZcn?|z zRA=h!6R-F5>D@=XH{s=rNh5czIoI)bvBOt z-!?juK;Hd;UT5~Z99e^X(|&mFen0OK2N+ovHtHYL-S_L(jt@Owf=l;h$XbuU z_xD5Cc6;v+hOz`B#y?nD&tBgCS~XjsovNRq&(T-vYxP_7JN3=_m-TPy&*?Aef7J&X zLJX0H35LmrX$Fg-+_1(_WL#<7Z#-eVZoIjDQVlD&GDQqC7gH-zk*1iT*r3>^P%FnP zGnEC(BIPn=gL1d>u+pwPsl26RRoztHDy?d|YMyGLs!X*;^|tDL)jw3XR6nR<)PmZm z96(2dtk(q-vNbrrf=-DcfR-C>4@ol z(wtSQk{Yic!{Gs6VMctN%*R z)GS?h)6)5oKRx zurgN3DYeRx$~0w`GDn$*84#~pq$*W4s$NvRta_w!Q+uh0s+HpAG74H@IYu2+Jy%3@8V7Id+~wzQ0x$i-c#R8 z@23yahhY?A^-8^bfbd}<0Zhnb^r=PS*E z*=!zR9%r6xo^H-DFEB4LuP|5Q{@-lgZf-KaY(8Ut*ZixQX>`>VM$t{-t>~i&P=qR? z6mbf*La#_tj8;rkWLXun6$=$h6w4Kr3cKQ*;s-^$!jtR8`Efy97#GFGa!O9X+(_oe zaB18WE{9vnm2&I3?c4$G70i|s+;#327ov<(#^WNbSMF85g!%HW@{;np^0x8^2wN|xPwO@5obw<@k&8ZFQ(dsm{HA|hX-lu+5{krQmR0$xwAiONtg;wD^;hyjl zE|QxrN*Als>(a4=oz}gpyP@kL2IKYx@l#Qy7qLv=)khkV4a=;Cw+vqxelq7>o^u%VkFo6BGyP=hg+=F&(3{N3<}v0p^Az*9<~!!!%*+vn3BX<6 zPqB=%a_hLw+zxIJ#^-O`Y3?${$HCp>e&T-NqLf3GMl9{Ku(oW*(%z^%ptPP+UR8dk z{HO9;Wv*(8Y86)Xt*S#bOv6m9>iLEu!!kpeVYOkM;W