ptp4tsync.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
  1. /*!
  2. * Copyright (C) Fraunhofer-Institut for Photonic Microsystems (IPMS)
  3. * Maria-Reiche-Str. 2
  4. * 01109 Dresden
  5. *
  6. * Unauthorized copying of this file, via any medium is strictly prohibited
  7. * Proprietary and confidential
  8. *
  9. * \file ptp4tsync.c
  10. * \author zimmerli
  11. * \date 2020-01-17
  12. * \brief PTP (802.1as) for TSN-EP (TSYNC)
  13. *
  14. */
  15. #include <kernel/base/kernel.h>
  16. #if (USE_TSN_RTOS_NET_PTP4SYNC == 1)
  17. #include "ptp4tsync.h"
  18. #include <kernel/base/rtc.h>
  19. #include <kernel/base/klibc.h>
  20. #include <kernel/net/netdev.h>
  21. #include <kernel/net/ether.h>
  22. #include <string.h>
  23. #include <stdlib.h>
  24. #include <time.h>
  25. // include PTP Stack
  26. #include "tsn_ptp_stack.h"
  27. #include "traceptp.h"
  28. // ----------------------------------------------------------------
  29. /*
  30. * PTP Trace:
  31. * weak definition of trace functions,
  32. * get overridden if traceptp.c is compiled
  33. *
  34. */
  35. void traceptp_print(uint16_t portnum) __attribute__((weak));
  36. void traceptp_tx(uint16_t portnum, uint8_t msgtype, uint16_t seqid) __attribute__((weak));
  37. void traceptp_tx_done(uint16_t portnum, uint8_t msgtype, uint32_t sec, uint32_t nsec) __attribute__((weak));
  38. void traceptp_rx(uint16_t portnum, addr_t addr, uint32_t len, uint32_t sec, uint32_t nsec) __attribute__((weak));
  39. void traceptp_tmr(uint16_t portnum, uint8_t timer_id) __attribute__((weak));
  40. void traceptp_reselect(uint64_t gmid) __attribute__((weak));
  41. /* weak default implementations */
  42. void traceptp_print(uint16_t portnum)
  43. {
  44. (void)portnum;
  45. }
  46. void traceptp_tx(uint16_t portnum, uint8_t msgtype, uint16_t seqid)
  47. {
  48. (void)portnum;
  49. (void)msgtype;
  50. (void)seqid;
  51. }
  52. void traceptp_tx_done(uint16_t portnum, uint8_t msgtype, uint32_t sec, uint32_t nsec)
  53. {
  54. (void)portnum;
  55. (void)msgtype;
  56. (void)sec, (void)nsec;
  57. }
  58. void traceptp_rx(uint16_t portnum, addr_t addr, uint32_t len, uint32_t sec, uint32_t nsec)
  59. {
  60. (void)portnum;
  61. (void)addr;
  62. (void)len;
  63. (void)sec, (void)nsec;
  64. }
  65. void traceptp_tmr(uint16_t portnum, uint8_t timer_id)
  66. {
  67. (void)portnum;
  68. (void)timer_id;
  69. }
  70. void traceptp_reselect(uint64_t gmid)
  71. {
  72. (void)gmid;
  73. }
  74. /* trace macros */
  75. #define TRACE_PTP_TX(portnum, msgtype, seqid) traceptp_tx(portnum, msgtype, seqid)
  76. #define TRACE_PTP_TX_DONE(portnum, msgtype, sec, ns) traceptp_tx_done(portnum, msgtype, sec, ns)
  77. #define TRACE_PTP_RX(portnum, addr, len, sec, ns) traceptp_rx(portnum, addr, len, sec, ns)
  78. #define TRACE_PTP_TMR(portnum, timerid) traceptp_tmr(portnum, timerid)
  79. #define TRACE_PTP_RESELECT(gmid) traceptp_reselect(gmid)
  80. // ----------------------------------------------------------------
  81. //TODO ........
  82. #define EGRESS_LAT 0
  83. #define INGRESS_LAT 0
  84. #define ADJ_FREQ_IVAL 1000000000LL
  85. #define PORTSYNC_QSZ 8
  86. #define SYNC_DOMAIN 1
  87. #define SYNC_RTC 2
  88. // ----------------------------------------------------------------
  89. #define MSG_NUM_MAX 7
  90. static uint8_t _map_idx_to_msgtype[MSG_NUM_MAX + 1] = { PTP_MSGTYPE_SYNC, PTP_MSGTYPE_FOLLOWUP, PTP_MSGTYPE_PDELAY_REQ, PTP_MSGTYPE_PDELAY_RESP,
  91. PTP_MSGTYPE_PDELAY_FUP, PTP_MSGTYPE_ANNOUNCE, PTP_MSGTYPE_SIGNALING, PTP_MSGTYPE_MASK };
  92. static uint8_t _msgtype2idx(uint8_t msgtype)
  93. {
  94. uint8_t idx;
  95. for (idx = 0; idx < MSG_NUM_MAX; ++idx) {
  96. if (_map_idx_to_msgtype[idx] == msgtype)
  97. break;
  98. }
  99. return idx;
  100. }
  101. static const char *_syncmode2string(enum ptp4sync_sync_e mode)
  102. {
  103. if (mode == PTP4SYNC_NONE)
  104. return "none";
  105. if (mode == PTP4SYNC_FALLBACK)
  106. return "fallback";
  107. if (mode == PTP4SYNC_PRIMARY)
  108. return "primary";
  109. return "Unknown sync mode!";
  110. }
  111. static const char *_portrole2string(enum ptp4sync_port_role_e role)
  112. {
  113. if (role == PTP4SYNC_ROLE_DYNAMIC)
  114. return "dynamic";
  115. if (role == PTP4SYNC_ROLE_MASTER)
  116. return "master";
  117. if (role == PTP4SYNC_ROLE_SLAVE)
  118. return "slave";
  119. if (role == PTP4SYNC_ROLE_PASSIVE)
  120. return "passive";
  121. if (role == PTP4SYNC_ROLE_DISABLED)
  122. return "disabled";
  123. return "Unknown port role!";
  124. }
  125. // ----------------------------------------------------------------
  126. // stats
  127. #define TMRMAX 5
  128. typedef struct {
  129. uint16_t rx[MSG_NUM_MAX + 1];
  130. uint16_t tx[MSG_NUM_MAX + 1];
  131. uint16_t timer[TMRMAX];
  132. } ptp_portstats_t;
  133. typedef struct {
  134. ptp_portstats_t stats;
  135. struct netdev_s *device;
  136. uint32_t tmr[TMRMAX];
  137. } domain_port_handle_t;
  138. typedef struct {
  139. uint8_t domainNumber;
  140. uint32_t portNumber;
  141. tsn_ptp_instance_t stack;
  142. uint8_t trafficPrio;
  143. domain_port_handle_t *ports;
  144. enum ptp4sync_sync_e syncMode;
  145. uint8_t synced;
  146. uint8_t syncFlags;
  147. void *syncDomainSource;
  148. void *syncDomainTarget;
  149. } domain_handle_t;
  150. typedef struct {
  151. int enablePrint;
  152. list_data_t *domainList;
  153. struct rtc_s *rtc;
  154. TaskHandle_t hSyncTask;
  155. TaskHandle_t hPrintTask;
  156. uint8_t mac_addr[MAC_ADDR_LEN];
  157. domain_handle_t *syncRTC;
  158. } ptp_site_t;
  159. ptp_site_t _ptp_site;
  160. // task
  161. void task_ptp_sync(void *pvParameters);
  162. void task_ptp_print(void *pvParameters);
  163. static void printTime(domain_handle_t *pDomain);
  164. static domain_handle_t *searchDomain(uint8_t domainNumber);
  165. static void timerDomain(domain_handle_t *pDomain);
  166. // hook functions
  167. static void hookfunc_txFrame(void *cbParam, uint16_t portIndex, struct ptp_header_s *pPtpHeader, uint8_t *pPayload, uint32_t payloadLen);
  168. static void hookfunc_setTimer(void *cbParam, ptp_timer_event_t timer, uint32_t id, int8_t logInterval, uint8_t mult);
  169. static void hookfunc_getLocalClock(void *cbParam, ptp_timestamp_t *localtime);
  170. static void hookfunc_getLocalClockOffset(void *cbParam, ptp_timestamp_t *offset);
  171. static void hookfunc_statusEvent(void *cbParam, ptp_status_event_t event, void *param);
  172. static void hook_tsync_rx_frame(struct netdev_s *netdev, void *rxdata, uint32_t rxlen, uint32_t localtm_ns, uint32_t localtm_sec);
  173. static void hook_tsync_tx_done(struct netdev_s *netdev, void *arg, uint32_t localtm_ns, uint32_t localtm_sec);
  174. static const char *PTP4TSYNC_CONTEXT = "PTP4TSYNC";
  175. /**
  176. * \brief Start ptp4tsync task
  177. *
  178. * @param rtc RTC to be used
  179. * @param mac_addr pointer to the MAC address of the domain
  180. */
  181. void ptp4sync_start(struct rtc_s *rtc, uint8_t *mac_addr)
  182. {
  183. // init site struct
  184. _ptp_site.enablePrint = 1;
  185. _ptp_site.rtc = rtc;
  186. _ptp_site.syncRTC = NULL;
  187. memcpy(_ptp_site.mac_addr, mac_addr, MAC_ADDR_LEN);
  188. _ptp_site.domainList = list_create();
  189. if (!_ptp_site.domainList)
  190. goto fail;
  191. // create task
  192. kTaskCreate_a(PTP4TSYNC_CONTEXT, task_ptp_sync, (const char *)"ptp sync", 10 * 1024, (void *)&_ptp_site, KERNEL_PRIO_NORMAL, &_ptp_site.hSyncTask);
  193. kTaskCreate_a(PTP4TSYNC_CONTEXT, task_ptp_print, (const char *)"ptp print", 10 * 1024, (void *)&_ptp_site, KERNEL_PRIO_NORMAL, &_ptp_site.hPrintTask);
  194. return;
  195. fail:
  196. printf("%s: init failed\n", PTP4TSYNC_CONTEXT);
  197. return;
  198. }
  199. /**
  200. * \brief Add a new PTP domain
  201. *
  202. * @param domainNumber number of the domain
  203. * @param gmPrio1 priority of the PTP domain grandmaster
  204. * @param port_num number of ports in the PTP domain
  205. * @param type sync type for RTC sync
  206. */
  207. void ptp4sync_add_domain(uint8_t domainNumber, uint8_t gmPrio1, uint32_t port_num, enum ptp4sync_sync_e type)
  208. {
  209. domain_handle_t *pDomain;
  210. //reuse resources for domain, ptp4sync_rm_domain will not remove resources
  211. pDomain = searchDomain(domainNumber);
  212. if (!pDomain) {
  213. pDomain = (domain_handle_t *)kallocz(sizeof(domain_handle_t));
  214. if (!pDomain)
  215. goto fail;
  216. pDomain->ports = (domain_port_handle_t *)kallocz(port_num * sizeof(domain_port_handle_t));
  217. if (!pDomain->ports)
  218. goto fail;
  219. }
  220. printf("%s | add domain %u\n", PTP4TSYNC_CONTEXT, domainNumber);
  221. pDomain->portNumber = port_num;
  222. pDomain->domainNumber = domainNumber;
  223. pDomain->syncMode = type;
  224. pDomain->synced = 0;
  225. pDomain->syncDomainSource = NULL;
  226. pDomain->syncDomainTarget = NULL;
  227. pDomain->syncFlags = 0;
  228. // stack init function
  229. tsn_ptp_stack_init(&pDomain->stack, _ptp_site.mac_addr, domainNumber);
  230. // init PTP stack hook functions
  231. pDomain->stack.cbParam = pDomain;
  232. pDomain->stack.hookSetTimer = hookfunc_setTimer;
  233. pDomain->stack.hookTxFrame = hookfunc_txFrame;
  234. pDomain->stack.hookGetLocalTime = hookfunc_getLocalClock;
  235. pDomain->stack.hookGetLocalOffset = hookfunc_getLocalClockOffset;
  236. pDomain->stack.hookStatusEvent = hookfunc_statusEvent;
  237. tsn_ptp_stack_enable(&pDomain->stack, 1);
  238. pDomain->stack.systemId.prio1 = gmPrio1;
  239. pDomain->stack.systemId.prio2 = 255;
  240. tsn_ptp_stack_portroleselection(&pDomain->stack);
  241. list_add(_ptp_site.domainList, pDomain);
  242. return;
  243. fail:
  244. printf("%s | add domain failed\n", PTP4TSYNC_CONTEXT);
  245. return;
  246. }
  247. /**
  248. * \brief Add a new PTP domain
  249. *
  250. * @param domainNumber number of the domain
  251. * @param gmPrio1 priority of the PTP domain grandmaster
  252. * @param port_num number of ports in the PTP domain
  253. * @param mac_addr pointer to the MAC address of the domain
  254. */
  255. void ptp4sync_rm_domain(uint8_t domainNumber)
  256. {
  257. domain_handle_t *pDomain;
  258. pDomain = searchDomain(domainNumber);
  259. if (!pDomain) {
  260. printf("%s | remove domain failed\n", PTP4TSYNC_CONTEXT);
  261. return;
  262. }
  263. //disable all ports
  264. for (uint16_t portIndex = 0; portIndex < pDomain->portNumber; portIndex++) {
  265. struct netdev_s *netdev = pDomain->ports[portIndex].device;
  266. if (netdev) {
  267. printf("%s | remove %s from domain %u\n", PTP4TSYNC_CONTEXT, netdev->name, domainNumber);
  268. tsn_ptp_stack_port_enable(&pDomain->stack, portIndex, 0);
  269. }
  270. }
  271. tsn_ptp_stack_enable(&pDomain->stack, 0);
  272. //free of domain resources need locking of domain access, will be implemented later
  273. vTaskDelay(1000);
  274. kfree(pDomain->ports);
  275. kfree(pDomain);
  276. list_del(_ptp_site.domainList, pDomain);
  277. return;
  278. }
  279. void ptp4sync_add_sync(uint8_t sourceDomainNumber, uint8_t targetDomainNumber)
  280. {
  281. domain_handle_t *pDomainSource;
  282. domain_handle_t *pDomainTarget;
  283. pDomainSource = searchDomain(sourceDomainNumber);
  284. pDomainTarget = searchDomain(targetDomainNumber);
  285. if (!pDomainSource || !pDomainTarget) {
  286. printf("%s | add domain sync failed\n", PTP4TSYNC_CONTEXT);
  287. return;
  288. }
  289. printf("%s | add sync from domain %u to domain %u\n", PTP4TSYNC_CONTEXT, sourceDomainNumber, targetDomainNumber);
  290. pDomainSource->syncDomainTarget = pDomainTarget;
  291. pDomainTarget->syncDomainSource = pDomainSource;
  292. pDomainSource->syncFlags |= SYNC_DOMAIN;
  293. pDomainSource->stack.enableInvoke = 1;
  294. }
  295. void ptp4sync_rm_sync(uint8_t sourceDomainNumber)
  296. {
  297. domain_handle_t *pDomainSource;
  298. domain_handle_t *pDomainTarget;
  299. pDomainSource = searchDomain(sourceDomainNumber);
  300. if (!pDomainSource) {
  301. printf("%s | rm domain sync failed\n", PTP4TSYNC_CONTEXT);
  302. return;
  303. }
  304. printf("%s | remove sync from domain %u\n", PTP4TSYNC_CONTEXT, sourceDomainNumber);
  305. if (pDomainSource->syncDomainTarget) {
  306. pDomainTarget = pDomainSource->syncDomainTarget;
  307. pDomainTarget->syncDomainSource = NULL;
  308. }
  309. pDomainSource->syncDomainTarget = NULL;
  310. pDomainSource->syncFlags &= ~SYNC_DOMAIN;
  311. if (pDomainSource->syncFlags == 0)
  312. pDomainSource->stack.enableInvoke = 0;
  313. }
  314. /**
  315. * \brief Add netdev to PTP domain
  316. *
  317. * Will use the port->id for assignment to PTP port index
  318. *
  319. * @param domainNumber number of the domain
  320. * @param portIndex port index in the PTP domain
  321. * @param device pointer to the netdev
  322. */
  323. void ptp4sync_add_port(uint8_t domainNumber, uint16_t portIndex, struct netdev_s *device)
  324. {
  325. domain_handle_t *pDomain;
  326. if (!device)
  327. goto fail;
  328. if (portIndex >= TSN_PTP_CFG_PORTNUM)
  329. goto fail;
  330. pDomain = searchDomain(domainNumber);
  331. if (!pDomain)
  332. goto fail;
  333. printf("%s | add %s to domain %u\n", PTP4TSYNC_CONTEXT, device->name, domainNumber);
  334. pDomain->ports[portIndex].device = device;
  335. device->tsync.cb_rx_frame = hook_tsync_rx_frame;
  336. device->tsync.cb_tx_done = hook_tsync_tx_done;
  337. tsn_ptp_stack_port_enable(&pDomain->stack, portIndex, 1);
  338. return;
  339. fail:
  340. printf("%s | add port failed\n", PTP4TSYNC_CONTEXT);
  341. return;
  342. }
  343. /**
  344. * \brief Remove port from PTP domain
  345. *
  346. * @param domainNumber number of the domain
  347. * @param portIndex port index in the PTP domain
  348. */
  349. void ptp4sync_rm_port(uint8_t domainNumber, uint16_t portIndex)
  350. {
  351. domain_handle_t *pDomain;
  352. struct netdev_s *device;
  353. if (portIndex >= TSN_PTP_CFG_PORTNUM)
  354. goto fail;
  355. pDomain = searchDomain(domainNumber);
  356. if (!pDomain)
  357. goto fail;
  358. device = pDomain->ports[portIndex].device;
  359. if (!device)
  360. goto fail;
  361. printf("%s | remove %s from domain %u\n", PTP4TSYNC_CONTEXT, device->name, domainNumber);
  362. tsn_ptp_stack_port_enable(&pDomain->stack, portIndex, 0);
  363. return;
  364. fail:
  365. printf("%s | remove port failed\n", PTP4TSYNC_CONTEXT);
  366. return;
  367. }
  368. /**
  369. * \brief Set priority for all PTP traffic
  370. *
  371. * @param domainNumber number of the domain
  372. * @param prio traffic priority number
  373. */
  374. void ptp4sync_set_traffic_prio(uint8_t domainNumber, uint8_t prio)
  375. {
  376. domain_handle_t *pDomain;
  377. pDomain = searchDomain(domainNumber);
  378. if (!pDomain)
  379. return;
  380. pDomain->trafficPrio = prio;
  381. printf("%s | set ptp traffic priority=%d (%s) for domain %u\n", PTP4TSYNC_CONTEXT, prio, ETH_PRIO_NAMES[prio], domainNumber);
  382. }
  383. /**
  384. * \brief Set priority for all PTP traffic
  385. *
  386. * @param domainNumber number of the domain
  387. * @param syncMode sync mode selcetion
  388. */
  389. void ptp4sync_set_rtcsync_mode(uint8_t domainNumber, enum ptp4sync_sync_e syncMode)
  390. {
  391. domain_handle_t *pDomain;
  392. pDomain = searchDomain(domainNumber);
  393. if (!pDomain)
  394. return;
  395. pDomain->syncMode = syncMode;
  396. printf("%s | set RTC sync mode to %s for domain %u\n", PTP4TSYNC_CONTEXT, _syncmode2string(syncMode), domainNumber);
  397. }
  398. void ptp4sync_set_ext_port_state(uint8_t domainNumber, uint32_t index, enum ptp4sync_port_role_e role)
  399. {
  400. if (index >= TSN_PTP_CFG_PORTNUM)
  401. return;
  402. domain_handle_t *pDomain = searchDomain(domainNumber);
  403. if (!pDomain)
  404. return;
  405. printf("%s | set port role to %s for domain %u index %u\n", PTP4TSYNC_CONTEXT, _portrole2string(role), domainNumber, index);
  406. if (role == PTP4SYNC_ROLE_DYNAMIC) {
  407. pDomain->stack.port[index].port.rcvdPortStateInd = 0;
  408. } else {
  409. pDomain->stack.port[index].port.rcvdPortStateInd = 1;
  410. switch (role) {
  411. case PTP4SYNC_ROLE_DISABLED:
  412. pDomain->stack.port[index].port.rcvdPortState = PTP_PORT_ROLE_DISABLED;
  413. break;
  414. case PTP4SYNC_ROLE_MASTER:
  415. pDomain->stack.port[index].port.rcvdPortState = PTP_PORT_ROLE_MASTER;
  416. break;
  417. case PTP4SYNC_ROLE_SLAVE:
  418. pDomain->stack.port[index].port.rcvdPortState = PTP_PORT_ROLE_SLAVE;
  419. break;
  420. case PTP4SYNC_ROLE_PASSIVE:
  421. pDomain->stack.port[index].port.rcvdPortState = PTP_PORT_ROLE_PASSIVE;
  422. break;
  423. default:
  424. break;
  425. }
  426. }
  427. tsn_ptp_stack_portroleselection(&pDomain->stack);
  428. }
  429. /**
  430. * \brief Get current PTP status
  431. *
  432. * @param gmid dest pointer to Grandmaster id
  433. * @param gmchanges dest pointer to number of GM changes
  434. * @param gmoffset dest pointer to offset to GM at last sync
  435. */
  436. void ptp4sync_get_state(uint8_t domainNumber, uint64_t *gmid, uint16_t *gmchanges, int32_t *gmoffset)
  437. {
  438. domain_handle_t *pDomain = searchDomain(domainNumber);
  439. if (!pDomain)
  440. return;
  441. if (gmid)
  442. *gmid = pDomain->stack.gmPriority.rootSystemId.clockIdentity;
  443. if (gmchanges)
  444. *gmchanges = pDomain->stack.gmChanges;
  445. if (gmoffset)
  446. *gmoffset = pDomain->stack.siteSync.gmOffset;
  447. }
  448. /**
  449. * \brief Get current PTP status of port
  450. *
  451. * @param index port index (0 .. n)
  452. * @param ascapable dest pointer to asCapable flag
  453. * @param portstate dest pointer to port role
  454. * @param pdelay dest pointer to pdelay (nanoseconds)
  455. */
  456. void ptp4sync_get_port_state(uint8_t domainNumber, uint32_t index, uint8_t *ascapable, uint8_t *portstate, uint16_t *pdelay)
  457. {
  458. uint16_t delay;
  459. if (index >= TSN_PTP_CFG_PORTNUM)
  460. return;
  461. domain_handle_t *pDomain = searchDomain(domainNumber);
  462. if (!pDomain)
  463. return;
  464. if (ascapable)
  465. *ascapable = pDomain->stack.port[index].port.asCapable;
  466. if (portstate)
  467. *portstate = pDomain->stack.port[index].port.selectedRole;
  468. delay = 0;
  469. if (pDomain->stack.port[index].md.isMeasuringDelay)
  470. delay = pDomain->stack.port[index].port.meanLinkDelay;
  471. if (pdelay)
  472. *pdelay = delay;
  473. }
  474. void ptp4sync_set_gm_prio1(uint8_t domainNumber, uint8_t prio1)
  475. {
  476. domain_handle_t *pDomain = searchDomain(domainNumber);
  477. if (!pDomain)
  478. return;
  479. pDomain->stack.systemId.prio1 = prio1; // set prio1
  480. tsn_ptp_stack_portroleselection(&pDomain->stack);
  481. TRACE_PTP_RESELECT(pDomain->stack.gmPriority.rootSystemId.clockIdentity);
  482. }
  483. // ----------------------------------------------------------------
  484. void task_ptp_sync(void *pvParameters)
  485. {
  486. ptp_site_t *site;
  487. KERNEL_TASK_FPU();
  488. site = (ptp_site_t *)pvParameters;
  489. list_data_t *entry;
  490. domain_handle_t *pDomain;
  491. while (1) {
  492. vTaskDelay(32);
  493. entry = site->domainList;
  494. while (entry != NULL) {
  495. pDomain = (domain_handle_t *)entry->data;
  496. timerDomain(pDomain);
  497. entry = list_next(_ptp_site.domainList, entry);
  498. }
  499. }
  500. }
  501. // ----------------------------------------------------------------
  502. void task_ptp_print(void *pvParameters)
  503. {
  504. ptp_site_t *site;
  505. domain_handle_t *pDomain;
  506. list_data_t *entry;
  507. KERNEL_TASK_FPU();
  508. site = (ptp_site_t *)pvParameters;
  509. while (1) {
  510. vTaskDelay(3000);
  511. if (site->enablePrint) {
  512. entry = site->domainList;
  513. while (entry != NULL) {
  514. pDomain = (domain_handle_t *)entry->data;
  515. printTime(pDomain);
  516. entry = list_next(_ptp_site.domainList, entry);
  517. }
  518. }
  519. }
  520. }
  521. // ----------------------------------------------------------------
  522. static domain_handle_t *searchDomain(uint8_t domainNumber)
  523. {
  524. list_data_t *entry;
  525. domain_handle_t *pDomain;
  526. entry = _ptp_site.domainList;
  527. if (!entry)
  528. return NULL;
  529. while (entry != NULL) {
  530. pDomain = (domain_handle_t *)entry->data;
  531. if (pDomain->domainNumber == domainNumber) {
  532. return pDomain;
  533. }
  534. entry = list_next(_ptp_site.domainList, entry);
  535. }
  536. return NULL;
  537. }
  538. static void timerDomain(domain_handle_t *pDomain)
  539. {
  540. uint32_t tmrCnt;
  541. for (uint8_t i = 0; i < pDomain->portNumber; i++) {
  542. for (int j = 0; j < TMRMAX; j++) {
  543. tmrCnt = pDomain->ports[i].tmr[j];
  544. if (tmrCnt > 0) {
  545. pDomain->ports[i].tmr[j] = tmrCnt - 1;
  546. if (tmrCnt == 1) {
  547. TRACE_PTP_TMR(i + 1, j);
  548. tsn_ptp_stack_timerevent(&pDomain->stack, j, i);
  549. pDomain->ports[i].stats.timer[j]++;
  550. }
  551. }
  552. }
  553. }
  554. }
  555. static void syncRTC(void)
  556. {
  557. list_data_t *entry;
  558. domain_handle_t *pDomain;
  559. domain_handle_t *pDomainSync = NULL;
  560. enum ptp4sync_sync_e curtype = PTP4SYNC_NONE;
  561. entry = _ptp_site.domainList;
  562. if (!entry)
  563. return;
  564. while (entry != NULL) {
  565. pDomain = (domain_handle_t *)entry->data;
  566. if (pDomain->synced && pDomain->syncMode < curtype) {
  567. pDomainSync = pDomain;
  568. curtype = pDomain->syncMode;
  569. }
  570. entry = list_next(_ptp_site.domainList, entry);
  571. }
  572. if (pDomainSync != _ptp_site.syncRTC) {
  573. printf("%s | assign sync source to domain ", PTP4TSYNC_CONTEXT);
  574. if (pDomainSync)
  575. printf("%d\n", pDomainSync->domainNumber);
  576. else
  577. printf("none\n");
  578. if (_ptp_site.syncRTC) {
  579. _ptp_site.syncRTC->syncFlags &= ~SYNC_RTC;
  580. if (_ptp_site.syncRTC->syncFlags == 0)
  581. _ptp_site.syncRTC->stack.enableInvoke = 0;
  582. }
  583. if (pDomainSync) {
  584. pDomainSync->syncFlags |= SYNC_RTC;
  585. pDomainSync->stack.enableInvoke = 1;
  586. }
  587. _ptp_site.syncRTC = pDomainSync;
  588. }
  589. }
  590. static void printTime(domain_handle_t *pDomain)
  591. {
  592. struct timespec64 rtctime;
  593. uint64_t time_ns = tsn_ptp_time_get(&pDomain->stack);
  594. rtctime.tv_sec = time_ns / 1000000000;
  595. rtctime.tv_nsec = time_ns % 1000000000;
  596. time_t time = rtctime.tv_sec;
  597. struct tm *utc = NULL;
  598. utc = gmtime(&time);
  599. printf("domain %3u: ", pDomain->domainNumber);
  600. printf("domaintime=%d.%09d ", (uint32_t)rtctime.tv_sec, rtctime.tv_nsec); // sec.nsec
  601. if (utc != NULL) {
  602. printf("or %s", asctime(utc));
  603. } else {
  604. printf("\n");
  605. }
  606. }
  607. // ----------------------------------------------------------------
  608. static void hookfunc_txFrame(void *cbParam, uint16_t portIndex, struct ptp_header_s *pPtpHeader, uint8_t *pPayload, uint32_t payloadLen)
  609. {
  610. addr_t addr;
  611. uint32_t ofs;
  612. uint8_t _ptp_tq;
  613. struct netdev_s *netdev;
  614. struct netb_s *netb;
  615. netb = kalloc_netb(10);
  616. if (!netb)
  617. return; //TODO implement TX error counter
  618. domain_handle_t *pDomain = (domain_handle_t *)cbParam;
  619. netdev = pDomain->ports[portIndex].device;
  620. netb_reserve(netb, netdev->needed_head);
  621. _ptp_tq = netdev->prio2tq[pDomain->trafficPrio];
  622. addr = (netb->data);
  623. ofs = tsn_ptp_set_eth_header(addr, netdev->dev_addr);
  624. ofs += tsn_ptp_set_ptp_header(addr + ofs, pPtpHeader);
  625. memcpy((uint8_t *)addr + ofs, pPayload, payloadLen);
  626. netb_put(netb, payloadLen + ofs);
  627. // update data and trigger tx
  628. TRACE_PTP_TX(portIndex + 1, pPtpHeader->msgType, pPtpHeader->sequenceId);
  629. if (tsn_ctrl_tsync_tx(netdev, netb, _ptp_tq, netb) != NETDEV_RESULT_OK) {
  630. kfree_netb(netb);
  631. return;
  632. }
  633. // debug counter
  634. pDomain->ports[portIndex].stats.tx[_msgtype2idx(pPtpHeader->msgType & PTP_MSGTYPE_MASK)]++;
  635. }
  636. static void hookfunc_setTimer(void *cbParam, ptp_timer_event_t timer, uint32_t id, int8_t logInterval, uint8_t mult)
  637. {
  638. domain_handle_t *pDomain = (domain_handle_t *)cbParam;
  639. uint32_t period;
  640. // disable timer when logInterval > 127
  641. // 1:1 assignment from ptp_timer_event -> timer index of TSN.TSYNC
  642. // (logInterval <= -5) || (logInterval >= 5) --> ignore
  643. // HW should support up to -10 .. 10 (when mult=1)
  644. if (logInterval == 127) {
  645. pDomain->ports[id].tmr[timer] = 0;
  646. return;
  647. }
  648. if (logInterval <= -5) {
  649. return;
  650. }
  651. if (logInterval >= 5) {
  652. return;
  653. }
  654. period = (1 << (logInterval + 5)); // 1/32th second tmr tick
  655. period *= mult;
  656. if (period == 0) {
  657. period++; // 1/32th second tick
  658. }
  659. pDomain->ports[id].tmr[timer] = period;
  660. }
  661. static void hookfunc_getLocalClock(void *cbParam, ptp_timestamp_t *localtime)
  662. {
  663. (void)cbParam;
  664. struct timespec64 rtc;
  665. rtc_get_local_time64(_ptp_site.rtc, &rtc);
  666. localtime->seconds = (uint64_t)rtc.tv_sec;
  667. localtime->nanoseconds = rtc.tv_nsec;
  668. }
  669. static void hookfunc_getLocalClockOffset(void *cbParam, ptp_timestamp_t *offset)
  670. {
  671. (void)cbParam;
  672. struct timespec64 ofs;
  673. rtc_get_rtc_offset(_ptp_site.rtc, &ofs);
  674. offset->seconds = (uint64_t)ofs.tv_sec;
  675. offset->nanoseconds = ofs.tv_nsec;
  676. }
  677. static void hookfunc_statusEvent(void *cbParam, ptp_status_event_t event, void *param)
  678. {
  679. domain_handle_t *pDomain = (domain_handle_t *)cbParam;
  680. if (event == PTP_STS_EVENT_PHASE_CHANGE) {
  681. uint64_t *p_ns = (uint64_t *)param;
  682. if (pDomain->syncFlags & SYNC_DOMAIN) {
  683. domain_handle_t *pDomainTarget = pDomain->syncDomainTarget;
  684. pDomainTarget->stack.localClock.offset_ns = *p_ns;
  685. }
  686. if (pDomain->syncFlags & SYNC_RTC) {
  687. struct timespec64 rtcoffset;
  688. rtcoffset.tv_sec = *p_ns / NSEC_PER_SEC;
  689. rtcoffset.tv_nsec = *p_ns % NSEC_PER_SEC;
  690. rtc_set_rtc_offset(_ptp_site.rtc, &rtcoffset);
  691. }
  692. return;
  693. }
  694. if (event == PTP_STS_EVENT_FREQ_CHANGE) {
  695. int32_t *p_ppb = (int32_t *)param;
  696. if (pDomain->syncFlags & SYNC_DOMAIN) {
  697. domain_handle_t *pDomainTarget = pDomain->syncDomainTarget;
  698. pDomainTarget->stack.localClock.rateOfs = *p_ppb;
  699. }
  700. if (pDomain->syncFlags & SYNC_RTC) {
  701. rtc_adjfine(_ptp_site.rtc, *p_ppb);
  702. }
  703. return;
  704. }
  705. if (event == PTP_STS_EVENT_SYNCRECV) {
  706. pDomain->synced = 1;
  707. }
  708. if (event == PTP_STS_EVENT_SYNCLOST) {
  709. pDomain->synced = 0;
  710. }
  711. //reassignment of sync domain
  712. syncRTC();
  713. }
  714. // ----------------------------------------------------------------
  715. static void hook_tsync_rx_frame(struct netdev_s *netdev, void *rxdata, uint32_t rxlen, uint32_t localtm_ns, uint32_t localtm_sec)
  716. {
  717. ptp_timestamp_t timestamp;
  718. if (netdev->tsn_port >= TSN_PTP_CFG_PORTNUM)
  719. return;
  720. uint8_t domainNumber = tsn_ptp_domain((addr_t)rxdata + 14);
  721. uint8_t msgType = tsn_ptp_msg((addr_t)rxdata + 14);
  722. domain_handle_t *pDomain = searchDomain(domainNumber);
  723. if (!pDomain)
  724. return;
  725. TRACE_PTP_RX(netdev->tsn_port + 1, (addr_t)rxdata + 14, rxlen - 14, localtm_sec, localtm_ns);
  726. // adjust timestamp and process
  727. timestamp.nanoseconds = localtm_ns;
  728. timestamp.seconds = localtm_sec;
  729. tsn_ptp_adjust_timestamp(&timestamp, -INGRESS_LAT);
  730. tsn_ptp_stack_rxframe(&pDomain->stack, netdev->tsn_port, (addr_t)rxdata + 14, rxlen - 14, &timestamp);
  731. // debug counter
  732. pDomain->ports[netdev->tsn_port].stats.rx[_msgtype2idx(msgType)]++;
  733. }
  734. static void hook_tsync_tx_done(struct netdev_s *netdev, void *arg, uint32_t localtm_ns, uint32_t localtm_sec)
  735. {
  736. struct netb_s *netb = (struct netb_s *)arg;
  737. ptp_timestamp_t timestamp;
  738. tsn_ptp_port_t *ptpport;
  739. if (netdev->tsn_port >= TSN_PTP_CFG_PORTNUM)
  740. return;
  741. if (!netb)
  742. return;
  743. uint8_t domainNumber = tsn_ptp_domain(netb->data + 14);
  744. uint8_t msgType = tsn_ptp_msg(netb->data + 14);
  745. kfree_netb(netb);
  746. domain_handle_t *pDomain = searchDomain(domainNumber);
  747. if (!pDomain)
  748. return;
  749. ptpport = &pDomain->stack.port[netdev->tsn_port];
  750. timestamp.nanoseconds = localtm_ns;
  751. timestamp.seconds = localtm_sec;
  752. tsn_ptp_adjust_timestamp(&timestamp, EGRESS_LAT); // adjust TX timestamp
  753. TRACE_PTP_TX_DONE(ptpport->port.thisPort, msgType, localtm_sec, localtm_ns);
  754. tsn_ptp_stack_txdone(&pDomain->stack, ptpport, msgType, &timestamp);
  755. }
  756. #endif /* USE_TSN_RTOS_NET_PTP4SYNC */