]> git.llucax.com Git - z.facultad/66.09/etherled.git/blob - src/dp8390.c
Implementa el protocolo ELP sobre UDP. Tiene problemas cuando los leds estan
[z.facultad/66.09/etherled.git] / src / dp8390.c
1 // vim: set et sw=4 sts=4 :     
2
3 #include "debug.h"
4 #include "eth.h"
5 #include "dp8390.h"
6
7 /** Tamaño del frame */
8 byte netdev_len;
9
10 // Próximo frame a obtener
11 static struct
12 {
13     byte next_buf;
14     byte curr_buf;
15     byte curr_off;
16 }
17 recv_state;
18
19 /// Tamaño de la cabecera de los buffers de la placa de red
20 #define BUF_HDR_SIZE 4
21
22 /// Cambia de página sin modificar los demás bits del CR
23 #define SELECT_REG_PAGE(page)                         \
24     do                                                \
25     {                                                 \
26         write_reg(CR, read_reg(CR) & ~(PS1 | PS0));   \
27         write_reg(CR, read_reg(CR) | (page << 6));    \
28     }                                                 \
29     while (0)
30
31 /// Aborta (o completa) el DMA limpiando el ISR
32 #define ABORT_DMA(flags)        \
33     do                          \
34     {                           \
35         write_reg(CR, flags);   \
36         write_reg(ISR, RDC);    \
37     }                           \
38     while (0)
39
40
41 static void write_reg(unsigned char reg, unsigned char wr_data)
42 {
43     // Select register address.
44     ADDR_PORT &= ~ADDR_PORT_MASK; 
45     ADDR_PORT |= reg;
46
47     // Output register data to port.
48     DATA_PORT = wr_data;
49
50     // Clock register data into RTL8019AS.
51     // IOR & IOW are both active low.
52     NICE = 0;
53     IOW = 0;
54     IOW = 1;
55     NICE = 1;
56
57     // Set register data port as input again.
58     DATA_PORT = DATA_PORT_MASK;
59
60
61
62 static unsigned char read_reg(unsigned char reg)
63 {
64     // Select register address.
65     ADDR_PORT &= ~ADDR_PORT_MASK;
66     ADDR_PORT |= reg;
67
68     // Enable register data output from RTL8019AS.
69     NICE = 0;
70     IOR = 0;
71
72     // Read register data from port.
73     reg = DATA_PORT;
74
75     // Disable register data output from RTL8019AS.
76     IOR = 1; 
77     NICE = 1;   
78
79     return reg;
80
81
82 /** Resetea placa de red en caso de buffer overflow */
83 static void reset()
84 {
85     bit retransmit = read_reg(CR) & TXP;
86
87     // If the receive buffer ring has overflowed we dump the whole
88     // thing and start over. There is no way of knowing whether the
89     // data it contains is uncorrupted, or will cause us grief.
90
91     // Stop RTL8019AS and abort DMA operation.
92     write_reg(CR, STOP);
93
94     // Wait for controller to halt after any current tx completes.
95     while(!(read_reg(ISR) & RST)) continue;
96
97     // Reset remote byte count registers.
98     write_reg(RBCR0, 0x00);
99     write_reg(RBCR1, 0x00);
100
101     // Check whether currently transmitting a packet.
102     if(retransmit)
103     {
104         // If neither a successful transmission nor a tx abort error 
105         // has occured, then flag current tx packet for resend.
106         if(read_reg(ISR) & (PTX | TXE))
107         {
108             retransmit = 0;
109         }
110     }
111
112     // Set transmit configuration register to loopback internally.
113     write_reg(TCR, MODE1);
114
115     // Restart the RTL8019AS.
116     write_reg(CR, START);
117
118     // Re-initialise last receive buffer read pointer.
119     write_reg(BNRY, RX_PAGE_START);
120
121     // Select RTL8019AS register page 1.
122     SELECT_REG_PAGE(1);
123
124     // Re-initialise current packet receive buffer page pointer.
125     write_reg(CURR, RX_PAGE_START + 1);
126
127     // Select RTL8019AS register page 0.
128     SELECT_REG_PAGE(0);
129
130     // Clear rx buffer overflow & packet received interrupt flags.
131     write_reg(ISR, PRX | OVW);
132
133     // Re-itialise transmit configuration reg for normal operation.
134     write_reg(TCR, MODE0);
135
136     if(retransmit)
137     {
138         // Retransmit packet in RTL8019AS local tx buffer.
139         write_reg(CR, START | TXP);
140     }
141 }
142
143
144 /** Inicializa dispositivo de red
145  * @return true si se inicializó correctamente, false si no
146  */
147 bool netdev_init()
148 {
149     byte i;
150
151     // Set IOR & IOW as they're active low.
152     IOR = 1;
153     IOW = 1;
154     NICE = 1;
155
156     // Set register data port as input.
157     DATA_PORT = DATA_PORT_MASK;
158
159     // Configure RTL8019AS ethernet controller.
160
161     // Keil startup code takes 4ms to execute (18.432MHz, X1 mode).
162     // That leaves plenty of time for the RTL8019AS to read it's
163     // configuration in from the 9346 EEPROM before we get here.
164
165     // Select RTL8019AS register page 0.
166     SELECT_REG_PAGE(0);
167
168     // Check if RTL8019AS fully reset.
169     if(!(read_reg(ISR) & RST))
170     {
171         return 0;
172     }
173
174     // Stop RTL8019AS, select page 0 and abort DMA operation.
175     write_reg(CR, STOP);
176
177     // Initialise data configuration register. 
178     // FIFO threshold 8 bytes, no loopback, don't use auto send packet.
179     write_reg(DCR, FT1 | LS);
180
181     // Reset remote byte count registers.
182     write_reg(RBCR0, 0u);
183     write_reg(RBCR1, 0u);
184
185     // Receive configuration register to monitor mode.
186     write_reg(RCR, MON);
187
188     // Initialise transmit configuration register to loopback internally.
189     write_reg(TCR, MODE1);
190
191     // Clear interrupt status register bits by writing 1 to each.
192     write_reg(ISR, ALL);
193
194     // Mask all interrupts in mask register.
195     write_reg(IMR, NONE);
196
197     // Obtengo MAC de la placa
198     write_reg(RBCR0, 12u); // Vamos a leer 12 bytes (2 x 6)
199     write_reg(RBCR1, 0u); 
200     write_reg(RSAR0, 0u); // En la dirección 0x0000
201     write_reg(RSAR1, 0u);
202     write_reg(CR, READ); // Comienza lectura
203     for (i = 0; i < ETH_ADDR_SIZE; ++i)
204     {
205         eth_addr_local[i] = read_reg(RDMA);
206         read_reg(RDMA); // Ignoramos porque viene como un word
207     }
208
209     // Abort/ complete DMA operation.
210     ABORT_DMA(STOP);
211
212     // Set transmit page start.
213     write_reg(TPSR, TX_PAGE_START);
214
215     // Set receive buffer page start.
216     write_reg(PSTART, RX_PAGE_START);
217
218     // Initialise last receive buffer read pointer.
219     write_reg(BNRY, RX_PAGE_START);
220
221     // Set receive buffer page stop.
222     write_reg(PSTOP, RX_PAGE_STOP);
223
224     // Select RTL8019AS register page 1.
225     SELECT_REG_PAGE(1);
226
227     // Initialise current packet receive buffer page pointer
228     write_reg(CURR, RX_PAGE_START + 1);
229
230     // Set physical address
231     for (i = 0; i < ETH_ADDR_SIZE; ++i)
232         write_reg(PAR_BASE + i, eth_addr_local[i]);
233
234     // Restart RTL8019AS. 
235     write_reg(CR, START);
236
237     // Initialise transmit configuration register for normal operation.
238     write_reg(TCR, MODE0);
239
240     // Receive configuration register to accept broadcast packets.
241     write_reg(RCR, AB);
242
243     return 1;
244 }
245
246
247 /** Comienza el envío de un nuevo frame */
248 void netdev_send_start()
249 {
250     // Wait until pending transmit operation completes.
251     while (read_reg(CR) & TXP) continue;
252     write_reg(ISR, PTX); // Limpio bit de interrupción
253
254     // Set remote DMA start address registers to indicate where to load packet.
255     write_reg(RSAR0, 0u);
256     write_reg(RSAR1, TX_PAGE_START);
257 }
258
259 /** Finaliza el envío del frame
260  * @precond netdev_send_start() debe haber sido ejecutada
261  * @precond se copiaron datos al dispositivo para enviar
262  * @param len Cantidad de bytes a transmitir
263  */
264 void netdev_send_end(byte len)
265 {
266     // Set transmit page start to indicate packet start.
267     write_reg(TPSR, TX_PAGE_START);
268
269     // Ethernet packets must be > 60 bytes, otherwise are rejected as runts.
270     if (len < MIN_PACKET_LEN)
271         len = MIN_PACKET_LEN;
272
273     // Set transmit byte count registers to indicate packet length.
274     write_reg(TBCR0, len);
275     write_reg(TBCR1, 0u);
276
277     // Issue command for RTL8019AS to transmit packet from it's local buffer.
278     write_reg(CR, START | TXP);
279 }
280
281 void netdev_write_start(byte len)
282 {
283     // Set remote DMA byte count registers to indicate length of packet load.
284     write_reg(RBCR0, len);
285     write_reg(RBCR1, 0u);
286
287     // Initiate DMA transfer of uip_buf & uip_appdata buffers to RTL8019AS.
288     write_reg(CR, WRITE);
289 }
290
291 void netdev_write_start_at(byte offset, byte len)
292 {
293     // Set remote DMA start address registers to packet data.
294     write_reg(RSAR0, offset);
295     write_reg(RSAR1, TX_PAGE_START);
296
297     // Set remote DMA byte count registers to indicate length of packet load.
298     write_reg(RBCR0, len);
299     write_reg(RBCR1, 0u);
300
301     // Initiate DMA transfer of uip_buf & uip_appdata buffers to RTL8019AS.
302     write_reg(CR, WRITE);
303 }
304
305 /** Escribe un byte al buffer de la placa de red para ser enviado
306  * @precond netdev_send_start() debe haber sido ejecutada
307  * @param b Byte a enviar
308  */
309 void netdev_write_byte(byte b)
310 {
311     write_reg(RDMA, b);
312 }
313
314 /** Escribe un word al buffer de la placa de red para ser enviado
315  * @precond netdev_send_start() debe haber sido ejecutada
316  * @param w Word a enviar
317  */
318 void netdev_write_word(uint16 w)
319 {
320     write_reg(RDMA, HIGH(w));
321     write_reg(RDMA, LOW(w));
322 }
323
324 void netdev_write_end()
325 {
326     // Abort/ complete DMA operation.
327     ABORT_DMA(START);
328 }
329
330 /** Comienza la lectura de un nuevo frame
331  * @return Cantidad de bytes a recibir
332  */
333 byte netdev_recv_start()
334 {
335     // Check if the rx buffer has overflowed.
336     if (read_reg(ISR) & OVW)
337     {
338         byte current;
339
340         SELECT_REG_PAGE(1);
341         current = read_reg(CURR);
342         SELECT_REG_PAGE(0);
343
344         // Hack: a veces reporta mal el flag de OVW, así que verificamos que
345         // relamente haya habido overflow.
346         if (read_reg(BNRY) == current)
347         {
348             printb(read_reg(ISR), 0x01);
349             printb(read_reg(BNRY), 0x02);
350             printb(current, 0x04);
351             printb(0x00, 0x00);
352             reset();
353         }
354         return 0;
355     }
356     // Check if there is a packet in the rx buffer.
357     else if (read_reg(ISR) & PRX)
358     {
359         byte status;
360         byte len;
361         byte current;
362
363         // Retrieve packet header. (status, next_ptr, length_l, length_h)
364
365         // Obtiene el buffer a leer actualmente
366         recv_state.curr_buf = read_reg(BNRY) + 1;
367         if (recv_state.curr_buf >= RX_PAGE_STOP)
368             recv_state.curr_buf = RX_PAGE_START;
369
370         // Select RTL8019AS register page 1.
371         SELECT_REG_PAGE(1);
372
373         // Retrieve current receive buffer page
374         current = read_reg(CURR);
375
376         // Select RTL8019AS register page 1.
377         SELECT_REG_PAGE(0);
378
379         // Check if last packet has been removed from rx buffer.
380         if(recv_state.curr_buf == current)
381         {
382             // Clear packet received interrupt flag.
383             write_reg(ISR, PRX | RXE);
384             return 0;
385         }
386
387         // Set remote DMA byte count registers to packet header length.
388         recv_state.curr_off = 0;
389         netdev_read_start(BUF_HDR_SIZE);
390
391         // Packet status.
392         status = netdev_read_byte();
393
394         // Save next packet pointer.
395         recv_state.next_buf = netdev_read_byte() - 1;
396
397         // Retrieve packet data length and subtract CRC bytes.
398         len = netdev_read_byte() - BUF_HDR_SIZE;
399
400         // Si es muy grande, muy chico o hubo error, lo descartamos
401         if ((len < MIN_PACKET_LEN) || (len > MAX_PACKET_LEN)
402                 || ((status & 0x0F) != RXSOK)
403                 || netdev_read_byte()) // Parte alta del tamaño
404         {
405             // Terminamos DMA y pasamos al próximo frame
406             netdev_read_end();
407             write_reg(BNRY, recv_state.next_buf);
408             return 0;
409         }
410
411         // Abort/ complete DMA operation.
412         netdev_read_end();
413
414         return len;
415     }
416
417     return 0;
418 }
419
420 /** Finaliza la recepción del frame
421  * @precond netdev_recv_start() debe haber sido ejecutada
422  */
423 void netdev_recv_end()
424 {
425     // Pasa el próximo frame
426     write_reg(BNRY, recv_state.next_buf);
427 }
428
429 void netdev_read_start(byte len)
430 {
431     // Set remote DMA start address registers to packet data.
432     write_reg(RSAR0, recv_state.curr_off);
433     write_reg(RSAR1, recv_state.curr_buf);
434     recv_state.curr_off += len;
435
436     // Set remote DMA byte count registers to packet data length.
437     write_reg(RBCR0, len);
438     write_reg(RBCR1, 0);
439
440     // Initiate DMA transfer of packet data.
441     write_reg(CR, READ);
442 }
443
444 /** Lee un byte del buffer de la placa de red
445  * @precond netdev_recv_start() debe haber sido ejecutada
446  */
447 byte netdev_read_byte()
448 {
449     return read_reg(RDMA);
450 }
451
452 /** Lee un word del buffer de la placa de red
453  * @precond netdev_recv_start() debe haber sido ejecutada
454  */
455 uint16 netdev_read_word()
456 {
457     uint16 w = read_reg(RDMA) << 8;
458     return w + read_reg(RDMA);
459 }
460
461 /** Finaliza la lectura del frame
462  * @precond netdev_recv_start() debe haber sido ejecutada
463  */
464 void netdev_read_end()
465 {
466     // Completa DMA
467     ABORT_DMA(START);
468 }
469