xref: /haiku/src/system/boot/platform/openfirmware/network.cpp (revision edfce7b32702f506cc35552ce257855a73f9aa54)
1d561d0adSIngo Weinhold /*
2d561d0adSIngo Weinhold  * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
37c5a2487SStephan Aßmus  * Copyright 2010, Andreas Faerber <andreas.faerber@web.de>
49a515368SPulkoMandy  * Copyright 2002, Adrien Destugues <pulkomandy@pulkomandy.tk>
5d561d0adSIngo Weinhold  * All rights reserved. Distributed under the terms of the MIT License.
6d561d0adSIngo Weinhold  */
7d561d0adSIngo Weinhold 
8e0d09e23SAxel Dörfler 
9d561d0adSIngo Weinhold #include <new>
10d561d0adSIngo Weinhold 
11d561d0adSIngo Weinhold #include <stdio.h>
12d561d0adSIngo Weinhold #include <stdlib.h>
13d561d0adSIngo Weinhold #include <string.h>
14d561d0adSIngo Weinhold 
15d561d0adSIngo Weinhold #include <OS.h>
16d561d0adSIngo Weinhold 
17e355ce92SStephan Aßmus #include <boot/platform.h>
18d561d0adSIngo Weinhold #include <boot/net/Ethernet.h>
1963b69becSAxel Dörfler #include <boot/net/IP.h>
20d561d0adSIngo Weinhold #include <boot/net/NetStack.h>
21957a1b17SIngo Weinhold #include <platform/openfirmware/openfirmware.h>
22d561d0adSIngo Weinhold 
23d561d0adSIngo Weinhold 
24d561d0adSIngo Weinhold //#define TRACE_NETWORK
25d561d0adSIngo Weinhold #ifdef TRACE_NETWORK
26d561d0adSIngo Weinhold #	define TRACE(x) dprintf x
27d561d0adSIngo Weinhold #else
28d561d0adSIngo Weinhold #	define TRACE(x) ;
29d561d0adSIngo Weinhold #endif
30d561d0adSIngo Weinhold 
31d561d0adSIngo Weinhold 
32e0d09e23SAxel Dörfler class OFEthernetInterface : public EthernetInterface {
33e0d09e23SAxel Dörfler public:
34e0d09e23SAxel Dörfler 						OFEthernetInterface();
35e0d09e23SAxel Dörfler 	virtual				~OFEthernetInterface();
36e0d09e23SAxel Dörfler 
377c5a2487SStephan Aßmus 			status_t	Init(const char *device, const char *parameters);
38e0d09e23SAxel Dörfler 
39e0d09e23SAxel Dörfler 	virtual mac_addr_t	MACAddress() const;
40e0d09e23SAxel Dörfler 
41e0d09e23SAxel Dörfler 	virtual	void *		AllocateSendReceiveBuffer(size_t size);
42e0d09e23SAxel Dörfler 	virtual	void		FreeSendReceiveBuffer(void *buffer);
43e0d09e23SAxel Dörfler 
44e0d09e23SAxel Dörfler 	virtual ssize_t		Send(const void *buffer, size_t size);
45e0d09e23SAxel Dörfler 	virtual ssize_t		Receive(void *buffer, size_t size);
46e0d09e23SAxel Dörfler 
47e0d09e23SAxel Dörfler private:
489a515368SPulkoMandy 			status_t	FindMACAddress();
499a515368SPulkoMandy 
509a515368SPulkoMandy private:
5195958839SPulkoMandy 			intptr_t	fHandle;
52e0d09e23SAxel Dörfler 			mac_addr_t	fMACAddress;
53e0d09e23SAxel Dörfler };
54e0d09e23SAxel Dörfler 
55e0d09e23SAxel Dörfler 
56d561d0adSIngo Weinhold #ifdef TRACE_NETWORK
57d561d0adSIngo Weinhold 
58d561d0adSIngo Weinhold static void
hex_dump(const void * _data,int length)59d561d0adSIngo Weinhold hex_dump(const void *_data, int length)
60d561d0adSIngo Weinhold {
61d561d0adSIngo Weinhold 	uint8 *data = (uint8*)_data;
62d561d0adSIngo Weinhold 	for (int i = 0; i < length; i++) {
63d561d0adSIngo Weinhold 		if (i % 4 == 0) {
64d561d0adSIngo Weinhold 			if (i % 32 == 0) {
65d561d0adSIngo Weinhold 				if (i != 0)
66d561d0adSIngo Weinhold 					printf("\n");
67d561d0adSIngo Weinhold 				printf("%03x: ", i);
68d561d0adSIngo Weinhold 			} else
69d561d0adSIngo Weinhold 				printf(" ");
70d561d0adSIngo Weinhold 		}
71d561d0adSIngo Weinhold 
72d561d0adSIngo Weinhold 		printf("%02x", data[i]);
73d561d0adSIngo Weinhold 	}
74d561d0adSIngo Weinhold 	printf("\n");
75d561d0adSIngo Weinhold }
76d561d0adSIngo Weinhold 
77d561d0adSIngo Weinhold #else	// !TRACE_NETWORK
78d561d0adSIngo Weinhold 
79d561d0adSIngo Weinhold #define hex_dump(data, length)
80d561d0adSIngo Weinhold 
81d561d0adSIngo Weinhold #endif	// !TRACE_NETWORK
82d561d0adSIngo Weinhold 
83d561d0adSIngo Weinhold 
84e0d09e23SAxel Dörfler // #pragma mark -
85d561d0adSIngo Weinhold 
86d561d0adSIngo Weinhold 
OFEthernetInterface()87d561d0adSIngo Weinhold OFEthernetInterface::OFEthernetInterface()
88e0d09e23SAxel Dörfler 	:
89e0d09e23SAxel Dörfler 	EthernetInterface(),
90d561d0adSIngo Weinhold 	fHandle(OF_FAILED),
91d561d0adSIngo Weinhold 	fMACAddress(kNoMACAddress)
92d561d0adSIngo Weinhold {
93d561d0adSIngo Weinhold }
94d561d0adSIngo Weinhold 
95e0d09e23SAxel Dörfler 
~OFEthernetInterface()96d561d0adSIngo Weinhold OFEthernetInterface::~OFEthernetInterface()
97d561d0adSIngo Weinhold {
98d561d0adSIngo Weinhold 	if (fHandle != OF_FAILED)
99d561d0adSIngo Weinhold 		of_close(fHandle);
100d561d0adSIngo Weinhold }
101d561d0adSIngo Weinhold 
102e0d09e23SAxel Dörfler 
103d561d0adSIngo Weinhold status_t
FindMACAddress()1049a515368SPulkoMandy OFEthernetInterface::FindMACAddress()
1059a515368SPulkoMandy {
1069a515368SPulkoMandy 	intptr_t package = of_instance_to_package(fHandle);
1079a515368SPulkoMandy 
1089a515368SPulkoMandy 	// get MAC address
1099a515368SPulkoMandy 	int bytesRead = of_getprop(package, "local-mac-address", &fMACAddress,
1109a515368SPulkoMandy 		sizeof(fMACAddress));
1119a515368SPulkoMandy 	if (bytesRead == (int)sizeof(fMACAddress))
1129a515368SPulkoMandy 		return B_OK;
1139a515368SPulkoMandy 
1149a515368SPulkoMandy 	// Failed to get the MAC address of the network device. The system may
1159a515368SPulkoMandy 	// have a global standard MAC address.
1169a515368SPulkoMandy 	bytesRead = of_getprop(gChosen, "mac-address", &fMACAddress,
1179a515368SPulkoMandy 		sizeof(fMACAddress));
1189a515368SPulkoMandy 	if (bytesRead == (int)sizeof(fMACAddress)) {
1199a515368SPulkoMandy 		return B_OK;
1209a515368SPulkoMandy 	}
1219a515368SPulkoMandy 
1229a515368SPulkoMandy 	// On Sun machines, there is a global word 'mac-address' which returns
1239a515368SPulkoMandy 	// the size and a pointer to the MAC address
1249a515368SPulkoMandy 	size_t size;
1259a515368SPulkoMandy 	void* ptr;
1269a515368SPulkoMandy 	if (of_interpret("mac-address", 0, 2, &size, &ptr) != OF_FAILED) {
1279a515368SPulkoMandy 		if (size == sizeof(fMACAddress)) {
1289a515368SPulkoMandy 			memcpy(&fMACAddress, ptr, size);
1299a515368SPulkoMandy 			return B_OK;
1309a515368SPulkoMandy 		}
1319a515368SPulkoMandy 	}
1329a515368SPulkoMandy 
1339a515368SPulkoMandy 	return B_ERROR;
1349a515368SPulkoMandy }
1359a515368SPulkoMandy 
1369a515368SPulkoMandy 
1379a515368SPulkoMandy status_t
Init(const char * device,const char * parameters)1387c5a2487SStephan Aßmus OFEthernetInterface::Init(const char *device, const char *parameters)
139d561d0adSIngo Weinhold {
140d561d0adSIngo Weinhold 	if (!device)
141d561d0adSIngo Weinhold 		return B_BAD_VALUE;
142d561d0adSIngo Weinhold 
143d561d0adSIngo Weinhold 	// open device
144d561d0adSIngo Weinhold 	fHandle = of_open(device);
145d561d0adSIngo Weinhold 	if (fHandle == OF_FAILED) {
146d561d0adSIngo Weinhold 		printf("opening ethernet device failed\n");
147d561d0adSIngo Weinhold 		return B_ERROR;
148d561d0adSIngo Weinhold 	}
149d561d0adSIngo Weinhold 
1509a515368SPulkoMandy 	if (FindMACAddress() != B_OK) {
151d561d0adSIngo Weinhold 		printf("Failed to get MAC address\n");
152d561d0adSIngo Weinhold 		return B_ERROR;
153d561d0adSIngo Weinhold 	}
154d561d0adSIngo Weinhold 
155d561d0adSIngo Weinhold 	// get IP address
156e0d09e23SAxel Dörfler 
157d561d0adSIngo Weinhold 	// Note: This is a non-standardized way. On my Mac mini the response of the
158e0d09e23SAxel Dörfler 	// DHCP server is stored as property of /chosen. We try to get it and use
159e0d09e23SAxel Dörfler 	// the IP address we find in there.
1600f33dfccSPulkoMandy 	// TODO Sun machines may use bootp-response instead?
161d561d0adSIngo Weinhold 	struct {
162d561d0adSIngo Weinhold 		uint8	irrelevant[16];
163d561d0adSIngo Weinhold 		uint32	ip_address;
164d561d0adSIngo Weinhold 		// ...
165d561d0adSIngo Weinhold 	} dhcpResponse;
1660f33dfccSPulkoMandy 	int bytesRead = of_getprop(gChosen, "dhcp-response", &dhcpResponse,
167d561d0adSIngo Weinhold 		sizeof(dhcpResponse));
168431b9a31SStephan Aßmus 	if (bytesRead != OF_FAILED && bytesRead == (int)sizeof(dhcpResponse)) {
169d561d0adSIngo Weinhold 		SetIPAddress(ntohl(dhcpResponse.ip_address));
1700f33dfccSPulkoMandy 		return B_OK;
1710f33dfccSPulkoMandy 	}
1720f33dfccSPulkoMandy 
1737c5a2487SStephan Aßmus 	// try to read manual client IP from boot path
1747c5a2487SStephan Aßmus 	if (parameters != NULL) {
1757c5a2487SStephan Aßmus 		char *comma = strrchr(parameters, ',');
1767c5a2487SStephan Aßmus 		if (comma != NULL && comma != strchr(parameters, ',')) {
17763b69becSAxel Dörfler 			SetIPAddress(ip_parse_address(comma + 1));
1780f33dfccSPulkoMandy 			return B_OK;
1797c5a2487SStephan Aßmus 		}
1807c5a2487SStephan Aßmus 	}
1810f33dfccSPulkoMandy 
1827c5a2487SStephan Aßmus 	// try to read default-client-ip setting
1837c5a2487SStephan Aßmus 	char defaultClientIP[16];
1840f33dfccSPulkoMandy 	intptr_t package = of_finddevice("/options");
1857c5a2487SStephan Aßmus 	bytesRead = of_getprop(package, "default-client-ip",
1867c5a2487SStephan Aßmus 		defaultClientIP, sizeof(defaultClientIP) - 1);
1877c5a2487SStephan Aßmus 	if (bytesRead != OF_FAILED && bytesRead > 1) {
1887c5a2487SStephan Aßmus 		defaultClientIP[bytesRead] = '\0';
18963b69becSAxel Dörfler 		ip_addr_t address = ip_parse_address(defaultClientIP);
1907c5a2487SStephan Aßmus 		SetIPAddress(address);
1910f33dfccSPulkoMandy 		return B_OK;
192431b9a31SStephan Aßmus 	}
193d561d0adSIngo Weinhold 
1940f33dfccSPulkoMandy 	return B_ERROR;
195d561d0adSIngo Weinhold }
196d561d0adSIngo Weinhold 
197e0d09e23SAxel Dörfler 
198d561d0adSIngo Weinhold mac_addr_t
MACAddress() const199d561d0adSIngo Weinhold OFEthernetInterface::MACAddress() const
200d561d0adSIngo Weinhold {
201d561d0adSIngo Weinhold 	return fMACAddress;
202d561d0adSIngo Weinhold }
203d561d0adSIngo Weinhold 
204e0d09e23SAxel Dörfler 
205d561d0adSIngo Weinhold void *
AllocateSendReceiveBuffer(size_t size)206d561d0adSIngo Weinhold OFEthernetInterface::AllocateSendReceiveBuffer(size_t size)
207d561d0adSIngo Weinhold {
208*edfce7b3SPulkoMandy 	void *dmaMemory = NULL;
209*edfce7b3SPulkoMandy 
210d561d0adSIngo Weinhold 	if (of_call_method(fHandle, "dma-alloc", 1, 1, size, &dmaMemory)
211*edfce7b3SPulkoMandy 			!= OF_FAILED) {
212d561d0adSIngo Weinhold 		return dmaMemory;
213d561d0adSIngo Weinhold 	}
214d561d0adSIngo Weinhold 
215*edfce7b3SPulkoMandy 	// The dma-alloc method could be on the parent node (PCI bus, for example),
216*edfce7b3SPulkoMandy 	// rather than the device itself
217*edfce7b3SPulkoMandy 	intptr_t parentPackage = of_parent(of_instance_to_package(fHandle));
218*edfce7b3SPulkoMandy 
219*edfce7b3SPulkoMandy 	// FIXME surely there's a way to create an instance without going through
220*edfce7b3SPulkoMandy 	// the path?
221*edfce7b3SPulkoMandy 	char path[256];
222*edfce7b3SPulkoMandy 	of_package_to_path(parentPackage, path, sizeof(path));
223*edfce7b3SPulkoMandy 	intptr_t parentInstance = of_open(path);
224*edfce7b3SPulkoMandy 
225*edfce7b3SPulkoMandy 	if (of_call_method(parentInstance, "dma-alloc", 1, 1, size, &dmaMemory)
226*edfce7b3SPulkoMandy 			!= OF_FAILED) {
227*edfce7b3SPulkoMandy 		of_close(parentInstance);
228*edfce7b3SPulkoMandy 		return dmaMemory;
229*edfce7b3SPulkoMandy 	}
230*edfce7b3SPulkoMandy 
231*edfce7b3SPulkoMandy 	of_close(parentInstance);
232*edfce7b3SPulkoMandy 
233*edfce7b3SPulkoMandy 	return NULL;
234*edfce7b3SPulkoMandy }
235*edfce7b3SPulkoMandy 
236e0d09e23SAxel Dörfler 
237d561d0adSIngo Weinhold void
FreeSendReceiveBuffer(void * buffer)238d561d0adSIngo Weinhold OFEthernetInterface::FreeSendReceiveBuffer(void *buffer)
239d561d0adSIngo Weinhold {
240d561d0adSIngo Weinhold 	if (buffer)
241d561d0adSIngo Weinhold 		of_call_method(fHandle, "dma-free", 1, 0, buffer);
242d561d0adSIngo Weinhold }
243d561d0adSIngo Weinhold 
244e0d09e23SAxel Dörfler 
245d561d0adSIngo Weinhold ssize_t
Send(const void * buffer,size_t size)246d561d0adSIngo Weinhold OFEthernetInterface::Send(const void *buffer, size_t size)
247d561d0adSIngo Weinhold {
248d561d0adSIngo Weinhold 	TRACE(("OFEthernetInterface::Send(%p, %lu)\n", buffer, size));
249d561d0adSIngo Weinhold 
250d561d0adSIngo Weinhold 	if (!buffer)
251d561d0adSIngo Weinhold 		return B_BAD_VALUE;
252d561d0adSIngo Weinhold 
253d561d0adSIngo Weinhold 	hex_dump(buffer, size);
254d561d0adSIngo Weinhold 
255d561d0adSIngo Weinhold 	int result = of_write(fHandle, buffer, size);
256d561d0adSIngo Weinhold 	return (result == OF_FAILED ? B_ERROR : result);
257d561d0adSIngo Weinhold }
258d561d0adSIngo Weinhold 
259e0d09e23SAxel Dörfler 
260d561d0adSIngo Weinhold ssize_t
Receive(void * buffer,size_t size)261d561d0adSIngo Weinhold OFEthernetInterface::Receive(void *buffer, size_t size)
262d561d0adSIngo Weinhold {
263d561d0adSIngo Weinhold 	if (!buffer)
264d561d0adSIngo Weinhold 		return B_BAD_VALUE;
265d561d0adSIngo Weinhold 
266d561d0adSIngo Weinhold 	int result = of_read(fHandle, buffer, size);
267d561d0adSIngo Weinhold 
268d561d0adSIngo Weinhold 	if (result != OF_FAILED && result >= 0) {
269d561d0adSIngo Weinhold 		TRACE(("OFEthernetInterface::Receive(%p, %lu): received %d bytes\n",
270d561d0adSIngo Weinhold 			buffer, size, result));
271d561d0adSIngo Weinhold 		hex_dump(buffer, result);
272d561d0adSIngo Weinhold 	}
273d561d0adSIngo Weinhold 
274d561d0adSIngo Weinhold 	return (result == OF_FAILED ? B_ERROR : result);
275d561d0adSIngo Weinhold }
276d561d0adSIngo Weinhold 
277d561d0adSIngo Weinhold 
278d561d0adSIngo Weinhold // #pragma mark -
279d561d0adSIngo Weinhold 
280e0d09e23SAxel Dörfler 
281d561d0adSIngo Weinhold status_t
platform_net_stack_init()282d561d0adSIngo Weinhold platform_net_stack_init()
283d561d0adSIngo Weinhold {
284d561d0adSIngo Weinhold 	// Note: At the moment we only do networking at all, if the boot device
285d561d0adSIngo Weinhold 	// is a network device. If it isn't, we simply fail here. For serious
286d561d0adSIngo Weinhold 	// support we would want to iterate through the device tree and add all
287d561d0adSIngo Weinhold 	// network devices.
288d561d0adSIngo Weinhold 
289d561d0adSIngo Weinhold 	// get boot path
290d561d0adSIngo Weinhold 	char bootPath[192];
291d561d0adSIngo Weinhold 	int length = of_getprop(gChosen, "bootpath", bootPath, sizeof(bootPath));
292d561d0adSIngo Weinhold 	if (length <= 1)
293d561d0adSIngo Weinhold 		return B_ERROR;
294d561d0adSIngo Weinhold 
295d561d0adSIngo Weinhold 	// we chop off parameters; otherwise opening the network device might have
296d561d0adSIngo Weinhold 	// side effects
297d561d0adSIngo Weinhold 	char *lastComponent = strrchr(bootPath, '/');
298d561d0adSIngo Weinhold 	char *parameters = strchr((lastComponent ? lastComponent : bootPath), ':');
299d561d0adSIngo Weinhold 	if (parameters)
300d561d0adSIngo Weinhold 		*parameters = '\0';
301d561d0adSIngo Weinhold 
302d561d0adSIngo Weinhold 	// get device node
30395958839SPulkoMandy 	intptr_t node = of_finddevice(bootPath);
304d561d0adSIngo Weinhold 	if (node == OF_FAILED)
305d561d0adSIngo Weinhold 		return B_ERROR;
306d561d0adSIngo Weinhold 
307d561d0adSIngo Weinhold 	// get device type
308d561d0adSIngo Weinhold 	char type[16];
309d561d0adSIngo Weinhold 	if (of_getprop(node, "device_type", type, sizeof(type)) == OF_FAILED
310d561d0adSIngo Weinhold 		|| strcmp("network", type) != 0) {
311d561d0adSIngo Weinhold 		return B_ERROR;
312d561d0adSIngo Weinhold 	}
313d561d0adSIngo Weinhold 
314d561d0adSIngo Weinhold 	// create an EthernetInterface object for the device
315d561d0adSIngo Weinhold 	OFEthernetInterface *interface = new(nothrow) OFEthernetInterface;
316d561d0adSIngo Weinhold 	if (!interface)
317d561d0adSIngo Weinhold 		return B_NO_MEMORY;
318d561d0adSIngo Weinhold 
3197c5a2487SStephan Aßmus 	status_t error = interface->Init(bootPath, parameters + 1);
320d561d0adSIngo Weinhold 	if (error != B_OK) {
321d561d0adSIngo Weinhold 		delete interface;
322d561d0adSIngo Weinhold 		return error;
323d561d0adSIngo Weinhold 	}
324d561d0adSIngo Weinhold 
325d561d0adSIngo Weinhold 	// add it to the net stack
326d561d0adSIngo Weinhold 	error = NetStack::Default()->AddEthernetInterface(interface);
327d561d0adSIngo Weinhold 	if (error != B_OK) {
328d561d0adSIngo Weinhold 		delete interface;
329d561d0adSIngo Weinhold 		return error;
330d561d0adSIngo Weinhold 	}
331d561d0adSIngo Weinhold 
332d561d0adSIngo Weinhold 	return B_OK;
333d561d0adSIngo Weinhold }
334