dpdk/examples/ptpclient/ptpclient.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: BSD-3-Clause
   2 * Copyright(c) 2015 Intel Corporation
   3 */
   4
   5/*
   6 * This application is a simple Layer 2 PTP v2 client. It shows delta values
   7 * which are used to synchronize the PHC clock. if the "-T 1" parameter is
   8 * passed to the application the Linux kernel clock is also synchronized.
   9 */
  10
  11#include <stdint.h>
  12#include <inttypes.h>
  13#include <rte_eal.h>
  14#include <rte_ethdev.h>
  15#include <rte_cycles.h>
  16#include <rte_lcore.h>
  17#include <rte_mbuf.h>
  18#include <rte_ip.h>
  19#include <limits.h>
  20#include <sys/time.h>
  21#include <getopt.h>
  22
  23#define RX_RING_SIZE 1024
  24#define TX_RING_SIZE 1024
  25
  26#define NUM_MBUFS            8191
  27#define MBUF_CACHE_SIZE       250
  28
  29/* Values for the PTP messageType field. */
  30#define SYNC                  0x0
  31#define DELAY_REQ             0x1
  32#define PDELAY_REQ            0x2
  33#define PDELAY_RESP           0x3
  34#define FOLLOW_UP             0x8
  35#define DELAY_RESP            0x9
  36#define PDELAY_RESP_FOLLOW_UP 0xA
  37#define ANNOUNCE              0xB
  38#define SIGNALING             0xC
  39#define MANAGEMENT            0xD
  40
  41#define NSEC_PER_SEC        1000000000L
  42#define KERNEL_TIME_ADJUST_LIMIT  20000
  43#define PTP_PROTOCOL             0x88F7
  44
  45struct rte_mempool *mbuf_pool;
  46uint32_t ptp_enabled_port_mask;
  47uint8_t ptp_enabled_port_nb;
  48static uint8_t ptp_enabled_ports[RTE_MAX_ETHPORTS];
  49
  50static const struct rte_eth_conf port_conf_default = {
  51        .rxmode = {
  52                .max_rx_pkt_len = RTE_ETHER_MAX_LEN,
  53        },
  54};
  55
  56static const struct rte_ether_addr ether_multicast = {
  57        .addr_bytes = {0x01, 0x1b, 0x19, 0x0, 0x0, 0x0}
  58};
  59
  60/* Structs used for PTP handling. */
  61struct tstamp {
  62        uint16_t   sec_msb;
  63        uint32_t   sec_lsb;
  64        uint32_t   ns;
  65}  __rte_packed;
  66
  67struct clock_id {
  68        uint8_t id[8];
  69};
  70
  71struct port_id {
  72        struct clock_id        clock_id;
  73        uint16_t               port_number;
  74}  __rte_packed;
  75
  76struct ptp_header {
  77        uint8_t              msg_type;
  78        uint8_t              ver;
  79        uint16_t             message_length;
  80        uint8_t              domain_number;
  81        uint8_t              reserved1;
  82        uint8_t              flag_field[2];
  83        int64_t              correction;
  84        uint32_t             reserved2;
  85        struct port_id       source_port_id;
  86        uint16_t             seq_id;
  87        uint8_t              control;
  88        int8_t               log_message_interval;
  89} __rte_packed;
  90
  91struct sync_msg {
  92        struct ptp_header   hdr;
  93        struct tstamp       origin_tstamp;
  94} __rte_packed;
  95
  96struct follow_up_msg {
  97        struct ptp_header   hdr;
  98        struct tstamp       precise_origin_tstamp;
  99        uint8_t             suffix[0];
 100} __rte_packed;
 101
 102struct delay_req_msg {
 103        struct ptp_header   hdr;
 104        struct tstamp       origin_tstamp;
 105} __rte_packed;
 106
 107struct delay_resp_msg {
 108        struct ptp_header    hdr;
 109        struct tstamp        rx_tstamp;
 110        struct port_id       req_port_id;
 111        uint8_t              suffix[0];
 112} __rte_packed;
 113
 114struct ptp_message {
 115        union {
 116                struct ptp_header          header;
 117                struct sync_msg            sync;
 118                struct delay_req_msg       delay_req;
 119                struct follow_up_msg       follow_up;
 120                struct delay_resp_msg      delay_resp;
 121        } __rte_packed;
 122};
 123
 124struct ptpv2_data_slave_ordinary {
 125        struct rte_mbuf *m;
 126        struct timespec tstamp1;
 127        struct timespec tstamp2;
 128        struct timespec tstamp3;
 129        struct timespec tstamp4;
 130        struct clock_id client_clock_id;
 131        struct clock_id master_clock_id;
 132        struct timeval new_adj;
 133        int64_t delta;
 134        uint16_t portid;
 135        uint16_t seqID_SYNC;
 136        uint16_t seqID_FOLLOWUP;
 137        uint8_t ptpset;
 138        uint8_t kernel_time_set;
 139        uint16_t current_ptp_port;
 140};
 141
 142static struct ptpv2_data_slave_ordinary ptp_data;
 143
 144static inline uint64_t timespec64_to_ns(const struct timespec *ts)
 145{
 146        return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
 147}
 148
 149static struct timeval
 150ns_to_timeval(int64_t nsec)
 151{
 152        struct timespec t_spec = {0, 0};
 153        struct timeval t_eval = {0, 0};
 154        int32_t rem;
 155
 156        if (nsec == 0)
 157                return t_eval;
 158        rem = nsec % NSEC_PER_SEC;
 159        t_spec.tv_sec = nsec / NSEC_PER_SEC;
 160
 161        if (rem < 0) {
 162                t_spec.tv_sec--;
 163                rem += NSEC_PER_SEC;
 164        }
 165
 166        t_spec.tv_nsec = rem;
 167        t_eval.tv_sec = t_spec.tv_sec;
 168        t_eval.tv_usec = t_spec.tv_nsec / 1000;
 169
 170        return t_eval;
 171}
 172
 173/*
 174 * Initializes a given port using global settings and with the RX buffers
 175 * coming from the mbuf_pool passed as a parameter.
 176 */
 177static inline int
 178port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 179{
 180        struct rte_eth_dev_info dev_info;
 181        struct rte_eth_conf port_conf = port_conf_default;
 182        const uint16_t rx_rings = 1;
 183        const uint16_t tx_rings = 1;
 184        int retval;
 185        uint16_t q;
 186        uint16_t nb_rxd = RX_RING_SIZE;
 187        uint16_t nb_txd = TX_RING_SIZE;
 188
 189        if (!rte_eth_dev_is_valid_port(port))
 190                return -1;
 191
 192        retval = rte_eth_dev_info_get(port, &dev_info);
 193        if (retval != 0) {
 194                printf("Error during getting device (port %u) info: %s\n",
 195                                port, strerror(-retval));
 196
 197                return retval;
 198        }
 199
 200        if (dev_info.rx_offload_capa & DEV_RX_OFFLOAD_TIMESTAMP)
 201                port_conf.rxmode.offloads |= DEV_RX_OFFLOAD_TIMESTAMP;
 202
 203        if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
 204                port_conf.txmode.offloads |=
 205                        DEV_TX_OFFLOAD_MBUF_FAST_FREE;
 206        /* Force full Tx path in the driver, required for IEEE1588 */
 207        port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MULTI_SEGS;
 208
 209        /* Configure the Ethernet device. */
 210        retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
 211        if (retval != 0)
 212                return retval;
 213
 214        retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd);
 215        if (retval != 0)
 216                return retval;
 217
 218        /* Allocate and set up 1 RX queue per Ethernet port. */
 219        for (q = 0; q < rx_rings; q++) {
 220                retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
 221                                rte_eth_dev_socket_id(port), NULL, mbuf_pool);
 222
 223                if (retval < 0)
 224                        return retval;
 225        }
 226
 227        /* Allocate and set up 1 TX queue per Ethernet port. */
 228        for (q = 0; q < tx_rings; q++) {
 229                struct rte_eth_txconf *txconf;
 230
 231                txconf = &dev_info.default_txconf;
 232                txconf->offloads = port_conf.txmode.offloads;
 233
 234                retval = rte_eth_tx_queue_setup(port, q, nb_txd,
 235                                rte_eth_dev_socket_id(port), txconf);
 236                if (retval < 0)
 237                        return retval;
 238        }
 239
 240        /* Start the Ethernet port. */
 241        retval = rte_eth_dev_start(port);
 242        if (retval < 0)
 243                return retval;
 244
 245        /* Enable timesync timestamping for the Ethernet device */
 246        retval = rte_eth_timesync_enable(port);
 247        if (retval < 0) {
 248                printf("Timesync enable failed: %d\n", retval);
 249                return retval;
 250        }
 251
 252        /* Enable RX in promiscuous mode for the Ethernet device. */
 253        retval = rte_eth_promiscuous_enable(port);
 254        if (retval != 0) {
 255                printf("Promiscuous mode enable failed: %s\n",
 256                        rte_strerror(-retval));
 257                return retval;
 258        }
 259
 260        return 0;
 261}
 262
 263static void
 264print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
 265{
 266        int64_t nsec;
 267        struct timespec net_time, sys_time;
 268
 269        printf("Master Clock id: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
 270                ptp_data->master_clock_id.id[0],
 271                ptp_data->master_clock_id.id[1],
 272                ptp_data->master_clock_id.id[2],
 273                ptp_data->master_clock_id.id[3],
 274                ptp_data->master_clock_id.id[4],
 275                ptp_data->master_clock_id.id[5],
 276                ptp_data->master_clock_id.id[6],
 277                ptp_data->master_clock_id.id[7]);
 278
 279        printf("\nT2 - Slave  Clock.  %lds %ldns",
 280                        (ptp_data->tstamp2.tv_sec),
 281                        (ptp_data->tstamp2.tv_nsec));
 282
 283        printf("\nT1 - Master Clock.  %lds %ldns ",
 284                        ptp_data->tstamp1.tv_sec,
 285                        (ptp_data->tstamp1.tv_nsec));
 286
 287        printf("\nT3 - Slave  Clock.  %lds %ldns",
 288                        ptp_data->tstamp3.tv_sec,
 289                        (ptp_data->tstamp3.tv_nsec));
 290
 291        printf("\nT4 - Master Clock.  %lds %ldns ",
 292                        ptp_data->tstamp4.tv_sec,
 293                        (ptp_data->tstamp4.tv_nsec));
 294
 295        printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
 296                        ptp_data->delta);
 297
 298        clock_gettime(CLOCK_REALTIME, &sys_time);
 299        rte_eth_timesync_read_time(ptp_data->current_ptp_port, &net_time);
 300
 301        time_t ts = net_time.tv_sec;
 302
 303        printf("\n\nComparison between Linux kernel Time and PTP:");
 304
 305        printf("\nCurrent PTP Time: %.24s %.9ld ns",
 306                        ctime(&ts), net_time.tv_nsec);
 307
 308        nsec = (int64_t)timespec64_to_ns(&net_time) -
 309                        (int64_t)timespec64_to_ns(&sys_time);
 310        ptp_data->new_adj = ns_to_timeval(nsec);
 311
 312        gettimeofday(&ptp_data->new_adj, NULL);
 313
 314        time_t tp = ptp_data->new_adj.tv_sec;
 315
 316        printf("\nCurrent SYS Time: %.24s %.6ld ns",
 317                                ctime(&tp), ptp_data->new_adj.tv_usec);
 318
 319        printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
 320                                nsec);
 321
 322        printf("[Ctrl+C to quit]\n");
 323
 324        /* Clear screen and put cursor in column 1, row 1 */
 325        printf("\033[2J\033[1;1H");
 326}
 327
 328static int64_t
 329delta_eval(struct ptpv2_data_slave_ordinary *ptp_data)
 330{
 331        int64_t delta;
 332        uint64_t t1 = 0;
 333        uint64_t t2 = 0;
 334        uint64_t t3 = 0;
 335        uint64_t t4 = 0;
 336
 337        t1 = timespec64_to_ns(&ptp_data->tstamp1);
 338        t2 = timespec64_to_ns(&ptp_data->tstamp2);
 339        t3 = timespec64_to_ns(&ptp_data->tstamp3);
 340        t4 = timespec64_to_ns(&ptp_data->tstamp4);
 341
 342        delta = -((int64_t)((t2 - t1) - (t4 - t3))) / 2;
 343
 344        return delta;
 345}
 346
 347/*
 348 * Parse the PTP SYNC message.
 349 */
 350static void
 351parse_sync(struct ptpv2_data_slave_ordinary *ptp_data, uint16_t rx_tstamp_idx)
 352{
 353        struct ptp_header *ptp_hdr;
 354
 355        ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(ptp_data->m, char *)
 356                        + sizeof(struct rte_ether_hdr));
 357        ptp_data->seqID_SYNC = rte_be_to_cpu_16(ptp_hdr->seq_id);
 358
 359        if (ptp_data->ptpset == 0) {
 360                rte_memcpy(&ptp_data->master_clock_id,
 361                                &ptp_hdr->source_port_id.clock_id,
 362                                sizeof(struct clock_id));
 363                ptp_data->ptpset = 1;
 364        }
 365
 366        if (memcmp(&ptp_hdr->source_port_id.clock_id,
 367                        &ptp_hdr->source_port_id.clock_id,
 368                        sizeof(struct clock_id)) == 0) {
 369
 370                if (ptp_data->ptpset == 1)
 371                        rte_eth_timesync_read_rx_timestamp(ptp_data->portid,
 372                                        &ptp_data->tstamp2, rx_tstamp_idx);
 373        }
 374
 375}
 376
 377/*
 378 * Parse the PTP FOLLOWUP message and send DELAY_REQ to the main clock.
 379 */
 380static void
 381parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
 382{
 383        struct rte_ether_hdr *eth_hdr;
 384        struct rte_ether_addr eth_addr;
 385        struct ptp_header *ptp_hdr;
 386        struct clock_id *client_clkid;
 387        struct ptp_message *ptp_msg;
 388        struct rte_mbuf *created_pkt;
 389        struct tstamp *origin_tstamp;
 390        struct rte_ether_addr eth_multicast = ether_multicast;
 391        size_t pkt_size;
 392        int wait_us;
 393        struct rte_mbuf *m = ptp_data->m;
 394        int ret;
 395
 396        eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
 397        ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
 398                        + sizeof(struct rte_ether_hdr));
 399        if (memcmp(&ptp_data->master_clock_id,
 400                        &ptp_hdr->source_port_id.clock_id,
 401                        sizeof(struct clock_id)) != 0)
 402                return;
 403
 404        ptp_data->seqID_FOLLOWUP = rte_be_to_cpu_16(ptp_hdr->seq_id);
 405        ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
 406                                          sizeof(struct rte_ether_hdr));
 407
 408        origin_tstamp = &ptp_msg->follow_up.precise_origin_tstamp;
 409        ptp_data->tstamp1.tv_nsec = ntohl(origin_tstamp->ns);
 410        ptp_data->tstamp1.tv_sec =
 411                ((uint64_t)ntohl(origin_tstamp->sec_lsb)) |
 412                (((uint64_t)ntohs(origin_tstamp->sec_msb)) << 32);
 413
 414        if (ptp_data->seqID_FOLLOWUP == ptp_data->seqID_SYNC) {
 415                ret = rte_eth_macaddr_get(ptp_data->portid, &eth_addr);
 416                if (ret != 0) {
 417                        printf("\nCore %u: port %u failed to get MAC address: %s\n",
 418                                rte_lcore_id(), ptp_data->portid,
 419                                rte_strerror(-ret));
 420                        return;
 421                }
 422
 423                created_pkt = rte_pktmbuf_alloc(mbuf_pool);
 424                pkt_size = sizeof(struct rte_ether_hdr) +
 425                        sizeof(struct ptp_message);
 426                created_pkt->data_len = pkt_size;
 427                created_pkt->pkt_len = pkt_size;
 428                eth_hdr = rte_pktmbuf_mtod(created_pkt, struct rte_ether_hdr *);
 429                rte_ether_addr_copy(&eth_addr, &eth_hdr->s_addr);
 430
 431                /* Set multicast address 01-1B-19-00-00-00. */
 432                rte_ether_addr_copy(&eth_multicast, &eth_hdr->d_addr);
 433
 434                eth_hdr->ether_type = htons(PTP_PROTOCOL);
 435                ptp_msg = (struct ptp_message *)
 436                        (rte_pktmbuf_mtod(created_pkt, char *) +
 437                        sizeof(struct rte_ether_hdr));
 438
 439                ptp_msg->delay_req.hdr.seq_id = htons(ptp_data->seqID_SYNC);
 440                ptp_msg->delay_req.hdr.msg_type = DELAY_REQ;
 441                ptp_msg->delay_req.hdr.ver = 2;
 442                ptp_msg->delay_req.hdr.control = 1;
 443                ptp_msg->delay_req.hdr.log_message_interval = 127;
 444                ptp_msg->delay_req.hdr.message_length =
 445                        htons(sizeof(struct delay_req_msg));
 446                ptp_msg->delay_req.hdr.domain_number = ptp_hdr->domain_number;
 447
 448                /* Set up clock id. */
 449                client_clkid =
 450                        &ptp_msg->delay_req.hdr.source_port_id.clock_id;
 451
 452                client_clkid->id[0] = eth_hdr->s_addr.addr_bytes[0];
 453                client_clkid->id[1] = eth_hdr->s_addr.addr_bytes[1];
 454                client_clkid->id[2] = eth_hdr->s_addr.addr_bytes[2];
 455                client_clkid->id[3] = 0xFF;
 456                client_clkid->id[4] = 0xFE;
 457                client_clkid->id[5] = eth_hdr->s_addr.addr_bytes[3];
 458                client_clkid->id[6] = eth_hdr->s_addr.addr_bytes[4];
 459                client_clkid->id[7] = eth_hdr->s_addr.addr_bytes[5];
 460
 461                rte_memcpy(&ptp_data->client_clock_id,
 462                           client_clkid,
 463                           sizeof(struct clock_id));
 464
 465                /* Enable flag for hardware timestamping. */
 466                created_pkt->ol_flags |= PKT_TX_IEEE1588_TMST;
 467
 468                /*Read value from NIC to prevent latching with old value. */
 469                rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
 470                                &ptp_data->tstamp3);
 471
 472                /* Transmit the packet. */
 473                rte_eth_tx_burst(ptp_data->portid, 0, &created_pkt, 1);
 474
 475                wait_us = 0;
 476                ptp_data->tstamp3.tv_nsec = 0;
 477                ptp_data->tstamp3.tv_sec = 0;
 478
 479                /* Wait at least 1 us to read TX timestamp. */
 480                while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
 481                                &ptp_data->tstamp3) < 0) && (wait_us < 1000)) {
 482                        rte_delay_us(1);
 483                        wait_us++;
 484                }
 485        }
 486}
 487
 488/*
 489 * Update the kernel time with the difference between it and the current NIC
 490 * time.
 491 */
 492static inline void
 493update_kernel_time(void)
 494{
 495        int64_t nsec;
 496        struct timespec net_time, sys_time;
 497
 498        clock_gettime(CLOCK_REALTIME, &sys_time);
 499        rte_eth_timesync_read_time(ptp_data.current_ptp_port, &net_time);
 500
 501        nsec = (int64_t)timespec64_to_ns(&net_time) -
 502               (int64_t)timespec64_to_ns(&sys_time);
 503
 504        ptp_data.new_adj = ns_to_timeval(nsec);
 505
 506        /*
 507         * If difference between kernel time and system time in NIC is too big
 508         * (more than +/- 20 microseconds), use clock_settime to set directly
 509         * the kernel time, as adjtime is better for small adjustments (takes
 510         * longer to adjust the time).
 511         */
 512
 513        if (nsec > KERNEL_TIME_ADJUST_LIMIT || nsec < -KERNEL_TIME_ADJUST_LIMIT)
 514                clock_settime(CLOCK_REALTIME, &net_time);
 515        else
 516                adjtime(&ptp_data.new_adj, 0);
 517
 518
 519}
 520
 521/*
 522 * Parse the DELAY_RESP message.
 523 */
 524static void
 525parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
 526{
 527        struct rte_mbuf *m = ptp_data->m;
 528        struct ptp_message *ptp_msg;
 529        struct tstamp *rx_tstamp;
 530        uint16_t seq_id;
 531
 532        ptp_msg = (struct ptp_message *) (rte_pktmbuf_mtod(m, char *) +
 533                                        sizeof(struct rte_ether_hdr));
 534        seq_id = rte_be_to_cpu_16(ptp_msg->delay_resp.hdr.seq_id);
 535        if (memcmp(&ptp_data->client_clock_id,
 536                   &ptp_msg->delay_resp.req_port_id.clock_id,
 537                   sizeof(struct clock_id)) == 0) {
 538                if (seq_id == ptp_data->seqID_FOLLOWUP) {
 539                        rx_tstamp = &ptp_msg->delay_resp.rx_tstamp;
 540                        ptp_data->tstamp4.tv_nsec = ntohl(rx_tstamp->ns);
 541                        ptp_data->tstamp4.tv_sec =
 542                                ((uint64_t)ntohl(rx_tstamp->sec_lsb)) |
 543                                (((uint64_t)ntohs(rx_tstamp->sec_msb)) << 32);
 544
 545                        /* Evaluate the delta for adjustment. */
 546                        ptp_data->delta = delta_eval(ptp_data);
 547
 548                        rte_eth_timesync_adjust_time(ptp_data->portid,
 549                                                     ptp_data->delta);
 550
 551                        ptp_data->current_ptp_port = ptp_data->portid;
 552
 553                        /* Update kernel time if enabled in app parameters. */
 554                        if (ptp_data->kernel_time_set == 1)
 555                                update_kernel_time();
 556
 557
 558
 559                }
 560        }
 561}
 562
 563/* This function processes PTP packets, implementing slave PTP IEEE1588 L2
 564 * functionality.
 565 */
 566
 567/* Parse ptp frames. 8< */
 568static void
 569parse_ptp_frames(uint16_t portid, struct rte_mbuf *m) {
 570        struct ptp_header *ptp_hdr;
 571        struct rte_ether_hdr *eth_hdr;
 572        uint16_t eth_type;
 573
 574        eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
 575        eth_type = rte_be_to_cpu_16(eth_hdr->ether_type);
 576
 577        if (eth_type == PTP_PROTOCOL) {
 578                ptp_data.m = m;
 579                ptp_data.portid = portid;
 580                ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
 581                                        + sizeof(struct rte_ether_hdr));
 582
 583                switch (ptp_hdr->msg_type) {
 584                case SYNC:
 585                        parse_sync(&ptp_data, m->timesync);
 586                        break;
 587                case FOLLOW_UP:
 588                        parse_fup(&ptp_data);
 589                        break;
 590                case DELAY_RESP:
 591                        parse_drsp(&ptp_data);
 592                        print_clock_info(&ptp_data);
 593                        break;
 594                default:
 595                        break;
 596                }
 597        }
 598}
 599/* >8 End of function processes PTP packets. */
 600
 601/*
 602 * The lcore main. This is the main thread that does the work, reading from an
 603 * input port and writing to an output port.
 604 */
 605static __rte_noreturn void
 606lcore_main(void)
 607{
 608        uint16_t portid;
 609        unsigned nb_rx;
 610        struct rte_mbuf *m;
 611
 612        printf("\nCore %u Waiting for SYNC packets. [Ctrl+C to quit]\n",
 613                        rte_lcore_id());
 614
 615        /* Run until the application is quit or killed. */
 616
 617        while (1) {
 618                /* Read packet from RX queues. 8< */
 619                for (portid = 0; portid < ptp_enabled_port_nb; portid++) {
 620
 621                        portid = ptp_enabled_ports[portid];
 622                        nb_rx = rte_eth_rx_burst(portid, 0, &m, 1);
 623
 624                        if (likely(nb_rx == 0))
 625                                continue;
 626
 627                        /* Packet is parsed to determine which type. 8< */
 628                        if (m->ol_flags & PKT_RX_IEEE1588_PTP)
 629                                parse_ptp_frames(portid, m);
 630                        /* >8 End of packet is parsed to determine which type. */
 631
 632                        rte_pktmbuf_free(m);
 633                }
 634                /* >8 End of read packets from RX queues. */
 635        }
 636}
 637
 638static void
 639print_usage(const char *prgname)
 640{
 641        printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
 642                " -T VALUE: 0 - Disable, 1 - Enable Linux Clock"
 643                " Synchronization (0 default)\n"
 644                " -p PORTMASK: hexadecimal bitmask of ports to configure\n",
 645                prgname);
 646}
 647
 648static int
 649ptp_parse_portmask(const char *portmask)
 650{
 651        char *end = NULL;
 652        unsigned long pm;
 653
 654        /* Parse the hexadecimal string. */
 655        pm = strtoul(portmask, &end, 16);
 656
 657        if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
 658                return 0;
 659
 660        return pm;
 661}
 662
 663static int
 664parse_ptp_kernel(const char *param)
 665{
 666        char *end = NULL;
 667        unsigned long pm;
 668
 669        /* Parse the hexadecimal string. */
 670        pm = strtoul(param, &end, 16);
 671
 672        if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
 673                return -1;
 674        if (pm == 0)
 675                return 0;
 676
 677        return 1;
 678}
 679
 680/* Parse the commandline arguments. */
 681static int
 682ptp_parse_args(int argc, char **argv)
 683{
 684        int opt, ret;
 685        char **argvopt;
 686        int option_index;
 687        char *prgname = argv[0];
 688        static struct option lgopts[] = { {NULL, 0, 0, 0} };
 689
 690        argvopt = argv;
 691
 692        while ((opt = getopt_long(argc, argvopt, "p:T:",
 693                                  lgopts, &option_index)) != EOF) {
 694
 695                switch (opt) {
 696
 697                /* Portmask. */
 698                case 'p':
 699                        ptp_enabled_port_mask = ptp_parse_portmask(optarg);
 700                        if (ptp_enabled_port_mask == 0) {
 701                                printf("invalid portmask\n");
 702                                print_usage(prgname);
 703                                return -1;
 704                        }
 705                        break;
 706                /* Time synchronization. */
 707                case 'T':
 708                        ret = parse_ptp_kernel(optarg);
 709                        if (ret < 0) {
 710                                print_usage(prgname);
 711                                return -1;
 712                        }
 713
 714                        ptp_data.kernel_time_set = ret;
 715                        break;
 716
 717                default:
 718                        print_usage(prgname);
 719                        return -1;
 720                }
 721        }
 722
 723        argv[optind-1] = prgname;
 724
 725        optind = 1; /* Reset getopt lib. */
 726
 727        return 0;
 728}
 729
 730/*
 731 * The main function, which does initialization and calls the per-lcore
 732 * functions.
 733 */
 734int
 735main(int argc, char *argv[])
 736{
 737        unsigned nb_ports;
 738
 739        uint16_t portid;
 740
 741        /* Initialize the Environment Abstraction Layer (EAL). 8< */
 742        int ret = rte_eal_init(argc, argv);
 743
 744        if (ret < 0)
 745                rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
 746        /* >8 End of initialization of EAL. */
 747
 748        memset(&ptp_data, '\0', sizeof(struct ptpv2_data_slave_ordinary));
 749
 750        /* Parse specific arguments. 8< */
 751        argc -= ret;
 752        argv += ret;
 753
 754        ret = ptp_parse_args(argc, argv);
 755        if (ret < 0)
 756                rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
 757        /* >8 End of parsing specific arguments. */
 758
 759        /* Check that there is an even number of ports to send/receive on. */
 760        nb_ports = rte_eth_dev_count_avail();
 761
 762        /* Creates a new mempool in memory to hold the mbufs. 8< */
 763        mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
 764                MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
 765        /* >8 End of a new mempool in memory to hold the mbufs. */
 766
 767        if (mbuf_pool == NULL)
 768                rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
 769
 770        /* Initialize all ports. 8< */
 771        RTE_ETH_FOREACH_DEV(portid) {
 772                if ((ptp_enabled_port_mask & (1 << portid)) != 0) {
 773                        if (port_init(portid, mbuf_pool) == 0) {
 774                                ptp_enabled_ports[ptp_enabled_port_nb] = portid;
 775                                ptp_enabled_port_nb++;
 776                        } else {
 777                                rte_exit(EXIT_FAILURE,
 778                                         "Cannot init port %"PRIu8 "\n",
 779                                         portid);
 780                        }
 781                } else
 782                        printf("Skipping disabled port %u\n", portid);
 783        }
 784        /* >8 End of initialization of all ports. */
 785
 786        if (ptp_enabled_port_nb == 0) {
 787                rte_exit(EXIT_FAILURE,
 788                        "All available ports are disabled."
 789                        " Please set portmask.\n");
 790        }
 791
 792        if (rte_lcore_count() > 1)
 793                printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
 794
 795        /* Call lcore_main on the main core only. */
 796        lcore_main();
 797
 798        /* clean up the EAL */
 799        rte_eal_cleanup();
 800
 801        return 0;
 802}
 803