xref: /haiku/src/libs/compat/freebsd_network/device_hooks.c (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
1 /*
2  * Copyright 2007-2009, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2007, Hugo Santos. All Rights Reserved.
4  * Copyright 2004, Marcus Overhagen. All Rights Reserved.
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include "device.h"
10 
11 #include <stdlib.h>
12 #include <sys/sockio.h>
13 
14 #include <Drivers.h>
15 #include <ether_driver.h>
16 
17 #include <compat/sys/haiku-module.h>
18 
19 #include <compat/sys/bus.h>
20 #include <compat/sys/mbuf.h>
21 #include <compat/net/ethernet.h>
22 #include <compat/net/if_media.h>
23 
24 
25 static status_t
26 compat_open(const char *name, uint32 flags, void **cookie)
27 {
28 	struct ifnet *ifp;
29 	struct ifreq ifr;
30 	int i;
31 	status_t status;
32 
33 	for (i = 0; i < MAX_DEVICES; i++) {
34 		if (gDevices[i] != NULL && !strcmp(gDevices[i]->device_name, name))
35 			break;
36 	}
37 
38 	if (i == MAX_DEVICES)
39 		return B_ERROR;
40 
41 	if (get_module(NET_STACK_MODULE_NAME, (module_info **)&gStack) != B_OK)
42 		return B_ERROR;
43 
44 	ifp = gDevices[i];
45 	if_printf(ifp, "compat_open(0x%" B_PRIx32 ")\n", flags);
46 
47 	if (atomic_or(&ifp->open_count, 1)) {
48 		put_module(NET_STACK_MODULE_NAME);
49 		return B_BUSY;
50 	}
51 
52 	IFF_LOCKGIANT(ifp);
53 
54 	if (ifp->if_init != NULL)
55 		ifp->if_init(ifp->if_softc);
56 
57 	if (!HAIKU_DRIVER_REQUIRES(FBSD_WLAN_FEATURE)) {
58 		ifp->if_flags &= ~IFF_UP;
59 		ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
60 
61 		memset(&ifr, 0, sizeof(ifr));
62 		ifr.ifr_media = IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0);
63 		status = ifp->if_ioctl(ifp, SIOCSIFMEDIA, (caddr_t)&ifr);
64 		if (status != B_OK) {
65 			ifr.ifr_media = IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0);
66 			status = ifp->if_ioctl(ifp, SIOCSIFMEDIA, (caddr_t)&ifr);
67 		}
68 	}
69 
70 	ifp->if_flags |= IFF_UP;
71 	ifp->flags &= ~DEVICE_CLOSED;
72 	ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
73 
74 	IFF_UNLOCKGIANT(ifp);
75 
76 	*cookie = ifp;
77 	return B_OK;
78 }
79 
80 
81 static status_t
82 compat_close(void *cookie)
83 {
84 	struct ifnet *ifp = cookie;
85 
86 	if_printf(ifp, "compat_close()\n");
87 	IFF_LOCKGIANT(ifp);
88 
89 	atomic_or(&ifp->flags, DEVICE_CLOSED);
90 
91 	wlan_close(cookie);
92 
93 	release_sem_etc(ifp->receive_sem, 1, B_RELEASE_ALL);
94 
95 	IFF_UNLOCKGIANT(ifp);
96 	return B_OK;
97 }
98 
99 
100 static status_t
101 compat_free(void *cookie)
102 {
103 	struct ifnet *ifp = cookie;
104 
105 	if_printf(ifp, "compat_free()\n");
106 
107 	// TODO: empty out the send queue
108 
109 	atomic_and(&ifp->open_count, 0);
110 	put_module(NET_STACK_MODULE_NAME);
111 	return B_OK;
112 }
113 
114 
115 static status_t
116 compat_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
117 {
118 	struct ifnet *ifp = cookie;
119 	uint32 semFlags = B_CAN_INTERRUPT;
120 	status_t status;
121 	struct mbuf *mb;
122 	size_t length = *numBytes;
123 
124 	//if_printf(ifp, "compat_read(%lld, %p, [%lu])\n", position,
125 	//	buffer, *numBytes);
126 
127 	if (ifp->flags & DEVICE_CLOSED)
128 		return B_INTERRUPTED;
129 
130 	if (ifp->flags & DEVICE_NON_BLOCK)
131 		semFlags |= B_RELATIVE_TIMEOUT;
132 
133 	do {
134 		status = acquire_sem_etc(ifp->receive_sem, 1, semFlags, 0);
135 		if (ifp->flags & DEVICE_CLOSED)
136 			return B_INTERRUPTED;
137 
138 		if (status == B_WOULD_BLOCK) {
139 			*numBytes = 0;
140 			return B_OK;
141 		} else if (status < B_OK)
142 			return status;
143 
144 		IF_DEQUEUE(&ifp->receive_queue, mb);
145 	} while (mb == NULL);
146 
147 	if (mb->m_pkthdr.len > length) {
148 		if_printf(ifp, "error reading packet: too large! (%d > %" B_PRIuSIZE ")\n",
149 			mb->m_pkthdr.len, length);
150 		m_freem(mb);
151 		*numBytes = 0;
152 		return E2BIG;
153 	}
154 
155 	length = min_c(max_c((size_t)mb->m_pkthdr.len, 0), length);
156 
157 	m_copydata(mb, 0, length, buffer);
158 	*numBytes = length;
159 
160 	m_freem(mb);
161 	return B_OK;
162 }
163 
164 
165 static status_t
166 compat_write(void *cookie, off_t position, const void *buffer,
167 	size_t *numBytes)
168 {
169 	struct ifnet *ifp = cookie;
170 	struct mbuf *mb;
171 	int length = *numBytes;
172 
173 	//if_printf(ifp, "compat_write(%lld, %p, [%lu])\n", position,
174 	//	buffer, *numBytes);
175 
176 	if (length <= MHLEN) {
177 		mb = m_gethdr(0, MT_DATA);
178 		if (mb == NULL)
179 			return ENOBUFS;
180 	} else {
181 		mb = m_get2(length, 0, MT_DATA, M_PKTHDR);
182 		if (mb == NULL)
183 			return E2BIG;
184 
185 		length = min_c(length, mb->m_ext.ext_size);
186 	}
187 
188 	// if we waited, check after if the ifp is still valid
189 
190 	mb->m_pkthdr.len = mb->m_len = length;
191 	memcpy(mtod(mb, void *), buffer, mb->m_len);
192 	*numBytes = length;
193 
194 	IFF_LOCKGIANT(ifp);
195 	int result = ifp->if_output(ifp, mb, NULL, NULL);
196 	IFF_UNLOCKGIANT(ifp);
197 
198 	return result;
199 }
200 
201 
202 static status_t
203 compat_control(void *cookie, uint32 op, void *arg, size_t length)
204 {
205 	struct ifnet *ifp = cookie;
206 	status_t status;
207 
208 	//if_printf(ifp, "compat_control(op %lu, %p, [%lu])\n", op,
209 	//	arg, length);
210 
211 	switch (op) {
212 		case ETHER_INIT:
213 			return B_OK;
214 
215 		case ETHER_GETADDR:
216 			return user_memcpy(arg, IF_LLADDR(ifp), ETHER_ADDR_LEN);
217 
218 		case ETHER_NONBLOCK:
219 		{
220 			int32 value;
221 			if (length < 4)
222 				return B_BAD_VALUE;
223 			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
224 				return B_BAD_ADDRESS;
225 			if (value)
226 				ifp->flags |= DEVICE_NON_BLOCK;
227 			else
228 				ifp->flags &= ~DEVICE_NON_BLOCK;
229 			return B_OK;
230 		}
231 
232 		case ETHER_SETPROMISC:
233 		{
234 			int32 value;
235 			if (length < 4)
236 				return B_BAD_VALUE;
237 			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
238 				return B_BAD_ADDRESS;
239 			if (value)
240 				ifp->if_flags |= IFF_PROMISC;
241 			else
242 				ifp->if_flags &= ~IFF_PROMISC;
243 
244 			IFF_LOCKGIANT(ifp);
245 			status = ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
246 			IFF_UNLOCKGIANT(ifp);
247 			return status;
248 		}
249 
250 		case ETHER_GETFRAMESIZE:
251 		{
252 			uint32 frameSize;
253 			if (length < 4)
254 				return B_BAD_VALUE;
255 
256 			const int MTUs[] = {
257 				ETHERMTU_JUMBO,
258 				PAGESIZE - (ETHER_HDR_LEN + ETHER_CRC_LEN),
259 				2290, /* IEEE80211_MTU_MAX */
260 				0
261 			};
262 
263 			// This is (usually) only invoked during initialization to get the
264 			// maximum frame size. Thus we try a few common possible values,
265 			// as there is no way to determine what is supported (or required).
266 			for (int i = 0; MTUs[i] != 0; i++) {
267 				struct ifreq ifr;
268 				ifr.ifr_mtu = MTUs[i];
269 				if (compat_control(cookie, SIOCSIFMTU, &ifr, sizeof(ifr)) == 0)
270 					break;
271 			}
272 
273 			frameSize = ifp->if_mtu + (ETHER_HDR_LEN + ETHER_CRC_LEN);
274 			return user_memcpy(arg, &frameSize, 4);
275 		}
276 
277 		case ETHER_ADDMULTI:
278 		case ETHER_REMMULTI:
279 		{
280 			struct sockaddr_dl address;
281 
282 			if ((ifp->if_flags & IFF_MULTICAST) == 0)
283 				return B_NOT_SUPPORTED;
284 			if (length != ETHER_ADDR_LEN)
285 				return B_BAD_VALUE;
286 
287 			memset(&address, 0, sizeof(address));
288 			address.sdl_family = AF_LINK;
289 			if (user_memcpy(LLADDR(&address), arg, ETHER_ADDR_LEN) < B_OK)
290 				return B_BAD_ADDRESS;
291 
292 			IFF_LOCKGIANT(ifp);
293 			if (op == ETHER_ADDMULTI)
294 				status = if_addmulti(ifp, (struct sockaddr *)&address, NULL);
295 			else
296 				status = if_delmulti(ifp, (struct sockaddr *)&address);
297 			IFF_UNLOCKGIANT(ifp);
298 			return status;
299 		}
300 
301 		case ETHER_GET_LINK_STATE:
302 		{
303 			struct ifmediareq mediareq;
304 			ether_link_state_t state;
305 
306 			if (length < sizeof(ether_link_state_t))
307 				return EINVAL;
308 
309 			memset(&mediareq, 0, sizeof(mediareq));
310 			IFF_LOCKGIANT(ifp);
311 			status = ifp->if_ioctl(ifp, SIOCGIFMEDIA, (caddr_t)&mediareq);
312 			IFF_UNLOCKGIANT(ifp);
313 			if (status < B_OK)
314 				return status;
315 
316 			state.media = mediareq.ifm_active;
317 			if ((mediareq.ifm_status & IFM_ACTIVE) != 0)
318 				state.media |= IFM_ACTIVE;
319 			state.speed = ifmedia_baudrate(mediareq.ifm_active);
320 			state.quality = 1000;
321 
322 			return user_memcpy(arg, &state, sizeof(ether_link_state_t));
323 		}
324 
325 		case ETHER_SET_LINK_STATE_SEM:
326 			if (user_memcpy(&ifp->link_state_sem, arg, sizeof(sem_id)) < B_OK) {
327 				ifp->link_state_sem = -1;
328 				return B_BAD_ADDRESS;
329 			}
330 			return B_OK;
331 
332 		case SIOCSIFFLAGS:
333 		case SIOCSIFMEDIA:
334 		case SIOCSIFMTU:
335 		{
336 			IFF_LOCKGIANT(ifp);
337 			status = ifp->if_ioctl(ifp, op, (caddr_t)arg);
338 			IFF_UNLOCKGIANT(ifp);
339 			return status;
340 		}
341 	}
342 
343 	return wlan_control(cookie, op, arg, length);
344 }
345 
346 
347 device_hooks gDeviceHooks = {
348 	compat_open,
349 	compat_close,
350 	compat_free,
351 	compat_control,
352 	compat_read,
353 	compat_write,
354 };
355