#include <stdio.h> #include <string.h> #include <errno.h> #include "hal_exports.h" #include "ptpd_netif.h" #include "board.h" #include "pps_gen.h" #include "minic.h" #include "endpoint.h" #include "softpll_ng.h" #define min(x,y) ((x) < (y) ? (x) : (y)) __attribute__ ((packed)) struct ethhdr { uint8_t dstmac[6]; uint8_t srcmac[6]; uint16_t ethtype; }; struct timeout { uint64_t start_tics; uint64_t timeout; }; struct sockq { uint8_t buf[NET_SKBUF_SIZE]; uint16_t head, tail, avail; uint16_t n; }; struct my_socket { int in_use; wr_sockaddr_t bind_addr; mac_addr_t local_mac; uint32_t phase_transition; uint32_t dmtd_phase; struct sockq queue; }; static struct my_socket socks[NET_MAX_SOCKETS]; int ptpd_netif_init() { memset(socks, 0, sizeof(socks)); return PTPD_NETIF_OK; } int ptpd_netif_get_hw_addr(wr_socket_t * sock, mac_addr_t * mac) { get_mac_addr((uint8_t *) mac); return 0; } void ptpd_netif_set_phase_transition(uint32_t phase) { int i; for (i=0; i< NET_MAX_SOCKETS; ++i) { socks[i].phase_transition = phase; } } wr_socket_t *ptpd_netif_create_socket(int sock_type, int flags, wr_sockaddr_t * bind_addr) { int i; hexp_port_state_t pstate; struct my_socket *sock; /* Look for the first available socket. */ for (sock = NULL, i = 0; i < NET_MAX_SOCKETS; i++) if (!socks[i].in_use) { sock = &socks[i]; break; } if (!sock) { TRACE_WRAP("No sockets left.\n"); return NULL; } if (sock_type != PTPD_SOCK_RAW_ETHERNET) return NULL; if (halexp_get_port_state(&pstate, bind_addr->if_name) < 0) return NULL; memcpy(&sock->bind_addr, bind_addr, sizeof(wr_sockaddr_t)); /*get mac from endpoint */ get_mac_addr(sock->local_mac); sock->phase_transition = pstate.t2_phase_transition; sock->dmtd_phase = pstate.phase_val; /*packet queue */ sock->queue.head = sock->queue.tail = 0; sock->queue.avail = NET_SKBUF_SIZE; sock->queue.n = 0; sock->in_use = 1; return (wr_socket_t *) (sock); } int ptpd_netif_close_socket(wr_socket_t * sock) { struct my_socket *s = (struct my_socket *)sock; if (s) s->in_use = 0; return 0; } static inline int inside_range(int min, int max, int x) { if (min < max) return (x >= min && x <= max); else return (x <= max || x >= min); } void ptpd_netif_linearize_rx_timestamp(wr_timestamp_t * ts, int32_t dmtd_phase, int cntr_ahead, int transition_point, int clock_period) { int trip_lo, trip_hi; int phase; // "phase" transition: DMTD output value (in picoseconds) // at which the transition of rising edge // TS counter will appear ts->raw_phase = dmtd_phase; phase = clock_period - 1 - dmtd_phase; // calculate the range within which falling edge timestamp is stable // (no possible transitions) trip_lo = transition_point - clock_period / 4; if (trip_lo < 0) trip_lo += clock_period; trip_hi = transition_point + clock_period / 4; if (trip_hi >= clock_period) trip_hi -= clock_period; if (inside_range(trip_lo, trip_hi, phase)) { // We are within +- 25% range of transition area of // rising counter. Take the falling edge counter value as the // "reliable" one. cntr_ahead will be 1 when the rising edge //counter is 1 tick ahead of the falling edge counter ts->nsec -= cntr_ahead ? (clock_period / 1000) : 0; // check if the phase is before the counter transition value // and eventually increase the counter by 1 to simulate a // timestamp transition exactly at s->phase_transition //DMTD phase value if (inside_range(trip_lo, transition_point, phase)) ts->nsec += clock_period / 1000; } ts->phase = phase - transition_point - 1; if (ts->phase < 0) ts->phase += clock_period; ts->phase = clock_period - 1 - ts->phase; } /* Slow, but we don't care much... */ static int wrap_copy_in(void *dst, struct sockq *q, size_t len) { char *dptr = dst; int i = len; TRACE_WRAP("copy_in: tail %d avail %d len %d\n", q->tail, q->avail, len); while (i--) { *dptr++ = q->buf[q->tail]; q->tail++; if (q->tail == NET_SKBUF_SIZE) q->tail = 0; } return len; } static int wrap_copy_out(struct sockq *q, void *src, size_t len) { char *sptr = src; int i = len; TRACE_WRAP("copy_out: head %d avail %d len %d\n", q->head, q->avail, len); while (i--) { q->buf[q->head++] = *sptr++; if (q->head == NET_SKBUF_SIZE) q->head = 0; } return len; } int ptpd_netif_recvfrom(wr_socket_t * sock, wr_sockaddr_t * from, void *data, size_t data_length, wr_timestamp_t * rx_timestamp) { struct my_socket *s = (struct my_socket *)sock; struct sockq *q = &s->queue; uint16_t size; struct ethhdr hdr; struct hw_timestamp hwts; /*check if there is something to fetch */ if (!q->n) return 0; q->n--; q->avail += wrap_copy_in(&size, q, 2); q->avail += wrap_copy_in(&hdr, q, sizeof(struct ethhdr)); q->avail += wrap_copy_in(&hwts, q, sizeof(struct hw_timestamp)); q->avail += wrap_copy_in(data, q, min(size, data_length)); from->ethertype = ntohs(hdr.ethtype); memcpy(from->mac, hdr.srcmac, 6); memcpy(from->mac_dest, hdr.dstmac, 6); if (rx_timestamp) { rx_timestamp->raw_nsec = hwts.nsec; rx_timestamp->raw_ahead = hwts.ahead; spll_read_ptracker(0, &rx_timestamp->raw_phase, NULL); rx_timestamp->sec = hwts.sec; rx_timestamp->nsec = hwts.nsec; rx_timestamp->phase = 0; rx_timestamp->correct = hwts.valid; ptpd_netif_linearize_rx_timestamp(rx_timestamp, rx_timestamp->raw_phase, hwts.ahead, s->phase_transition, REF_CLOCK_PERIOD_PS); } TRACE_WRAP("RX: Size %d tail %d Smac %x:%x:%x:%x:%x:%x\n", size, q->tail, hdr.srcmac[0], hdr.srcmac[1], hdr.srcmac[2], hdr.srcmac[3], hdr.srcmac[4], hdr.srcmac[5]); /* TRACE_WRAP("%s: received data from %02x:%02x:%02x:%02x:%02x:%02x to %02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__, from->mac[0],from->mac[1],from->mac[2],from->mac[3], from->mac[4],from->mac[5],from->mac[6],from->mac[7], from->mac_dest[0],from->mac_dest[1],from->mac_dest[2],from->mac_dest[3], from->mac_dest[4],from->mac_dest[5],from->mac_dest[6],from->mac_dest[7]);*/ return min(size - sizeof(struct ethhdr), data_length); return 0; } int ptpd_netif_select(wr_socket_t * wrSock) { return 0; } int ptpd_netif_sendto(wr_socket_t * sock, wr_sockaddr_t * to, void *data, size_t data_length, wr_timestamp_t * tx_timestamp) { struct my_socket *s = (struct my_socket *)sock; struct hw_timestamp hwts; struct ethhdr hdr; int rval; memcpy(hdr.dstmac, to->mac, 6); memcpy(hdr.srcmac, s->local_mac, 6); hdr.ethtype = to->ethertype; rval = minic_tx_frame((uint8_t *) & hdr, (uint8_t *) data, data_length + ETH_HEADER_SIZE, &hwts); if (tx_timestamp) { tx_timestamp->sec = hwts.sec; tx_timestamp->nsec = hwts.nsec; tx_timestamp->phase = 0; tx_timestamp->correct = hwts.valid; } return rval; } void update_rx_queues() { struct my_socket *s = NULL; struct sockq *q; struct hw_timestamp hwts; static struct ethhdr hdr; int recvd, i, q_required; static uint8_t payload[NET_SKBUF_SIZE - 32]; uint16_t size; recvd = minic_rx_frame((uint8_t *) & hdr, payload, NET_SKBUF_SIZE - 32, &hwts); if (recvd <= 0) /* No data received? */ return; for (i = 0; i < NET_MAX_SOCKETS; i++) { s = &socks[i]; if (s->in_use && !memcmp(hdr.dstmac, s->bind_addr.mac, 6) && hdr.ethtype == s->bind_addr.ethertype) break; /*they match */ s = NULL; } if (!s) { TRACE_WRAP("%s: could not find socket for packet\n", __FUNCTION__); return; } q = &s->queue; q_required = sizeof(struct ethhdr) + recvd + sizeof(struct hw_timestamp) + 2; if (q->avail < q_required) { TRACE_WRAP ("%s: queue for socket full; [avail %d required %d]\n", __FUNCTION__, q->avail, q_required); return; } size = recvd; q->avail -= wrap_copy_out(q, &size, 2); q->avail -= wrap_copy_out(q, &hdr, sizeof(struct ethhdr)); q->avail -= wrap_copy_out(q, &hwts, sizeof(struct hw_timestamp)); q->avail -= wrap_copy_out(q, payload, size); q->n++; TRACE_WRAP("Q: Size %d head %d Smac %x:%x:%x:%x:%x:%x\n", recvd, q->head, hdr.srcmac[0], hdr.srcmac[1], hdr.srcmac[2], hdr.srcmac[3], hdr.srcmac[4], hdr.srcmac[5]); TRACE_WRAP("%s: saved packet to queue [avail %d n %d size %d]\n", __FUNCTION__, q->avail, q->n, q_required); }