2 * Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
12 #include <ether_driver.h>
13 #include <net_buffer.h>
14 #include <net_device.h>
15 #include <net_stack.h>
18 #include <util/AutoLock.h>
19 #include <util/DoublyLinkedList.h>
21 #include <KernelExport.h>
25 #include <net/if_dl.h>
26 #include <net/if_media.h>
27 #include <net/if_types.h>
33 struct ethernet_device
: net_device
, DoublyLinkedListLinkImpl
<ethernet_device
> {
38 static const bigtime_t kLinkCheckInterval
= 1000000;
41 net_buffer_module_info
*gBufferModule
;
42 static net_stack_module_info
*sStackModule
;
44 static mutex sListLock
;
45 static DoublyLinkedList
<ethernet_device
> sCheckList
;
46 static sem_id sLinkChangeSemaphore
;
47 static thread_id sLinkCheckerThread
;
51 update_link_state(ethernet_device
*device
, bool notify
= true)
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
;
60 state
.media
|= IFM_ETHER
;
61 // make sure the media type is returned correctly
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
;
70 if (device
->media
& IFM_ACTIVE
)
71 device
->flags
|= IFF_LINK
;
73 device
->flags
&= ~IFF_LINK
;
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
);
81 sStackModule
->device_link_changed(device
);
89 ethernet_link_checker(void *)
92 status_t status
= acquire_sem_etc(sLinkChangeSemaphore
, 1,
93 B_RELATIVE_TIMEOUT
, kLinkCheckInterval
);
94 if (status
== B_BAD_SEM_ID
)
97 MutexLocker
_(sListLock
);
99 if (sCheckList
.IsEmpty())
102 // check link state of all existing devices
104 DoublyLinkedList
<ethernet_device
>::Iterator iterator
105 = sCheckList
.GetIterator();
106 while (iterator
.HasNext()) {
107 update_link_state(iterator
.Next());
119 ethernet_init(const char *name
, net_device
**_device
)
121 // Make sure this is a device in /dev/net, but not the
122 // networking (userland) stack driver.
123 // Also make sure the user didn't pass a path like
125 if (strncmp(name
, "/dev/net/", 9)
126 || !strcmp(name
, "/dev/net/userland_server")
127 || strstr(name
, "..") != NULL
)
130 if (access(name
, F_OK
) != 0)
133 status_t status
= get_module(NET_BUFFER_MODULE_NAME
, (module_info
**)&gBufferModule
);
137 ethernet_device
*device
= new (std::nothrow
) ethernet_device
;
138 if (device
== NULL
) {
139 put_module(NET_BUFFER_MODULE_NAME
);
143 memset(device
, 0, sizeof(ethernet_device
));
145 strcpy(device
->name
, name
);
146 device
->flags
= IFF_BROADCAST
| IFF_LINK
;
147 device
->type
= IFT_ETHER
;
149 device
->media
= IFM_ACTIVE
| IFM_ETHER
;
150 device
->header_length
= ETHER_HEADER_LENGTH
;
159 ethernet_uninit(net_device
*device
)
161 put_module(NET_BUFFER_MODULE_NAME
);
169 ethernet_up(net_device
*_device
)
171 ethernet_device
*device
= (ethernet_device
*)_device
;
173 device
->fd
= open(device
->name
, O_RDWR
);
178 if (ioctl(device
->fd
, ETHER_INIT
, &dummy
, sizeof(dummy
)) < 0)
181 if (ioctl(device
->fd
, ETHER_GETADDR
, device
->address
.data
, ETHER_ADDRESS_LENGTH
) < 0)
184 if (ioctl(device
->fd
, ETHER_GETFRAMESIZE
, &device
->frame_size
, sizeof(uint32
)) < 0) {
185 // this call is obviously optional
186 device
->frame_size
= ETHER_MAX_FRAME_SIZE
;
189 if (update_link_state(device
, false) == B_OK
) {
190 // device supports retrieval of the link state
192 // Set the change notification semaphore; doesn't matter
193 // if this is supported by the device or not
194 ioctl(device
->fd
, ETHER_SET_LINK_STATE_SEM
, &sLinkChangeSemaphore
,
197 MutexLocker
_(&sListLock
);
199 if (sCheckList
.IsEmpty()) {
201 sLinkCheckerThread
= spawn_kernel_thread(ethernet_link_checker
,
202 "ethernet link state checker", B_LOW_PRIORITY
, NULL
);
203 if (sLinkCheckerThread
>= B_OK
)
204 resume_thread(sLinkCheckerThread
);
207 sCheckList
.Add(device
);
210 device
->address
.length
= ETHER_ADDRESS_LENGTH
;
211 device
->mtu
= device
->frame_size
- device
->header_length
;
222 ethernet_down(net_device
*_device
)
224 ethernet_device
*device
= (ethernet_device
*)_device
;
226 MutexLocker
_(sListLock
);
228 // if the device is still part of the list, remove it
229 if (device
->GetDoublyLinkedListLink()->next
!= NULL
230 || device
->GetDoublyLinkedListLink()->previous
!= NULL
231 || device
== sCheckList
.Head())
232 sCheckList
.Remove(device
);
240 ethernet_control(net_device
*_device
, int32 op
, void *argument
,
243 ethernet_device
*device
= (ethernet_device
*)_device
;
244 return ioctl(device
->fd
, op
, argument
, length
);
249 ethernet_send_data(net_device
*_device
, net_buffer
*buffer
)
251 ethernet_device
*device
= (ethernet_device
*)_device
;
253 //dprintf("try to send ethernet packet of %lu bytes (flags %ld):\n", buffer->size, buffer->flags);
254 if (buffer
->size
> device
->frame_size
|| buffer
->size
< ETHER_HEADER_LENGTH
)
257 net_buffer
*allocated
= NULL
;
258 net_buffer
*original
= buffer
;
260 if (gBufferModule
->count_iovecs(buffer
) > 1) {
261 // TODO: for now, create a new buffer containing the data
262 buffer
= gBufferModule
->duplicate(original
);
268 if (gBufferModule
->count_iovecs(buffer
) > 1) {
269 dprintf("scattered I/O is not yet supported by ethernet device.\n");
270 gBufferModule
->free(buffer
);
271 device
->stats
.send
.errors
++;
272 return B_NOT_SUPPORTED
;
277 gBufferModule
->get_iovecs(buffer
, &iovec
, 1);
279 //dump_block((const char *)iovec.iov_base, buffer->size, " ");
280 ssize_t bytesWritten
= write(device
->fd
, iovec
.iov_base
, iovec
.iov_len
);
281 //dprintf("sent: %ld\n", bytesWritten);
283 if (bytesWritten
< 0) {
284 device
->stats
.send
.errors
++;
286 gBufferModule
->free(allocated
);
290 device
->stats
.send
.packets
++;
291 device
->stats
.send
.bytes
+= bytesWritten
;
293 gBufferModule
->free(original
);
295 gBufferModule
->free(allocated
);
301 ethernet_receive_data(net_device
*_device
, net_buffer
**_buffer
)
303 ethernet_device
*device
= (ethernet_device
*)_device
;
305 if (device
->fd
== -1)
308 // TODO: better header space
309 net_buffer
*buffer
= gBufferModule
->create(256);
313 // TODO: this only works for standard ethernet frames - we need iovecs
314 // for jumbo frame support (or a separate read buffer)!
315 // It would be even nicer to get net_buffers from the ethernet driver
321 status_t status
= gBufferModule
->append_size(buffer
, device
->frame_size
, &data
);
322 if (status
== B_OK
&& data
== NULL
) {
323 dprintf("scattered I/O is not yet supported by ethernet device.\n");
324 status
= B_NOT_SUPPORTED
;
329 bytesRead
= read(device
->fd
, data
, device
->frame_size
);
331 device
->stats
.receive
.errors
++;
335 //dump_block((const char *)data, bytesRead, "rcv: ");
337 status
= gBufferModule
->trim(buffer
, bytesRead
);
339 device
->stats
.receive
.dropped
++;
343 device
->stats
.receive
.bytes
+= bytesRead
;
344 device
->stats
.receive
.packets
++;
350 gBufferModule
->free(buffer
);
356 ethernet_set_mtu(net_device
*_device
, size_t mtu
)
358 ethernet_device
*device
= (ethernet_device
*)_device
;
360 if (mtu
> device
->frame_size
- ETHER_HEADER_LENGTH
361 || mtu
<= ETHER_HEADER_LENGTH
+ 10)
370 ethernet_set_promiscuous(net_device
*_device
, bool promiscuous
)
372 ethernet_device
*device
= (ethernet_device
*)_device
;
374 int32 value
= (int32
)promiscuous
;
375 if (ioctl(device
->fd
, ETHER_SETPROMISC
, &value
, sizeof(value
)) < 0)
376 return B_NOT_SUPPORTED
;
383 ethernet_set_media(net_device
*device
, uint32 media
)
385 return B_NOT_SUPPORTED
;
390 ethernet_add_multicast(struct net_device
*_device
, const sockaddr
*_address
)
392 ethernet_device
*device
= (ethernet_device
*)_device
;
394 if (_address
->sa_family
!= AF_LINK
)
397 const sockaddr_dl
*address
= (const sockaddr_dl
*)_address
;
398 if (address
->sdl_type
!= IFT_ETHER
)
401 return ioctl(device
->fd
, ETHER_ADDMULTI
, LLADDR(address
), 6);
406 ethernet_remove_multicast(struct net_device
*_device
, const sockaddr
*_address
)
408 ethernet_device
*device
= (ethernet_device
*)_device
;
410 if (_address
->sa_family
!= AF_LINK
)
413 const sockaddr_dl
*address
= (const sockaddr_dl
*)_address
;
414 if (address
->sdl_type
!= IFT_ETHER
)
417 return ioctl(device
->fd
, ETHER_REMMULTI
, LLADDR(address
), 6);
422 ethernet_std_ops(int32 op
, ...)
427 status_t status
= get_module(NET_STACK_MODULE_NAME
,
428 (module_info
**)&sStackModule
);
432 new (&sCheckList
) DoublyLinkedList
<ethernet_device
>;
433 // static C++ objects are not initialized in the module startup
435 sLinkCheckerThread
= -1;
437 sLinkChangeSemaphore
= create_sem(0, "ethernet link change");
438 if (sLinkChangeSemaphore
< B_OK
) {
439 put_module(NET_STACK_MODULE_NAME
);
440 return sLinkChangeSemaphore
;
443 mutex_init(&sListLock
, "ethernet devices");
448 case B_MODULE_UNINIT
:
450 delete_sem(sLinkChangeSemaphore
);
453 wait_for_thread(sLinkCheckerThread
, &status
);
455 mutex_destroy(&sListLock
);
456 put_module(NET_STACK_MODULE_NAME
);
466 net_device_module_info sEthernetModule
= {
468 "network/devices/ethernet/v1",
478 ethernet_receive_data
,
480 ethernet_set_promiscuous
,
482 ethernet_add_multicast
,
483 ethernet_remove_multicast
,
486 module_info
*modules
[] = {
487 (module_info
*)&sEthernetModule
,