summaryrefslogtreecommitdiff
path: root/src/drivers/net/rtl8139.c
blob: c5008423b14423c219e737c932de1faf9f3def12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
#include "rtl8139.h"

#include "string.h"
#include "stdio.h"
#include "stdlib.h"

#define DEBUG

static rtl8139_vtable_t const rtl8139_vtable = {
	{
		{
			rtl8139_activate,
			rtl8139_deactivate,
			rtl8139_deinit,
			rtl8139_print_name,
			rtl8139_print_info
		},
		network_register_handler,
		rtl8139_write
	}
};

// registers
#define REG_MAC0 0x00
#define REG_TR_STATUS0 0x10
#define REG_TR_ADDR0 0x20
#define REG_RBSTART 0x30
#define REG_CMD 0x37
#define REG_CAPR 0x38
#define REG_IMR 0x3C
#define REG_ISR 0x3E
#define REG_TCR 0x40
#define REG_RCR 0x44
#define REG_MPC 0x4C

// commands for REG_CMD (command register)
#define CMD_RESET 0x10
#define CMD_RECEIVER_ENABLE 0x08
#define CMD_TRANSMITTER_ENABLE 0x04
#define CMD_BUFFER_EMPTY 0x01

// mask for reading the version in from the REG_TCR (transmit configuration register)
#define TCR_MODEL_VERSION_AM 0x7C000000
#define TCR_MODEL_VERSION_BM 0x00C00000

// vendor identifiers received from the REG_TCR (transmit configuration register)
#define	TCR_MODEL_VERSION_RTL8139 0x60000000
#define	TCR_MODEL_VERSION_RTL8139A 0x70000000
#define	TCR_MODEL_VERSION_RTL8139B 0x78000000
#define	TCR_MODEL_VERSION_RTL8130 0x78000000
#define	TCR_MODEL_VERSION_RTL8139AG 0x74000000
#define	TCR_MODEL_VERSION_RTL8139C 0x74000000
#define	TCR_MODEL_VERSION_RTL8100 0x78800000
#define	TCR_MODEL_VERSION_RTL8100B 0x74400000
#define	TCR_MODEL_VERSION_RTL8139CP 0x74800000
#define	TCR_MODEL_VERSION_RTL8101 0x74C00000

// values for REG_TCR (transmit configuration register)
#define TCR_IFG_STANDARD 0x03000000
#define TCR_MXDMA_2048 0x00000700
#define TCR_APPEND_CRC 0x00010000

// values for REG_RCR (receive configuration register)
#define RCR_MXDMA_UNLIMITED 0x00000700
#define RCR_RECEIVE_BUFFER_8K_16 0x00000000
#define RCR_WRAP 0x00000080
#define RCR_ACCEPT_BROADCAST 0x00000008
#define RCR_ACCEPT_MULTICAST 0x00000004
#define RCR_ACCEPT_PHYS_MATCH 0x00000002

// values for REG_IMR/REG_ISR (interrupt mask/status registers)
#define ISR_SYSTEM_ERROR 0x8000
#define ISR_TIMEOUT 0x4000
#define ISR_CABLE_LENGTH_CHANGED 0x2000
#define ISR_RECEIVE_FIFO_OVERFLOW 0x0040
#define ISR_PACKET_UNDERRUN_LINK_CHANGED 0x0020
#define ISR_RECEIVE_BUFFER_OVERFLOW 0x0010
#define ISR_TRANSMIT_ERROR 0x0008
#define ISR_TRANSMIT_OK 0x0004
#define ISR_RECEIVE_ERROR 0x0002
#define ISR_RECEIVE_OK 0x0001

#define ISR_ALL \
	ISR_CABLE_LENGTH_CHANGED | ISR_TIMEOUT | ISR_SYSTEM_ERROR | \
	ISR_RECEIVE_FIFO_OVERFLOW | ISR_PACKET_UNDERRUN_LINK_CHANGED | \
	ISR_RECEIVE_BUFFER_OVERFLOW | ISR_TRANSMIT_ERROR | ISR_TRANSMIT_OK | \
	ISR_RECEIVE_ERROR | ISR_RECEIVE_OK

// header 
#define PACKET_HEADER_ROK 0x0001

// receive buffer, set to 8k + header (16) + wrap (2k)
#define RECEIVE_BUFFER_SIZE 8192
#define RECEIVE_TOTAL_BUFFER_SIZE RECEIVE_BUFFER_SIZE + 16 + 2048
 
