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