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