Update incorrectly checked in Makefile.am
[debian/gnuradio] / usrp2 / firmware / apps / app_passthru_v2.c
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2007,2008 Free Software Foundation, Inc.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "app_passthru_v2.h"
24 #include "buffer_pool.h"
25 #include "memcpy_wa.h"
26 #include "ethernet.h"
27 #include "nonstdio.h"
28 #include "print_rmon_regs.h"
29 #include "db.h"
30 #include "clocks.h"
31 #include <string.h>
32
33 volatile bool link_is_up = false;       // eth handler sets this
34
35
36 // If this is non-zero, this dbsm could be writing to the ethernet
37 dbsm_t *ac_could_be_sending_to_eth;
38
39 //static unsigned char exp_seqno = 0;
40
41 void
42 set_reply_hdr(u2_eth_packet_t *reply_pkt, u2_eth_packet_t const *cmd_pkt)
43 {
44   reply_pkt->ehdr.dst = cmd_pkt->ehdr.src;
45   reply_pkt->ehdr.src = *ethernet_mac_addr();
46   reply_pkt->ehdr.ethertype = U2_ETHERTYPE;
47   reply_pkt->thdr.flags = 0;
48   reply_pkt->thdr.fifo_status = 0;      // written by protocol engine
49   reply_pkt->thdr.seqno = 0;            // written by protocol engine
50   reply_pkt->thdr.ack = 0;              // written by protocol engine
51   u2p_set_word0(&reply_pkt->fixed, 0, CONTROL_CHAN);
52   reply_pkt->fixed.timestamp = timer_regs->time;
53 }
54
55 static void
56 send_reply(unsigned char *reply, size_t reply_len)
57 {
58   if (reply_len < 64)
59     reply_len = 64;
60
61   // wait for buffer to become idle
62   hal_set_leds(0x4, 0x4);
63   while((buffer_pool_status->status & BPS_IDLE(CPU_TX_BUF)) == 0)
64     ;
65   hal_set_leds(0x0, 0x4);
66
67   // copy reply into CPU_TX_BUF
68   memcpy_wa(buffer_ram(CPU_TX_BUF), reply, reply_len);
69
70   // wait until nobody else is sending to the ethernet
71   if (ac_could_be_sending_to_eth){
72     hal_set_leds(0x8, 0x8);
73     dbsm_wait_for_opening(ac_could_be_sending_to_eth);
74     hal_set_leds(0x0, 0x8);
75   }
76
77   // fire it off
78   bp_send_from_buf(CPU_TX_BUF, PORT_ETH, 1, 0, reply_len/4);
79
80   // wait for it to complete (not long, it's a small pkt)
81   while((buffer_pool_status->status & (BPS_DONE(CPU_TX_BUF) | BPS_ERROR(CPU_TX_BUF))) == 0)
82     ;
83
84   bp_clear_buf(CPU_TX_BUF);
85 }
86
87
88 static size_t
89 op_id_cmd(const op_generic_t *p,
90           void *reply_payload, size_t reply_payload_space)
91 {
92   op_id_reply_t *r = (op_id_reply_t *) reply_payload;
93   if (reply_payload_space < sizeof(*r)) // no room
94     return 0;
95
96   // Build reply subpacket
97
98   r->opcode = OP_ID_REPLY;
99   r->len = sizeof(op_id_reply_t);
100   r->rid = p->rid;
101   r->addr = *ethernet_mac_addr();
102   r->hw_rev = 0x0000;   // FIXME
103   // r->fpga_md5sum = ; // FIXME
104   // r->sw_md5sum = ;   // FIXME
105
106   // FIXME Add d'board info, including dbid, min/max gain, min/max freq
107
108   return r->len;
109 }
110
111 static size_t
112 add_eop(void *reply_payload, size_t reply_payload_space)
113 {
114   op_generic_t *r = (op_generic_t *) reply_payload;
115   if (reply_payload_space < sizeof(*r))         
116     return 0;                                   // no room
117
118   r->opcode = OP_EOP;
119   r->len = sizeof(*r);
120   r->rid = 0;
121   r->ok =  0;
122
123   return r->len;
124 }
125
126 bool
127 handle_control_chan_frame(u2_eth_packet_t *pkt, size_t len)
128 {
129   unsigned char reply[sizeof(u2_eth_packet_t) + 4 * sizeof(u2_subpkt_t)] _AL4;
130   unsigned char *reply_payload = &reply[sizeof(u2_eth_packet_t)];
131   int reply_payload_space = sizeof(reply) - sizeof(u2_eth_packet_t);
132
133   bool handled_it = false;
134
135   // initialize reply
136   memset(reply, 0, sizeof(reply));
137   set_reply_hdr((u2_eth_packet_t *) reply, pkt);
138
139   // point to beginning of payload (subpackets)
140   unsigned char *payload = ((unsigned char *) pkt) + sizeof(u2_eth_packet_t);
141   int payload_len = len - sizeof(u2_eth_packet_t);
142
143   size_t subpktlen = 0;
144
145   while (payload_len >= sizeof(op_generic_t)){
146     const op_generic_t *gp = (const op_generic_t *) payload;
147     subpktlen = 0;
148
149     switch(gp->opcode){
150     case OP_EOP:                // end of subpackets
151       goto end_of_subpackets;
152
153     case OP_ID:
154       subpktlen = op_id_cmd(gp, reply_payload, reply_payload_space);
155       handled_it = true;
156       break;
157
158     default:
159       if (0){
160         printf("\npassing on %d\n", gp->opcode);
161       }
162       break;
163     }
164
165     int t = (gp->len + 3) & ~3;         // bump to a multiple of 4
166     payload += t;
167     payload_len -= t;
168
169     subpktlen = (subpktlen + 3) & ~3;   // bump to a multiple of 4
170     reply_payload += subpktlen;
171     reply_payload_space -= subpktlen;
172   }
173
174  end_of_subpackets:
175
176   if (handled_it){
177     // add the EOP marker
178     subpktlen = add_eop(reply_payload, reply_payload_space);
179     subpktlen = (subpktlen + 3) & ~3;   // bump to a multiple of 4
180     reply_payload += subpktlen;
181     reply_payload_space -= subpktlen;
182
183     send_reply(reply, reply_payload - reply);
184   }
185
186   return handled_it;
187 }
188
189
190 /*
191  * Called when an ethernet packet is received.
192  * Return true if we handled it here, otherwise
193  * it'll be passed on to the DSP Tx pipe
194  */
195 bool
196 eth_pkt_inspector(dbsm_t *sm, int bufno)
197 {
198   u2_eth_packet_t *pkt = (u2_eth_packet_t *) buffer_ram(bufno);
199   size_t byte_len = (buffer_pool_status->last_line[bufno] - 3) * 4;
200
201   //static size_t last_len = 0;
202
203   // hal_toggle_leds(0x1);
204
205   // inspect rcvd frame and figure out what do do.
206
207   if (pkt->ehdr.ethertype != U2_ETHERTYPE)
208     return true;        // ignore, probably bogus PAUSE frame from MAC
209
210   int chan = u2p_chan(&pkt->fixed);
211
212   switch (chan){
213   case CONTROL_CHAN:
214     return handle_control_chan_frame(pkt, byte_len);
215     break;
216
217   case 0:
218   default:
219 #if 0
220     if (last_len != 0){
221       if (byte_len != last_len){
222         printf("Len: %d last: %d\n", byte_len, last_len);
223       }
224     }
225     last_len = byte_len;
226
227     if((pkt->thdr.seqno) == exp_seqno){
228       exp_seqno++;
229       //putchar('.');
230     }
231     else {
232       // putchar('S');
233       //printf("S%d %d ",exp_seqno,pkt->thdr.seqno);
234       exp_seqno = pkt->thdr.seqno + 1;
235     }
236 #endif
237     return false;       // pass it on to Tx DSP
238     break;
239   }
240 }
241
242 /*
243  * Called when eth phy state changes (w/ interrupts disabled)
244  */
245 void
246 link_changed_callback(int speed)
247 {
248   link_is_up = speed != 0;
249   hal_set_leds(link_is_up ? LED_RJ45 : 0x0, LED_RJ45);
250   printf("\neth link changed: speed = %d\n", speed);
251 }