xref: /haiku/src/system/boot/platform/openfirmware/network.cpp (revision edfce7b32702f506cc35552ce257855a73f9aa54)
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 
32 class OFEthernetInterface : public EthernetInterface {
33 public:
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 
47 private:
48 			status_t	FindMACAddress();
49 
50 private:
51 			intptr_t	fHandle;
52 			mac_addr_t	fMACAddress;
53 };
54 
55 
56 #ifdef TRACE_NETWORK
57 
58 static void
hex_dump(const void * _data,int length)59 hex_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 
OFEthernetInterface()87 OFEthernetInterface::OFEthernetInterface()
88 	:
89 	EthernetInterface(),
90 	fHandle(OF_FAILED),
91 	fMACAddress(kNoMACAddress)
92 {
93 }
94 
95 
~OFEthernetInterface()96 OFEthernetInterface::~OFEthernetInterface()
97 {
98 	if (fHandle != OF_FAILED)
99 		of_close(fHandle);
100 }
101 
102 
103 status_t
FindMACAddress()104 OFEthernetInterface::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 
137 status_t
Init(const char * device,const char * parameters)138 OFEthernetInterface::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 
198 mac_addr_t
MACAddress() const199 OFEthernetInterface::MACAddress() const
200 {
201 	return fMACAddress;
202 }
203 
204 
205 void *
AllocateSendReceiveBuffer(size_t size)206 OFEthernetInterface::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 
237 void
FreeSendReceiveBuffer(void * buffer)238 OFEthernetInterface::FreeSendReceiveBuffer(void *buffer)
239 {
240 	if (buffer)
241 		of_call_method(fHandle, "dma-free", 1, 0, buffer);
242 }
243 
244 
245 ssize_t
Send(const void * buffer,size_t size)246 OFEthernetInterface::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 
260 ssize_t
Receive(void * buffer,size_t size)261 OFEthernetInterface::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 
281 status_t
platform_net_stack_init()282 platform_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