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