]> rtime.felk.cvut.cz Git - arc.git/blob - communication/CanIf/CanIf.c
Merge with 7250e20c985c4a8c7c4d9655ca50c49ef37a0443
[arc.git] / communication / CanIf / CanIf.c
1 /* -------------------------------- Arctic Core ------------------------------\r
2  * Arctic Core - the open source AUTOSAR platform http://arccore.com\r
3  *\r
4  * Copyright (C) 2009  ArcCore AB <contact@arccore.com>\r
5  *\r
6  * This source code is free software; you can redistribute it and/or modify it\r
7  * under the terms of the GNU General Public License version 2 as published by the\r
8  * Free Software Foundation; See <http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>.\r
9  *\r
10  * This program is distributed in the hope that it will be useful, but\r
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r
13  * for more details.\r
14  * -------------------------------- Arctic Core ------------------------------*/\r
15 \r
16 \r
17 \r
18 \r
19 \r
20 \r
21 \r
22 \r
23 #include "Det.h"\r
24 #include "CanIf.h"\r
25 \r
26 #include "Can.h"\r
27 #include "CanIf_Cbk.h"\r
28 #include <string.h>\r
29 \r
30 #include "debug.h"\r
31 #include "PduR.h"\r
32 \r
33 #if defined(USE_CANTP)\r
34 #include "CanTp_Cbk.h"\r
35 #endif\r
36 \r
37 #if defined(USE_J1939TP)\r
38 #include "J1939Tp_Cbk.h"\r
39 #endif\r
40 \r
41 #if defined(USE_CANNM)\r
42 #include "CanNm.h"\r
43 #endif\r
44 \r
45 #if 0\r
46 // TODO: Include upper layer functions, See CANIF208 and CANIF233\r
47 #include "PduR_CanIf.h"\r
48 #include "CanNm.h"\r
49 #include "CanTp.h"\r
50 \r
51 #include "PduR_Cbk.h"\r
52 #include "CanNm_Cbk.h"\r
53 #include "CanTp_Cbk.h"\r
54 #endif\r
55 \r
56 #if  ( CANIF_DEV_ERROR_DETECT == STD_ON )\r
57 #define VALIDATE(_exp,_api,_err ) \\r
58         if( !(_exp) ) { \\r
59           Det_ReportError(MODULE_ID_CANIF, 0, _api, _err); \\r
60           return E_NOT_OK; \\r
61         }\r
62 \r
63 #define VALIDATE_NO_RV(_exp,_api,_err ) \\r
64   if( !(_exp) ) { \\r
65           Det_ReportError(MODULE_ID_CANIF, 0, _api, _err); \\r
66           return; \\r
67         }\r
68 #define DET_REPORTERROR(_x,_y,_z,_q) Det_ReportError(_x, _y, _z, _q)\r
69 \r
70 #else\r
71 #define VALIDATE(_exp,_api,_err )\r
72 #define VALIDATE_NO_RV(_exp,_api,_err )\r
73 #define DET_REPORTERROR(_x,_y,_z,_q)\r
74 #endif\r
75 \r
76 \r
77 // Helper to get the Can Controller refered to by a CanIf Channel\r
78 #define ARC_GET_CHANNEL_CONTROLLER(_channel) \\r
79         CanIf_ConfigPtr->Arc_ChannelToControllerMap[_channel]\r
80 \r
81 /* Global configure */\r
82 static const CanIf_ConfigType *CanIf_ConfigPtr;\r
83 \r
84 // Struct of controller private data.\r
85 typedef struct\r
86 {\r
87   CanIf_ControllerModeType  ControllerMode;\r
88   CanIf_ChannelGetModeType  PduMode;\r
89 } CanIf_ChannelPrivateType;\r
90 \r
91 typedef struct\r
92 {\r
93   boolean initRun;\r
94   CanIf_ChannelPrivateType channelData[CANIF_CHANNEL_CNT];\r
95 } CanIf_GlobalType;\r
96 \r
97 void CanIf_PreInit_InitController(uint8 Controller, uint8 ConfigurationIndex);\r
98 \r
99 static CanIf_Arc_ChannelIdType CanIf_Arc_FindHrhChannel( Can_Arc_HRHType hrh )\r
100 {\r
101   const CanIf_InitHohConfigType *hohConfig;\r
102   const CanIf_HrhConfigType *hrhConfig;\r
103 \r
104   // foreach(hoh){ foreach(hrh in hoh) {} }\r
105   hohConfig = CanIf_ConfigPtr->InitConfig->CanIfHohConfigPtr;\r
106   hohConfig--;\r
107   do\r
108   {\r
109     hohConfig++;\r
110 \r
111     hrhConfig = hohConfig->CanIfHrhConfig;\r
112     hrhConfig--;\r
113     do\r
114     {\r
115       hrhConfig++;\r
116       if (hrhConfig->CanIfHrhIdSymRef == hrh){\r
117         return hrhConfig->CanIfCanControllerHrhIdRef;\r
118       }\r
119     } while(!hrhConfig->CanIf_Arc_EOL);\r
120   } while(!hohConfig->CanIf_Arc_EOL);\r
121 \r
122   DET_REPORTERROR(MODULE_ID_CANIF, 0, CANIF_RXINDICATION_ID, CANIF_E_PARAM_HRH);\r
123 \r
124   return (CanIf_Arc_ChannelIdType) -1;\r
125 }\r
126 \r
127 // Global config\r
128 CanIf_GlobalType CanIf_Global;\r
129 \r
130 void CanIf_Init(const CanIf_ConfigType *ConfigPtr)\r
131 {\r
132   VALIDATE_NO_RV(ConfigPtr != 0, CANIF_INIT_ID, CANIF_E_PARAM_POINTER); // Only PostBuild case supported\r
133 \r
134   CanIf_ConfigPtr = ConfigPtr;\r
135 \r
136   for (uint8 i = 0; i < CANIF_CHANNEL_CNT; i++)\r
137   {\r
138     CanIf_Global.channelData[i].ControllerMode = CANIF_CS_STOPPED;\r
139     CanIf_Global.channelData[i].PduMode = CANIF_GET_OFFLINE;\r
140     CanIf_PreInit_InitController(i, CanIf_ConfigPtr->Arc_ChannelDefaultConfIndex[i]);\r
141   }\r
142 \r
143 \r
144   CanIf_Global.initRun = TRUE;\r
145 }\r
146 \r
147 \r
148 \r
149 \r
150 //-------------------------------------------------------------------\r
151 /*\r
152  * Controller :: CanIf_Arc_ChannelIdType (CanIf-specific id to abstract from Can driver/controllers)\r
153  * ConfigurationIndex :: CanIf_Arc_ConfigurationIndexType\r
154  */\r
155 void CanIf_InitController(uint8 Controller, uint8 ConfigurationIndex)\r
156 {\r
157   // We call this a CanIf channel. Hopefully makes it easier to follow.\r
158   CanIf_Arc_ChannelIdType channel = (CanIf_Arc_ChannelIdType) Controller;\r
159   CanIf_ControllerModeType mode;\r
160 \r
161   VALIDATE_NO_RV(CanIf_Global.initRun, CANIF_INIT_CONTROLLER_ID, CANIF_E_UNINIT );\r
162   VALIDATE_NO_RV(channel < CANIF_CHANNEL_CNT, CANIF_INIT_CONTROLLER_ID, CANIF_E_PARAM_CONTROLLER);\r
163   VALIDATE_NO_RV(ConfigurationIndex < CANIF_CHANNEL_CONFIGURATION_CNT, CANIF_INIT_CONTROLLER_ID, CANIF_E_PARAM_POINTER);\r
164 \r
165   if (CanIf_GetControllerMode(channel, &mode) == E_OK)\r
166   {\r
167     if (mode == CANIF_CS_STARTED)\r
168     {\r
169       CanIf_SetControllerMode(channel, CANIF_CS_STOPPED); // CANIF092\r
170     }\r
171     else if (mode != CANIF_CS_STOPPED)\r
172     {\r
173       VALIDATE_NO_RV(FALSE, CANIF_INIT_CONTROLLER_ID, CANIF_E_PARAM_CONTROLLER_MODE); // CANIF092\r
174     }\r
175   }\r
176   else\r
177   {\r
178     VALIDATE_NO_RV(FALSE, CANIF_INIT_CONTROLLER_ID, CANIF_E_PARAM_CONTROLLER_MODE);\r
179   }\r
180 \r
181   // CANIF293: ..Subsequently the CAN Interface calls the corresponding\r
182   //             CAN Driver initialization services.\r
183 \r
184   // CANIF066: The CAN Interface has access to the CAN Driver configuration data. All\r
185   // public CAN Driver configuration data are described in [8] Specification of CAN Driver.\r
186 \r
187   // Grab the configuration from the Can Controller\r
188   const Can_ControllerConfigType *canConfig;\r
189   const CanControllerIdType canControllerId = ARC_GET_CHANNEL_CONTROLLER(channel);\r
190 \r
191   // Validate that the configuration at the index match the right channel\r
192   VALIDATE_NO_RV(CanIf_ConfigPtr->ControllerConfig[ConfigurationIndex].CanIfControllerIdRef == channel, CANIF_INIT_CONTROLLER_ID, CANIF_E_PARAM_CONTROLLER);\r
193 \r
194   canConfig = CanIf_ConfigPtr->ControllerConfig[ConfigurationIndex].CanIfInitControllerRef;\r
195 \r
196   // Validate that the CanIfControllerConfig points to configuration for the right Can Controller\r
197   VALIDATE_NO_RV(canConfig->CanControllerId == canControllerId, CANIF_INIT_CONTROLLER_ID, CANIF_E_PARAM_CONTROLLER);\r
198 \r
199   Can_InitController(canControllerId, canConfig);\r
200 \r
201   // Set mode to stopped\r
202   CanIf_SetControllerMode(channel, CANIF_CS_STOPPED);\r
203 }\r
204 \r
205 void CanIf_PreInit_InitController(uint8 Controller, uint8 ConfigurationIndex){\r
206         // We call this a CanIf channel. Hopefully makes it easier to follow.\r
207         CanIf_Arc_ChannelIdType channel = (CanIf_Arc_ChannelIdType) Controller;\r
208 \r
209         VALIDATE_NO_RV(channel < CANIF_CHANNEL_CNT, CANIF_INIT_ID, CANIF_E_PARAM_CONTROLLER);\r
210         VALIDATE_NO_RV(ConfigurationIndex < CANIF_CHANNEL_CONFIGURATION_CNT, CANIF_INIT_ID, CANIF_E_PARAM_POINTER);\r
211 \r
212 \r
213         const CanControllerIdType canControllerId = ARC_GET_CHANNEL_CONTROLLER(channel);\r
214         // Validate that the configuration at the index match the right channel\r
215         VALIDATE_NO_RV(CanIf_ConfigPtr->ControllerConfig[ConfigurationIndex].CanIfControllerIdRef == channel, CANIF_INIT_CONTROLLER_ID, CANIF_E_PARAM_CONTROLLER);\r
216         const Can_ControllerConfigType *canConfig = CanIf_ConfigPtr->ControllerConfig[ConfigurationIndex].CanIfInitControllerRef;\r
217         // Validate that the CanIfControllerConfig points to configuration for the right Can Controller\r
218         VALIDATE_NO_RV(canConfig->CanControllerId == canControllerId, CANIF_INIT_CONTROLLER_ID, CANIF_E_PARAM_CONTROLLER);\r
219 \r
220         Can_InitController(canControllerId, canConfig);\r
221 }\r
222 \r
223 //-------------------------------------------------------------------\r
224 \r
225 Std_ReturnType CanIf_SetControllerMode(uint8 Controller,\r
226     CanIf_ControllerModeType ControllerMode)\r
227 {\r
228   // We call this a CanIf channel. Hopefully makes it easier to follow.\r
229   CanIf_Arc_ChannelIdType channel = (CanIf_Arc_ChannelIdType) Controller;\r
230 \r
231 \r
232   CanIf_ControllerModeType oldMode;\r
233 \r
234   VALIDATE( CanIf_Global.initRun, CANIF_SET_CONTROLLER_MODE_ID, CANIF_E_UNINIT );\r
235   VALIDATE( channel < CANIF_CHANNEL_CNT, CANIF_SET_CONTROLLER_MODE_ID, CANIF_E_PARAM_CONTROLLER );\r
236 \r
237   oldMode = CanIf_Global.channelData[channel].ControllerMode;\r
238 \r
239   if (oldMode == CANIF_CS_UNINIT)\r
240   {\r
241     VALIDATE(FALSE, CANIF_SET_CONTROLLER_MODE_ID, CANIF_E_UNINIT); // See figure 32, 33\r
242     return E_NOT_OK;\r
243   }\r
244   CanControllerIdType canControllerId = ARC_GET_CHANNEL_CONTROLLER(Controller);\r
245   switch (ControllerMode)\r
246   {\r
247   case CANIF_CS_STARTED:   // Figure 32\r
248   {\r
249     switch (oldMode)\r
250     {\r
251       case CANIF_CS_SLEEP:\r
252         if (Can_SetControllerMode(canControllerId, CAN_T_STOP) == CAN_NOT_OK){\r
253           return E_NOT_OK;\r
254         }\r
255         CanIf_Global.channelData[channel].ControllerMode = CANIF_CS_STOPPED;\r
256         break;\r
257       default:\r
258         // Just fall through\r
259         break;\r
260     }\r
261 \r
262     CanIf_SetPduMode(channel, CANIF_SET_ONLINE);\r
263     if (Can_SetControllerMode(canControllerId, CAN_T_START) == CAN_NOT_OK){\r
264       return E_NOT_OK;\r
265     }\r
266     CanIf_Global.channelData[channel].ControllerMode = CANIF_CS_STARTED;\r
267   }\r
268   break;\r
269 \r
270   case CANIF_CS_SLEEP: // Figure 33\r
271   {\r
272     switch (oldMode) {\r
273       case CANIF_CS_STARTED:\r
274         if (Can_SetControllerMode(canControllerId, CAN_T_STOP) == CAN_NOT_OK){\r
275           return E_NOT_OK;\r
276         }\r
277         CanIf_Global.channelData[channel].ControllerMode = CANIF_CS_STOPPED;\r
278         break;\r
279       default:\r
280         // Just fall through for other cases\r
281         break;\r
282     }\r
283 \r
284     if (Can_SetControllerMode(canControllerId, CAN_T_SLEEP) == CAN_NOT_OK){\r
285       return E_NOT_OK;\r
286     }\r
287     CanIf_Global.channelData[channel].ControllerMode = CANIF_CS_SLEEP;\r
288   }\r
289 \r
290   case CANIF_CS_STOPPED:\r
291   {\r
292     switch (oldMode)\r
293     {\r
294       case CANIF_CS_SLEEP:\r
295         if (Can_SetControllerMode(canControllerId, CAN_T_WAKEUP) == CAN_NOT_OK){\r
296           return E_NOT_OK;\r
297         }\r
298         break;\r
299       default:\r
300         // Just fall through for other cases\r
301         break;\r
302     }\r
303 \r
304     CanIf_SetPduMode(channel, CANIF_SET_OFFLINE);\r
305     if (Can_SetControllerMode(canControllerId, CAN_T_STOP) == CAN_NOT_OK){\r
306       return E_NOT_OK;\r
307     }\r
308     CanIf_Global.channelData[channel].ControllerMode = CANIF_CS_STOPPED;\r
309     break;\r
310   }\r
311 \r
312   case CANIF_CS_UNINIT:\r
313     // Just fall through\r
314     break;\r
315   }\r
316   return E_OK;\r
317 }\r
318 \r
319 //-------------------------------------------------------------------\r
320 \r
321 Std_ReturnType CanIf_GetControllerMode(uint8 Controller,\r
322     CanIf_ControllerModeType *ControllerModePtr)\r
323 {\r
324   // We call this a CanIf channel. Hopefully makes it easier to follow.\r
325   CanIf_Arc_ChannelIdType channel = (CanIf_Arc_ChannelIdType) Controller;\r
326 \r
327   VALIDATE(CanIf_Global.initRun, CANIF_GET_CONTROLLER_MODE_ID, CANIF_E_UNINIT );\r
328   VALIDATE(channel < CANIF_CHANNEL_CNT, CANIF_GET_CONTROLLER_MODE_ID, CANIF_E_PARAM_CONTROLLER );\r
329   VALIDATE(ControllerModePtr != NULL, CANIF_GET_CONTROLLER_MODE_ID, CANIF_E_PARAM_POINTER );\r
330 \r
331   *ControllerModePtr = CanIf_Global.channelData[channel].ControllerMode;\r
332 \r
333   return E_OK;\r
334 }\r
335 \r
336 //-------------------------------------------------------------------\r
337 /**\r
338  * Matches a Tx PDU id agaist the ones that are in the database.\r
339  *\r
340  * @returns Ptr a TxPdu\r
341  */\r
342 #if ( CANIF_ARC_RUNTIME_PDU_CONFIGURATION == STD_ON )\r
343 CanIf_TxPduConfigType * CanIf_FindTxPduEntry(PduIdType id)\r
344 #else\r
345 static const CanIf_TxPduConfigType * CanIf_FindTxPduEntry(PduIdType id)\r
346 #endif\r
347 {\r
348         if (id >= CanIf_ConfigPtr->InitConfig->CanIfNumberOfCanTXPduIds) {\r
349                 return NULL;\r
350         } else {\r
351                 return &CanIf_ConfigPtr->InitConfig->CanIfTxPduConfigPtr[id];\r
352         }\r
353 }\r
354 \r
355
356 #if ( CANIF_ARC_RUNTIME_PDU_CONFIGURATION == STD_ON )\r
357 CanIf_RxPduConfigType * CanIf_FindRxPduEntry(PduIdType id) {\r
358         if (id >= CanIf_ConfigPtr->InitConfig->CanIfNumberOfCanRxPduIds) {\r
359                 return NULL;\r
360         } else {\r
361                 return &CanIf_ConfigPtr->InitConfig->CanIfRxPduConfigPtr[id];\r
362         }\r
363 }\r
364
365 const CanIf_HrhConfigType* CanIf_Arc_GetReceiveHandler(CanIf_Arc_ChannelIdType Channel) {
366   const CanIf_InitHohConfigType *hohConfig;
367   const CanIf_HrhConfigType *hrhConfig;
368
369   // foreach(hoh){ foreach(hrh in hoh) {} }
370   hohConfig = CanIf_ConfigPtr->InitConfig->CanIfHohConfigPtr;
371   hohConfig--;
372   do
373   {
374         hohConfig++;
375
376         hrhConfig = hohConfig->CanIfHrhConfig;
377     hrhConfig--;
378     do
379     {
380       hrhConfig++;
381       if (hrhConfig->CanIfCanControllerHrhIdRef == Channel)
382         return hrhConfig;
383         } while(!hrhConfig->CanIf_Arc_EOL);
384
385   } while(!hohConfig->CanIf_Arc_EOL);
386
387   DET_REPORTERROR(MODULE_ID_CANIF, 0, 0xFF, CANIF_E_PARAM_HRH);
388
389   return NULL;
390 }
391
392 const CanIf_HthConfigType* CanIf_Arc_GetTransmitHandler(CanIf_Arc_ChannelIdType Channel) {
393   const CanIf_InitHohConfigType *hohConfig;
394   const CanIf_HthConfigType *hthConfig;
395
396   // foreach(hoh){ foreach(hrh in hoh) {} }
397   hohConfig = CanIf_ConfigPtr->InitConfig->CanIfHohConfigPtr;
398   hohConfig--;
399   do
400   {
401         hohConfig++;
402
403         hthConfig = hohConfig->CanIfHthConfig;
404         hthConfig--;
405     do
406     {
407         hthConfig++;
408       if (hthConfig->CanIfCanControllerIdRef == Channel)
409         return hthConfig;
410         } while(!hthConfig->CanIf_Arc_EOL);
411
412   } while(!hohConfig->CanIf_Arc_EOL);
413
414   DET_REPORTERROR(MODULE_ID_CANIF, 0, 0xFF, CANIF_E_PARAM_HTH);
415
416   return NULL;
417 }
418 #endif
419
420 //-------------------------------------------------------------------\r
421 \r
422 Std_ReturnType CanIf_Transmit(PduIdType CanTxPduId,\r
423     const PduInfoType *PduInfoPtr)\r
424 {\r
425   Can_PduType canPdu;\r
426   const CanIf_TxPduConfigType *txEntry;\r
427   CanIf_ControllerModeType csMode;\r
428   CanIf_ChannelGetModeType pduMode;\r
429 \r
430   VALIDATE(CanIf_Global.initRun, CANIF_TRANSMIT_ID, CANIF_E_UNINIT );\r
431   VALIDATE((PduInfoPtr != 0), CANIF_TRANSMIT_ID, CANIF_E_PARAM_POINTER );\r
432 \r
433   // Get the controller from L-PDU handle\r
434   txEntry = CanIf_FindTxPduEntry(CanTxPduId);\r
435 \r
436   if (txEntry == 0)\r
437   {\r
438     VALIDATE(FALSE, CANIF_TRANSMIT_ID, CANIF_E_INVALID_TXPDUID);\r
439     return E_NOT_OK;\r
440   }\r
441 \r
442   CanIf_Arc_ChannelIdType channel = txEntry->CanIfCanTxPduHthRef->CanIfCanControllerIdRef;\r
443 \r
444   // Get and verify the controller mode\r
445   if (CanIf_GetControllerMode(channel, &csMode) == E_NOT_OK){\r
446     return E_NOT_OK;\r
447   }\r
448 \r
449   if (csMode != CANIF_CS_STARTED){  // CANIF_161\r
450     return E_NOT_OK;\r
451   }\r
452 \r
453   // Get and verify the PDU channel mode control\r
454   if (CanIf_GetPduMode(channel, &pduMode) == E_NOT_OK){\r
455     return E_NOT_OK;\r
456   }\r
457 \r
458   if ((pduMode != CANIF_GET_TX_ONLINE) && (pduMode != CANIF_GET_ONLINE)){\r
459     return E_NOT_OK;\r
460   }\r
461 \r
462   canPdu.id = txEntry->CanIfCanTxPduIdCanId;\r
463 \r
464   canPdu.length = PduInfoPtr->SduLength;\r
465   canPdu.sdu = PduInfoPtr->SduDataPtr;\r
466   canPdu.swPduHandle = CanTxPduId;\r
467 \r
468   Can_ReturnType rVal = Can_Write(txEntry->CanIfCanTxPduHthRef->CanIfHthIdSymRef, &canPdu);\r
469 \r
470   if (rVal == CAN_NOT_OK){\r
471     return E_NOT_OK;\r
472   }\r
473 \r
474   if (rVal == CAN_BUSY)  // CANIF 082, CANIF 161\r
475   {\r
476     // Tx buffering not supported so just return.\r
477     return E_NOT_OK;\r
478   }\r
479 \r
480   return E_OK;\r
481 }\r
482 \r
483 //-------------------------------------------------------------------\r
484 \r
485 #if ( CANIF_READRXPDU_DATA_API == STD_ON )\r
486 Std_ReturnType CanIf_ReadRxPduData(PduIdType CanRxPduId,\r
487     PduInfoType *PduInfoPtr)\r
488 {\r
489   VALIDATE(FALSE, CANIF_READTXPDUDATA_ID, CANIF_E_NOK_NOSUPPORT);\r
490   VALIDATE(CanIf_Global.initRun == STD_ON, CANIF_READTXPDUDATA_ID, CANIF_E_UNINIT );\r
491   VALIDATE(PduInfoPtr != 0, CANIF_READTXPDUDATA_ID, CANIF_E_PARAM_POINTER );\r
492 \r
493   // This function is not supported\r
494 \r
495   return E_NOT_OK;\r
496 }\r
497 #endif\r
498 \r
499 //-------------------------------------------------------------------\r
500 \r
501 #if ( CANIF_READTXPDU_NOTIFY_STATUS_API == STD_ON )\r
502 CanIf_NotifStatusType CanIf_ReadTxNotifStatus(PduIdType CanTxPduId)\r
503 {\r
504   const CanIf_TxPduConfigType *txEntry;\r
505   VALIDATE(FALSE, CANIF_READTXNOTIFSTATUS_ID, CANIF_E_NOK_NOSUPPORT);\r
506   VALIDATE(CanIf_Global.initRun, CANIF_READTXNOTIFSTATUS_ID, CANIF_E_UNINIT );\r
507 \r
508   // Get the controller from L-PDU handle\r
509   txEntry = CanIf_FindTxPduEntry(CanTxPduId);\r
510 \r
511   if (txEntry == 0)\r
512   {\r
513     VALIDATE(FALSE, CANIF_READTXNOTIFSTATUS_ID, CANIF_E_INVALID_TXPDUID);\r
514     return CANIF_NO_NOTIFICATION;\r
515   }\r
516 \r
517   if (txEntry->CanIfReadTxPduNotifyStatus == FALSE)\r
518   {\r
519     VALIDATE(FALSE, CANIF_READTXNOTIFSTATUS_ID, CANIF_E_INVALID_TXPDUID);\r
520     return CANIF_NO_NOTIFICATION;\r
521   }\r
522 \r
523   // This function is not supported\r
524 \r
525   return CANIF_NO_NOTIFICATION;\r
526 }\r
527 #endif\r
528 \r
529 //-------------------------------------------------------------------\r
530 \r
531 #if ( CANIF_READRXPDU_NOTIFY_STATUS_API == STD_ON )\r
532 CanIf_NotifStatusType CanIf_ReadRxNotifStatus(PduIdType CanRxPduId)\r
533 {\r
534   VALIDATE(FALSE, CANIF_READRXNOTIFSTATUS_ID, CANIF_E_NOK_NOSUPPORT);\r
535   VALIDATE(CanIf_Global.initRun, CANIF_READRXNOTIFSTATUS_ID, CANIF_E_UNINIT );\r
536 \r
537   return CANIF_NO_NOTIFICATION;\r
538 }\r
539 #endif\r
540 \r
541 //-------------------------------------------------------------------\r
542 \r
543 Std_ReturnType CanIf_SetPduMode(uint8 Controller,\r
544     CanIf_ChannelSetModeType PduModeRequest)\r
545 {\r
546   // We call this a CanIf channel. Hopefully makes it easier to follow.\r
547   CanIf_Arc_ChannelIdType channel = (CanIf_Arc_ChannelIdType) Controller;\r
548 \r
549   VALIDATE( CanIf_Global.initRun, CANIF_SETPDUMODE_ID, CANIF_E_UNINIT );\r
550   VALIDATE( channel < CANIF_CHANNEL_CNT, CANIF_SETPDUMODE_ID, CANIF_E_PARAM_CONTROLLER );\r
551 \r
552   CanIf_ChannelGetModeType oldMode = CanIf_Global.channelData[channel].PduMode;\r
553 \r
554   switch(PduModeRequest)\r
555   {\r
556   case CANIF_SET_OFFLINE:\r
557     CanIf_Global.channelData[channel].PduMode = CANIF_GET_OFFLINE;\r
558     break;\r
559   case CANIF_SET_RX_OFFLINE:\r
560     if (oldMode == CANIF_GET_RX_ONLINE){\r
561       CanIf_Global.channelData[channel].PduMode = CANIF_GET_OFFLINE;\r
562     } else if (oldMode == CANIF_GET_ONLINE){\r
563       CanIf_Global.channelData[channel].PduMode = CANIF_GET_TX_ONLINE;\r
564     } else if (oldMode == CANIF_GET_OFFLINE_ACTIVE_RX_ONLINE){\r
565       CanIf_Global.channelData[channel].PduMode = CANIF_GET_OFFLINE_ACTIVE;\r
566     }\r
567 \r
568     // Other oldmodes don't care\r
569     break;\r
570   case CANIF_SET_RX_ONLINE:\r
571     if (oldMode == CANIF_GET_OFFLINE){\r
572       CanIf_Global.channelData[channel].PduMode = CANIF_GET_RX_ONLINE;\r
573     } else if (oldMode == CANIF_GET_TX_ONLINE) {\r
574       CanIf_Global.channelData[channel].PduMode = CANIF_GET_ONLINE;\r
575     } else if (oldMode == CANIF_GET_OFFLINE_ACTIVE){\r
576       CanIf_Global.channelData[channel].PduMode = CANIF_GET_OFFLINE_ACTIVE_RX_ONLINE;\r
577     }\r
578 \r
579     // Other oldmodes don't care\r
580     break;\r
581   case CANIF_SET_TX_OFFLINE:\r
582     if (oldMode == CANIF_GET_TX_ONLINE){\r
583       CanIf_Global.channelData[channel].PduMode = CANIF_GET_OFFLINE;\r
584     } else if (oldMode == CANIF_GET_ONLINE) {\r
585       CanIf_Global.channelData[channel].PduMode = CANIF_GET_RX_ONLINE;\r
586     } else if (oldMode == CANIF_GET_OFFLINE_ACTIVE) {\r
587       CanIf_Global.channelData[channel].PduMode = CANIF_GET_OFFLINE;\r
588     } else if (oldMode == CANIF_GET_OFFLINE_ACTIVE_RX_ONLINE) {\r
589       CanIf_Global.channelData[channel].PduMode = CANIF_GET_RX_ONLINE;\r
590     }\r
591 \r
592     // Other oldmodes don't care\r
593     break;\r
594   case CANIF_SET_TX_ONLINE:\r
595     if (oldMode == CANIF_GET_OFFLINE){\r
596       CanIf_Global.channelData[channel].PduMode = CANIF_GET_TX_ONLINE;\r
597     } else if (oldMode == CANIF_GET_RX_ONLINE) {\r
598       CanIf_Global.channelData[channel].PduMode = CANIF_GET_ONLINE;\r
599     } else if (oldMode == CANIF_GET_OFFLINE_ACTIVE) {\r
600       CanIf_Global.channelData[channel].PduMode = CANIF_GET_TX_ONLINE;\r
601     } else if (oldMode == CANIF_GET_OFFLINE_ACTIVE_RX_ONLINE) {\r
602       CanIf_Global.channelData[channel].PduMode = CANIF_GET_ONLINE;\r
603     }\r
604 \r
605     // Other oldmodes don't care\r
606     break;\r
607   case CANIF_SET_ONLINE:\r
608     CanIf_Global.channelData[channel].PduMode = CANIF_GET_ONLINE;\r
609     break;\r
610 \r
611   case CANIF_SET_TX_OFFLINE_ACTIVE:\r
612     if (oldMode == CANIF_GET_OFFLINE) {\r
613       CanIf_Global.channelData[channel].PduMode = CANIF_GET_OFFLINE_ACTIVE;\r
614     } else if (oldMode == CANIF_GET_RX_ONLINE) {\r
615       CanIf_Global.channelData[channel].PduMode = CANIF_GET_OFFLINE_ACTIVE_RX_ONLINE;\r
616     } else if (oldMode == CANIF_GET_TX_ONLINE) {\r
617       CanIf_Global.channelData[channel].PduMode = CANIF_GET_OFFLINE_ACTIVE;\r
618     } else if (oldMode == CANIF_GET_ONLINE) {\r
619       CanIf_Global.channelData[channel].PduMode = CANIF_GET_OFFLINE_ACTIVE_RX_ONLINE;\r
620     }\r
621 \r
622     // Other oldmodes don't care\r
623     break;\r
624   }\r
625 \r
626   return E_OK;\r
627 }\r
628 \r
629 //-------------------------------------------------------------------\r
630 \r
631 Std_ReturnType CanIf_GetPduMode(uint8 Controller,\r
632     CanIf_ChannelGetModeType *PduModePtr)\r
633 {\r
634   // We call this a CanIf channel. Hopefully makes it easier to follow.\r
635   CanIf_Arc_ChannelIdType channel = (CanIf_Arc_ChannelIdType) Controller;\r
636 \r
637   VALIDATE( CanIf_Global.initRun, CANIF_GETPDUMODE_ID, CANIF_E_UNINIT );\r
638   VALIDATE( channel < CANIF_CHANNEL_CNT, CANIF_GETPDUMODE_ID, CANIF_E_PARAM_CONTROLLER );\r
639 \r
640   *PduModePtr = CanIf_Global.channelData[channel].PduMode;\r
641 \r
642   return E_OK;\r
643 }\r
644 \r
645 #if ( CANIF_ARC_RUNTIME_PDU_CONFIGURATION == STD_ON )
646 void CanIf_SetDynamicTxId(PduIdType CanTxPduId, Can_IdType CanId)\r
647 {\r
648   CanIf_TxPduConfigType *txEntry;\r
649   VALIDATE_NO_RV(CanIf_Global.initRun, CANIF_SETDYNAMICTX_ID, CANIF_E_UNINIT );\r
650 \r
651   // Get the controller from L-PDU handle\r
652   txEntry = CanIf_FindTxPduEntry(CanTxPduId);\r
653 \r
654   if (txEntry == 0)\r
655   {\r
656     VALIDATE_NO_RV(FALSE, CANIF_SETDYNAMICTX_ID, CANIF_E_INVALID_TXPDUID);\r
657     return;\r
658   }\r
659 \r
660   // Check that this is a dymanic PDU\r
661   if (txEntry->CanIfCanTxPduType != ARC_PDU_TYPE_DYNAMIC)\r
662   {\r
663     VALIDATE_NO_RV(FALSE, CANIF_SETDYNAMICTX_ID, CANIF_E_INVALID_TXPDUID);\r
664     return;\r
665   }\r
666 \r
667   // Check that this is an extended or standard id\r
668   if (((CanId & U0x80000000) && (txEntry->CanIfTxPduIdCanIdType == ARC_CAN_ID_TYPE_29)) ||\r
669       (((CanId & U0x80000000) == 0) && (txEntry->CanIfTxPduIdCanIdType == ARC_CAN_ID_TYPE_11)))\r
670   {\r
671     // Update the CanID\r
672     //txEntry->CanIfCanTxPduIdCanId = CanId;  // TODO How do we fix this from a const pointer\r
673 \r
674     // NOT SUPPORTED\r
675   }\r
676   else\r
677   {\r
678     // Inavlid Canid to configuration\r
679     VALIDATE_NO_RV(FALSE, CANIF_SETDYNAMICTX_ID, CANIF_E_PARAM_CANID);\r
680   }\r
681 }\r
682 #endif\r
683 \r
684 #if ( CANIF_TRANSCEIVER_API == STD_ON )\r
685 Std_ReturnType CanIf_SetTransceiverMode(uint8 Transceiver,\r
686     CanIf_TransceiverModeType TransceiverMode)\r
687 {\r
688   VALIDATE(FALSE, CANIF_SET_TRANSCEIVERMODE_ID, CANIF_E_NOK_NOSUPPORT);\r
689 // Not supported\r
690 \r
691   return E_NOT_OK;\r
692 }\r
693 \r
694 Std_ReturnType CanIf_GetTransceiverMode(uint8 Transceiver,\r
695     CanIf_TransceiverModeType *TransceiverModePtr)\r
696 {\r
697   VALIDATE(FALSE, CANIF_GET_TRANSCEIVERMODE_ID, CANIF_E_NOK_NOSUPPORT);\r
698   // Not supported\r
699 \r
700   return E_NOT_OK;\r
701 }\r
702 \r
703 Std_ReturnType CanIf_GetTrcvWakeupReason(uint8 Transceiver,\r
704     CanIf_TrcvWakeupReasonType *TrcvWuReasonPtr)\r
705 {\r
706   VALIDATE(FALSE, CANIF_GET_TRCVMODEREASON_ID, CANIF_E_NOK_NOSUPPORT);\r
707   // Not supported\r
708 \r
709   return E_NOT_OK;\r
710 }\r
711 \r
712 Std_ReturnType CanIf_SetTransceiverWakeupMode(uint8 Transceiver,\r
713     CanIf_TrcvWakeupModeType *TrcvWakeupMode)\r
714 {\r
715   VALIDATE(FALSE, CANIF_SET_TRANSCEIVERWAKEMODE_ID, CANIF_E_NOK_NOSUPPORT);\r
716   // Not supported\r
717 \r
718   return E_NOT_OK;\r
719 }\r
720 #endif\r
721 \r
722 #if ( CANIF_WAKEUP_EVENT_API == STD_ON )\r
723 Std_ReturnType CanIf_CheckWakeup(EcuM_WakeupSourceType WakeupSource)\r
724 {\r
725   VALIDATE(FALSE, CANIF_CHECKWAKEUP_ID, CANIF_E_NOK_NOSUPPORT);\r
726   // Not supported\r
727 \r
728   return E_NOT_OK;\r
729 }\r
730 \r
731 Std_ReturnType CanIf_CheckValidation(EcuM_WakeupSourceType WakeupSource)\r
732 {\r
733   VALIDATE(FALSE, CANIF_CHECKVALIDATION_ID, CANIF_E_NOK_NOSUPPORT);\r
734   // Not supported\r
735 \r
736   return E_NOT_OK;\r
737 }\r
738 #endif\r
739 \r
740 /*\r
741  * Callback interface from driver\r
742  */\r
743 void CanIf_TxConfirmation(PduIdType canTxPduId)\r
744 {\r
745   VALIDATE_NO_RV(CanIf_Global.initRun, CANIF_TXCONFIRMATION_ID, CANIF_E_UNINIT)\r
746   VALIDATE_NO_RV(canTxPduId < CanIf_ConfigPtr->InitConfig->CanIfNumberOfCanTXPduIds, CANIF_TXCONFIRMATION_ID, CANIF_E_PARAM_LPDU);\r
747 \r
748   const CanIf_TxPduConfigType* entry =\r
749     &CanIf_ConfigPtr->InitConfig->CanIfTxPduConfigPtr[canTxPduId];\r
750 \r
751       if (entry->CanIfUserTxConfirmation != NULL)\r
752       {\r
753         CanIf_ChannelGetModeType mode;\r
754         CanIf_GetPduMode(entry->CanIfCanTxPduHthRef->CanIfCanControllerIdRef, &mode);\r
755         if ((mode == CANIF_GET_TX_ONLINE) || (mode == CANIF_GET_ONLINE)\r
756             || (mode == CANIF_GET_OFFLINE_ACTIVE) || (mode == CANIF_GET_OFFLINE_ACTIVE_RX_ONLINE) )\r
757         {\r
758           entry->CanIfUserTxConfirmation(entry->CanIfTxPduId);  /* CANIF053 */\r
759         }\r
760       }\r
761       return;\r
762 }\r
763 \r
764 void CanIf_RxIndication(uint8 Hrh, Can_IdType CanId, uint8 CanDlc,\r
765               const uint8 *CanSduPtr)\r
766 {\r
767   VALIDATE_NO_RV(CanIf_Global.initRun, CANIF_RXINDICATION_ID, CANIF_E_UNINIT);\r
768   VALIDATE_NO_RV(CanSduPtr != NULL, CANIF_RXINDICATION_ID, CANIF_E_PARAM_POINTER);\r
769 \r
770   /* Check PDU mode before continue processing */\r
771   CanIf_ChannelGetModeType mode;\r
772   CanIf_Arc_ChannelIdType channel = CanIf_Arc_FindHrhChannel( (Can_Arc_HRHType) Hrh);\r
773   if (channel == -1)  // Invalid HRH\r
774   {\r
775     return;\r
776   }\r
777 \r
778   if (CanIf_GetPduMode(channel, &mode) == E_OK)\r
779   {\r
780     if ( (mode == CANIF_GET_OFFLINE) || (mode == CANIF_GET_TX_ONLINE) ||\r
781         (mode == CANIF_GET_OFFLINE_ACTIVE) )\r
782     {\r
783       // Receiver path is disabled so just drop it\r
784       return;\r
785     }\r
786   }\r
787   else\r
788   {\r
789     return;  // No mode so just return\r
790   }\r
791 \r
792   const CanIf_RxPduConfigType *entry = CanIf_ConfigPtr->InitConfig->CanIfRxPduConfigPtr;\r
793 \r
794   /* Find the CAN id in the RxPduList */\r
795   for (uint16 i = 0; i < CanIf_ConfigPtr->InitConfig->CanIfNumberOfCanRxPduIds; i++)\r
796   {\r
797     if (entry->CanIfCanRxPduHrhRef->CanIfHrhIdSymRef == Hrh)\r
798     {\r
799       // Software filtering\r
800       if (entry->CanIfCanRxPduHrhRef->CanIfHrhType == CAN_ARC_HANDLE_TYPE_BASIC)\r
801       {\r
802         if (entry->CanIfCanRxPduHrhRef->CanIfSoftwareFilterHrh)\r
803         {\r
804           if (entry->CanIfSoftwareFilterType == CANIF_SOFTFILTER_TYPE_MASK)\r
805           {\r
806             if ((CanId & entry->CanIfCanRxPduCanIdMask ) ==\r
807                 ( entry->CanIfCanRxPduCanId & entry->CanIfCanRxPduCanIdMask))\r
808             {\r
809               // We found a pdu so call higher layers\r
810             }\r
811             else\r
812             {\r
813               entry++;\r
814               continue; // Go to next entry
815             }\r
816           }\r
817           else\r
818           {\r
819             DET_REPORTERROR(MODULE_ID_CAN, 0, CANIF_RXINDICATION_ID, CANIF_E_PARAM_HRH);\r
820             continue; // Not a supported filter type, so just drop the frame\r
821           }\r
822         }\r
823       }\r
824 \r
825 #if (CANIF_DLC_CHECK == STD_ON)\r
826       if (CanDlc < entry->CanIfCanRxPduDlc)\r
827       {\r
828         VALIDATE_NO_RV(FALSE, CANIF_RXINDICATION_ID, CANIF_E_PARAM_DLC);\r
829         return;\r
830       }\r
831 #endif\r
832 \r
833       switch (entry->CanIfRxUserType)\r
834       {\r
835         case CANIF_USER_TYPE_CAN_SPECIAL:\r
836         {\r
837           ( (CanIf_FuncTypeCanSpecial)(entry->CanIfUserRxIndication) )(
838             entry->CanIfCanRxPduHrhRef->CanIfCanControllerHrhIdRef,
839             entry->CanIfCanRxPduId,
840             CanSduPtr,
841             CanDlc,
842             CanId);
843
844             return;\r
845         }\r
846         break;\r
847 \r
848         case CANIF_USER_TYPE_CAN_NM:\r
849 #if defined(USE_CANNM)\r
850                 CanNm_RxIndication(entry->CanIfCanRxPduId,CanSduPtr);\r
851                 return;\r
852 #endif\r
853                 break;\r
854 \r
855         case CANIF_USER_TYPE_CAN_PDUR:\r
856             // Send Can frame to PDU router\r
857 #if defined(USE_PDUR)\r
858                 {\r
859                         PduInfoType pduInfo;\r
860                         pduInfo.SduLength = CanDlc;\r
861                         pduInfo.SduDataPtr = (uint8 *)CanSduPtr;\r
862                 PduR_CanIfRxIndication(entry->CanIfCanRxPduId,&pduInfo);\r
863                 }\r
864             return;\r
865 #endif\r
866             break;\r
867 \r
868         case CANIF_USER_TYPE_CAN_TP:\r
869           // Send Can frame to CAN TP\r
870 #if defined(USE_CANTP)\r
871             {\r
872                     PduInfoType CanTpRxPdu;\r
873                     CanTpRxPdu.SduLength = CanDlc;\r
874                     CanTpRxPdu.SduDataPtr = (uint8 *)CanSduPtr;\r
875                 CanTp_RxIndication(entry->CanIfCanRxPduId, &CanTpRxPdu);\r
876             }\r
877             return;\r
878 #endif\r
879             break;\r
880         case CANIF_USER_TYPE_J1939TP:\r
881           // Send Can frame to CAN TP\r
882 #if defined(USE_J1939TP)\r
883             {\r
884                     PduInfoType J1939TpRxPdu;\r
885                     J1939TpRxPdu.SduLength = CanDlc;\r
886                     J1939TpRxPdu.SduDataPtr = (uint8 *)CanSduPtr;\r
887                     J1939Tp_RxIndication(entry->CanIfCanRxPduId, &J1939TpRxPdu);\r
888             }\r
889             return;\r
890 #endif\r
891             break;            \r
892       }\r
893     }\r
894 \r
895     entry++;\r
896   }\r
897 \r
898   // Did not find the PDU, something is wrong\r
899   VALIDATE_NO_RV(FALSE, CANIF_RXINDICATION_ID, CANIF_E_PARAM_LPDU);\r
900 }\r
901 \r
902 #if ( CANIF_TRANSMIT_CANCELLATION == STD_ON )\r
903 void CanIf_CancelTxConfirmation(const Can_PduType *PduInfoPtr)\r
904 {\r
905   VALIDATE(FALSE, CANIF_CANCELTXCONFIRMATION_ID, CANIF_E_NOK_NOSUPPORT);\r
906   VALIDATE_NO_RV(CanIf_Global.initRun, CANIF_CANCELTXCONFIRMATION_ID, CANIF_E_UNINIT);\r
907   VALIDATE_NO_RV(PduInfoPtr != NULL, CANIF_RXINDICATION_ID, CANIF_E_PARAM_POINTER);\r
908 \r
909   const CanIf_TxPduConfigType *entry =\r
910     CanIf_ConfigPtr->InitConfig->CanIfTxPduConfigPtr;\r
911 \r
912   // Not supported\r
913 \r
914   // Did not find the PDU, something is wrong\r
915   VALIDATE_NO_RV(FALSE, CANIF_TXCONFIRMATION_ID, CANIF_E_PARAM_LPDU);\r
916 }\r
917 #endif\r
918 \r
919 void CanIf_ControllerBusOff(uint8 Controller)\r
920 {\r
921   CanIf_Arc_ChannelIdType channel = 0xff;\r
922 \r
923   VALIDATE_NO_RV( CanIf_Global.initRun, CANIF_CONTROLLER_BUSOFF_ID, CANIF_E_UNINIT );\r
924 \r
925   for(int i = 0; i < CANIF_CHANNEL_CNT; i++)\r
926   {\r
927           if(CanIf_ConfigPtr->Arc_ChannelToControllerMap[i] == Controller)\r
928           {\r
929                   channel = i;\r
930           }\r
931   }\r
932 \r
933   VALIDATE_NO_RV( Controller < CANIF_CHANNEL_CNT, CANIF_CONTROLLER_BUSOFF_ID, CANIF_E_PARAM_CONTROLLER );\r
934 \r
935   // According to figure 35 in canif spec this should be done in\r
936   // Can driver but it is better to do it here\r
937   CanIf_SetControllerMode(channel, CANIF_CS_STOPPED);\r
938 \r
939   if (CanIf_ConfigPtr->DispatchConfig->CanIfBusOffNotification != NULL)\r
940   {\r
941     CanIf_ConfigPtr->DispatchConfig->CanIfBusOffNotification(channel);\r
942   }\r
943 }\r
944 \r
945 void CanIf_SetWakeupEvent(uint8 Controller)\r
946 {\r
947         CanIf_Arc_ChannelIdType channel = 0xff;\r
948 \r
949         VALIDATE_NO_RV( CanIf_Global.initRun, CANIF_SETWAKEUPEVENT_ID, CANIF_E_UNINIT );\r
950 \r
951         for(int i = 0; i < CANIF_CHANNEL_CNT; i++)\r
952         {\r
953           if(CanIf_ConfigPtr->Arc_ChannelToControllerMap[i] == Controller)\r
954           {\r
955                   channel = i;\r
956           }\r
957         }\r
958 \r
959         VALIDATE_NO_RV(FALSE, CANIF_SETWAKEUPEVENT_ID, CANIF_E_NOK_NOSUPPORT);\r
960         VALIDATE_NO_RV( channel < CANIF_CHANNEL_CNT, CANIF_SETWAKEUPEVENT_ID, CANIF_E_PARAM_CONTROLLER );\r
961 \r
962         // Not supported\r
963 }\r
964 \r
965 void CanIf_Arc_Error(uint8 Controller, Can_Arc_ErrorType Error)\r
966 {\r
967   CanIf_Arc_ChannelIdType channel = 0xff;\r
968 \r
969   VALIDATE_NO_RV( CanIf_Global.initRun, CANIF_ARCERROR_ID, CANIF_E_UNINIT );\r
970 \r
971   for(int i = 0; i < CANIF_CHANNEL_CNT; i++)\r
972   {\r
973           if(CanIf_ConfigPtr->Arc_ChannelToControllerMap[i] == Controller)\r
974           {\r
975                   channel = i;\r
976           }\r
977   }\r
978 \r
979   VALIDATE_NO_RV( channel < CANIF_CHANNEL_CNT, CANIF_ARCERROR_ID, CANIF_E_PARAM_CONTROLLER );\r
980 \r
981   if (CanIf_ConfigPtr->DispatchConfig->CanIfErrorNotificaton != NULL)\r
982   {\r
983     CanIf_ConfigPtr->DispatchConfig->CanIfErrorNotificaton(Controller, Error);\r
984   }\r
985 \r
986   // Special fix for restart of bus incase of general can error i.e. connection to CanSM\r
987   if (CanIf_ConfigPtr->DispatchConfig->CanIfBusOffNotification != NULL)\r
988   {\r
989     CanIf_ConfigPtr->DispatchConfig->CanIfBusOffNotification(channel);\r
990   }\r
991 }\r
992 \r
993 uint8 CanIf_Arc_GetChannelDefaultConfIndex(CanIf_Arc_ChannelIdType Channel)
994 {
995         return CanIf_Config.Arc_ChannelDefaultConfIndex[Channel];
996 }
997