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