void rtl8139_init( rtl8139_t *rtl8139, pci_controller_t *pci, pci_device_descriptor_t *descriptor, interrupt_t *interrupt, void *context )
{
	memset( rtl8139, 0, sizeof( rtl8139_t ) );

	network_init( (network_t *)rtl8139, interrupt, context );

	rtl8139->pci = pci;
	rtl8139->pci_descriptor = *descriptor;

	puts( "Initializing driver for Realtek 8139 network card.." );

	for( int i = 0; i < NOF_MAC_REGISTERS; i++ ) {
		port8_init( &rtl8139->MAC_port[i], descriptor->port_base + REG_MAC0 + i );
	}
	for( int i = 0; i < NOF_TRANSMIT_BUFFERS; i++ ) {
		port32_init( &rtl8139->TSD_port[i], descriptor->port_base + REG_TR_STATUS0 +i );
		port32_init( &rtl8139->TSAD_port[i], descriptor->port_base + REG_TR_ADDR0 + i );
	}

	port8_init( &rtl8139->CMD_port, descriptor->port_base + REG_CMD );
	port16_init( &rtl8139->CAPR_port, descriptor->port_base + REG_CAPR );
	port32_init( &rtl8139->RBSTART_port, descriptor->port_base + REG_RBSTART );
	port16_init( &rtl8139->IMR_port, descriptor->port_base + REG_IMR );
	port16_init( &rtl8139->ISR_port, descriptor->port_base + REG_ISR );
	port32_init( &rtl8139->TCR_port, descriptor->port_base + REG_TCR );
	port32_init( &rtl8139->RCR_port, descriptor->port_base + REG_RCR );
	port32_init( &rtl8139->MPC_port, descriptor->port_base + REG_MPC );

	// software reset, the CR_RESET bit remains high till the
	// reset is finished
	port8_write( &rtl8139->CMD_port, CMD_RESET );
	while( ( port8_read( &rtl8139->CMD_port ) & CMD_RESET ) == CMD_RESET );

	// get MAC
	for( int i = 0; i < NOF_MAC_REGISTERS; i++ ) {
		rtl8139->base.mac_address.byte[i] = port8_read( &rtl8139->MAC_port[i] );
	}

	// enable receiver and transmitter early (TODO: lowlevel says as later as
	// possible, Osdev and others say as early as possible)
	port8_write( &rtl8139->CMD_port, CMD_RECEIVER_ENABLE | CMD_TRANSMITTER_ENABLE );

	// get model
	rtl8139->model_id = port32_read( &rtl8139->TCR_port ) & ( TCR_MODEL_VERSION_AM | TCR_MODEL_VERSION_BM );
	switch( rtl8139->model_id ) {

		case TCR_MODEL_VERSION_RTL8139:
			rtl8139->model = "RTL8139";
			break;

		case TCR_MODEL_VERSION_RTL8139A:
			rtl8139->model = "RTL8139A";
			break;

		// case TCR_MODEL_VERSION_RTL8139B:
		case TCR_MODEL_VERSION_RTL8130:
			rtl8139->model = "RTL8130/RTL8130B";
			break;

		// case TCR_MODEL_VERSION_RTL8139AG:
		case TCR_MODEL_VERSION_RTL8139C:
			rtl8139->model = "RTL8139A-G/RTL8139C";
			break;

		case TCR_MODEL_VERSION_RTL8100:
			rtl8139->model = "RTL8100";
			break;

		case TCR_MODEL_VERSION_RTL8100B:
			rtl8139->model = "RTL8100B";
			break;

		case TCR_MODEL_VERSION_RTL8139CP:
			rtl8139->model = "RTL8139C+";
			break;

		case TCR_MODEL_VERSION_RTL8101:
			rtl8139->model = "RTL8101";
			break;

		default:
			rtl8139->model = NULL;
			break;
	}

	// initialize TCR/RCR (take Lowlevel suggestions: TCR = 0x03000700, RCR = 0x0000070a)
	port32_write( &rtl8139->TCR_port, TCR_IFG_STANDARD | TCR_APPEND_CRC | TCR_MXDMA_2048 );
	port32_write( &rtl8139->RCR_port, RCR_MXDMA_UNLIMITED |
		RCR_RECEIVE_BUFFER_8K_16 | RCR_WRAP |
		RCR_ACCEPT_BROADCAST | RCR_ACCEPT_MULTICAST | RCR_ACCEPT_PHYS_MATCH );

	// allocate receive buffer and register it
	rtl8139->receive_buffer = (uint8_t *)aligned_alloc( 16, RECEIVE_TOTAL_BUFFER_SIZE );
	memset( rtl8139->receive_buffer, 0, RECEIVE_TOTAL_BUFFER_SIZE );
	port32_write( &rtl8139->RBSTART_port, (uint32_t)rtl8139->receive_buffer );
	rtl8139->receive_buffer_pos = 0;
	
	// allocate transmit buffers and set index to first transmit buffer
	for( int i = 0; i < NOF_TRANSMIT_BUFFERS; i++ ) {
		rtl8139->transmit_buffer[i] = (uint8_t *)aligned_alloc( 16, ETHERNET_MAX_FRAME_SIZE_IEEE_802_3 );
		memset( rtl8139->transmit_buffer[i], 0, ETHERNET_MAX_FRAME_SIZE_IEEE_802_3 );
		port32_write( &rtl8139->TSAD_port[i], (uint32_t)rtl8139->transmit_buffer[i] );
	}
	rtl8139->transmit_buffer_idx = 0;
	rtl8139->transmitting = false;

	// reset missed packet counter
	port32_write( &rtl8139->MPC_port, 0 );

	// enable bus mastering for DMA transfers (without this the receive
	// buffer remains empty!)
	pci_enable_bus_mastering( rtl8139->pci, &rtl8139->pci_descriptor );

	interrupt_handler_init( &rtl8139->interrupt_handler, IRQ_BASE + descriptor->interrupt, interrupt, rtl8139_handle_interrupt, rtl8139 );
	interrupts_register_interrupt_handler( rtl8139->interrupt_handler );

	// register interrupts we are interested in (we want all and
	// filter them in the interrupt handler), read ISR to clear
	// the current values (if there are bits set)
	port16_write( &rtl8139->IMR_port, ISR_ALL );
	(void)port16_read( &rtl8139->ISR_port );
	
	rtl8139_print_info( rtl8139 );

	((driver_t *)rtl8139)->vtable = (driver_vtable_t *)&rtl8139_vtable;
}

