xref: /haiku/src/libs/compat/freebsd_network/device_hooks.c (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 
88 	atomic_or(&ifp->flags, DEVICE_CLOSED);
89 
90 	wlan_close(cookie);
91 
92 	release_sem_etc(ifp->receive_sem, 1, B_RELEASE_ALL);
93 
94 	return B_OK;
95 }
96 
97 
98 static status_t
99 compat_free(void *cookie)
100 {
101 	struct ifnet *ifp = cookie;
102 
103 	if_printf(ifp, "compat_free()\n");
104 
105 	// TODO: empty out the send queue
106 
107 	atomic_and(&ifp->open_count, 0);
108 	put_module(NET_STACK_MODULE_NAME);
109 	return B_OK;
110 }
111 
112 
113 static status_t
114 compat_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
115 {
116 	struct ifnet *ifp = cookie;
117 	uint32 semFlags = B_CAN_INTERRUPT;
118 	status_t status;
119 	struct mbuf *mb;
120 	size_t length = *numBytes;
121 
122 	//if_printf(ifp, "compat_read(%lld, %p, [%lu])\n", position,
123 	//	buffer, *numBytes);
124 
125 	if (ifp->flags & DEVICE_CLOSED)
126 		return B_INTERRUPTED;
127 
128 	if (ifp->flags & DEVICE_NON_BLOCK)
129 		semFlags |= B_RELATIVE_TIMEOUT;
130 
131 	do {
132 		status = acquire_sem_etc(ifp->receive_sem, 1, semFlags, 0);
133 		if (ifp->flags & DEVICE_CLOSED)
134 			return B_INTERRUPTED;
135 
136 		if (status == B_WOULD_BLOCK) {
137 			*numBytes = 0;
138 			return B_OK;
139 		} else if (status < B_OK)
140 			return status;
141 
142 		IF_DEQUEUE(&ifp->receive_queue, mb);
143 	} while (mb == NULL);
144 
145 	if (mb->m_pkthdr.len > length) {
146 		if_printf(ifp, "error reading packet: too large! (%d > %" B_PRIuSIZE ")\n",
147 			mb->m_pkthdr.len, length);
148 		m_freem(mb);
149 		*numBytes = 0;
150 		return E2BIG;
151 	}
152 
153 	length = min_c(max_c((size_t)mb->m_pkthdr.len, 0), length);
154 
155 	m_copydata(mb, 0, length, buffer);
156 	*numBytes = length;
157 
158 	m_freem(mb);
159 	return B_OK;
160 }
161 
162 
163 static status_t
164 compat_write(void *cookie, off_t position, const void *buffer,
165 	size_t *numBytes)
166 {
167 	struct ifnet *ifp = cookie;
168 	struct mbuf *mb;
169 	int length = *numBytes;
170 
171 	//if_printf(ifp, "compat_write(%lld, %p, [%lu])\n", position,
172 	//	buffer, *numBytes);
173 
174 	if (length <= MHLEN) {
175 		mb = m_gethdr(0, MT_DATA);
176 		if (mb == NULL)
177 			return ENOBUFS;
178 	} else {
179 		mb = m_get2(length, 0, MT_DATA, M_PKTHDR);
180 		if (mb == NULL)
181 			return E2BIG;
182 
183 		length = min_c(length, mb->m_ext.ext_size);
184 	}
185 
186 	// if we waited, check after if the ifp is still valid
187 
188 	mb->m_pkthdr.len = mb->m_len = length;
189 	memcpy(mtod(mb, void *), buffer, mb->m_len);
190 	*numBytes = length;
191 
192 	IFF_LOCKGIANT(ifp);
193 	int result = ifp->if_output(ifp, mb, NULL, NULL);
194 	IFF_UNLOCKGIANT(ifp);
195 
196 	return result;
197 }
198 
199 
200 static status_t
201 compat_control(void *cookie, uint32 op, void *arg, size_t length)
202 {
203 	struct ifnet *ifp = cookie;
204 	status_t status;
205 
206 	//if_printf(ifp, "compat_control(op %lu, %p, [%lu])\n", op,
207 	//	arg, length);
208 
209 	switch (op) {
210 		case ETHER_INIT:
211 			return B_OK;
212 
213 		case ETHER_GETADDR:
214 			return user_memcpy(arg, IF_LLADDR(ifp), ETHER_ADDR_LEN);
215 
216 		case ETHER_NONBLOCK:
217 		{
218 			int32 value;
219 			if (length < 4)
220 				return B_BAD_VALUE;
221 			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
222 				return B_BAD_ADDRESS;
223 			if (value)
224 				ifp->flags |= DEVICE_NON_BLOCK;
225 			else
226 				ifp->flags &= ~DEVICE_NON_BLOCK;
227 			return B_OK;
228 		}
229 
230 		case ETHER_SETPROMISC:
231 		{
232 			int32 value;
233 			if (length < 4)
234 				return B_BAD_VALUE;
235 			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
236 				return B_BAD_ADDRESS;
237 			if (value)
238 				ifp->if_flags |= IFF_PROMISC;
239 			else
240 				ifp->if_flags &= ~IFF_PROMISC;
241 
242 			IFF_LOCKGIANT(ifp);
243 			status = ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
244 			IFF_UNLOCKGIANT(ifp);
245 			return status;
246 		}
247 
248 		case ETHER_GETFRAMESIZE:
249 		{
250 			uint32 frameSize;
251 			if (length < 4)
252 				return B_BAD_VALUE;
253 
254 			const int MTUs[] = {
255 				ETHERMTU_JUMBO,
256 				PAGESIZE - (ETHER_HDR_LEN + ETHER_CRC_LEN),
257 				2290, /* IEEE80211_MTU_MAX */
258 				0
259 			};
260 
261 			// This is (usually) only invoked during initialization to get the
262 			// maximum frame size. Thus we try a few common possible values,
263 			// as there is no way to determine what is supported (or required).
264 			for (int i = 0; MTUs[i] != 0; i++) {
265 				struct ifreq ifr;
266 				ifr.ifr_mtu = MTUs[i];
267 				if (compat_control(cookie, SIOCSIFMTU, &ifr, sizeof(ifr)) == 0)
268 					break;
269 			}
270 
271 			frameSize = ifp->if_mtu + (ETHER_HDR_LEN + ETHER_CRC_LEN);
272 			return user_memcpy(arg, &frameSize, 4);
273 		}
274 
275 		case ETHER_ADDMULTI:
276 		case ETHER_REMMULTI:
277 		{
278 			struct sockaddr_dl address;
279 
280 			if ((ifp->if_flags & IFF_MULTICAST) == 0)
281 				return B_NOT_SUPPORTED;
282 			if (length != ETHER_ADDR_LEN)
283 				return B_BAD_VALUE;
284 
285 			memset(&address, 0, sizeof(address));
286 			address.sdl_family = AF_LINK;
287 			if (user_memcpy(LLADDR(&address), arg, ETHER_ADDR_LEN) < B_OK)
288 				return B_BAD_ADDRESS;
289 
290 			IFF_LOCKGIANT(ifp);
291 			if (op == ETHER_ADDMULTI)
292 				status = if_addmulti(ifp, (struct sockaddr *)&address, NULL);
293 			else
294 				status = if_delmulti(ifp, (struct sockaddr *)&address);
295 			IFF_UNLOCKGIANT(ifp);
296 			return status;
297 		}
298 
299 		case ETHER_GET_LINK_STATE:
300 		{
301 			struct ifmediareq mediareq;
302 			ether_link_state_t state;
303 
304 			if (length < sizeof(ether_link_state_t))
305 				return EINVAL;
306 
307 			memset(&mediareq, 0, sizeof(mediareq));
308 			IFF_LOCKGIANT(ifp);
309 			status = ifp->if_ioctl(ifp, SIOCGIFMEDIA, (caddr_t)&mediareq);
310 			IFF_UNLOCKGIANT(ifp);
311 			if (status < B_OK)
312 				return status;
313 
314 			state.media = mediareq.ifm_active;
315 			if ((mediareq.ifm_status & IFM_ACTIVE) != 0)
316 				state.media |= IFM_ACTIVE;
317 			state.speed = ifmedia_baudrate(mediareq.ifm_active);
318 			state.quality = 1000;
319 
320 			return user_memcpy(arg, &state, sizeof(ether_link_state_t));
321 		}
322 
323 		case ETHER_SET_LINK_STATE_SEM:
324 			if (user_memcpy(&ifp->link_state_sem, arg, sizeof(sem_id)) < B_OK) {
325 				ifp->link_state_sem = -1;
326 				return B_BAD_ADDRESS;
327 			}
328 			return B_OK;
329 
330 		case SIOCSIFFLAGS:
331 		case SIOCSIFMEDIA:
332 		case SIOCSIFMTU:
333 		{
334 			IFF_LOCKGIANT(ifp);
335 			status = ifp->if_ioctl(ifp, op, (caddr_t)arg);
336 			IFF_UNLOCKGIANT(ifp);
337 			return status;
338 		}
339 	}
340 
341 	return wlan_control(cookie, op, arg, length);
342 }
343 
344 
345 device_hooks gDeviceHooks = {
346 	compat_open,
347 	compat_close,
348 	compat_free,
349 	compat_control,
350 	compat_read,
351 	compat_write,
352 };
353