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