xref: /haiku/src/libs/compat/freebsd_network/device_hooks.c (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
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 	ifp->if_init(ifp->if_softc);
53 
54 	if (!HAIKU_DRIVER_REQUIRES(FBSD_WLAN_FEATURE)) {
55 		ifp->if_flags &= ~IFF_UP;
56 		ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
57 
58 		memset(&ifr, 0, sizeof(ifr));
59 		ifr.ifr_media = IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0);
60 		status = ifp->if_ioctl(ifp, SIOCSIFMEDIA, (caddr_t)&ifr);
61 		if (status != B_OK) {
62 			ifr.ifr_media = IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0);
63 			status = ifp->if_ioctl(ifp, SIOCSIFMEDIA, (caddr_t)&ifr);
64 		}
65 	}
66 
67 	ifp->if_flags |= IFF_UP;
68 	ifp->flags &= ~DEVICE_CLOSED;
69 	ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
70 
71 	*cookie = ifp;
72 	return B_OK;
73 }
74 
75 
76 static status_t
77 compat_close(void *cookie)
78 {
79 	struct ifnet *ifp = cookie;
80 
81 	if_printf(ifp, "compat_close()\n");
82 
83 	atomic_or(&ifp->flags, DEVICE_CLOSED);
84 
85 	wlan_close(cookie);
86 
87 	release_sem_etc(ifp->receive_sem, 1, B_RELEASE_ALL);
88 
89 	return B_OK;
90 }
91 
92 
93 static status_t
94 compat_free(void *cookie)
95 {
96 	struct ifnet *ifp = cookie;
97 
98 	if_printf(ifp, "compat_free()\n");
99 
100 	// TODO: empty out the send queue
101 
102 	atomic_and(&ifp->open_count, 0);
103 	put_module(NET_STACK_MODULE_NAME);
104 	return B_OK;
105 }
106 
107 
108 static status_t
109 compat_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
110 {
111 	struct ifnet *ifp = cookie;
112 	uint32 semFlags = B_CAN_INTERRUPT;
113 	status_t status;
114 	struct mbuf *mb;
115 	size_t length = *numBytes;
116 
117 	//if_printf(ifp, "compat_read(%lld, %p, [%lu])\n", position,
118 	//	buffer, *numBytes);
119 
120 	if (ifp->flags & DEVICE_CLOSED)
121 		return B_INTERRUPTED;
122 
123 	if (ifp->flags & DEVICE_NON_BLOCK)
124 		semFlags |= B_RELATIVE_TIMEOUT;
125 
126 	do {
127 		status = acquire_sem_etc(ifp->receive_sem, 1, semFlags, 0);
128 		if (ifp->flags & DEVICE_CLOSED)
129 			return B_INTERRUPTED;
130 
131 		if (status == B_WOULD_BLOCK) {
132 			*numBytes = 0;
133 			return B_OK;
134 		} else if (status < B_OK)
135 			return status;
136 
137 		IF_LOCK(&ifp->receive_queue);
138 		if (ifp->receive_queue.ifq_head != NULL
139 				&& ifp->receive_queue.ifq_head->m_pkthdr.len >= length) {
140 			IF_UNLOCK(&ifp->receive_queue);
141 			return E2BIG;
142 		}
143 		_IF_DEQUEUE(&ifp->receive_queue, mb);
144 		IF_UNLOCK(&ifp->receive_queue);
145 	} while (mb == NULL);
146 
147 	length = min_c(max_c((size_t)mb->m_pkthdr.len, 0), length);
148 
149 	m_copydata(mb, 0, length, buffer);
150 	*numBytes = length;
151 
152 	m_freem(mb);
153 	return B_OK;
154 }
155 
156 
157 static status_t
158 compat_write(void *cookie, off_t position, const void *buffer,
159 	size_t *numBytes)
160 {
161 	struct ifnet *ifp = cookie;
162 	struct mbuf *mb;
163 	int length = *numBytes;
164 
165 	//if_printf(ifp, "compat_write(%lld, %p, [%lu])\n", position,
166 	//	buffer, *numBytes);
167 
168 	if (length <= MHLEN) {
169 		mb = m_gethdr(0, MT_DATA);
170 		if (mb == NULL)
171 			return ENOBUFS;
172 	} else {
173 		mb = m_get2(length, 0, MT_DATA, M_PKTHDR);
174 		if (mb == NULL)
175 			return E2BIG;
176 
177 		length = min_c(length, mb->m_ext.ext_size);
178 	}
179 
180 	// if we waited, check after if the ifp is still valid
181 
182 	mb->m_pkthdr.len = mb->m_len = length;
183 	memcpy(mtod(mb, void *), buffer, mb->m_len);
184 	*numBytes = length;
185 
186 	return ifp->if_output(ifp, mb, NULL, NULL);
187 }
188 
189 
190 static status_t
191 compat_control(void *cookie, uint32 op, void *arg, size_t length)
192 {
193 	struct ifnet *ifp = cookie;
194 
195 	//if_printf(ifp, "compat_control(op %lu, %p, [%lu])\n", op,
196 	//	arg, length);
197 
198 	switch (op) {
199 		case ETHER_INIT:
200 			return B_OK;
201 
202 		case ETHER_GETADDR:
203 			return user_memcpy(arg, IF_LLADDR(ifp), ETHER_ADDR_LEN);
204 
205 		case ETHER_NONBLOCK:
206 		{
207 			int32 value;
208 			if (length < 4)
209 				return B_BAD_VALUE;
210 			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
211 				return B_BAD_ADDRESS;
212 			if (value)
213 				ifp->flags |= DEVICE_NON_BLOCK;
214 			else
215 				ifp->flags &= ~DEVICE_NON_BLOCK;
216 			return B_OK;
217 		}
218 
219 		case ETHER_SETPROMISC:
220 		{
221 			int32 value;
222 			if (length < 4)
223 				return B_BAD_VALUE;
224 			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
225 				return B_BAD_ADDRESS;
226 			if (value)
227 				ifp->if_flags |= IFF_PROMISC;
228 			else
229 				ifp->if_flags &= ~IFF_PROMISC;
230 			return ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
231 		}
232 
233 		case ETHER_GETFRAMESIZE:
234 		{
235 			uint32 frameSize;
236 			if (length < 4)
237 				return B_BAD_VALUE;
238 
239 			// This is (usually) only invoked during initialization to get the
240 			// maximum frame size. Thus we try to set the largest possible one,
241 			// as there is no way to determine what the driver might support.
242 			struct ifreq ifr;
243 			ifr.ifr_mtu = ETHERMTU_JUMBO;
244 			if (compat_control(cookie, SIOCSIFMTU, &ifr, sizeof(ifr)) != 0) {
245 				// Try again with 4K at least.
246 				ifr.ifr_mtu = 4096 - (ETHER_HDR_LEN + ETHER_CRC_LEN);
247 				compat_control(cookie, SIOCSIFMTU, &ifr, sizeof(ifr));
248 			}
249 
250 			frameSize = ifp->if_mtu + (ETHER_HDR_LEN + ETHER_CRC_LEN);
251 			return user_memcpy(arg, &frameSize, 4);
252 		}
253 
254 		case ETHER_ADDMULTI:
255 		case ETHER_REMMULTI:
256 		{
257 			struct sockaddr_dl address;
258 
259 			if ((ifp->if_flags & IFF_MULTICAST) == 0)
260 				return B_NOT_SUPPORTED;
261 			if (length != ETHER_ADDR_LEN)
262 				return B_BAD_VALUE;
263 
264 			memset(&address, 0, sizeof(address));
265 			address.sdl_family = AF_LINK;
266 			if (user_memcpy(LLADDR(&address), arg, ETHER_ADDR_LEN) < B_OK)
267 				return B_BAD_ADDRESS;
268 
269 			if (op == ETHER_ADDMULTI)
270 				return if_addmulti(ifp, (struct sockaddr *)&address, NULL);
271 
272 			return if_delmulti(ifp, (struct sockaddr *)&address);
273 		}
274 
275 		case ETHER_GET_LINK_STATE:
276 		{
277 			struct ifmediareq mediareq;
278 			ether_link_state_t state;
279 			status_t status;
280 
281 			if (length < sizeof(ether_link_state_t))
282 				return EINVAL;
283 
284 			memset(&mediareq, 0, sizeof(mediareq));
285 			status = ifp->if_ioctl(ifp, SIOCGIFMEDIA, (caddr_t)&mediareq);
286 			if (status < B_OK)
287 				return status;
288 
289 			state.media = mediareq.ifm_active;
290 			if ((mediareq.ifm_status & IFM_ACTIVE) != 0)
291 				state.media |= IFM_ACTIVE;
292 			state.speed = ifmedia_baudrate(mediareq.ifm_active);
293 			state.quality = 1000;
294 
295 			return user_memcpy(arg, &state, sizeof(ether_link_state_t));
296 		}
297 
298 		case ETHER_SET_LINK_STATE_SEM:
299 			if (user_memcpy(&ifp->link_state_sem, arg, sizeof(sem_id)) < B_OK) {
300 				ifp->link_state_sem = -1;
301 				return B_BAD_ADDRESS;
302 			}
303 			return B_OK;
304 
305 		case SIOCSIFFLAGS:
306 		case SIOCSIFMEDIA:
307 		case SIOCGIFMEDIA:
308 		case SIOCSIFMTU:
309 			return ifp->if_ioctl(ifp, op, (caddr_t)arg);
310 	}
311 
312 	return wlan_control(cookie, op, arg, length);
313 }
314 
315 
316 device_hooks gDeviceHooks = {
317 	compat_open,
318 	compat_close,
319 	compat_free,
320 	compat_control,
321 	compat_read,
322 	compat_write,
323 };
324