2 * Copyright (c) 2001-2007 pinc Software. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
6 /*! Device hooks for SiS 900 networking */
11 #include "interface.h"
14 #include <directories.h>
15 #include <driver_settings.h>
19 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
20 # include <net/if_media.h>
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
= {
51 //#define EXCESSIVE_DEBUG
52 //#define THE_BUSY_WAITING_WAY
54 #ifdef EXCESSIVE_DEBUG
60 bug(const char *format
, ...)
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));
73 i
= vsprintf(c
, format
, vl
);
76 write(file
, c
, strlen(c
));
83 #define DUMPED_BLOCK_SIZE 16
86 dumpBlock(const char *buffer
, int size
, const char *prefix
)
90 for (i
= 0; i
< size
;)
95 for (; i
< start
+DUMPED_BLOCK_SIZE
; i
++)
103 bug("%02x", *(unsigned char *)(buffer
+ i
));
107 for (i
= start
; i
< start
+ DUMPED_BLOCK_SIZE
; i
++)
125 #endif /* EXCESSIVE_DEBUG */
130 checkDeviceInfo(struct sis_info
*info
)
132 if (!info
|| info
->cookieMagic
!= SiS_COOKIE_MAGIC
)
140 deleteSemaphores(struct sis_info
*info
)
146 createSemaphores(struct sis_info
*info
)
148 if ((info
->rxSem
= create_sem(0, "sis900 receive")) < B_OK
)
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
);
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
)
163 set_sem_owner(gIOLock
, B_SYSTEM_TEAM
);
166 info
->rxLock
= info
->txLock
= 0;
173 readSettings(struct sis_info
*info
)
175 const char *parameter
;
177 void *handle
= load_driver_settings("sis900");
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)
201 unload_driver_settings(handle
);
205 //--------------------------------------------------------------------------
207 // the device will be accessed through the following functions (a.k.a. device hooks)
211 device_open(const char *name
, uint32 flags
, void **cookie
)
213 struct sis_info
*info
;
217 // verify device access (we only allow one user at a time)
222 // search for device name
223 for (id
= 0; (thisName
= gDeviceNames
[id
]) != NULL
; id
++) {
224 if (!strcmp(name
, thisName
))
230 // check if device is already open
232 if (atomic_or(&gDeviceOpenMask
, mask
) & mask
)
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
);
244 info
= (struct sis_info
*)*cookie
;
245 memset(info
, 0, sizeof(struct sis_info
));
247 info
->cookieMagic
= SiS_COOKIE_MAGIC
;
248 info
->thisArea
= area
;
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]);
258 dprintf(DEVICE_NAME
": could not get MAC address\n");
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);
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
);
287 dprintf(DEVICE_NAME
": could not initialize MII PHY: %s\n", strerror(status
));
288 deleteSemaphores(info
);
290 dprintf(DEVICE_NAME
": could not create semaphores.\n");
292 gDeviceOpenMask
&= ~(1L << id
);
300 device_close(void *data
)
302 struct sis_info
*info
;
304 if (checkDeviceInfo(info
= data
) != B_OK
)
307 info
->cookieMagic
= SiS_FREE_COOKIE_MAGIC
;
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
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
)
340 gDeviceOpenMask
&= ~(1L << info
->id
);
342 sis900_deleteRings(info
);
343 delete_area(info
->thisArea
);
350 device_ioctl(void *data
, uint32 msg
, void *buffer
, size_t bufferLength
)
352 struct sis_info
*info
;
354 if (checkDeviceInfo(info
= data
) != B_OK
)
359 TRACE(("ioctl: get MAC address\n"));
360 memcpy(buffer
, &info
->address
, 6);
364 TRACE(("ioctl: init\n"));
367 case ETHER_GETFRAMESIZE
:
368 TRACE(("ioctl: get frame size\n"));
369 *(uint32
*)buffer
= MAX_FRAME_SIZE
;
372 case ETHER_SETPROMISC
:
373 TRACE(("ioctl: set promisc\n"));
374 sis900_setPromiscuous(info
, *(uint32
*)buffer
!= 0);
378 info
->blockFlag
= *(int32
*)buffer
? B_TIMEOUT
: 0;
379 TRACE(("ioctl: non blocking ? %s\n", info
->blockFlag
? "yes" : "no"));
383 TRACE(("ioctl: add multicast\n"));
384 /* not yet implemented */
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
));
403 TRACE(("ioctl: unknown message %lu (length = %ld)\n", msg
, bufferLength
));
412 extern int32 intrCounter
;
413 extern int32 lastIntr
[100];
419 device_read(void *data
, off_t pos
, void *buffer
, size_t *_length
)
421 struct sis_info
*info
;
428 if (checkDeviceInfo(info
= data
) != B_OK
) {
431 // net_server work-around; it obviously doesn't care about error conditions
432 // For Haiku, this can be removed
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)) {
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);
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);
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
));
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
));
485 } else if (size
< *_length
)
488 memcpy(buffer
, (void *)info
->rxBuffer
[current
], size
);
490 info
->rxCurrent
= (current
+ 1) & NUM_Rx_MASK
;
492 /* update indexes and buffer ownership */
495 former
= disable_interrupts();
496 acquire_spinlock(&info
->rxSpinlock
);
498 // release buffer to ring
499 info
->rxDescriptor
[current
].status
= MAX_FRAME_SIZE
+ CRC_SIZE
;
502 release_spinlock(&info
->rxSpinlock
);
503 restore_interrupts(former
);
506 atomic_and(&info
->rxLock
, 0);
512 device_write(void *data
, off_t pos
, const void *buffer
, size_t *_length
)
514 struct sis_info
*info
;
520 if (checkDeviceInfo(info
= data
) != B_OK
)
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);
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);
551 /* Copy data to tx buffer */
552 memcpy((void *)info
->txBuffer
[current
], buffer
, frameSize
);
553 info
->txCurrent
= (current
+ 1) & NUM_Tx_MASK
;
557 former
= disable_interrupts();
558 acquire_spinlock(&info
->txSpinlock
);
560 info
->txDescriptor
[current
].status
= SiS900_DESCR_OWN
| frameSize
;
565 struct buffer_desc
*b
= (void *)read32(info
->registers
+ SiS900_MAC_Tx_DESCR
);
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");
577 dprintf("(hardware status %d = %lx)!\n", that
, info
->txDescriptor
[that
].status
);
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");
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
);
600 atomic_add(&info
->txLock
, -1);