source: ResearchApps/PHY/WARPLAB/WARPLab7/C_Code_Reference/wl_transport.c

Last change on this file was 4783, checked in by welsh, 8 years ago

Initial update to add new AGC, Trigger Manager, and Node commands. The AGC commands have been tested, but the other commands have not. Also, cleaned up Command ID names, Trigger Manager defines.

File size: 40.6 KB
Line 
1/** @file wl_transport.c
2 *  @brief WARPLab Framework (Transport)
3 *
4 *  This contains the code for WARPLab Framework transport.
5 *
6 *  @copyright Copyright 2013-2015, Mango Communications. All rights reserved.
7 *          Distributed under the WARP license  (http://warpproject.org/license)
8 *
9 *  @author Chris Hunter (chunter [at] mangocomm.com)
10 *  @author Patrick Murphy (murphpo [at] mangocomm.com)
11 *  @author Erik Welsh (welsh [at] mangocomm.com)
12 */
13
14
15/**********************************************************************************************************************/
16/**
17 * @brief Common Functions
18 *
19 **********************************************************************************************************************/
20
21/***************************** Include Files *********************************/
22
23// Xilinx / Standard library includes
24#include <xparameters.h>
25#include <xil_io.h>
26#include <xstatus.h>
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30
31// WARPLab includes
32#include "wl_common.h"
33#include "wl_transport.h"
34#include "wl_trigger_manager.h"
35#include "wl_node.h"
36
37
38/*************************** Constant Definitions ****************************/
39
40/*********************** Global Variable Definitions *************************/
41
42extern u16                   node;                         // Node ID (defined in wl_node.c)
43
44
45/*************************** Variable Definitions ****************************/
46
47// NOTE:  This structure has different member types depending on the WARP version
48wl_eth_dev_info              eth_devices[WL_NUM_ETH_DEVICES];
49
50// Callbacks
51volatile wl_function_ptr_t   process_hton_msg_callback;
52
53
54/*************************** Function Prototypes *****************************/
55
56void transport_wl_eth_dev_info_init(u32 eth_dev_num);
57
58int  transport_check_device(u32 eth_dev_num);
59
60void transport_receive(u32 eth_dev_num, int socket_index, struct sockaddr * from, warp_ip_udp_buffer * recv_buffer, warp_ip_udp_buffer * send_buffer);
61
62void transport_set_eth_phy_speed(u32 eth_dev_num, u32 speed);
63void transport_set_eth_phy_auto_negotiation(u32 eth_dev_num, u32 enable);
64
65
66/******************************** Functions **********************************/
67
68
69/*****************************************************************************/
70/**
71 * This function will poll the given Ethernet device
72 *
73 * @param   eth_dev_num      - Ethernet device number
74 *
75 * @return  None
76 *
77 * @note    Buffers are managed by the WARP UDP transport driver
78 *
79 *****************************************************************************/
80void transport_poll(u32 eth_dev_num) {
81
82    int                     recv_bytes;
83    int                     socket_index;
84    warp_ip_udp_buffer      recv_buffer;
85    warp_ip_udp_buffer    * send_buffer;
86    struct sockaddr         from;
87
88    // Check the socket to see if there is data
89    recv_bytes = socket_recvfrom_eth(eth_dev_num, &socket_index, &from, &recv_buffer);
90
91    // If we have received data, then we need to process it
92    if (recv_bytes > 0) {
93        // Allocate a send buffer from the transport driver
94        send_buffer = socket_alloc_send_buffer();
95
96        // Process the received packet
97        transport_receive(eth_dev_num, socket_index, &from, &recv_buffer, send_buffer);
98
99        // Need to communicate to the transport driver that the buffers can now be reused
100        socket_free_recv_buffer(socket_index, &recv_buffer);
101        socket_free_send_buffer(send_buffer);
102    }
103}
104
105
106
107/*****************************************************************************/
108/**
109 * Process the received UDP packet by the transport
110 *
111 * @param   eth_dev_num      - Ethernet device number
112 * @param   socket_index     - Index of the socket on which message was received
113 * @param   from             - Pointer to socket address structure from which message was received
114 * @param   recv_buffer      - Pointer to transport buffer with received message
115 * @param   send_buffer      - Pointer to transport buffer for a node response to the message
116 *
117 * @return  None
118 *
119 * @note    If this packet is a host to node message, then the  process_hton_msg_callback
120 *          is used to further process the packet.  This method will strip off the
121 *          WL transport header for future packet processing.
122 *
123 *****************************************************************************/
124void transport_receive(u32 eth_dev_num, int socket_index, struct sockaddr * from, warp_ip_udp_buffer * recv_buffer, warp_ip_udp_buffer * send_buffer) {
125
126    int                           status;
127    u32                           trigger_ethernet_id;
128    u16                           dest_id;
129    u16                           src_id;
130    u16                           seq_num;
131    u16                           flags;
132    u32                           group_id;
133
134    wl_transport_header         * wl_header_rx    = (wl_transport_header*)(recv_buffer->offset);   // Contains entire Ethernet frame; offset points to UDP payload
135    wl_transport_header         * wl_header_tx    = (wl_transport_header*)(send_buffer->offset);   // New buffer for UDP payload
136
137    // Get the transport headers for the send / receive buffers
138    //     NOTE:  For the receive buffer, offset points to UDP payload of the Ethernet frame
139    //     NOTE:  For the send buffer, the offset points to the start of the buffer but since we will use the
140    //            UDP header of the socket to transmit the frame, this is effectively the start of the UDP payload
141    //
142    wl_header_rx             = (wl_transport_header*)(recv_buffer->offset);
143    wl_header_tx             = (wl_transport_header*)(send_buffer->offset);
144
145    // Update the buffers to account for the transport headers
146    recv_buffer->offset     += sizeof(wl_transport_header);
147    recv_buffer->length     -= sizeof(wl_transport_header);                    // Remaining bytes in receive buffer
148
149    send_buffer->offset     += sizeof(wl_transport_header);
150    send_buffer->length     += sizeof(wl_transport_header);                    // Adding bytes to the send buffer
151    send_buffer->size       += sizeof(wl_transport_header);                    // Keep size in sync
152
153    // Update the green LEDs for every received packet
154    increment_green_leds_one_hot();
155
156    // Process the data based on the packet type
157    //     NOTE:  The pkt_type does not need to be endian swapped because it is a u8
158    //
159    switch(wl_header_rx->pkt_type){
160
161        //-------------------------------
162        // Trigger Manager Trigger packet
163        //
164        case PKTTYPE_TRIGGER:
165
166            // Get the Trigger Ethernet ID from the receive packet
167            trigger_ethernet_id  = Xil_Ntohl(*((u32 *)(recv_buffer->offset)));
168
169            // Process the Trigger Ethernet ID
170            trigmngr_trigger_in(trigger_ethernet_id, eth_dev_num);
171        break;
172
173        //-------------------------------
174        // Message from the Host to the Node
175        //
176        case PKTTYPE_HTON_MSG:
177            // Extract values from the received transport header
178            //
179            dest_id  = Xil_Ntohs(wl_header_rx->dest_id);
180            src_id   = Xil_Ntohs(wl_header_rx->src_id);
181            seq_num  = Xil_Ntohs(wl_header_rx->seq_num);
182            flags    = Xil_Ntohs(wl_header_rx->flags);
183
184            group_id = eth_devices[eth_dev_num].group_id;
185
186            // If this message is not for the given node, then ignore it
187            if((dest_id != node) && (dest_id != BROADCAST_DEST_ID) && ((dest_id & (0xFF00 | group_id)) == 0)) { return; }
188
189            // Form outgoing WARPLab header for any outgoing packet in response to this message
190            //     NOTE:  The u16/u32 fields here will be endian swapped in transport_send
191            //     NOTE:  The length field of the header will be set in transport_send
192            //
193            wl_header_tx->dest_id  = src_id;
194            wl_header_tx->src_id   = node;
195            wl_header_tx->pkt_type = PKTTYPE_NTOH_MSG;
196            wl_header_tx->seq_num  = seq_num;
197            wl_header_tx->flags    = 0;
198            wl_header_tx->reserved = 0;
199
200            // Call the callback to further process the recv_buffer
201            status = process_hton_msg_callback(socket_index, from, recv_buffer, send_buffer);
202
203            if (send_buffer->size != send_buffer->length) {
204                wl_printf(WL_PRINT_WARNING, print_type_transport, "Send buffer length (%d) does not match size (%d)\n", send_buffer->length, send_buffer->size);
205            }
206
207            // Based on the status, return a message to the host
208            switch(status) {
209
210                //-------------------------------
211                // No response has been sent by the node
212                //
213                case NO_RESP_SENT:
214                    // Check if the host requires a response from the node
215                    if (flags & TRANSPORT_HDR_ROBUST_FLAG) {
216
217                        // Check that the node has something to send to the host
218                        if ((send_buffer->length) > sizeof(wl_transport_header)) {
219                            transport_send(socket_index, from, &send_buffer, 1);                        // Send the buffer of data
220                        } else {
221                            wl_printf(WL_PRINT_WARNING, print_type_transport, "Host requires response but node has nothing to send.\n");
222                        }
223                    }
224                break;
225
226                //-------------------------------
227                // A response has already been sent by the node
228                //
229                case RESP_SENT:
230                    // The transport does not need to do anything else
231                break;
232
233                //-------------------------------
234                // The node is not ready to process the message; host needs to wait and send it again
235                //
236                case NODE_NOT_READY:
237                    wl_printf(WL_PRINT_NONE, NULL, "\nWARNING:  Node not ready for command.\n    Please add a pause() with the appropriate time to your Matlab code.\n\n");
238
239                    // Set the flag to indicate the node is not ready
240                    wl_header_tx->flags  = TRANSPORT_HDR_NODE_NOT_READY_FLAG;
241
242                    // Send a packet to the host
243                    transport_send(socket_index, from, &send_buffer, 1);                        // Send the buffer of data
244                break;
245
246                default:
247                    wl_printf(WL_PRINT_ERROR, print_type_transport, "Received unknown status for message: %d\n", status);
248                break;
249            }
250        break;
251
252        default:
253            wl_printf(WL_PRINT_ERROR, print_type_transport, "Received packet with unknown packet type: %d\n", (wl_header_rx->pkt_type));
254        break;
255    }
256}
257
258
259
260/*****************************************************************************/
261/**
262 * This function is used to send a message over Ethernet
263 *
264 * @param   socket_index     - Index of the socket on which to send message
265 * @param   to               - Pointer to socket address structure to send message
266 * @param   buffers          - Array of transport buffers to send
267 * @param   num_buffers      - Number of transport buffers in 'buffers' array
268 *
269 * @return  None
270 *
271 * @note    This function requires that the first transport buffer in the 'buffers'
272 *          array contain the WARPLab transport header.
273 *
274 *****************************************************************************/
275void transport_send(int socket_index, struct sockaddr * to, warp_ip_udp_buffer ** buffers, u32 num_buffers) {
276
277    u32                   i;
278    int                   status;
279    wl_transport_header * wl_header_tx;
280    u16                   buffer_length = 0;
281
282    // Check that we have a valid socket to send a message on
283    if (socket_index == SOCKET_INVALID_SOCKET) {
284        wl_printf(WL_PRINT_ERROR, print_type_transport, "Invalid socket.\n");
285        return;
286    }
287
288    // Initialize the header
289    //     NOTE:  We require that the first warp_ip_udp_buffer always contain the wl_transport_header
290    //
291    wl_header_tx = (wl_transport_header *)(buffers[0]->data);
292
293    // Compute the length
294    for (i = 0; i < num_buffers; i++) {
295        buffer_length += buffers[i]->size;
296    }
297
298    //
299    // NOTE:  Through performance testing, we found that it was faster to just manipulate the header
300    //   in place vs creating a copy, updating the header and then restoring the copy.
301    //
302
303    // Make the outgoing transport header endian safe for sending on the network
304    //     NOTE:  Set the 'length' to the computed value above
305    //
306    wl_header_tx->dest_id = Xil_Htons(wl_header_tx->dest_id);
307    wl_header_tx->src_id  = Xil_Htons(wl_header_tx->src_id);
308    wl_header_tx->length  = Xil_Htons(buffer_length + WARP_IP_UDP_DELIM_LEN);
309    wl_header_tx->seq_num = Xil_Htons(wl_header_tx->seq_num);
310    wl_header_tx->flags   = Xil_Htons(wl_header_tx->flags);
311
312    // Update the green LEDs for every packet sent
313    increment_green_leds_one_hot();
314
315    // Send the Ethernet packet
316    status = socket_sendto(socket_index, to, buffers, num_buffers);
317
318    // Restore wl_header_tx
319    wl_header_tx->dest_id = Xil_Ntohs(wl_header_tx->dest_id);
320    wl_header_tx->src_id  = Xil_Ntohs(wl_header_tx->src_id);
321    wl_header_tx->length  = 0;
322    wl_header_tx->seq_num = Xil_Ntohs(wl_header_tx->seq_num);
323    wl_header_tx->flags   = Xil_Ntohs(wl_header_tx->flags);
324
325    // Check that the packet was sent correctly
326    if (status == WARP_IP_UDP_FAILURE) {
327        wl_printf(WL_PRINT_WARNING, print_type_transport, "Issue sending packet %d to host.\n", wl_header_tx->seq_num);
328    }
329}
330
331
332
333
334/*****************************************************************************/
335/**
336 * Process Transport Commands
337 *
338 * This function is part of the Ethernet processing system and will process the
339 * various transport related commands.
340 *
341 * @param   socket_index     - Index of the socket on which to send message
342 * @param   from             - Pointer to socket address structure (struct sockaddr *) where command is from
343 * @param   command          - Pointer to WARPLab Command
344 * @param   response         - Pointer to WARPLab Response
345 *
346 * @return  int              - Status of the command:
347 *                                 NO_RESP_SENT - No response has been sent
348 *                                 RESP_SENT    - A response has been sent
349 *
350 * @note    See on-line documentation for more information about the Ethernet
351 *          packet structure for WARPLab:  www.warpproject.org
352 *
353 *****************************************************************************/
354int transport_process_cmd(int socket_index, void * from, wl_cmd_resp * command, wl_cmd_resp * response) {
355
356    //
357    // IMPORTANT ENDIAN NOTES:
358    //     - command
359    //         - header - Already endian swapped by the framework (safe to access directly)
360    //         - args   - Must be endian swapped as necessary by code (framework does not know the contents of the command)
361    //     - response
362    //         - header - Will be endian swapped by the framework (safe to write directly)
363    //         - args   - Must be endian swapped as necessary by code (framework does not know the contents of the response)
364    //
365
366    // Standard variables
367    u32                 resp_sent      = NO_RESP_SENT;
368
369    wl_cmd_resp_hdr   * cmd_hdr        = command->header;
370    u32               * cmd_args_32    = command->args;
371    u32                 cmd_id         = WL_CMD_TO_CMDID(cmd_hdr->cmd);
372
373    wl_cmd_resp_hdr   * resp_hdr       = response->header;
374    u32               * resp_args_32   = response->args;
375    u32                 resp_index     = 0;
376
377    // Specific command variables
378    u32                 temp;
379    u32                 eth_dev_num;
380    u32                 size_index;
381    u32                 payload_size;
382    u32                 header_size;
383
384    // Set up the response header
385    resp_hdr->cmd       = cmd_hdr->cmd;
386    resp_hdr->length    = 0;
387    resp_hdr->num_args  = 0;
388
389    // Process the command
390    switch(cmd_id){
391
392        //---------------------------------------------------------------------
393        case CMDID_TRANSPORT_PING:
394            //
395            // Nothing actually needs to be done when receiving the ping command. The framework is going
396            // to respond regardless, which is all the host wants.
397            //
398        break;
399
400        //---------------------------------------------------------------------
401        case CMDID_TRANSPORT_PAYLOAD_SIZE_TEST:
402            //
403            // Due to packet fragmentation, it is not safe to just return the packet length.  We have seen
404            // an issue where the host will send a 1514 byte fragment which results in a payload size of
405            // 1472 and causes the transport to not behave correctly.  Therefore, we need to find the
406            // last valid command argument and check that against the packet length.
407            //
408            header_size  = (sizeof(wl_transport_header) + sizeof(wl_cmd_resp_hdr));                            // Transport / Command headers
409            size_index   = (((warp_ip_udp_buffer *)(command->buffer))->length - sizeof(wl_cmd_resp_hdr)) / 4;  // Final index into command args (/4 truncates)
410
411            // Check the value in the command args to make sure it matches the size_index
412            //   NOTE:  This indexing step works because the payload is filled with incrementing number starting at 1.
413            //
414            payload_size = (Xil_Htonl(cmd_args_32[size_index - 1]) * 4) + header_size;
415            temp         = ((size_index * 4) + header_size);
416
417            if (payload_size != temp) {
418                wl_printf(WL_PRINT_WARNING, print_type_transport, "Payload size mismatch.  Value in command args does not match index:  %d != %d\n", payload_size, temp);
419            }
420
421            resp_args_32[resp_index++] = Xil_Ntohl(payload_size);
422
423            resp_hdr->length  += (resp_index * sizeof(resp_args_32));
424            resp_hdr->num_args = 1;
425        break;
426
427        //---------------------------------------------------------------------
428        case CMDID_TRANSPORT_NODE_GROUP_ID_ADD:
429            eth_dev_num = socket_get_eth_dev_num(socket_index);
430
431            if (eth_dev_num != WARP_IP_UDP_INVALID_ETH_DEVICE) {
432                eth_devices[eth_dev_num].group_id = (eth_devices[eth_dev_num].group_id | Xil_Htonl(cmd_args_32[0]));
433            } else {
434                wl_printf(WL_PRINT_ERROR, print_type_transport, "Add Group ID - Invalid socket index: %d\n", socket_index);
435            }
436        break;
437
438        //---------------------------------------------------------------------
439        case CMDID_TRANSPORT_NODE_GROUP_ID_CLEAR:
440            eth_dev_num = socket_get_eth_dev_num(socket_index);
441
442            if (eth_dev_num != WARP_IP_UDP_INVALID_ETH_DEVICE) {
443                eth_devices[eth_dev_num].group_id = (eth_devices[eth_dev_num].group_id & ~Xil_Htonl(cmd_args_32[0]));
444            } else {
445                wl_printf(WL_PRINT_ERROR, print_type_transport, "Clear Group ID - Invalid socket index: %d\n", socket_index);
446            }
447        break;
448
449        //---------------------------------------------------------------------
450        default:
451            wl_printf(WL_PRINT_ERROR, print_type_transport, "Unknown user command ID: %d\n", cmd_id);
452        break;
453    }
454
455    return resp_sent;
456}
457
458
459
460/*****************************************************************************/
461/**
462 * Close the unicast and broadcast sockets associated with a given Ethernet device
463 *
464 * @param   eth_dev_num      - Ethernet device number
465 *
466 * @return  None
467 *
468 *****************************************************************************/
469void transport_close(u32 eth_dev_num) {
470
471    if (transport_check_device(eth_dev_num) == XST_SUCCESS) {
472        socket_close(eth_devices[eth_dev_num].unicast_socket);
473        socket_close(eth_devices[eth_dev_num].broadcast_socket);
474    }
475}
476
477
478
479/*****************************************************************************/
480/**
481 * Create and bind a socket for the Ethernet device
482 *
483 * @param   eth_dev_num      - Ethernet device number
484 * @param   socket_index     - Socket index (return value)
485 * @param   udp_port         - UDP port number
486 *
487 * @return  int              - Status of the command:
488 *                                 XST_SUCCESS - Command completed successfully
489 *                                 XST_FAILURE - There was an error in the command
490 *
491 *****************************************************************************/
492int transport_config_socket(u32 eth_dev_num, int * socket_index, u32 udp_port) {
493
494    int status;
495    int tmp_socket  = *socket_index;
496
497    // Release socket if it is already bound
498    if (tmp_socket != SOCKET_INVALID_SOCKET) {
499        socket_close(tmp_socket);
500    }
501
502    // Create a new socket
503    tmp_socket = socket_socket(AF_INET, SOCK_DGRAM, 0);
504
505    if (tmp_socket == SOCKET_INVALID_SOCKET) {
506        wl_printf(WL_PRINT_ERROR, print_type_transport, "Could not create socket\n");
507
508        * socket_index = SOCKET_INVALID_SOCKET;
509
510        return XST_FAILURE;
511    }
512
513    // Bind the socket
514    status = socket_bind_eth(tmp_socket, eth_dev_num, udp_port);
515
516    if (status == WARP_IP_UDP_FAILURE) {
517        wl_printf(WL_PRINT_ERROR, print_type_transport, "Unable to bind socket on port: %d\n", udp_port);
518
519        socket_close(tmp_socket);
520
521        * socket_index = SOCKET_INVALID_SOCKET;
522
523        return XST_FAILURE;
524    }
525
526    * socket_index = tmp_socket;
527
528    return XST_SUCCESS;
529}
530
531
532
533/*****************************************************************************/
534/**
535 * This function will configure the unicast and broadcast sockets to be used
536 * by the transport using the default transport_receive_callback() function.
537 *
538 * @param   eth_dev_num      - Ethernet device number
539 * @param   unicast_port     - Unicast port for the node
540 * @param   bcast_port       - Broadcast port for the node
541 *
542 * @return  int              - Status of the command:
543 *                                 XST_SUCCESS - Command completed successfully
544 *                                 XST_FAILURE - There was an error in the command
545 *
546 *****************************************************************************/
547int transport_config_sockets(u32 eth_dev_num, u32 unicast_port, u32 bcast_port) {
548    int status = XST_SUCCESS;
549
550    status = transport_config_socket(eth_dev_num, &(eth_devices[eth_dev_num].unicast_socket), unicast_port);
551    if (status == XST_FAILURE) { return status; }
552
553    status = transport_config_socket(eth_dev_num, &(eth_devices[eth_dev_num].broadcast_socket), bcast_port);
554    if (status == XST_FAILURE) { return status; }
555
556    wl_printf(WL_PRINT_NONE, NULL, "  Listening on UDP ports %d (unicast) and %d (broadcast)\n", unicast_port, bcast_port);
557
558    return status;
559}
560
561
562
563/*****************************************************************************/
564/**
565 * This function is the Transport callback that allows WARPLab to process
566 * a received Ethernet packet
567 *
568 * @param   hander           - Pointer to the transport receive callback function
569 *
570 * @return  int              - Status of the command:
571 *                                 XST_SUCCESS - Command completed successfully
572 *
573 *****************************************************************************/
574int transport_set_process_hton_msg_callback(void(*handler)) {
575    process_hton_msg_callback = handler;
576
577    return XST_SUCCESS;
578}
579
580
581
582/*****************************************************************************/
583/**
584 * This function will check the link status of a Ethernet controller
585 *
586 * @param   eth_dev_num      - Ethernet device number
587 * @param   unicast_port     - Unicast port for the node
588 * @param   bcast_port       - Broadcast port for the node
589 *
590 * @return  int              - Status of the command:
591 *                                 LINK_READY     - Ethernet controller is ready to be used
592 *                                 LINK_NOT_READY - Ethernet controller is not ready to be used
593 *
594 *****************************************************************************/
595int transport_link_status(u32 eth_dev_num) {
596
597    int status  = LINK_READY;
598    u16 reg_val = transport_get_ethernet_status(eth_dev_num);
599
600    if(reg_val & ETH_PHY_REG_17_0_LINKUP) {
601        status = LINK_READY;
602    } else {
603        status = LINK_NOT_READY;
604    }
605
606    return status;
607}
608
609
610
611/*****************************************************************************/
612/**
613 * This function will update the link speed of a Ethernet controller
614 *
615 * @param   eth_dev_num           - Ethernet device number
616 * @param   wait_for_negotiation  - Flag to wait for auto-negotiation of Ethernet link speed
617 *
618 * @return  speed            - Ethernet link speed that was chosen
619 *
620 *****************************************************************************/
621u32 transport_update_link_speed(u32 eth_dev_num, u32 wait_for_negotiation) {
622
623    volatile u16 reg_val          = 0;
624    u32          negotiated       = 1;
625    u16          speed            = 0;
626
627    u32          start_time       = get_usec_timestamp();
628    u32          end_time         = start_time;
629
630    // Make sure the Ethernet device is initialized
631    if (eth_devices[eth_dev_num].initialized == WL_ETH_DEV_INITIALIZED) {
632
633        xil_printf("  ETH %c speed ", warp_conv_eth_dev_num(eth_dev_num));
634
635        reg_val = transport_get_ethernet_status(eth_dev_num);
636
637        if (wait_for_negotiation == ETH_WAIT_FOR_AUTO_NEGOTIATION) {
638
639            while((reg_val & ETH_PHY_REG_17_0_SPEED_RESOLVED) == 0) {
640                usleep(1000);
641                reg_val = transport_get_ethernet_status(eth_dev_num);
642            }
643
644            speed = ETH_PHY_SPEED_TO_MBPS((reg_val & ETH_PHY_REG_17_0_SPEED));
645            end_time = get_usec_timestamp();
646
647        } else {
648            // Check to see if the Ethernet controller has auto-negotiated a speed
649            if (reg_val & ETH_PHY_REG_17_0_SPEED_RESOLVED) {
650                speed      = ETH_PHY_SPEED_TO_MBPS((reg_val & ETH_PHY_REG_17_0_SPEED));
651            } else {
652                speed      = eth_devices[eth_dev_num].default_speed;
653                negotiated = 0;
654            }
655        }
656
657        // Set the operating speed of the Ethernet controller
658        eth_set_operating_speed(eth_dev_num, speed);
659
660        // Set the operating speed of the Ethernet PHY
661        transport_set_eth_phy_speed(eth_dev_num, speed);
662
663        // Sleep for a short period of time to let everything settle
664        usleep(1 * 10000);
665
666    } else {
667        wl_printf(WL_PRINT_NONE, NULL, "  ETH %c not initialized.  Link speed not updated.\n", warp_conv_eth_dev_num(eth_dev_num));
668    }
669
670    if (negotiated) {
671        xil_printf("%d Mbps (auto-negotiated", speed);
672
673        if (start_time != end_time) {
674            xil_printf(" in %d usec)\n", (end_time - start_time));
675        } else {
676            xil_printf(")\n");
677        }
678    } else {
679        xil_printf("%d Mbps (default)\n", speed);
680    }
681
682    return speed;
683}
684
685
686
687/*****************************************************************************/
688/**
689 * This function set the speed of a Ethernet PHY
690 *
691 * @param   eth_dev_num           - Ethernet device number
692 * @param   speed                 - Speed
693 *
694 * @return  None
695 *
696 *****************************************************************************/
697void transport_set_eth_phy_speed(u32 eth_dev_num, u32 speed) {
698
699    // See Ethernet PHY specification for documentation on the values used for PHY commands
700    u16          phy_ctrl_reg_val;
701
702    // Read the PHY Control register
703    eth_read_phy_reg(eth_dev_num, eth_devices[eth_dev_num].phy_addr, ETH_PHY_CONTROL_REG, &phy_ctrl_reg_val);
704
705    // Based on the argument configure the appropriate bits to set the desired speed
706    switch (speed) {
707        case ETH_PHY_SPEED_1000_MBPS:
708            // Set speed to 1000 Mbps (MSB = 1; LSB = 0)
709            phy_ctrl_reg_val = (phy_ctrl_reg_val & ~ETH_PHY_REG_0_SPEED_LSB) | ETH_PHY_REG_0_SPEED_MSB;
710        break;
711        case ETH_PHY_SPEED_100_MBPS:
712            // Set speed to 100 Mbps (MSB = 0; LSB = 1)
713            phy_ctrl_reg_val = (phy_ctrl_reg_val & ~ETH_PHY_REG_0_SPEED_MSB) | ETH_PHY_REG_0_SPEED_LSB;
714        break;
715        case ETH_PHY_SPEED_10_MBPS:
716            // Set speed to 10 Mbps (MSB = 0; LSB = 0)
717            phy_ctrl_reg_val = phy_ctrl_reg_val & ~(ETH_PHY_REG_0_SPEED_MSB | ETH_PHY_REG_0_SPEED_LSB);
718        break;
719        default:
720            wl_printf(WL_PRINT_ERROR, print_type_transport, "Ethernet %c invalid speed: %d.\n", warp_conv_eth_dev_num(eth_dev_num), speed);
721        break;
722    }
723
724    // Write the value to the PHY and trigger a reset to update PHY internal state
725    eth_write_phy_reg(eth_dev_num, eth_devices[eth_dev_num].phy_addr, ETH_PHY_CONTROL_REG, phy_ctrl_reg_val);
726    eth_write_phy_reg(eth_dev_num, eth_devices[eth_dev_num].phy_addr, ETH_PHY_CONTROL_REG, (ETH_PHY_REG_0_RESET | phy_ctrl_reg_val));
727}
728
729
730
731/*****************************************************************************/
732/**
733 * This function set the auto-negotiation state of a Ethernet controller
734 *
735 * @param   eth_dev_num           - Ethernet device number
736 * @param   enable                - Enable / Disable auto-negotiation
737 *
738 * @return  None
739 *
740 *****************************************************************************/
741void transport_set_eth_phy_auto_negotiation(u32 eth_dev_num, u32 enable) {
742
743    // See Ethernet PHY specification for documentation on the values used for PHY commands
744    u16          phy_ctrl_reg_val;
745
746    // Read the PHY Control register
747    eth_read_phy_reg(eth_dev_num, eth_devices[eth_dev_num].phy_addr, ETH_PHY_CONTROL_REG, &phy_ctrl_reg_val);
748
749    // Based on the argument enable or disable auto-negotiation
750    if (enable) {
751        // Enable auto-negotiation
752        phy_ctrl_reg_val = phy_ctrl_reg_val | ETH_PHY_REG_0_AUTO_NEGOTIATION;
753    } else {
754        // Disable auto-negotiation
755        phy_ctrl_reg_val = phy_ctrl_reg_val & ~ETH_PHY_REG_0_AUTO_NEGOTIATION;
756    }
757
758    // Write the value to the PHY and trigger a reset to update PHY internal state
759    eth_write_phy_reg(eth_dev_num, eth_devices[eth_dev_num].phy_addr, ETH_PHY_CONTROL_REG, phy_ctrl_reg_val);
760    eth_write_phy_reg(eth_dev_num, eth_devices[eth_dev_num].phy_addr, ETH_PHY_CONTROL_REG, (ETH_PHY_REG_0_RESET | phy_ctrl_reg_val));
761}
762
763
764
765/*****************************************************************************/
766/**
767 * Check the Ethernet device of the transport
768 *
769 * @param   eth_dev_num      - Ethernet device number
770 *
771 * @return  int              - Status of the command:
772 *                                 XST_SUCCESS - Command completed successfully
773 *                                 XST_FAILURE - There was an error in the command
774 *
775 ******************************************************************************/
776
777int transport_check_device(u32 eth_dev_num) {
778
779    // Check that we have a valid Ethernet device for the transport
780    if (eth_dev_num >= WL_NUM_ETH_DEVICES) {
781        wl_printf(WL_PRINT_ERROR, print_type_transport, "Ethernet %c is not available on WARP HW.\n", warp_conv_eth_dev_num(eth_dev_num));
782        return XST_FAILURE;
783    }
784
785    return XST_SUCCESS;
786}
787
788
789
790/**********************************************************************************************************************/
791/**
792 * @brief WARP v3 Specific Functions
793 *
794 **********************************************************************************************************************/
795
796
797/***************************** Include Files *********************************/
798
799#include <w3_iic_eeprom.h>
800#include <w3_userio.h>
801
802
803/*************************** Constant Definitions ****************************/
804
805/*********************** Global Variable Definitions *************************/
806
807/*************************** Variable Definitions ****************************/
808
809/*************************** Functions Prototypes ****************************/
810
811int transport_read_ip_addr(u32 eth_dev_num, u8 * ip_addr);
812
813
814/******************************** Functions **********************************/
815
816/*****************************************************************************/
817/**
818 * Read the IP / MAC address from the node for the Ethernet device
819 *
820 * @param   eth_dev_num      - Ethernet device number
821 * @param   hw_addr          - Pointer to an u8 array to return the MAC address
822 * @param   ip_addr          - Pointer to an u8 array to return the IP address
823 *
824 * @return  int              - Status of the command:
825 *                                 XST_SUCCESS - Command completed successfully
826 *                                 XST_FAILURE - There was an error in the command
827 *
828 ******************************************************************************/
829int transport_get_hw_info(u32 eth_dev_num, u8 * hw_addr, u8 * ip_addr) {
830
831    int status = XST_SUCCESS;
832
833    // Read the MAC address from the node
834    w3_eeprom_readEthAddr(EEPROM_BASEADDR, eth_dev_num, hw_addr);
835
836    wl_printf(WL_PRINT_NONE, NULL, "  ETH %c MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
837            warp_conv_eth_dev_num(eth_dev_num), hw_addr[0], hw_addr[1], hw_addr[2], hw_addr[3], hw_addr[4], hw_addr[5]);
838
839    // Read the IP address from the node
840    transport_read_ip_addr(eth_dev_num, ip_addr);
841
842    wl_printf(WL_PRINT_NONE, NULL, "  ETH %c IP  Address: %d.%d.%d.%d\n",
843            warp_conv_eth_dev_num(eth_dev_num), ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]);
844
845    return status;
846}
847
848
849
850/*****************************************************************************/
851/**
852 * Read the IP address from the node
853 *
854 * @param   eth_dev_num      - Ethernet device number
855 * @param   ip_addr          - Pointer to an u8 array to return the IP address
856 *
857 * @return  int              - Status of the command:
858 *                                 XST_SUCCESS - Command completed successfully
859 *                                 XST_FAILURE - There was an error in the command
860 *
861 ******************************************************************************/
862int transport_read_ip_addr(u32 eth_dev_num, u8 * ip_addr) {
863
864    u32 tmp_ip_address       = 0;
865
866    switch (eth_dev_num) {
867        case WL_ETH_A:
868            tmp_ip_address = WL_ETH_A_IP_ADDR_BASE;
869        break;
870        case WL_ETH_B:
871            tmp_ip_address = WL_ETH_B_IP_ADDR_BASE;
872        break;
873    }
874
875    if (tmp_ip_address == 0) {
876        return XST_FAILURE;
877    }
878
879    // Populate the output array
880    ip_addr[0] = (tmp_ip_address >> 24) & 0xFF;
881    ip_addr[1] = (tmp_ip_address >> 16) & 0xFF;
882    ip_addr[2] = (tmp_ip_address >>  8) & 0xFF;
883    ip_addr[3] = (node + 1);                         // IP ADDR = x.y.z.( node + 1 )
884
885    return XST_SUCCESS;
886}
887
888
889
890/*****************************************************************************/
891/**
892 * This function will read the status of a Ethernet controller
893 *
894 * @param   eth_dev_num      - Ethernet device number
895 *
896 * @return  u16              - Bits are defined for the WARP Ethernet chip.  See wl_transport.h
897 *                             for defines for relevant fields
898 *
899 *****************************************************************************/
900u16 transport_get_ethernet_status(u32 eth_dev_num) {
901
902    u16 reg_val = 0;
903
904    // Check that we are initializing a valid Ethernet device for the transport
905    if (transport_check_device(eth_dev_num) != XST_SUCCESS) {
906        return LINK_NOT_READY;
907    }
908
909    if (eth_devices[eth_dev_num].initialized == WL_ETH_DEV_INITIALIZED) {
910
911        // Check if the Ethernet PHY reports a valid link
912        eth_read_phy_reg(eth_dev_num, eth_devices[eth_dev_num].phy_addr, ETH_PHY_STATUS_REG, &reg_val);
913    }
914
915    return reg_val;
916}
917
918
919
920/*****************************************************************************/
921/**
922 * Initialize the information about the Ethernet device
923 *
924 * @param   eth_dev_num      - Ethernet device number
925 *
926 * @return  None
927 *
928 ******************************************************************************/
929void transport_wl_eth_dev_info_init(u32 eth_dev_num) {
930
931    // Initialize Ethernet device
932    switch (eth_dev_num) {
933        case WL_ETH_A:
934            eth_devices[eth_dev_num].default_speed         = WL_ETH_A_DEFAULT_SPEED;
935            eth_devices[eth_dev_num].phy_addr              = WL_ETH_A_MDIO_PHYADDR;
936        break;
937
938        case WL_ETH_B:
939            eth_devices[eth_dev_num].default_speed         = WL_ETH_B_DEFAULT_SPEED;
940            eth_devices[eth_dev_num].phy_addr              = WL_ETH_B_MDIO_PHYADDR;
941        break;
942
943        default:
944            xil_printf("  **** ERROR:  Ethernet device %d not configured in hardware.", (eth_dev_num + 1));
945        break;
946    }
947
948    // Common initialization
949    eth_devices[eth_dev_num].type                  = WL_IP_UDP_TRANSPORT;
950    eth_devices[eth_dev_num].hw_addr[0]            = 0;
951    eth_devices[eth_dev_num].hw_addr[1]            = 0;
952    eth_devices[eth_dev_num].ip_addr               = 0;
953    eth_devices[eth_dev_num].unicast_socket        = SOCKET_INVALID_SOCKET;
954    eth_devices[eth_dev_num].broadcast_socket      = SOCKET_INVALID_SOCKET;
955    eth_devices[eth_dev_num].group_id              = 0;
956    eth_devices[eth_dev_num].initialized           = WL_ETH_DEV_INITIALIZED;
957}
958
959
960
961/*****************************************************************************/
962/**
963 * @brief Transport subsystem initialization
964 *
965 * Initializes the transport subsystem
966 *
967 * @param   eth_dev_num      - Ethernet device number
968 * @param   init_driver      - Initialize the WARP IP/UDP driver (should only be done once)
969 *
970 * @return  int              - Status of the command:
971 *                                 XST_SUCCESS - Command completed successfully
972 *                                 XST_FAILURE - There was an error in the command
973 ******************************************************************************/
974int transport_init(u32 eth_dev_num, u8 init_driver) {
975
976    int       status = XST_SUCCESS;
977
978    u8        node_ip_addr[IP_ADDR_LEN];
979    u8        node_hw_addr[ETH_MAC_ADDR_LEN];
980
981    // Print initialization message
982    wl_printf(WL_PRINT_NONE, NULL, "Configuring transport ...\n");
983
984    // Initialize the User callback for processing a packet
985    process_hton_msg_callback = wl_null_callback;
986
987    // Check that we are initializing a valid Ethernet device for the transport
988    if (transport_check_device(eth_dev_num) != XST_SUCCESS) {
989        return XST_FAILURE;
990    }
991
992    // Initialize the WARP UDP transport global variables
993    if (init_driver) {
994        warp_ip_udp_init();
995    }
996
997    // Get device specific MAC / IP address from the EEPROM
998    status = transport_get_hw_info(eth_dev_num, (u8 *)&node_hw_addr, (u8 *)&node_ip_addr);
999
1000    if (status != XST_SUCCESS) {
1001        wl_printf(WL_PRINT_ERROR, print_type_transport, "Error retrieving node specific HW info from EEPROM:  %d \n", status);
1002    }
1003
1004    // Initialize the Ethernet device (use verbose mode)
1005    status = eth_init(eth_dev_num, node_hw_addr, node_ip_addr, 0x1);
1006
1007    if (status != XST_SUCCESS) {
1008        wl_printf(WL_PRINT_ERROR, print_type_transport, "Ethernet %c initialization error\n", warp_conv_eth_dev_num(eth_dev_num));
1009    }
1010
1011    // Initialize the wl_eth_devices structure
1012    transport_wl_eth_dev_info_init(eth_dev_num);
1013
1014    // Set the Ethernet link speed
1015    if (WL_NEGOTIATE_ETH_LINK_SPEED) {
1016        // Enable auto-negotiation in the Ethernet PHY
1017        transport_set_eth_phy_auto_negotiation(eth_dev_num, WL_ENABLE);
1018
1019        // Update the link speed
1020        transport_update_link_speed(eth_dev_num, ETH_WAIT_FOR_AUTO_NEGOTIATION);
1021
1022    } else {
1023        // Disable auto-negotiation in the Ethernet PHY
1024        transport_set_eth_phy_auto_negotiation(eth_dev_num, WL_DISABLE);
1025
1026        // Update the link speed
1027        transport_update_link_speed(eth_dev_num, ETH_DO_NOT_WAIT_FOR_AUTO_NEGOTIATION);
1028    }
1029
1030    // Start Ethernet device
1031    status = eth_start_device(eth_dev_num);
1032
1033    if (status != XST_SUCCESS) {
1034        wl_printf(WL_PRINT_ERROR, print_type_transport, "Cannot start Ethernet %c\n", warp_conv_eth_dev_num(eth_dev_num));
1035    }
1036
1037    // Configure the Sockets for each Ethernet Interface
1038    status = transport_config_sockets(eth_dev_num, NODE_UDP_UNICAST_PORT_BASE, NODE_UDP_MCAST_BASE);
1039
1040    if (status != XST_SUCCESS) {
1041        wl_printf(WL_PRINT_ERROR, print_type_transport, "Cannot configure sockets for Ethernet %c\n", warp_conv_eth_dev_num(eth_dev_num));
1042    }
1043
1044    return status;
1045}
1046
Note: See TracBrowser for help on using the repository browser.