1 //
2 // Copyright 2014 Ettus Research LLC
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 //
17 
18 #include "n230_eth_handlers.h"
19 
20 #include <wb_utils.h>
21 #include <string.h> //memcmp
22 #include <u3_net_stack.h>
23 #include <print_addrs.h>
24 #include <trace.h>
25 #include "../../../host/lib/usrp/n230/n230_fw_comm_protocol.h"
26 #include "../../../host/lib/usrp/n230/n230_fw_defs.h"
27 #include "../n230/n230_fw_host_iface.h"
28 #include "../../../host/lib/usrp/n230/n230_eeprom.h"
29 
30 static n230_host_shared_mem_t* host_shared_mem_ptr;
31 
32 static const soft_reg_field_t LED_REG_FIELD_ETH_LINK2   = {.num_bits=1, .shift=0};
33 static const soft_reg_field_t LED_REG_FIELD_ETH_LINK1   = {.num_bits=1, .shift=1};
34 static const soft_reg_field_t LED_REG_FIELD_ETH_ACT2    = {.num_bits=1, .shift=2};
35 static const soft_reg_field_t LED_REG_FIELD_ETH_ACT1    = {.num_bits=1, .shift=3};
36 
37 /***********************************************************************
38  * Handler for host <-> firmware communication
39  **********************************************************************/
40 
n230_poke32(const uint32_t addr,const uint32_t data)41 static inline void n230_poke32(const uint32_t addr, const uint32_t data)
42 {
43     if (addr >= N230_FW_HOST_SHMEM_RW_BASE_ADDR && addr <= N230_FW_HOST_SHMEM_MAX_ADDR) {
44         host_shared_mem_ptr->buff[(addr - N230_FW_HOST_SHMEM_BASE_ADDR)/sizeof(uint32_t)] = data;
45     } else if (addr < N230_FW_HOST_SHMEM_BASE_ADDR) {
46         wb_poke32(addr, data);
47     }
48 }
49 
n230_peek32(const uint32_t addr)50 static inline uint32_t n230_peek32(const uint32_t addr)
51 {
52     if (addr >= N230_FW_HOST_SHMEM_BASE_ADDR && addr <= N230_FW_HOST_SHMEM_MAX_ADDR) {
53         return host_shared_mem_ptr->buff[(addr - N230_FW_HOST_SHMEM_BASE_ADDR)/sizeof(uint32_t)];
54     } else if (addr < N230_FW_HOST_SHMEM_BASE_ADDR) {
55         return wb_peek32(addr);
56     } else {
57         return 0;
58     }
59 }
60 
n230_handle_udp_fw_comms(const uint8_t ethno,const struct ip_addr * src,const struct ip_addr * dst,const uint16_t src_port,const uint16_t dst_port,const void * buff,const size_t num_bytes)61 void n230_handle_udp_fw_comms(
62     const uint8_t ethno,
63     const struct ip_addr *src, const struct ip_addr *dst,
64     const uint16_t src_port, const uint16_t dst_port,
65     const void *buff, const size_t num_bytes)
66 {
67     if (buff == NULL) {
68         UHD_FW_TRACE(WARN, "n230_handle_udp_fw_comms got an ICMP_DUR");
69         /* We got here from ICMP_DUR undeliverable packet */
70         /* Future space for hooks to tear down streaming radios etc */
71     } else if (num_bytes != sizeof(fw_comm_pkt_t)) {
72         UHD_FW_TRACE(WARN, "n230_handle_udp_fw_comms got an unknown request (bad size).");
73     } else {
74         const fw_comm_pkt_t *request = (const fw_comm_pkt_t *)buff;
75         fw_comm_pkt_t response;
76         bool send_response = process_fw_comm_protocol_pkt(
77             request, &response,
78             N230_FW_PRODUCT_ID,
79             (uint32_t)ethno,
80             n230_poke32, n230_peek32);
81 
82         if (send_response) {
83             u3_net_stack_send_udp_pkt(ethno, src, dst_port, src_port, &response, sizeof(response));
84         }
85     }
86 }
87 
n230_register_udp_fw_comms_handler(n230_host_shared_mem_t * shared_mem_ptr)88 void n230_register_udp_fw_comms_handler(n230_host_shared_mem_t* shared_mem_ptr)
89 {
90     host_shared_mem_ptr = shared_mem_ptr;
91     u3_net_stack_register_udp_handler(N230_FW_COMMS_UDP_PORT, &n230_handle_udp_fw_comms);
92 }
93 
94 
95 /***********************************************************************
96  * Handler for UDP framer program packets
97  **********************************************************************/
program_udp_framer(const uint8_t ethno,const uint32_t sid,const struct ip_addr * dst_ip,const uint16_t dst_port,const uint16_t src_port)98 void program_udp_framer(
99     const uint8_t ethno,
100     const uint32_t sid,
101     const struct ip_addr *dst_ip,
102     const uint16_t dst_port,
103     const uint16_t src_port)
104 {
105     const eth_mac_addr_t *dst_mac = u3_net_stack_arp_cache_lookup(dst_ip);
106     const size_t vdest = (sid >> 16) & 0xff;
107 
108     uint32_t framer_base =
109         ((ethno == 1) ? SR_ZPU_ETHINT1 : SR_ZPU_ETHINT0) + SR_ZPU_ETHINT_FRAMER_BASE;
110 
111     //setup source framer
112     const eth_mac_addr_t *src_mac = u3_net_stack_get_mac_addr(ethno);
113     wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_MAC_HI),
114         (((uint32_t)src_mac->addr[0]) << 8) | (((uint32_t)src_mac->addr[1]) << 0));
115     wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_MAC_LO),
116         (((uint32_t)src_mac->addr[2]) << 24) | (((uint32_t)src_mac->addr[3]) << 16) |
117         (((uint32_t)src_mac->addr[4]) << 8) | (((uint32_t)src_mac->addr[5]) << 0));
118     wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_IP_ADDR), u3_net_stack_get_ip_addr(ethno)->addr);
119     wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_UDP_PORT), src_port);
120 
121     //setup destination framer
122     wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_RAM_ADDR), vdest);
123     wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_IP_ADDR), dst_ip->addr);
124     wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_UDP_MAC),
125         (((uint32_t)dst_port) << 16) |
126         (((uint32_t)dst_mac->addr[0]) << 8) | (((uint32_t)dst_mac->addr[1]) << 0));
127     wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_MAC_LO),
128         (((uint32_t)dst_mac->addr[2]) << 24) | (((uint32_t)dst_mac->addr[3]) << 16) |
129         (((uint32_t)dst_mac->addr[4]) << 8) | (((uint32_t)dst_mac->addr[5]) << 0));
130 }
131 
handle_udp_prog_framer(const uint8_t ethno,const struct ip_addr * src,const struct ip_addr * dst,const uint16_t src_port,const uint16_t dst_port,const void * buff,const size_t num_bytes)132 void handle_udp_prog_framer(
133     const uint8_t ethno,
134     const struct ip_addr *src, const struct ip_addr *dst,
135     const uint16_t src_port, const uint16_t dst_port,
136     const void *buff, const size_t num_bytes)
137 {
138     if (buff == NULL) {
139         /* We got here from ICMP_DUR undeliverable packet */
140         /* Future space for hooks to tear down streaming radios etc */
141     } else {
142         const uint32_t sid = ((const uint32_t *)buff)[1];
143         program_udp_framer(ethno, sid, src, src_port, dst_port);
144         UHD_FW_TRACE_FSTR(INFO, "Reprogrammed eth%d framer. Src=%s:%d, Dest=%s:%d",
145             ethno,ip_addr_to_str(src),src_port,ip_addr_to_str(dst),dst_port);
146     }
147 }
148 
n230_register_udp_prog_framer()149 void n230_register_udp_prog_framer()
150 {
151     u3_net_stack_register_udp_handler(N230_FW_COMMS_CVITA_PORT, &handle_udp_prog_framer);
152 }
153 
154 
155 /***********************************************************************
156  * Handler for flash programming interface over UDP
157  **********************************************************************/
158 
n230_handle_flash_prog_comms(const uint8_t ethno,const struct ip_addr * src,const struct ip_addr * dst,const uint16_t src_port,const uint16_t dst_port,const void * buff,const size_t num_bytes)159 void n230_handle_flash_prog_comms(
160     const uint8_t ethno,
161     const struct ip_addr *src, const struct ip_addr *dst,
162     const uint16_t src_port, const uint16_t dst_port,
163     const void *buff, const size_t num_bytes)
164 {
165     if (buff == NULL) {
166         UHD_FW_TRACE(WARN, "n230_handle_flash_prog_comms got an ICMP_DUR");
167         /* We got here from ICMP_DUR undeliverable packet */
168         /* Future space for hooks to tear down streaming radios etc */
169     } else if (num_bytes != sizeof(n230_flash_prog_t)) {
170         UHD_FW_TRACE(WARN, "n230_handle_flash_prog_comms got an unknown request (bad size).");
171     } else {
172         const n230_flash_prog_t *request = (const n230_flash_prog_t *)buff;
173         n230_flash_prog_t response;
174         bool ack_requested = request->flags & N230_FLASH_COMM_FLAGS_ACK;
175 
176         //Request is valid. Copy it into the reply.
177         memcpy(&response, request, sizeof(n230_flash_prog_t));
178 
179         switch (request->flags & N230_FLASH_COMM_FLAGS_CMD_MASK) {
180             case N230_FLASH_COMM_CMD_READ_NV_DATA: {
181                 UHD_FW_TRACE(DEBUG, "n230_handle_flash_prog_comms::read_nv_data()");
182                 //Offset ignored because all non-volatile data fits in a packet.
183                 if (is_n230_eeprom_cache_dirty()) {
184                     read_n230_eeprom();
185                 }
186                 //EEPROM cache is up-to-date. Copy it into the packet.
187                 //Assumption: Cache size < 256. If this is no longer true, the offset field
188                 //will have to be used.
189                 memcpy(response.data, get_n230_const_eeprom_map(), sizeof(n230_eeprom_map_t));
190                 ack_requested = true;
191             } break;
192 
193             case N230_FLASH_COMM_CMD_WRITE_NV_DATA: {
194                 UHD_FW_TRACE(DEBUG, "n230_handle_flash_prog_comms::write_nv_data()");
195                 //Offset ignored because all non-volatile data fits in a packet.
196                 memcpy(get_n230_eeprom_map(), request->data, sizeof(n230_eeprom_map_t));
197                 if (!write_n230_eeprom()) {
198                     response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR;
199                 }
200             } break;
201 
202             case N230_FLASH_COMM_CMD_READ_FPGA: {
203                 UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::read_fpga_page(offset=0x%x, size=%d)",
204                     request->offset, request->size);
205                 read_n230_fpga_image_page(request->offset, response.data, request->size);
206                 ack_requested = true;
207             } break;
208 
209             case N230_FLASH_COMM_CMD_WRITE_FPGA: {
210                 UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::write_fpga_page(offset=0x%x, size=%d)",
211                     request->offset, request->size);
212                 if (!write_n230_fpga_image_page(request->offset, request->data, request->size)) {
213                     response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR;
214                 }
215             } break;
216 
217             case N230_FLASH_COMM_CMD_ERASE_FPGA: {
218                 UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::erase_fpga_sector(offset=0x%x)",
219                     request->offset);
220                 if (!erase_n230_fpga_image_sector(request->offset)) {
221                     response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR;
222                 }
223             } break;
224 
225             default :{
226                 UHD_FW_TRACE(ERROR, "n230_handle_flash_prog_comms got an invalid command.");
227                 response.flags |= FW_COMM_ERR_CMD_ERROR;
228             }
229         }
230         //Send a reply if ack requested
231         if (ack_requested) {
232             u3_net_stack_send_udp_pkt(ethno, src, dst_port, src_port, &response, sizeof(response));
233         }
234     }
235 }
236 
n230_register_flash_comms_handler()237 void n230_register_flash_comms_handler()
238 {
239     u3_net_stack_register_udp_handler(N230_FW_COMMS_FLASH_PROG_PORT, &n230_handle_flash_prog_comms);
240 }
241 
242 /***********************************************************************
243  * Handler for SFP state changes
244  **********************************************************************/
245 #define SFPP_STATUS_MODABS_CHG     (1 << 5)    // Has MODABS changed since last read?
246 #define SFPP_STATUS_TXFAULT_CHG    (1 << 4)    // Has TXFAULT changed since last read?
247 #define SFPP_STATUS_RXLOS_CHG      (1 << 3)    // Has RXLOS changed since last read?
248 #define SFPP_STATUS_MODABS         (1 << 2)    // MODABS state
249 #define SFPP_STATUS_TXFAULT        (1 << 1)    // TXFAULT state
250 #define SFPP_STATUS_RXLOS          (1 << 0)    // RXLOS state
251 
252 static bool     links_up[N230_MAX_NUM_ETH_PORTS] = {};
253 static uint32_t packet_count[N230_MAX_NUM_ETH_PORTS] = {};
254 
n230_poll_sfp_status(const uint32_t eth,bool force,bool * state_updated)255 void n230_poll_sfp_status(const uint32_t eth, bool force, bool* state_updated)
256 {
257     // Has MODDET/MODAbS changed since we last looked?
258     uint32_t rb = wb_peek32(SR_ADDR(WB_SBRB_BASE, (eth==0) ? RB_ZPU_SFP_STATUS0 : RB_ZPU_SFP_STATUS1));
259 
260     if (rb & SFPP_STATUS_RXLOS_CHG)
261         UHD_FW_TRACE_FSTR(DEBUG, "eth%1d RXLOS changed state: %d", eth, (rb & SFPP_STATUS_RXLOS));
262     if (rb & SFPP_STATUS_TXFAULT_CHG)
263         UHD_FW_TRACE_FSTR(DEBUG, "eth%1d TXFAULT changed state: %d", eth, ((rb & SFPP_STATUS_TXFAULT) >> 1));
264     if (rb & SFPP_STATUS_MODABS_CHG)
265         UHD_FW_TRACE_FSTR(DEBUG, "eth%1d MODABS changed state: %d", eth, ((rb & SFPP_STATUS_MODABS) >> 2));
266 
267     //update the link up status
268     if ((rb & SFPP_STATUS_RXLOS_CHG) || (rb & SFPP_STATUS_TXFAULT_CHG) || (rb & SFPP_STATUS_MODABS_CHG) || force)
269     {
270         const bool old_link_up = links_up[eth];
271         const uint32_t status_reg_addr = (eth==0) ? RB_ZPU_SFP_STATUS0 : RB_ZPU_SFP_STATUS1;
272 
273         uint32_t sfpp_status = wb_peek32(SR_ADDR(WB_SBRB_BASE, status_reg_addr)) & 0xFFFF;
274         if ((sfpp_status & (SFPP_STATUS_RXLOS|SFPP_STATUS_TXFAULT|SFPP_STATUS_MODABS)) == 0) {
275             int8_t timeout = 100;
276             bool link_up = false;
277             do {
278                 link_up = ((wb_peek32(SR_ADDR(WB_SBRB_BASE, status_reg_addr)) >> 16) & 0x1) != 0;
279             } while (!link_up && timeout-- > 0);
280 
281             links_up[eth] = link_up;
282         } else {
283             links_up[eth] = false;
284         }
285 
286         if (!old_link_up && links_up[eth]) u3_net_stack_send_arp_request(eth, u3_net_stack_get_ip_addr(eth));
287         UHD_FW_TRACE_FSTR(INFO, "The link on eth port %u is %s", eth, links_up[eth]?"up":"down");
288         if (rb & SFPP_STATUS_MODABS_CHG) {
289             // MODDET has changed state since last checked
290             if (rb & SFPP_STATUS_MODABS) {
291                 // MODDET is high, module currently removed.
292                 UHD_FW_TRACE_FSTR(INFO, "An SFP+ module has been removed from eth port %d.", eth);
293             } else {
294                 // MODDET is low, module currently inserted.
295                 // Return status.
296                 UHD_FW_TRACE_FSTR(INFO, "A new SFP+ module has been inserted into eth port %d.", eth);
297             }
298         }
299         *state_updated = true;
300     } else {
301         *state_updated = false;
302     }
303 }
304 
n230_update_link_act_state(soft_reg_t * led_reg)305 void n230_update_link_act_state(soft_reg_t* led_reg)
306 {
307     static bool first_poll = 1;
308     static uint32_t poll_cnt;
309 
310     bool activity[N230_MAX_NUM_ETH_PORTS] = {};
311     for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) {
312         if (first_poll) {
313             links_up[i] = 0;
314             packet_count[i] = 0;
315             poll_cnt = 0;
316         }
317 
318         //Check SFP status and update links_up
319         bool link_state_from_sfp = false;
320         n230_poll_sfp_status(i, first_poll, &link_state_from_sfp);
321 
322         //Check packet counters less frequently to keep the LED on for a visible duration
323         uint32_t cnt = wb_peek32(SR_ADDR(WB_SBRB_BASE, (i==0)?RB_ZPU_ETH0_PKT_CNT:RB_ZPU_ETH1_PKT_CNT));
324         activity[i] = (cnt != packet_count[i]);
325         packet_count[i] = cnt;
326 
327         //Update links_up if there is activity only if the SFP
328         //handler has not updated it
329         if (activity[i] && !link_state_from_sfp) links_up[i] = true;
330     }
331 
332     //TODO: Swap this when Ethernet port swap issues is fixed
333     soft_reg_write(led_reg, LED_REG_FIELD_ETH_LINK2, links_up[0]?1:0);
334     soft_reg_write(led_reg, LED_REG_FIELD_ETH_LINK1, links_up[1]?1:0);
335     soft_reg_write(led_reg, LED_REG_FIELD_ETH_ACT2,  activity[0]?1:0);
336     soft_reg_write(led_reg, LED_REG_FIELD_ETH_ACT1,  activity[1]?1:0);
337 
338     first_poll = 0;
339 }
340 
341