BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / drivers / network / sis900 / device.c
blob1ce20cd33f4b6f5d311150333eec9c6409029a37
1 /*
2 * Copyright (c) 2001-2007 pinc Software. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
4 */
6 /*! Device hooks for SiS 900 networking */
8 #include "device.h"
10 #include "driver.h"
11 #include "interface.h"
12 #include "sis900.h"
14 #include <directories.h>
15 #include <driver_settings.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
20 # include <net/if_media.h>
21 #endif
24 /* device hooks prototypes */
26 status_t device_open(const char *, uint32, void **);
27 status_t device_close(void *);
28 status_t device_free(void *);
29 status_t device_ioctl(void *, uint32, void *, size_t);
30 status_t device_read(void *, off_t, void *, size_t *);
31 status_t device_write(void *, off_t, const void *, size_t *);
34 int32 gDeviceOpenMask = 0;
36 device_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
49 #include <stdarg.h>
51 //#define EXCESSIVE_DEBUG
52 //#define THE_BUSY_WAITING_WAY
54 #ifdef EXCESSIVE_DEBUG
55 //# include <time.h>
57 sem_id gIOLock;
59 int
60 bug(const char *format, ...)
62 va_list vl;
63 char c[2048];
64 int i;
65 int file;
67 if ((file = open(kSystemLogDirectory "/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));
72 va_start(vl, format);
73 i = vsprintf(c, format, vl);
74 va_end(vl);
76 write(file, c, strlen(c));
77 close(file);
80 return i;
83 #define DUMPED_BLOCK_SIZE 16
85 void
86 dumpBlock(const char *buffer, int size, const char *prefix)
88 int i;
90 for (i = 0; i < size;)
92 int start = i;
94 bug(prefix);
95 for (; i < start+DUMPED_BLOCK_SIZE; i++)
97 if (!(i % 4))
98 bug(" ");
100 if (i >= size)
101 bug(" ");
102 else
103 bug("%02x", *(unsigned char *)(buffer + i));
105 bug(" ");
107 for (i = start; i < start + DUMPED_BLOCK_SIZE; i++)
109 if (i < size)
111 char c = buffer[i];
113 if (c < 30)
114 bug(".");
115 else
116 bug("%c", c);
118 else
119 break;
121 bug("\n");
125 #endif /* EXCESSIVE_DEBUG */
126 // #pragma mark -
129 static status_t
130 checkDeviceInfo(struct sis_info *info)
132 if (!info || info->cookieMagic != SiS_COOKIE_MAGIC)
133 return EINVAL;
135 return B_OK;
139 static void
140 deleteSemaphores(struct sis_info *info)
145 static status_t
146 createSemaphores(struct sis_info *info)
148 if ((info->rxSem = create_sem(0, "sis900 receive")) < B_OK)
149 return info->rxSem;
151 set_sem_owner(info->rxSem, B_SYSTEM_TEAM);
153 if ((info->txSem = create_sem(NUM_Tx_DESCR, "sis900 transmit")) < B_OK)
155 delete_sem(info->rxSem);
156 return info->txSem;
158 set_sem_owner(info->txSem, B_SYSTEM_TEAM);
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
166 info->rxLock = info->txLock = 0;
168 return B_OK;
172 static void
173 readSettings(struct sis_info *info)
175 const char *parameter;
177 void *handle = load_driver_settings("sis900");
178 if (handle == NULL)
179 return;
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;
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;
195 // it's either all or nothing
197 if ((info->fixedMode & LINK_DUPLEX_MASK) == 0
198 || (info->fixedMode & LINK_SPEED_MASK) == 0)
199 info->fixedMode = 0;
201 unload_driver_settings(handle);
205 //--------------------------------------------------------------------------
206 // #pragma mark -
207 // the device will be accessed through the following functions (a.k.a. device hooks)
210 status_t
211 device_open(const char *name, uint32 flags, void **cookie)
213 struct sis_info *info;
214 area_id area;
215 int id;
217 // verify device access (we only allow one user at a time)
219 char *thisName;
220 int32 mask;
222 // search for device name
223 for (id = 0; (thisName = gDeviceNames[id]) != NULL; id++) {
224 if (!strcmp(name, thisName))
225 break;
227 if (!thisName)
228 return EINVAL;
230 // check if device is already open
231 mask = 1L << id;
232 if (atomic_or(&gDeviceOpenMask, mask) & mask)
233 return B_BUSY;
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;
244 info = (struct sis_info *)*cookie;
245 memset(info, 0, sizeof(struct sis_info));
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];
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");
260 readSettings(info);
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)));
267 //sis900_configFIFOs(info);
268 sis900_reset(info);
270 // install & enable interrupts
271 install_io_interrupt_handler(info->pciInfo->u.h0.interrupt_line,
272 sis900_interrupt, info, 0);
273 sis900_enableInterrupts(info);
275 sis900_setRxFilter(info);
276 sis900_createRings(info);
278 // enable receiver's state machine
279 write32(info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Rx_ENABLE);
281 // check link mode & add timer (once every second)
282 sis900_checkMode(info);
283 add_timer(&info->timer, sis900_timer, 1000000LL, B_PERIODIC_TIMER);
285 return B_OK;
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");
292 gDeviceOpenMask &= ~(1L << id);
293 delete_area(area);
295 return B_ERROR;
299 status_t
300 device_close(void *data)
302 struct sis_info *info;
304 if (checkDeviceInfo(info = data) != B_OK)
305 return EINVAL;
307 info->cookieMagic = SiS_FREE_COOKIE_MAGIC;
309 // cancel timer
310 cancel_timer(&info->timer);
312 // remove & disable interrupt
313 sis900_disableInterrupts(info);
314 remove_io_interrupt_handler(info->pciInfo->u.h0.interrupt_line, sis900_interrupt, info);
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);
320 delete_sem(info->rxSem);
321 delete_sem(info->txSem);
323 #ifdef EXCESSIVE_DEBUG
324 delete_sem(gIOLock);
325 #endif
327 return B_OK;
331 status_t
332 device_free(void *data)
334 struct sis_info *info = data;
335 status_t retval = B_NO_ERROR;
337 if (info == NULL || info->cookieMagic != SiS_FREE_COOKIE_MAGIC)
338 retval = EINVAL;
340 gDeviceOpenMask &= ~(1L << info->id);
342 sis900_deleteRings(info);
343 delete_area(info->thisArea);
345 return retval;
349 status_t
350 device_ioctl(void *data, uint32 msg, void *buffer, size_t bufferLength)
352 struct sis_info *info;
354 if (checkDeviceInfo(info = data) != B_OK)
355 return EINVAL;
357 switch (msg) {
358 case ETHER_GETADDR:
359 TRACE(("ioctl: get MAC address\n"));
360 memcpy(buffer, &info->address, 6);
361 return B_OK;
363 case ETHER_INIT:
364 TRACE(("ioctl: init\n"));
365 return B_OK;
367 case ETHER_GETFRAMESIZE:
368 TRACE(("ioctl: get frame size\n"));
369 *(uint32*)buffer = MAX_FRAME_SIZE;
370 return B_OK;
372 case ETHER_SETPROMISC:
373 TRACE(("ioctl: set promisc\n"));
374 sis900_setPromiscuous(info, *(uint32 *)buffer != 0);
375 return B_OK;
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;
382 case ETHER_ADDMULTI:
383 TRACE(("ioctl: add multicast\n"));
384 /* not yet implemented */
385 break;
387 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
388 case ETHER_GET_LINK_STATE:
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;
398 return user_memcpy(buffer, &state, sizeof(ether_link_state_t));
400 #endif
402 default:
403 TRACE(("ioctl: unknown message %lu (length = %ld)\n", msg, bufferLength));
404 break;
406 return B_ERROR;
410 #ifdef DEBUG
411 // sis900.c
412 extern int32 intrCounter;
413 extern int32 lastIntr[100];
414 uint32 counter = 0;
415 #endif
418 status_t
419 device_read(void *data, off_t pos, void *buffer, size_t *_length)
421 struct sis_info *info;
422 status_t status;
423 size_t size;
424 int32 blockFlag;
425 uint32 check;
426 int16 current;
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;
437 blockFlag = info->blockFlag;
439 TRACE(("read: rx: isr = %d, free = %d, current = %d, blockFlag = %lx\n",
440 info->rxInterruptIndex, info->rxFree, info->rxCurrent, blockFlag));
442 // read is not reentrant
443 if (atomic_or(&info->rxLock, 1)) {
444 #ifndef __HAIKU__
445 *_length = 0;
446 #endif
447 return B_ERROR;
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;
460 /* three cases, frame is good, bad, or we don't own the descriptor */
461 current = info->rxCurrent;
462 check = info->rxDescriptor[current].status;
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;
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;
488 memcpy(buffer, (void *)info->rxBuffer[current], size);
490 info->rxCurrent = (current + 1) & NUM_Rx_MASK;
492 /* update indexes and buffer ownership */
494 cpu_status former;
495 former = disable_interrupts();
496 acquire_spinlock(&info->rxSpinlock);
498 // release buffer to ring
499 info->rxDescriptor[current].status = MAX_FRAME_SIZE + CRC_SIZE;
500 info->rxFree++;
502 release_spinlock(&info->rxSpinlock);
503 restore_interrupts(former);
506 atomic_and(&info->rxLock, 0);
507 return B_OK;
511 status_t
512 device_write(void *data, off_t pos, const void *buffer, size_t *_length)
514 struct sis_info *info;
515 status_t status;
516 uint16 frameSize;
517 int16 current;
518 uint32 check;
520 if (checkDeviceInfo(info = data) != B_OK)
521 return EINVAL;
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);
526 if (*_length > MAX_FRAME_SIZE)
527 *_length = MAX_FRAME_SIZE;
529 frameSize = *_length;
530 current = info->txCurrent;
532 //dprintf("\t%5ld: \twrite: tx: isr = %d, sent = %d, current = %d\n",counter++,
533 // info->txInterruptIndex,info->txSent,info->txCurrent);
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;
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;
551 /* Copy data to tx buffer */
552 memcpy((void *)info->txBuffer[current], buffer, frameSize);
553 info->txCurrent = (current + 1) & NUM_Tx_MASK;
556 cpu_status former;
557 former = disable_interrupts();
558 acquire_spinlock(&info->txSpinlock);
560 info->txDescriptor[current].status = SiS900_DESCR_OWN | frameSize;
561 info->txSent++;
563 #if 0
565 struct buffer_desc *b = (void *)read32(info->registers + SiS900_MAC_Tx_DESCR);
566 int16 that;
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));
571 for (that = 0;that < NUM_Tx_DESCR && (void *)physicalAddress(&info->txDescriptor[that],sizeof(struct buffer_desc)) != b;that++);
573 if (that == NUM_Tx_DESCR) {
574 //dprintf("not in ring!\n");
575 that = 0;
577 dprintf("(hardware status %d = %lx)!\n", that, info->txDescriptor[that].status);
579 #endif
580 release_spinlock(&info->txSpinlock);
581 restore_interrupts(former);
584 // enable transmit state machine
585 write32(info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Tx_ENABLE);
587 #ifdef EXCESSIVE_DEBUG
588 acquire_sem(gIOLock);
589 bug("\t\twrite last interrupts:\n");
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]);
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
600 atomic_add(&info->txLock, -1);
601 return B_OK;