void rtl8139_activate( void *obj )
{
	puts( "Activating driver for Realtek 8139 network card.." );

	rtl8139_t *rtl8139 = obj;
}

void rtl8139_deactivate( void *obj )
{
	puts( "Dectivating driver for Realtek 8139 network card.." );

	rtl8139_t *rtl8139 = obj;
}

void rtl8139_deinit( void *obj )
{
	rtl8139_t *rtl8139 = (rtl8139_t *)obj;
	
	free( rtl8139->receive_buffer );
}

static void handle_receive_packet( rtl8139_t *rtl8139 )
{
	while( true ) {
		uint8_t data = port8_read( &rtl8139->CMD_port );

		// still data in the buffer
		if( data & CMD_BUFFER_EMPTY ) {
			break;
		}
		
		uint8_t *buffer = rtl8139->receive_buffer + rtl8139->receive_buffer_pos;
		
		// packet header
		uint16_t header = *( (uint16_t *)buffer );
#ifdef DEBUG
		printf( "rtl8139: rcv header %X\n", header );
#endif

		if( ( header & PACKET_HEADER_ROK ) == 0 ) {
			printf( "rtl8139: illegal packet header, no ROK\n", header );
			break;
		}
		buffer += sizeof( uint16_t );
		
		// buffer is 16-bit alligned
		uint16_t len = *( (uint16_t *)buffer );
		buffer += sizeof( uint16_t );
#ifdef DEBUG
		printf( "rtl8139: rcv len: %d\n", len - 4 );
#endif

#ifdef DEBUG
		uint16_t capr = port16_read( &rtl8139->CAPR_port );
		uint32_t rbstart = port32_read( &rtl8139->RBSTART_port );
		uint32_t mpc = port32_read( &rtl8139->MPC_port );
		printf( "rtl8139: RBSTART: %X, CAPR: %X, RBPOS: %X, MPC: %X\n",
			rbstart, capr, rtl8139->receive_buffer_pos, mpc );
		printf( "rtl8139: " );
		for( int i = 0; i < len; i++ ) {
			printf( "%X ", rtl8139->receive_buffer[rtl8139->receive_buffer_pos+i] );
		}
		puts( "" );
#endif

		network_event_t event;

		if( len >= ETHERNET_MIN_FRAME_SIZE_IEEE_802_3 ) {
			// remove CRC (which is part of len)
			// copy data for the handler of the network event
			event.buf.length = len - sizeof( uint32_t );
			event.buf.data = (uint8_t *)malloc( event.buf.length );
			memcpy( event.buf.data, buffer, event.buf.length );
			memset( rtl8139->receive_buffer, 0, len + 2 * sizeof( uint16_t ) );
		} else {
			// make sure we don't have a runt package
			puts( "rtl8139: too small frame seen. packet ignored." );
		}

		// adjust buffer pos
		rtl8139->receive_buffer_pos += 2 * sizeof( uint16_t );
		rtl8139->receive_buffer_pos += len;
		rtl8139->receive_buffer_pos = ( rtl8139->receive_buffer_pos + 3 ) & ~0x3;
		if( rtl8139->receive_buffer_pos >= RECEIVE_TOTAL_BUFFER_SIZE ) {
			rtl8139->receive_buffer_pos = 0;
		}
		
		// initial CAPS is set to FFF0, we deduce from this that we have
		// to subtract 16 from the actual buffer offset (not mentioned in
		// documentation of the card!)
		port16_write( &rtl8139->CAPR_port, rtl8139->receive_buffer_pos - 16 );
		
		if( len >= ETHERNET_MIN_FRAME_SIZE_IEEE_802_3 ) {
			rtl8139->base.handler( &event, rtl8139 );
		}
	}
}

