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