xref: /haiku/src/add-ons/kernel/drivers/network/ether/wb840/device.c (revision f5682ebb5620940138647627d763d3fe6f461629)
1 /* Copyright (c) 2003-2011
2  * Stefano Ceccherini <stefano.ceccherini@gmail.com>. All rights reserved.
3  * This file is released under the MIT license
4  */
5 #include <KernelExport.h>
6 #include <Errors.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 
11 #include <net/if_media.h>
12 
13 #include "debug.h"
14 #include "device.h"
15 #include "driver.h"
16 #include "interface.h"
17 #include "wb840.h"
18 
19 
20 #define MAX_CARDS 4
21 
22 extern char* gDevNameList[];
23 extern pci_info* gDevList[];
24 
25 static int32 sOpenMask = 0;
26 
27 static status_t
wb840_open(const char * name,uint32 flags,void ** cookie)28 wb840_open(const char* name, uint32 flags, void** cookie)
29 {
30 	char* deviceName = NULL;
31 	int32 i;
32 	int32 mask;
33 	struct wb_device* data;
34 	status_t status;
35 
36 	LOG((DEVICE_NAME ": open()\n"));
37 
38 	for (i = 0; (deviceName = gDevNameList[i]) != NULL; i++) {
39 		if (!strcmp(name, deviceName))
40 			break;
41 	}
42 
43 	if (deviceName == NULL) {
44 		LOG(("invalid device name"));
45 		return EINVAL;
46 	}
47 
48 	// There can be only one access at time
49 	mask = 1L << i;
50 	if (atomic_or(&sOpenMask, mask) & mask)
51 		return B_BUSY;
52 
53 	// Allocate a wb_device structure
54 	if (!(data = (wb_device*)calloc(1, sizeof(wb_device)))) {
55 		sOpenMask &= ~(1L << i);
56 		return B_NO_MEMORY;
57 	}
58 
59 	*cookie = data;
60 
61 	data->devId = i;
62 	data->pciInfo = gDevList[i];
63 	data->deviceName = gDevNameList[i];
64 	data->blockFlag = 0;
65 	data->reg_base = data->pciInfo->u.h0.base_registers[0];
66 	data->wb_cachesize = gPci->read_pci_config(data->pciInfo->bus,
67 		data->pciInfo->device, data->pciInfo->function, PCI_line_size,
68 		sizeof(PCI_line_size)) & 0xff;
69 
70 	wb_read_eeprom(data, &data->MAC_Address, 0, 3, false);
71 
72 	status = wb_create_semaphores(data);
73 	if (status < B_OK) {
74 		LOG((DEVICE_NAME": couldn't create semaphores\n"));
75 		goto err;
76 	}
77 
78 	status = wb_stop(data);
79 	if (status < B_OK) {
80 		LOG((DEVICE_NAME": can't stop device\n"));
81 		goto err1;
82 	}
83 
84 	status = wb_initPHYs(data);
85 	if (status < B_OK) {
86 		LOG((DEVICE_NAME": can't init PHYs\n"));
87 		goto err1;
88 	}
89 
90 	wb_init(data);
91 
92 	/* Setup interrupts */
93 	data->irq = data->pciInfo->u.h0.interrupt_line;
94 	status = install_io_interrupt_handler(data->irq, wb_interrupt, data, 0);
95 	if (status < B_OK) {
96 		LOG((DEVICE_NAME
97 			": can't install interrupt handler: %s\n", strerror(status)));
98 		goto err1;
99 	}
100 
101 	LOG((DEVICE_NAME ": interrupts installed at irq line %x\n", data->irq));
102 
103 	status = wb_create_rings(data);
104 	if (status < B_OK) {
105 		LOG((DEVICE_NAME": can't create ring buffers\n"));
106 		goto err2;
107 	}
108 
109 	wb_enable_interrupts(data);
110 
111 	WB_SETBIT(data->reg_base + WB_NETCFG, WB_NETCFG_RX_ON);
112 	write32(data->reg_base + WB_RXSTART, 0xFFFFFFFF);
113 	WB_SETBIT(data->reg_base + WB_NETCFG, WB_NETCFG_TX_ON);
114 
115 	add_timer(&data->timer, wb_tick, 1000000LL, B_PERIODIC_TIMER);
116 
117 	return B_OK; // Everything after this line is an error
118 
119 err2:
120 	remove_io_interrupt_handler(data->irq, wb_interrupt, data);
121 
122 err1:
123 	wb_delete_semaphores(data);
124 
125 err:
126 	sOpenMask &= ~(1L << i);
127 
128 	free(data);
129 	LOG(("wb840: Open Failed\n"));
130 
131 	return status;
132 }
133 
134 
135 static status_t
wb840_read(void * cookie,off_t position,void * buf,size_t * num_bytes)136 wb840_read(void* cookie, off_t position, void* buf, size_t* num_bytes)
137 {
138 	wb_device* device = (wb_device*)cookie;
139 	int16 current;
140 	status_t status;
141 	size_t size;
142 	int32 blockFlag;
143 	uint32 check;
144 
145 	LOG((DEVICE_NAME ": read()\n"));
146 
147 	blockFlag = device->blockFlag;
148 
149 	if (atomic_or(&device->rxLock, 1)) {
150 		*num_bytes = 0;
151 		return B_ERROR;
152 	}
153 
154 	status = acquire_sem_etc(device->rxSem, 1, B_CAN_INTERRUPT | blockFlag, 0);
155 	if (status < B_OK) {
156 		atomic_and(&device->rxLock, 0);
157 		*num_bytes = 0;
158 		return status;
159 	}
160 
161 	current = device->rxCurrent;
162 	check = device->rxDescriptor[current].wb_status;
163 	if (check & WB_RXSTAT_OWN) {
164 		LOG((DEVICE_NAME ":ERROR: read: buffer %d still in use: %x\n",
165 			(int)current, (int)status));
166 		atomic_and(&device->rxLock, 0);
167 		*num_bytes = 0;
168 		return B_BUSY;
169 	}
170 
171 	if (check & (WB_RXSTAT_RXERR | WB_RXSTAT_CRCERR | WB_RXSTAT_RUNT)) {
172 		LOG(("Error read: packet with errors."));
173 		*num_bytes = 0;
174 	} else {
175 		size = WB_RXBYTES(check);
176 		size -= CRC_SIZE;
177 		LOG((DEVICE_NAME": received %ld bytes\n", size));
178 		if (size > WB_MAX_FRAMELEN || size > *num_bytes) {
179 			LOG(("ERROR: Bad frame size: %ld", size));
180 			size = *num_bytes;
181 		}
182 		*num_bytes = size;
183 		memcpy(buf, (void*)device->rxBuffer[current], size);
184 	}
185 
186 	device->rxCurrent = (current + 1) & WB_RX_CNT_MASK;
187 	{
188 		cpu_status former;
189 		former = disable_interrupts();
190 		acquire_spinlock(&device->rxSpinlock);
191 
192 		// release buffer to ring
193 		wb_put_rx_descriptor(&device->rxDescriptor[current]);
194 		device->rxFree++;
195 
196 		release_spinlock(&device->rxSpinlock);
197    		restore_interrupts(former);
198 	}
199 
200 	atomic_and(&device->rxLock, 0);
201 
202 	return B_OK;
203 }
204 
205 
206 static status_t
wb840_write(void * cookie,off_t position,const void * buffer,size_t * num_bytes)207 wb840_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
208 {
209 	wb_device* device = (wb_device*)cookie;
210 	status_t status = B_OK;
211 	uint16 frameSize;
212 	int16 current;
213 	uint32 check;
214 
215 	LOG((DEVICE_NAME ": write()\n"));
216 
217 	atomic_add(&device->txLock, 1);
218 
219 	if (*num_bytes > WB_MAX_FRAMELEN)
220 		*num_bytes = WB_MAX_FRAMELEN;
221 
222 	frameSize = *num_bytes;
223 	current = device->txCurrent;
224 
225 	// block until a free tx descriptor is available
226 	status = acquire_sem_etc(device->txSem, 1, B_TIMEOUT, ETHER_TRANSMIT_TIMEOUT);
227 	if (status < B_OK) {
228 		write32(device->reg_base + WB_TXSTART, 0xFFFFFFFF);
229 		LOG((DEVICE_NAME": write: acquiring sem failed: %ld, %s\n",
230 			status, strerror(status)));
231 		atomic_add(&device->txLock, -1);
232 		*num_bytes = 0;
233 		return status;
234 	}
235 
236 	check = device->txDescriptor[current].wb_status;
237 	if (check & WB_TXSTAT_OWN) {
238 		// descriptor is still in use
239 		dprintf(DEVICE_NAME ": card owns buffer %d\n", (int)current);
240 		atomic_add(&device->txLock, -1);
241 		*num_bytes = 0;
242 		return B_ERROR;
243 	}
244 
245 	/* Copy data to tx buffer */
246 	memcpy((void*)device->txBuffer[current], buffer, frameSize);
247 	device->txCurrent = (current + 1) & WB_TX_CNT_MASK;
248 	LOG((DEVICE_NAME ": %d bytes written\n", frameSize));
249 
250 	{
251 		cpu_status former = disable_interrupts();
252 		acquire_spinlock(&device->txSpinlock);
253 
254 		device->txDescriptor[current].wb_ctl = WB_TXCTL_TLINK | frameSize;
255 		device->txDescriptor[current].wb_ctl |= WB_TXCTL_FIRSTFRAG
256 			| WB_TXCTL_LASTFRAG;
257 		device->txDescriptor[current].wb_status = WB_TXSTAT_OWN;
258 		device->txSent++;
259 
260 		release_spinlock(&device->txSpinlock);
261    		restore_interrupts(former);
262 	}
263 
264 	// re-enable transmit state machine
265 	write32(device->reg_base + WB_TXSTART, 0xFFFFFFFF);
266 
267 	atomic_add(&device->txLock, -1);
268 
269 	return B_OK;
270 }
271 
272 
273 static status_t
wb840_control(void * cookie,uint32 op,void * arg,size_t len)274 wb840_control (void* cookie, uint32 op, void* arg, size_t len)
275 {
276 	wb_device* data = (wb_device*)cookie;
277 
278 	LOG((DEVICE_NAME ": control()\n"));
279 	switch (op) {
280 		case ETHER_INIT:
281 			LOG(("%s: ETHER_INIT\n", data->deviceName));
282 			return B_OK;
283 
284 		case ETHER_GETADDR:
285 			LOG(("%s: ETHER_GETADDR\n", data->deviceName));
286 			memcpy(arg, &data->MAC_Address, sizeof(data->MAC_Address));
287 			print_address(arg);
288 			return B_OK;
289 
290 		case ETHER_NONBLOCK:
291 			LOG(("ETHER_NON_BLOCK\n"));
292 			data->blockFlag = *(int32*)arg ? B_TIMEOUT : 0;
293 			return B_OK;
294 
295 		case ETHER_GETFRAMESIZE:
296 			LOG(("ETHER_GETFRAMESIZE\n"));
297 			*(uint32 *)arg = WB_MAX_FRAMELEN;
298 			return B_OK;
299 
300 		case ETHER_GET_LINK_STATE:
301 		{
302 			ether_link_state_t state;
303 			LOG(("ETHER_GET_LINK_STATE"));
304 
305 			state.media = (data->link ? IFM_ACTIVE : 0) | IFM_ETHER
306 				| (data->full_duplex ? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX)
307 				| (data->speed == LINK_SPEED_100_MBIT ? IFM_100_TX : IFM_10_T);
308 			state.speed = data->speed == LINK_SPEED_100_MBIT
309 				? 100000000 : 10000000;
310 			state.quality = 1000;
311 
312 			return user_memcpy(arg, &state, sizeof(ether_link_state_t));
313 		}
314 
315 		case ETHER_ADDMULTI:
316 			LOG(("ETHER_ADDMULTI\n"));
317 			break;
318 
319 		case ETHER_REMMULTI:
320 			LOG(("ETHER_REMMULTI\n"));
321 			break;
322 
323 		case ETHER_SETPROMISC:
324 			LOG(("ETHER_SETPROMISC\n"));
325 			break;
326 
327 		default:
328 			LOG(("Invalid command\n"));
329 			break;
330 	}
331 
332 	return B_ERROR;
333 }
334 
335 
336 static status_t
wb840_close(void * cookie)337 wb840_close(void* cookie)
338 {
339 	wb_device* device = (wb_device*)cookie;
340 
341 	LOG((DEVICE_NAME ": close()\n"));
342 
343 	cancel_timer(&device->timer);
344 
345 	wb_stop(device);
346 
347 	write32(device->reg_base + WB_TXADDR, 0x00000000);
348 	write32(device->reg_base + WB_RXADDR, 0x00000000);
349 
350 	wb_disable_interrupts(device);
351 	remove_io_interrupt_handler(device->irq, wb_interrupt, device);
352 
353 	delete_sem(device->rxSem);
354 	delete_sem(device->txSem);
355 
356 	return B_OK;
357 }
358 
359 
360 static status_t
wb840_free(void * cookie)361 wb840_free(void* cookie)
362 {
363 	wb_device* device = (wb_device*)cookie;
364 
365 	LOG((DEVICE_NAME ": free()\n"));
366 
367 	sOpenMask &= ~(1L << device->devId);
368 
369 	wb_delete_rings(device);
370 	free(device->firstPHY);
371 	free(device);
372 
373 	return B_OK;
374 }
375 
376 
377 device_hooks
378 gDeviceHooks = {
379 	wb840_open, 	/* -> open entry point */
380 	wb840_close, 	/* -> close entry point */
381 	wb840_free,		/* -> free cookie */
382 	wb840_control, 	/* -> control entry point */
383 	wb840_read,		/* -> read entry point */
384 	wb840_write,	/* -> write entry point */
385 	NULL,
386 	NULL,
387 	NULL,
388 	NULL
389 };
390