1/*
2 * Copyright (c) 2001-2007 pinc Software. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
4 */
5
6/*! Device hooks for SiS 900 networking */
7
8#include "device.h"
9
10#include "driver.h"
11#include "interface.h"
12#include "sis900.h"
13
14#include <directories.h>
15#include <driver_settings.h>
16
17#include <stdlib.h>
18#include <string.h>
19#ifdef HAIKU_TARGET_PLATFORM_HAIKU
20#	include <net/if_media.h>
21#endif
22
23
24/* device hooks prototypes */
25
26status_t device_open(const char *, uint32, void **);
27status_t device_close(void *);
28status_t device_free(void *);
29status_t device_ioctl(void *, uint32, void *, size_t);
30status_t device_read(void *, off_t, void *, size_t *);
31status_t device_write(void *, off_t, const void *, size_t *);
32
33
34int32 gDeviceOpenMask = 0;
35
36device_hooks gDeviceHooks = {
37	device_open,
38	device_close,
39	device_free,
40	device_ioctl,
41	device_read,
42	device_write,
43	NULL,
44	NULL,
45	NULL,
46	NULL
47};
48
49#include <stdarg.h>
50
51//#define EXCESSIVE_DEBUG
52//#define THE_BUSY_WAITING_WAY
53
54#ifdef EXCESSIVE_DEBUG
55//#	include <time.h>
56
57sem_id gIOLock;
58
59int
60bug(const char *format, ...)
61{
62	va_list vl;
63	char    c[2048];
64	int     i;
65	int     file;
66
67	if ((file = open(kCommonLogDirectory "/sis900-driver.log",
68		O_RDWR | O_APPEND | O_CREAT)) >= 0) {
69//		time_t timer = time(NULL);
70//		strftime(c, 2048, "%H:%M:S: ", localtime(&timer));
71
72		va_start(vl, format);
73		i = vsprintf(c, format, vl);
74		va_end(vl);
75
76		write(file, c, strlen(c));
77		close(file);
78	}
79
80	return i;
81}
82
83#define DUMPED_BLOCK_SIZE 16
84
85void
86dumpBlock(const char *buffer, int size, const char *prefix)
87{
88	int i;
89
90	for (i = 0; i < size;)
91	{
92		int start = i;
93
94		bug(prefix);
95		for (; i < start+DUMPED_BLOCK_SIZE; i++)
96		{
97			if (!(i % 4))
98				bug(" ");
99
100			if (i >= size)
101				bug("  ");
102			else
103				bug("%02x", *(unsigned char *)(buffer + i));
104		}
105		bug("  ");
106
107		for (i = start; i < start + DUMPED_BLOCK_SIZE; i++)
108		{
109			if (i < size)
110			{
111				char c = buffer[i];
112
113				if (c < 30)
114					bug(".");
115				else
116					bug("%c", c);
117			}
118			else
119				break;
120		}
121		bug("\n");
122	}
123}
124
125#endif	/* EXCESSIVE_DEBUG */
126//	#pragma mark -
127
128
129static status_t
130checkDeviceInfo(struct sis_info *info)
131{
132	if (!info || info->cookieMagic != SiS_COOKIE_MAGIC)
133		return EINVAL;
134
135	return B_OK;
136}
137
138
139static void
140deleteSemaphores(struct sis_info *info)
141{
142}
143
144
145static status_t
146createSemaphores(struct sis_info *info)
147{
148	if ((info->rxSem = create_sem(0, "sis900 receive")) < B_OK)
149		return info->rxSem;
150
151	set_sem_owner(info->rxSem, B_SYSTEM_TEAM);
152
153	if ((info->txSem = create_sem(NUM_Tx_DESCR, "sis900 transmit")) < B_OK)
154	{
155		delete_sem(info->rxSem);
156		return info->txSem;
157	}
158	set_sem_owner(info->txSem, B_SYSTEM_TEAM);
159
160#ifdef EXCESSIVE_DEBUG
161	if ((gIOLock = create_sem(1, "sis900 debug i/o lock")) < B_OK)
162		return gIOLock;
163	set_sem_owner(gIOLock, B_SYSTEM_TEAM);
164#endif
165
166	info->rxLock = info->txLock = 0;
167
168	return B_OK;
169}
170
171
172static void
173readSettings(struct sis_info *info)
174{
175	const char *parameter;
176
177	void *handle = load_driver_settings("sis900");
178	if (handle == NULL)
179		return;
180
181	parameter = get_driver_parameter(handle, "duplex", "auto", "auto");
182	if (!strcasecmp(parameter, "full"))
183		info->fixedMode = LINK_FULL_DUPLEX;
184	else if (!strcasecmp(parameter, "half"))
185		info->fixedMode = LINK_HALF_DUPLEX;
186
187	parameter = get_driver_parameter(handle, "speed", "auto", "auto");
188	if (!strcasecmp(parameter, "100"))
189		info->fixedMode |= LINK_SPEED_100_MBIT;
190	else if (!strcasecmp(parameter, "10"))
191		info->fixedMode |= LINK_SPEED_10_MBIT;
192	else if (!strcasecmp(parameter, "1"))
193		info->fixedMode |= LINK_SPEED_HOME;
194
195	// it's either all or nothing
196
197	if ((info->fixedMode & LINK_DUPLEX_MASK) == 0
198		|| (info->fixedMode & LINK_SPEED_MASK) == 0)
199		info->fixedMode = 0;
200
201	unload_driver_settings(handle);
202}
203
204
205//--------------------------------------------------------------------------
206//	#pragma mark -
207//	the device will be accessed through the following functions (a.k.a. device hooks)
208
209
210status_t
211device_open(const char *name, uint32 flags, void **cookie)
212{
213	struct sis_info *info;
214	area_id area;
215	int id;
216
217	// verify device access (we only allow one user at a time)
218	{
219		char *thisName;
220		int32 mask;
221
222		// search for device name
223		for (id = 0; (thisName = gDeviceNames[id]) != NULL; id++) {
224			if (!strcmp(name, thisName))
225				break;
226		}
227		if (!thisName)
228			return EINVAL;
229
230		// check if device is already open
231		mask = 1L << id;
232		if (atomic_or(&gDeviceOpenMask, mask) & mask)
233			return B_BUSY;
234	}
235
236	// allocate internal device structure
237	if ((area = create_area(DEVICE_NAME " private data", cookie,
238							B_ANY_KERNEL_ADDRESS,
239							ROUND_TO_PAGE_SIZE(sizeof(struct sis_info)),
240							B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA)) < B_OK) {
241		gDeviceOpenMask &= ~(1L << id);
242		return B_NO_MEMORY;
243	}
244	info = (struct sis_info *)*cookie;
245	memset(info, 0, sizeof(struct sis_info));
246
247	info->cookieMagic = SiS_COOKIE_MAGIC;
248	info->thisArea = area;
249	info->id = id;
250	info->pciInfo = pciInfo[id];
251	info->registers = (addr_t)pciInfo[id]->u.h0.base_registers[0];
252
253	if (sis900_getMACAddress(info))
254		dprintf(DEVICE_NAME ": MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
255			  info->address.ebyte[0], info->address.ebyte[1], info->address.ebyte[2],
256			  info->address.ebyte[3], info->address.ebyte[4], info->address.ebyte[5]);
257	else
258		dprintf(DEVICE_NAME ": could not get MAC address\n");
259
260	readSettings(info);
261
262	if (createSemaphores(info) == B_OK) {
263		status_t status = sis900_initPHYs(info);
264		if (status == B_OK) {
265			TRACE((DEVICE_NAME ": MII status = %d\n", mdio_read(info, MII_STATUS)));
266
267			//sis900_configFIFOs(info);
268			sis900_reset(info);
269
270			// install & enable interrupts
271			install_io_interrupt_handler(info->pciInfo->u.h0.interrupt_line,
272				sis900_interrupt, info, 0);
273			sis900_enableInterrupts(info);
274
275			sis900_setRxFilter(info);
276			sis900_createRings(info);
277
278			// enable receiver's state machine
279			write32(info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Rx_ENABLE);
280
281			// check link mode & add timer (once every second)
282			sis900_checkMode(info);
283			add_timer(&info->timer, sis900_timer, 1000000LL, B_PERIODIC_TIMER);
284
285			return B_OK;
286		}
287		dprintf(DEVICE_NAME ": could not initialize MII PHY: %s\n", strerror(status));
288		deleteSemaphores(info);
289	} else
290		dprintf(DEVICE_NAME ": could not create semaphores.\n");
291
292	gDeviceOpenMask &= ~(1L << id);
293	delete_area(area);
294
295	return B_ERROR;
296}
297
298
299status_t
300device_close(void *data)
301{
302	struct sis_info *info;
303
304	if (checkDeviceInfo(info = data) != B_OK)
305		return EINVAL;
306
307	info->cookieMagic = SiS_FREE_COOKIE_MAGIC;
308
309	// cancel timer
310	cancel_timer(&info->timer);
311
312	// remove & disable interrupt
313	sis900_disableInterrupts(info);
314	remove_io_interrupt_handler(info->pciInfo->u.h0.interrupt_line, sis900_interrupt, info);
315
316	// disable the transmitter's and receiver's state machine
317	write32(info->registers + SiS900_MAC_COMMAND,
318		SiS900_MAC_CMD_Rx_DISABLE | SiS900_MAC_CMD_Tx_DISABLE);
319
320	delete_sem(info->rxSem);
321	delete_sem(info->txSem);
322
323#ifdef EXCESSIVE_DEBUG
324	delete_sem(gIOLock);
325#endif
326
327	return B_OK;
328}
329
330
331status_t
332device_free(void *data)
333{
334	struct sis_info *info = data;
335	status_t retval = B_NO_ERROR;
336
337	if (info == NULL || info->cookieMagic != SiS_FREE_COOKIE_MAGIC)
338		retval = EINVAL;
339
340	gDeviceOpenMask &= ~(1L << info->id);
341
342	sis900_deleteRings(info);
343	delete_area(info->thisArea);
344
345	return retval;
346}
347
348
349status_t
350device_ioctl(void *data, uint32 msg, void *buffer, size_t bufferLength)
351{
352	struct sis_info *info;
353
354	if (checkDeviceInfo(info = data) != B_OK)
355		return EINVAL;
356
357	switch (msg) {
358		case ETHER_GETADDR:
359			TRACE(("ioctl: get MAC address\n"));
360			memcpy(buffer, &info->address, 6);
361			return B_OK;
362
363		case ETHER_INIT:
364			TRACE(("ioctl: init\n"));
365			return B_OK;
366
367		case ETHER_GETFRAMESIZE:
368			TRACE(("ioctl: get frame size\n"));
369			*(uint32*)buffer = MAX_FRAME_SIZE;
370			return B_OK;
371
372		case ETHER_SETPROMISC:
373			TRACE(("ioctl: set promisc\n"));
374			sis900_setPromiscuous(info, *(uint32 *)buffer != 0);
375			return B_OK;
376
377		case ETHER_NONBLOCK:
378			info->blockFlag = *(int32 *)buffer ? B_TIMEOUT : 0;
379			TRACE(("ioctl: non blocking ? %s\n", info->blockFlag ? "yes" : "no"));
380			return B_OK;
381
382		case ETHER_ADDMULTI:
383			TRACE(("ioctl: add multicast\n"));
384			/* not yet implemented */
385			break;
386
387#ifdef HAIKU_TARGET_PLATFORM_HAIKU
388		case ETHER_GET_LINK_STATE:
389		{
390			ether_link_state_t state;
391			state.media = (info->link ? IFM_ACTIVE : 0) | IFM_ETHER
392				| (info->full_duplex ? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX)
393				| (info->speed == LINK_SPEED_100_MBIT ? IFM_100_TX : IFM_10_T);
394			state.speed = info->speed == LINK_SPEED_100_MBIT
395				? 100000000 : 10000000;
396			state.quality = 1000;
397
398			return user_memcpy(buffer, &state, sizeof(ether_link_state_t));
399		}
400#endif
401
402		default:
403			TRACE(("ioctl: unknown message %lu (length = %ld)\n", msg, bufferLength));
404			break;
405	}
406	return B_ERROR;
407}
408
409
410#ifdef DEBUG
411// sis900.c
412extern int32 intrCounter;
413extern int32 lastIntr[100];
414uint32 counter = 0;
415#endif
416
417
418status_t
419device_read(void *data, off_t pos, void *buffer, size_t *_length)
420{
421	struct sis_info *info;
422	status_t status;
423	size_t size;
424	int32 blockFlag;
425	uint32 check;
426	int16 current;
427
428	if (checkDeviceInfo(info = data) != B_OK) {
429#ifndef __HAIKU__
430		*_length = 0;
431			// net_server work-around; it obviously doesn't care about error conditions
432			// For Haiku, this can be removed
433#endif
434		return B_BAD_VALUE;
435	}
436
437	blockFlag = info->blockFlag;
438
439	TRACE(("read: rx: isr = %d, free = %d, current = %d, blockFlag = %lx\n",
440		info->rxInterruptIndex, info->rxFree, info->rxCurrent, blockFlag));
441
442	// read is not reentrant
443	if (atomic_or(&info->rxLock, 1)) {
444#ifndef __HAIKU__
445		*_length = 0;
446#endif
447		return B_ERROR;
448	}
449
450	// block until data is available (if blocking is allowed)
451	if ((status = acquire_sem_etc(info->rxSem, 1, B_CAN_INTERRUPT | blockFlag, 0)) != B_NO_ERROR) {
452		TRACE(("cannot acquire read sem: %lx, %s\n", status, strerror(status)));
453		atomic_and(&info->rxLock, 0);
454#ifndef __HAIKU__
455		*_length = 0;
456#endif
457		return status;
458	}
459
460	/* three cases, frame is good, bad, or we don't own the descriptor */
461	current = info->rxCurrent;
462	check = info->rxDescriptor[current].status;
463
464	if (!(check & SiS900_DESCR_OWN)) {	// the buffer is still in use!
465		TRACE(("ERROR: read: buffer %d still in use: %lx\n", current, status));
466		atomic_and(&info->rxLock, 0);
467#ifndef __HAIKU__
468		*_length = 0;
469#endif
470		return B_BUSY;
471	}
472
473	if (check & (SiS900_DESCR_Rx_ABORT | SiS900_DESCR_Rx_OVERRUN |
474				 SiS900_DESCR_Rx_LONG_PACKET | SiS900_DESCR_Rx_RUNT_PACKET |
475				 SiS900_DESCR_Rx_INVALID_SYM | SiS900_DESCR_Rx_FRAME_ALIGN |
476				 SiS900_DESCR_Rx_CRC_ERROR)) {
477		TRACE(("ERROR read: packet with errors: %ld\n", check));
478		*_length = 0;
479	} else {
480		// copy buffer
481		size = (check & SiS900_DESCR_SIZE_MASK) - CRC_SIZE;
482		if (size > MAX_FRAME_SIZE || size > *_length) {
483			TRACE(("ERROR read: bad frame size %ld\n", size));
484			size = *_length;
485		} else if (size < *_length)
486			*_length = size;
487
488		memcpy(buffer, (void *)info->rxBuffer[current], size);
489	}
490	info->rxCurrent = (current + 1) & NUM_Rx_MASK;
491
492	/* update indexes and buffer ownership */
493	{
494		cpu_status former;
495		former = disable_interrupts();
496		acquire_spinlock(&info->rxSpinlock);
497
498		// release buffer to ring
499		info->rxDescriptor[current].status = MAX_FRAME_SIZE + CRC_SIZE;
500		info->rxFree++;
501
502		release_spinlock(&info->rxSpinlock);
503   		restore_interrupts(former);
504	}
505
506	atomic_and(&info->rxLock, 0);
507	return B_OK;
508}
509
510
511status_t
512device_write(void *data, off_t pos, const void *buffer, size_t *_length)
513{
514	struct sis_info *info;
515	status_t status;
516	uint16 frameSize;
517	int16 current;
518	uint32 check;
519
520	if (checkDeviceInfo(info = data) != B_OK)
521		return EINVAL;
522
523	//TRACE(("****\t%5ld: write... %lx, %ld (%d) thread: %ld, counter = %ld\n", counter++, buf, *len, info->txLock, threadID, intrCounter));
524	atomic_add(&info->txLock, 1);
525
526	if (*_length > MAX_FRAME_SIZE)
527		*_length = MAX_FRAME_SIZE;
528
529	frameSize = *_length;
530	current = info->txCurrent;
531
532	//dprintf("\t%5ld: \twrite: tx: isr = %d, sent = %d, current = %d\n",counter++,
533	//		info->txInterruptIndex,info->txSent,info->txCurrent);
534
535	// block until a free tx descriptor is available
536	if ((status = acquire_sem_etc(info->txSem, 1, B_TIMEOUT, ETHER_TRANSMIT_TIMEOUT)) < B_NO_ERROR) {
537		write32(info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Tx_ENABLE);
538		TRACE(("write: acquiring sem failed: %lx, %s\n", status, strerror(status)));
539		atomic_add(&info->txLock, -1);
540		return status;
541	}
542
543	check = info->txDescriptor[current].status;
544	if (check & SiS900_DESCR_OWN) {
545		// descriptor is still in use
546		dprintf(DEVICE_NAME ": card owns buffer %d\n", current);
547		atomic_add(&info->txLock, -1);
548		return B_ERROR;
549	}
550
551	/* Copy data to tx buffer */
552	memcpy((void *)info->txBuffer[current], buffer, frameSize);
553	info->txCurrent = (current + 1) & NUM_Tx_MASK;
554
555	{
556		cpu_status former;
557		former = disable_interrupts();
558		acquire_spinlock(&info->txSpinlock);
559
560		info->txDescriptor[current].status = SiS900_DESCR_OWN | frameSize;
561		info->txSent++;
562
563#if 0
564		{
565			struct buffer_desc *b = (void *)read32(info->registers + SiS900_MAC_Tx_DESCR);
566			int16 that;
567
568			dprintf("\twrite: status %d = %lx, sent = %d\n", current, info->txDescriptor[current].status,info->txSent);
569			dprintf("write: %d: mem = %lx : hardware = %lx\n", current, physicalAddress(&info->txDescriptor[current],sizeof(struct buffer_desc)),read32(info->registers + SiS900_MAC_Tx_DESCR));
570
571			for (that = 0;that < NUM_Tx_DESCR && (void *)physicalAddress(&info->txDescriptor[that],sizeof(struct buffer_desc)) != b;that++);
572
573			if (that == NUM_Tx_DESCR) {
574				//dprintf("not in ring!\n");
575				that = 0;
576			}
577			dprintf("(hardware status %d = %lx)!\n", that, info->txDescriptor[that].status);
578		}
579#endif
580		release_spinlock(&info->txSpinlock);
581   		restore_interrupts(former);
582	}
583
584	// enable transmit state machine
585	write32(info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Tx_ENABLE);
586
587#ifdef EXCESSIVE_DEBUG
588	acquire_sem(gIOLock);
589	bug("\t\twrite last interrupts:\n");
590	{
591		int ii;
592		for (ii = (intrCounter-2) % 100; ii < intrCounter; ii = (ii + 1) % 100)
593			bug("\t\t\t%ld: %08lx\n", ii, lastIntr[ii % 100]);
594	}
595	bug("\t\twrite block (%ld bytes) thread = %ld:\n", frameSize, threadID);
596	dumpBlock(buf,frameSize, "\t\t\t");
597	release_sem(gIOLock);
598#endif
599
600	atomic_add(&info->txLock, -1);
601	return B_OK;
602}
603
604