1/* 2 * Copyright 2006-2009, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 */ 8 9 10#include <ethernet.h> 11 12#include <ether_driver.h> 13#include <net_buffer.h> 14#include <net_device.h> 15#include <net_stack.h> 16 17#include <lock.h> 18#include <util/AutoLock.h> 19#include <util/DoublyLinkedList.h> 20 21#include <KernelExport.h> 22 23#include <errno.h> 24#include <net/if.h> 25#include <net/if_dl.h> 26#include <net/if_media.h> 27#include <net/if_types.h> 28#include <new> 29#include <stdlib.h> 30#include <string.h> 31 32 33struct ethernet_device : net_device, DoublyLinkedListLinkImpl<ethernet_device> { 34 int fd; 35 uint32 frame_size; 36}; 37 38static const bigtime_t kLinkCheckInterval = 1000000; 39 // 1 second 40 41net_buffer_module_info *gBufferModule; 42static net_stack_module_info *sStackModule; 43 44static mutex sListLock; 45static DoublyLinkedList<ethernet_device> sCheckList; 46static sem_id sLinkChangeSemaphore; 47static thread_id sLinkCheckerThread; 48 49 50static status_t 51update_link_state(ethernet_device *device, bool notify = true) 52{ 53 ether_link_state state; 54 if (ioctl(device->fd, ETHER_GET_LINK_STATE, &state, 55 sizeof(ether_link_state)) < 0) { 56 // This device does not support retrieving the link 57 return B_NOT_SUPPORTED; 58 } 59 60 state.media |= IFM_ETHER; 61 // make sure the media type is returned correctly 62 63 if (device->media != state.media 64 || device->link_quality != state.quality 65 || device->link_speed != state.speed) { 66 device->media = state.media; 67 device->link_quality = state.quality; 68 device->link_speed = state.speed; 69 70 if (device->media & IFM_ACTIVE) 71 device->flags |= IFF_LINK; 72 else 73 device->flags &= ~IFF_LINK; 74 75 dprintf("%s: media change, media 0x%0x quality %u speed %u\n", 76 device->name, (unsigned int)device->media, 77 (unsigned int)device->link_quality, 78 (unsigned int)device->link_speed); 79 80 if (notify) 81 sStackModule->device_link_changed(device); 82 } 83 84 return B_OK; 85} 86 87 88static status_t 89ethernet_link_checker(void *) 90{ 91 while (true) { 92 status_t status = acquire_sem_etc(sLinkChangeSemaphore, 1, 93 B_RELATIVE_TIMEOUT, kLinkCheckInterval); 94 if (status == B_BAD_SEM_ID) 95 break; 96 97 MutexLocker _(sListLock); 98 99 if (sCheckList.IsEmpty()) 100 break; 101 102 // check link state of all existing devices 103 104 DoublyLinkedList<ethernet_device>::Iterator iterator 105 = sCheckList.GetIterator(); 106 while (iterator.HasNext()) { 107 update_link_state(iterator.Next()); 108 } 109 } 110 111 return B_OK; 112} 113 114 115// #pragma mark - 116 117 118status_t 119ethernet_init(const char *name, net_device **_device) 120{ 121 // make sure this is a device in /dev/net, but not the 122 // networking (userland) stack driver 123 if (strncmp(name, "/dev/net/", 9) 124 || !strcmp(name, "/dev/net/userland_server")) 125 return B_BAD_VALUE; 126 127 status_t status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule); 128 if (status < B_OK) 129 return status; 130 131 ethernet_device *device = new (std::nothrow) ethernet_device; 132 if (device == NULL) { 133 put_module(NET_BUFFER_MODULE_NAME); 134 return B_NO_MEMORY; 135 } 136 137 memset(device, 0, sizeof(ethernet_device)); 138 139 strcpy(device->name, name); 140 device->flags = IFF_BROADCAST | IFF_LINK; 141 device->type = IFT_ETHER; 142 device->mtu = 1500; 143 device->media = IFM_ACTIVE | IFM_ETHER; 144 device->header_length = ETHER_HEADER_LENGTH; 145 device->fd = -1; 146 147 *_device = device; 148 return B_OK; 149} 150 151 152status_t 153ethernet_uninit(net_device *device) 154{ 155 put_module(NET_BUFFER_MODULE_NAME); 156 delete device; 157 158 return B_OK; 159} 160 161 162status_t 163ethernet_up(net_device *_device) 164{ 165 ethernet_device *device = (ethernet_device *)_device; 166 167 device->fd = open(device->name, O_RDWR); 168 if (device->fd < 0) 169 return errno; 170 171 uint64 dummy; 172 if (ioctl(device->fd, ETHER_INIT, &dummy, sizeof(dummy)) < 0) 173 goto err; 174 175 if (ioctl(device->fd, ETHER_GETADDR, device->address.data, ETHER_ADDRESS_LENGTH) < 0) 176 goto err; 177 178 if (ioctl(device->fd, ETHER_GETFRAMESIZE, &device->frame_size, sizeof(uint32)) < 0) { 179 // this call is obviously optional 180 device->frame_size = ETHER_MAX_FRAME_SIZE; 181 } 182 183 if (update_link_state(device, false) == B_OK) { 184 // device supports retrieval of the link state 185 186 // Set the change notification semaphore; doesn't matter 187 // if this is supported by the device or not 188 ioctl(device->fd, ETHER_SET_LINK_STATE_SEM, &sLinkChangeSemaphore, 189 sizeof(sem_id)); 190 191 MutexLocker _(&sListLock); 192 193 if (sCheckList.IsEmpty()) { 194 // start thread 195 sLinkCheckerThread = spawn_kernel_thread(ethernet_link_checker, 196 "ethernet link state checker", B_LOW_PRIORITY, NULL); 197 if (sLinkCheckerThread >= B_OK) 198 resume_thread(sLinkCheckerThread); 199 } 200 201 sCheckList.Add(device); 202 } 203 204 device->address.length = ETHER_ADDRESS_LENGTH; 205 device->mtu = device->frame_size - device->header_length; 206 return B_OK; 207 208err: 209 close(device->fd); 210 device->fd = -1; 211 return errno; 212} 213 214 215void 216ethernet_down(net_device *_device) 217{ 218 ethernet_device *device = (ethernet_device *)_device; 219 220 MutexLocker _(sListLock); 221 222 // if the device is still part of the list, remove it 223 if (device->GetDoublyLinkedListLink()->next != NULL 224 || device->GetDoublyLinkedListLink()->previous != NULL 225 || device == sCheckList.Head()) 226 sCheckList.Remove(device); 227 228 close(device->fd); 229 device->fd = -1; 230} 231 232 233status_t 234ethernet_control(net_device *_device, int32 op, void *argument, 235 size_t length) 236{ 237 ethernet_device *device = (ethernet_device *)_device; 238 return ioctl(device->fd, op, argument, length); 239} 240 241 242status_t 243ethernet_send_data(net_device *_device, net_buffer *buffer) 244{ 245 ethernet_device *device = (ethernet_device *)_device; 246 247//dprintf("try to send ethernet packet of %lu bytes (flags %ld):\n", buffer->size, buffer->flags); 248 if (buffer->size > device->frame_size || buffer->size < ETHER_HEADER_LENGTH) 249 return B_BAD_VALUE; 250 251 net_buffer *allocated = NULL; 252 net_buffer *original = buffer; 253 254 if (gBufferModule->count_iovecs(buffer) > 1) { 255 // TODO: for now, create a new buffer containing the data 256 buffer = gBufferModule->duplicate(original); 257 if (buffer == NULL) 258 return ENOBUFS; 259 260 allocated = buffer; 261 262 if (gBufferModule->count_iovecs(buffer) > 1) { 263 dprintf("scattered I/O is not yet supported by ethernet device.\n"); 264 gBufferModule->free(buffer); 265 device->stats.send.errors++; 266 return B_NOT_SUPPORTED; 267 } 268 } 269 270 struct iovec iovec; 271 gBufferModule->get_iovecs(buffer, &iovec, 1); 272 273//dump_block((const char *)iovec.iov_base, buffer->size, " "); 274 ssize_t bytesWritten = write(device->fd, iovec.iov_base, iovec.iov_len); 275//dprintf("sent: %ld\n", bytesWritten); 276 277 if (bytesWritten < 0) { 278 device->stats.send.errors++; 279 if (allocated) 280 gBufferModule->free(allocated); 281 return errno; 282 } 283 284 device->stats.send.packets++; 285 device->stats.send.bytes += bytesWritten; 286 287 gBufferModule->free(original); 288 if (allocated) 289 gBufferModule->free(allocated); 290 return B_OK; 291} 292 293 294status_t 295ethernet_receive_data(net_device *_device, net_buffer **_buffer) 296{ 297 ethernet_device *device = (ethernet_device *)_device; 298 299 if (device->fd == -1) 300 return B_FILE_ERROR; 301 302 // TODO: better header space 303 net_buffer *buffer = gBufferModule->create(256); 304 if (buffer == NULL) 305 return ENOBUFS; 306 307 // TODO: this only works for standard ethernet frames - we need iovecs 308 // for jumbo frame support (or a separate read buffer)! 309 // It would be even nicer to get net_buffers from the ethernet driver 310 // directly. 311 312 ssize_t bytesRead; 313 void *data; 314 315 status_t status = gBufferModule->append_size(buffer, device->frame_size, &data); 316 if (status == B_OK && data == NULL) { 317 dprintf("scattered I/O is not yet supported by ethernet device.\n"); 318 status = B_NOT_SUPPORTED; 319 } 320 if (status < B_OK) 321 goto err; 322 323 bytesRead = read(device->fd, data, device->frame_size); 324 if (bytesRead < 0) { 325 device->stats.receive.errors++; 326 status = errno; 327 goto err; 328 } 329//dump_block((const char *)data, bytesRead, "rcv: "); 330 331 status = gBufferModule->trim(buffer, bytesRead); 332 if (status < B_OK) { 333 device->stats.receive.dropped++; 334 goto err; 335 } 336 337 device->stats.receive.bytes += bytesRead; 338 device->stats.receive.packets++; 339 340 *_buffer = buffer; 341 return B_OK; 342 343err: 344 gBufferModule->free(buffer); 345 return status; 346} 347 348 349status_t 350ethernet_set_mtu(net_device *_device, size_t mtu) 351{ 352 ethernet_device *device = (ethernet_device *)_device; 353 354 if (mtu > device->frame_size - ETHER_HEADER_LENGTH 355 || mtu <= ETHER_HEADER_LENGTH + 10) 356 return B_BAD_VALUE; 357 358 device->mtu = mtu; 359 return B_OK; 360} 361 362 363status_t 364ethernet_set_promiscuous(net_device *_device, bool promiscuous) 365{ 366 ethernet_device *device = (ethernet_device *)_device; 367 368 int32 value = (int32)promiscuous; 369 if (ioctl(device->fd, ETHER_SETPROMISC, &value, sizeof(value)) < 0) 370 return B_NOT_SUPPORTED; 371 372 return B_OK; 373} 374 375 376status_t 377ethernet_set_media(net_device *device, uint32 media) 378{ 379 return B_NOT_SUPPORTED; 380} 381 382 383status_t 384ethernet_add_multicast(struct net_device *_device, const sockaddr *_address) 385{ 386 ethernet_device *device = (ethernet_device *)_device; 387 388 if (_address->sa_family != AF_LINK) 389 return B_BAD_VALUE; 390 391 const sockaddr_dl *address = (const sockaddr_dl *)_address; 392 if (address->sdl_type != IFT_ETHER) 393 return B_BAD_VALUE; 394 395 return ioctl(device->fd, ETHER_ADDMULTI, LLADDR(address), 6); 396} 397 398 399status_t 400ethernet_remove_multicast(struct net_device *_device, const sockaddr *_address) 401{ 402 ethernet_device *device = (ethernet_device *)_device; 403 404 if (_address->sa_family != AF_LINK) 405 return B_BAD_VALUE; 406 407 const sockaddr_dl *address = (const sockaddr_dl *)_address; 408 if (address->sdl_type != IFT_ETHER) 409 return B_BAD_VALUE; 410 411 return ioctl(device->fd, ETHER_REMMULTI, LLADDR(address), 6); 412} 413 414 415static status_t 416ethernet_std_ops(int32 op, ...) 417{ 418 switch (op) { 419 case B_MODULE_INIT: 420 { 421 status_t status = get_module(NET_STACK_MODULE_NAME, 422 (module_info **)&sStackModule); 423 if (status < B_OK) 424 return status; 425 426 new (&sCheckList) DoublyLinkedList<ethernet_device>; 427 // static C++ objects are not initialized in the module startup 428 429 sLinkCheckerThread = -1; 430 431 sLinkChangeSemaphore = create_sem(0, "ethernet link change"); 432 if (sLinkChangeSemaphore < B_OK) { 433 put_module(NET_STACK_MODULE_NAME); 434 return sLinkChangeSemaphore; 435 } 436 437 mutex_init(&sListLock, "ethernet devices"); 438 439 return B_OK; 440 } 441 442 case B_MODULE_UNINIT: 443 { 444 delete_sem(sLinkChangeSemaphore); 445 446 status_t status; 447 wait_for_thread(sLinkCheckerThread, &status); 448 449 mutex_destroy(&sListLock); 450 put_module(NET_STACK_MODULE_NAME); 451 return B_OK; 452 } 453 454 default: 455 return B_ERROR; 456 } 457} 458 459 460net_device_module_info sEthernetModule = { 461 { 462 "network/devices/ethernet/v1", 463 0, 464 ethernet_std_ops 465 }, 466 ethernet_init, 467 ethernet_uninit, 468 ethernet_up, 469 ethernet_down, 470 ethernet_control, 471 ethernet_send_data, 472 ethernet_receive_data, 473 ethernet_set_mtu, 474 ethernet_set_promiscuous, 475 ethernet_set_media, 476 ethernet_add_multicast, 477 ethernet_remove_multicast, 478}; 479 480module_info *modules[] = { 481 (module_info *)&sEthernetModule, 482 NULL 483}; 484