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 #include <kernel.h>
17 #include <net_buffer.h>
18
19 #include <compat/sys/haiku-module.h>
20
21 #include <compat/sys/bus.h>
22 #include <compat/sys/mbuf.h>
23 #include <compat/net/ethernet.h>
24 #include <compat/net/if_media.h>
25
26
27 static status_t
compat_open(const char * name,uint32 flags,void ** cookie)28 compat_open(const char *name, uint32 flags, void **cookie)
29 {
30 struct ifnet *ifp;
31 struct ifreq ifr;
32 int i;
33 status_t status;
34
35 for (i = 0; i < MAX_DEVICES; i++) {
36 if (gDevices[i] != NULL && !strcmp(gDevices[i]->device_name, name))
37 break;
38 }
39
40 if (i == MAX_DEVICES)
41 return B_ERROR;
42
43 ifp = gDevices[i];
44 if_printf(ifp, "compat_open(0x%" B_PRIx32 ")\n", flags);
45
46 if (atomic_or(&ifp->open_count, 1))
47 return B_BUSY;
48
49 IFF_LOCKGIANT(ifp);
50
51 if (ifp->if_init != NULL)
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 IFF_UNLOCKGIANT(ifp);
72
73 *cookie = ifp;
74 return B_OK;
75 }
76
77
78 static status_t
compat_close(void * cookie)79 compat_close(void *cookie)
80 {
81 struct ifnet *ifp = cookie;
82
83 if_printf(ifp, "compat_close()\n");
84 IFF_LOCKGIANT(ifp);
85
86 atomic_or(&ifp->flags, DEVICE_CLOSED);
87
88 wlan_close(cookie);
89
90 release_sem_etc(ifp->receive_sem, 1, B_RELEASE_ALL);
91
92 IFF_UNLOCKGIANT(ifp);
93 return B_OK;
94 }
95
96
97 static status_t
compat_free(void * cookie)98 compat_free(void *cookie)
99 {
100 struct ifnet *ifp = cookie;
101
102 if_printf(ifp, "compat_free()\n");
103
104 // TODO: empty out the send queue
105
106 atomic_and(&ifp->open_count, 0);
107 return B_OK;
108 }
109
110
111 static status_t
compat_receive(void * cookie,net_buffer ** _buffer)112 compat_receive(void *cookie, net_buffer **_buffer)
113 {
114 struct ifnet *ifp = cookie;
115 uint32 semFlags = B_CAN_INTERRUPT;
116 status_t status;
117 struct mbuf *mb;
118
119 //if_printf(ifp, "compat_receive(%p)\n", _buffer);
120
121 if ((ifp->flags & DEVICE_CLOSED) != 0)
122 return B_INTERRUPTED;
123
124 if (ifp->flags & DEVICE_NON_BLOCK)
125 semFlags |= B_RELATIVE_TIMEOUT;
126
127 do {
128 status = acquire_sem_etc(ifp->receive_sem, 1, semFlags, 0);
129 if ((ifp->flags & DEVICE_CLOSED) != 0)
130 return B_INTERRUPTED;
131
132 if (status != B_OK)
133 return status;
134
135 IF_DEQUEUE(&ifp->receive_queue, mb);
136 } while (mb == NULL);
137
138 net_buffer *buffer = gBufferModule->create(0);
139 if (buffer == NULL) {
140 m_freem(mb);
141 return B_NO_MEMORY;
142 }
143
144 for (struct mbuf *m = mb; m != NULL; m = m->m_next) {
145 status = gBufferModule->append(buffer, mtod(m, void *), m->m_len);
146 if (status != B_OK)
147 break;
148 }
149 if (status != B_OK) {
150 gBufferModule->free(buffer);
151 m_freem(mb);
152 return status;
153 }
154
155 if ((mb->m_pkthdr.csum_flags & CSUM_L3_VALID) != 0)
156 buffer->buffer_flags |= NET_BUFFER_L3_CHECKSUM_VALID;
157 if ((mb->m_pkthdr.csum_flags & CSUM_L4_VALID) != 0)
158 buffer->buffer_flags |= NET_BUFFER_L4_CHECKSUM_VALID;
159
160 *_buffer = buffer;
161 m_freem(mb);
162 return B_OK;
163 }
164
165
166 static status_t
compat_send(void * cookie,net_buffer * buffer)167 compat_send(void *cookie, net_buffer *buffer)
168 {
169 struct ifnet *ifp = cookie;
170 struct mbuf *mb;
171 int length = buffer->size;
172
173 //if_printf(ifp, "compat_send(%p, [%lu])\n", buffer, length);
174
175 if (length <= MHLEN) {
176 mb = m_gethdr(0, MT_DATA);
177 if (mb == NULL)
178 return ENOBUFS;
179 } else {
180 mb = m_get2(length, 0, MT_DATA, M_PKTHDR);
181 if (mb == NULL)
182 return E2BIG;
183
184 length = min_c(length, mb->m_ext.ext_size);
185 }
186
187 status_t status = gBufferModule->read(buffer, 0, mtod(mb, void *), length);
188 if (status != B_OK)
189 return status;
190 mb->m_pkthdr.len = mb->m_len = length;
191
192 if ((ifp->flags & DEVICE_CLOSED) != 0)
193 return B_INTERRUPTED;
194
195 IFF_LOCKGIANT(ifp);
196 int result = ifp->if_output(ifp, mb, NULL, NULL);
197 IFF_UNLOCKGIANT(ifp);
198
199 if (result == 0)
200 gBufferModule->free(buffer);
201 return result;
202 }
203
204
205 static status_t
compat_control(void * cookie,uint32 op,void * arg,size_t length)206 compat_control(void *cookie, uint32 op, void *arg, size_t length)
207 {
208 struct ifnet *ifp = cookie;
209 status_t status;
210
211 //if_printf(ifp, "compat_control(op %lu, %p, [%lu])\n", op,
212 // arg, length);
213
214 switch (op) {
215 case ETHER_INIT:
216 return B_OK;
217
218 case ETHER_GETADDR:
219 return user_memcpy(arg, IF_LLADDR(ifp), ETHER_ADDR_LEN);
220
221 case ETHER_NONBLOCK:
222 {
223 int32 value;
224 if (length < 4)
225 return B_BAD_VALUE;
226 if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
227 return B_BAD_ADDRESS;
228 if (value)
229 ifp->flags |= DEVICE_NON_BLOCK;
230 else
231 ifp->flags &= ~DEVICE_NON_BLOCK;
232 return B_OK;
233 }
234
235 case ETHER_SETPROMISC:
236 {
237 int32 value;
238 if (length < 4)
239 return B_BAD_VALUE;
240 if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
241 return B_BAD_ADDRESS;
242 if (value)
243 ifp->if_flags |= IFF_PROMISC;
244 else
245 ifp->if_flags &= ~IFF_PROMISC;
246
247 IFF_LOCKGIANT(ifp);
248 status = ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
249 IFF_UNLOCKGIANT(ifp);
250 return status;
251 }
252
253 case ETHER_GETFRAMESIZE:
254 {
255 uint32 frameSize;
256 if (length < 4)
257 return B_BAD_VALUE;
258
259 const int MTUs[] = {
260 ETHERMTU_JUMBO,
261 PAGESIZE - (ETHER_HDR_LEN + ETHER_CRC_LEN),
262 2290, /* IEEE80211_MTU_MAX */
263 0
264 };
265
266 // This is (usually) only invoked during initialization to get the
267 // maximum frame size. Thus we try a few common possible values,
268 // as there is no way to determine what is supported (or required).
269 for (int i = 0; MTUs[i] != 0; i++) {
270 struct ifreq ifr;
271 ifr.ifr_mtu = MTUs[i];
272 if (compat_control(cookie, SIOCSIFMTU, &ifr, sizeof(ifr)) == 0)
273 break;
274 }
275
276 frameSize = ifp->if_mtu + ETHER_HDR_LEN;
277 return user_memcpy(arg, &frameSize, 4);
278 }
279
280 case ETHER_ADDMULTI:
281 case ETHER_REMMULTI:
282 {
283 struct sockaddr_dl address;
284
285 if ((ifp->if_flags & IFF_MULTICAST) == 0)
286 return B_NOT_SUPPORTED;
287 if (length != ETHER_ADDR_LEN)
288 return B_BAD_VALUE;
289
290 memset(&address, 0, sizeof(address));
291 address.sdl_family = AF_LINK;
292 if (user_memcpy(LLADDR(&address), arg, ETHER_ADDR_LEN) < B_OK)
293 return B_BAD_ADDRESS;
294
295 IFF_LOCKGIANT(ifp);
296 if (op == ETHER_ADDMULTI)
297 status = if_addmulti(ifp, (struct sockaddr *)&address, NULL);
298 else
299 status = if_delmulti(ifp, (struct sockaddr *)&address);
300 IFF_UNLOCKGIANT(ifp);
301 return status;
302 }
303
304 case ETHER_GET_LINK_STATE:
305 {
306 struct ifmediareq mediareq;
307 ether_link_state_t state;
308
309 if (length < sizeof(ether_link_state_t))
310 return EINVAL;
311
312 memset(&mediareq, 0, sizeof(mediareq));
313 IFF_LOCKGIANT(ifp);
314 status = ifp->if_ioctl(ifp, SIOCGIFMEDIA, (caddr_t)&mediareq);
315 IFF_UNLOCKGIANT(ifp);
316 if (status < B_OK)
317 return status;
318
319 state.media = mediareq.ifm_active;
320 if ((mediareq.ifm_status & IFM_ACTIVE) != 0)
321 state.media |= IFM_ACTIVE;
322 state.speed = ifmedia_baudrate(mediareq.ifm_active);
323 state.quality = 1000;
324
325 return user_memcpy(arg, &state, sizeof(ether_link_state_t));
326 }
327
328 case ETHER_SET_LINK_STATE_SEM:
329 if (user_memcpy(&ifp->link_state_sem, arg, sizeof(sem_id)) < B_OK) {
330 ifp->link_state_sem = -1;
331 return B_BAD_ADDRESS;
332 }
333 return B_OK;
334
335 case ETHER_SEND_NET_BUFFER:
336 if (arg == NULL || length == 0)
337 return B_BAD_DATA;
338 if (!IS_KERNEL_ADDRESS(arg))
339 return B_BAD_ADDRESS;
340 return compat_send(cookie, (net_buffer*)arg);
341
342 case ETHER_RECEIVE_NET_BUFFER:
343 if (arg == NULL || length == 0)
344 return B_BAD_DATA;
345 if (!IS_KERNEL_ADDRESS(arg))
346 return B_BAD_ADDRESS;
347 return compat_receive(cookie, (net_buffer**)arg);
348
349 case SIOCGIFSTATS:
350 {
351 struct ifreq_stats stats;
352 stats.receive.packets = ifp->if_data.ifi_ipackets;
353 stats.receive.errors = ifp->if_data.ifi_ierrors;
354 stats.receive.bytes = ifp->if_data.ifi_ibytes;
355 stats.receive.multicast_packets = ifp->if_data.ifi_imcasts;
356 stats.receive.dropped = ifp->if_data.ifi_iqdrops;
357 stats.send.packets = ifp->if_data.ifi_opackets;
358 stats.send.errors = ifp->if_data.ifi_oerrors;
359 stats.send.bytes = ifp->if_data.ifi_obytes;
360 stats.send.multicast_packets = ifp->if_data.ifi_omcasts;
361 stats.send.dropped = ifp->if_data.ifi_oqdrops;
362 stats.collisions = ifp->if_data.ifi_collisions;
363 memcpy(arg, &stats, sizeof(stats));
364 return B_OK;
365 }
366
367 case SIOCSIFFLAGS:
368 case SIOCSIFMEDIA:
369 case SIOCSIFMTU:
370 {
371 IFF_LOCKGIANT(ifp);
372 status = ifp->if_ioctl(ifp, op, (caddr_t)arg);
373 IFF_UNLOCKGIANT(ifp);
374 return status;
375 }
376 }
377
378 return wlan_control(cookie, op, arg, length);
379 }
380
381
382 device_hooks gDeviceHooks = {
383 compat_open,
384 compat_close,
385 compat_free,
386 compat_control,
387 NULL,
388 NULL,
389 };
390