uint32_t rtl8139_handle_interrupt( interrupt_handler_t *handler, uint32_t esp )
{
	rtl8139_t *rtl8139 = (rtl8139_t *)handler->driver;

	uint16_t isr = port16_read( &rtl8139->ISR_port );
	uint16_t reset_isr = 0;

	// TODO: early interrupt line cleaning or later?
	port16_write( &rtl8139->ISR_port, isr );
	
#ifdef DEBUG
	printf( "rtl8139: INTERRUPT: %X\n", isr );
#endif

	if( isr & ISR_RECEIVE_OK ) {
		// TODO: manual says, we should reset the ISR bit first,
		// then go into a read loop?
		handle_receive_packet( rtl8139 );
	} else if( isr & ISR_TRANSMIT_OK ) {
		rtl8139->transmitting = false;
	}

	// reset interrupt lines when consumed
	//~ port16_write( &rtl8139->ISR_port, isr );
	
	return esp;
}

void rtl8139_print_name( void *obj )
{
	puts( "Realtek 8139 network card driver" );
}

void rtl8139_print_info( void *obj )
{
	rtl8139_t *rtl8139 = (rtl8139_t *)obj;

	char buf[NETWORK_ETHERNET_MAC_ADDR_STRING];
	char buf2[30];
	if( rtl8139->model == NULL ) {
		snprintf( buf2, 30, "unknown submodel 0x%X", rtl8139->model_id );
	}
	ethernet_mac_address_to_string( rtl8139->base.mac_address, buf, 20 );
	printf( "rtl8139 NIC type %s\n  at I/O base 0x%X, interrupt 0x%X, MAC: %s\n",
		( rtl8139->model != NULL ) ? rtl8139->model : buf2,
		rtl8139->pci_descriptor.port_base, rtl8139->pci_descriptor.interrupt, buf );
}

void rtl8139_write( void *obj, network_buffer_t buf )
{
	rtl8139_t *rtl8139 = (rtl8139_t *)obj;
	
	if( rtl8139->transmitting ) {
		puts( "rtl8139: currently transmitting, buffer overrun" );
		return;
	}
		
#ifdef DEBUG
	printf( "rtl8139: sending data of length %d\n", buf.length );
	printf( "rtl8139: " );
	for( int i = 0; i < buf.length; i++ ) {
		printf( "%X ", ((const uint8_t *)buf.data)[i] );
	}
	puts( "" );
#endif

	memcpy( rtl8139->transmit_buffer[rtl8139->transmit_buffer_idx], buf.data, buf.length );

	port32_write( &rtl8139->TSD_port[rtl8139->transmit_buffer_idx], buf.length & 0xFFF );
	
	rtl8139->transmit_buffer_idx += 1;
	rtl8139->transmit_buffer_idx %= NOF_TRANSMIT_BUFFERS;
}