1/*
2 * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * Copyright 2010, Andreas Faerber <andreas.faerber@web.de>
4 * Copyright 2002, Adrien Destugues <pulkomandy@pulkomandy.tk>
5 * All rights reserved. Distributed under the terms of the MIT License.
6 */
7
8
9#include <new>
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15#include <OS.h>
16
17#include <boot/platform.h>
18#include <boot/net/Ethernet.h>
19#include <boot/net/IP.h>
20#include <boot/net/NetStack.h>
21#include <platform/openfirmware/openfirmware.h>
22
23
24//#define TRACE_NETWORK
25#ifdef TRACE_NETWORK
26#	define TRACE(x) dprintf x
27#else
28#	define TRACE(x) ;
29#endif
30
31
32class OFEthernetInterface : public EthernetInterface {
33public:
34						OFEthernetInterface();
35	virtual				~OFEthernetInterface();
36
37			status_t	Init(const char *device, const char *parameters);
38
39	virtual mac_addr_t	MACAddress() const;
40
41	virtual	void *		AllocateSendReceiveBuffer(size_t size);
42	virtual	void		FreeSendReceiveBuffer(void *buffer);
43
44	virtual ssize_t		Send(const void *buffer, size_t size);
45	virtual ssize_t		Receive(void *buffer, size_t size);
46
47private:
48			status_t	FindMACAddress();
49
50private:
51			intptr_t	fHandle;
52			mac_addr_t	fMACAddress;
53};
54
55
56#ifdef TRACE_NETWORK
57
58static void
59hex_dump(const void *_data, int length)
60{
61	uint8 *data = (uint8*)_data;
62	for (int i = 0; i < length; i++) {
63		if (i % 4 == 0) {
64			if (i % 32 == 0) {
65				if (i != 0)
66					printf("\n");
67				printf("%03x: ", i);
68			} else
69				printf(" ");
70		}
71
72		printf("%02x", data[i]);
73	}
74	printf("\n");
75}
76
77#else	// !TRACE_NETWORK
78
79#define hex_dump(data, length)
80
81#endif	// !TRACE_NETWORK
82
83
84// #pragma mark -
85
86
87OFEthernetInterface::OFEthernetInterface()
88	:
89	EthernetInterface(),
90	fHandle(OF_FAILED),
91	fMACAddress(kNoMACAddress)
92{
93}
94
95
96OFEthernetInterface::~OFEthernetInterface()
97{
98	if (fHandle != OF_FAILED)
99		of_close(fHandle);
100}
101
102
103status_t
104OFEthernetInterface::FindMACAddress()
105{
106	intptr_t package = of_instance_to_package(fHandle);
107
108	// get MAC address
109	int bytesRead = of_getprop(package, "local-mac-address", &fMACAddress,
110		sizeof(fMACAddress));
111	if (bytesRead == (int)sizeof(fMACAddress))
112		return B_OK;
113
114	// Failed to get the MAC address of the network device. The system may
115	// have a global standard MAC address.
116	bytesRead = of_getprop(gChosen, "mac-address", &fMACAddress,
117		sizeof(fMACAddress));
118	if (bytesRead == (int)sizeof(fMACAddress)) {
119		return B_OK;
120	}
121
122	// On Sun machines, there is a global word 'mac-address' which returns
123	// the size and a pointer to the MAC address
124	size_t size;
125	void* ptr;
126	if (of_interpret("mac-address", 0, 2, &size, &ptr) != OF_FAILED) {
127		if (size == sizeof(fMACAddress)) {
128			memcpy(&fMACAddress, ptr, size);
129			return B_OK;
130		}
131	}
132
133	return B_ERROR;
134}
135
136
137status_t
138OFEthernetInterface::Init(const char *device, const char *parameters)
139{
140	if (!device)
141		return B_BAD_VALUE;
142
143	// open device
144	fHandle = of_open(device);
145	if (fHandle == OF_FAILED) {
146		printf("opening ethernet device failed\n");
147		return B_ERROR;
148	}
149
150	if (FindMACAddress() != B_OK) {
151		printf("Failed to get MAC address\n");
152		return B_ERROR;
153	}
154
155	// get IP address
156
157	// Note: This is a non-standardized way. On my Mac mini the response of the
158	// DHCP server is stored as property of /chosen. We try to get it and use
159	// the IP address we find in there.
160	// TODO Sun machines may use bootp-response instead?
161	struct {
162		uint8	irrelevant[16];
163		uint32	ip_address;
164		// ...
165	} dhcpResponse;
166	int bytesRead = of_getprop(gChosen, "dhcp-response", &dhcpResponse,
167		sizeof(dhcpResponse));
168	if (bytesRead != OF_FAILED && bytesRead == (int)sizeof(dhcpResponse)) {
169		SetIPAddress(ntohl(dhcpResponse.ip_address));
170		return B_OK;
171	}
172
173	// try to read manual client IP from boot path
174	if (parameters != NULL) {
175		char *comma = strrchr(parameters, ',');
176		if (comma != NULL && comma != strchr(parameters, ',')) {
177			SetIPAddress(ip_parse_address(comma + 1));
178			return B_OK;
179		}
180	}
181
182	// try to read default-client-ip setting
183	char defaultClientIP[16];
184	intptr_t package = of_finddevice("/options");
185	bytesRead = of_getprop(package, "default-client-ip",
186		defaultClientIP, sizeof(defaultClientIP) - 1);
187	if (bytesRead != OF_FAILED && bytesRead > 1) {
188		defaultClientIP[bytesRead] = '\0';
189		ip_addr_t address = ip_parse_address(defaultClientIP);
190		SetIPAddress(address);
191		return B_OK;
192	}
193
194	return B_ERROR;
195}
196
197
198mac_addr_t
199OFEthernetInterface::MACAddress() const
200{
201	return fMACAddress;
202}
203
204
205void *
206OFEthernetInterface::AllocateSendReceiveBuffer(size_t size)
207{
208	void *dmaMemory = NULL;
209
210	if (of_call_method(fHandle, "dma-alloc", 1, 1, size, &dmaMemory)
211			!= OF_FAILED) {
212		return dmaMemory;
213	}
214
215	// The dma-alloc method could be on the parent node (PCI bus, for example),
216	// rather than the device itself
217	intptr_t parentPackage = of_parent(of_instance_to_package(fHandle));
218
219	// FIXME surely there's a way to create an instance without going through
220	// the path?
221	char path[256];
222	of_package_to_path(parentPackage, path, sizeof(path));
223	intptr_t parentInstance = of_open(path);
224
225	if (of_call_method(parentInstance, "dma-alloc", 1, 1, size, &dmaMemory)
226			!= OF_FAILED) {
227		of_close(parentInstance);
228		return dmaMemory;
229	}
230
231	of_close(parentInstance);
232
233	return NULL;
234}
235
236
237void
238OFEthernetInterface::FreeSendReceiveBuffer(void *buffer)
239{
240	if (buffer)
241		of_call_method(fHandle, "dma-free", 1, 0, buffer);
242}
243
244
245ssize_t
246OFEthernetInterface::Send(const void *buffer, size_t size)
247{
248	TRACE(("OFEthernetInterface::Send(%p, %lu)\n", buffer, size));
249
250	if (!buffer)
251		return B_BAD_VALUE;
252
253	hex_dump(buffer, size);
254
255	int result = of_write(fHandle, buffer, size);
256	return (result == OF_FAILED ? B_ERROR : result);
257}
258
259
260ssize_t
261OFEthernetInterface::Receive(void *buffer, size_t size)
262{
263	if (!buffer)
264		return B_BAD_VALUE;
265
266	int result = of_read(fHandle, buffer, size);
267
268	if (result != OF_FAILED && result >= 0) {
269		TRACE(("OFEthernetInterface::Receive(%p, %lu): received %d bytes\n",
270			buffer, size, result));
271		hex_dump(buffer, result);
272	}
273
274	return (result == OF_FAILED ? B_ERROR : result);
275}
276
277
278// #pragma mark -
279
280
281status_t
282platform_net_stack_init()
283{
284	// Note: At the moment we only do networking at all, if the boot device
285	// is a network device. If it isn't, we simply fail here. For serious
286	// support we would want to iterate through the device tree and add all
287	// network devices.
288
289	// get boot path
290	char bootPath[192];
291	int length = of_getprop(gChosen, "bootpath", bootPath, sizeof(bootPath));
292	if (length <= 1)
293		return B_ERROR;
294
295	// we chop off parameters; otherwise opening the network device might have
296	// side effects
297	char *lastComponent = strrchr(bootPath, '/');
298	char *parameters = strchr((lastComponent ? lastComponent : bootPath), ':');
299	if (parameters)
300		*parameters = '\0';
301
302	// get device node
303	intptr_t node = of_finddevice(bootPath);
304	if (node == OF_FAILED)
305		return B_ERROR;
306
307	// get device type
308	char type[16];
309	if (of_getprop(node, "device_type", type, sizeof(type)) == OF_FAILED
310		|| strcmp("network", type) != 0) {
311		return B_ERROR;
312	}
313
314	// create an EthernetInterface object for the device
315	OFEthernetInterface *interface = new(nothrow) OFEthernetInterface;
316	if (!interface)
317		return B_NO_MEMORY;
318
319	status_t error = interface->Init(bootPath, parameters + 1);
320	if (error != B_OK) {
321		delete interface;
322		return error;
323	}
324
325	// add it to the net stack
326	error = NetStack::Default()->AddEthernetInterface(interface);
327	if (error != B_OK) {
328		delete interface;
329		return error;
330	}
331
332	return B_OK;
333}
334