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
25static status_t
26compat_open(const char *name, uint32 flags, void **cookie)
27{
28	struct ifnet *ifp;
29	struct ifreq ifr;
30	int i;
31
32	for (i = 0; i < MAX_DEVICES; i++) {
33		if (gDevices[i] != NULL && !strcmp(gDevices[i]->device_name, name))
34			break;
35	}
36
37	if (i == MAX_DEVICES)
38		return B_ERROR;
39
40	if (get_module(NET_STACK_MODULE_NAME, (module_info **)&gStack) != B_OK)
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		put_module(NET_STACK_MODULE_NAME);
48		return B_BUSY;
49	}
50
51	ifp->if_init(ifp->if_softc);
52
53	if (!HAIKU_DRIVER_REQUIRES(FBSD_WLAN)) {
54		ifp->if_flags &= ~IFF_UP;
55		ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
56	}
57
58	memset(&ifr, 0, sizeof(ifr));
59	ifr.ifr_media = IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0);
60	ifp->if_ioctl(ifp, SIOCSIFMEDIA, (caddr_t)&ifr);
61
62	ifp->if_flags |= IFF_UP;
63	ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
64
65	*cookie = ifp;
66	return B_OK;
67}
68
69
70static status_t
71compat_close(void *cookie)
72{
73	struct ifnet *ifp = cookie;
74
75	if_printf(ifp, "compat_close()\n");
76
77	atomic_or(&ifp->flags, DEVICE_CLOSED);
78
79	wlan_close(cookie);
80
81	release_sem_etc(ifp->receive_sem, 1, B_RELEASE_ALL);
82
83	return B_OK;
84}
85
86
87static status_t
88compat_free(void *cookie)
89{
90	struct ifnet *ifp = cookie;
91
92	if_printf(ifp, "compat_free()\n");
93
94	// TODO: empty out the send queue
95
96	atomic_and(&ifp->open_count, 0);
97	put_module(NET_STACK_MODULE_NAME);
98	return B_OK;
99}
100
101
102static status_t
103compat_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
104{
105	struct ifnet *ifp = cookie;
106	uint32 semFlags = B_CAN_INTERRUPT;
107	status_t status;
108	struct mbuf *mb;
109	size_t length;
110
111	//if_printf(ifp, "compat_read(%lld, %p, [%lu])\n", position,
112	//	buffer, *numBytes);
113
114	if (ifp->flags & DEVICE_CLOSED)
115		return B_INTERRUPTED;
116
117	if (ifp->flags & DEVICE_NON_BLOCK)
118		semFlags |= B_RELATIVE_TIMEOUT;
119
120	do {
121		status = acquire_sem_etc(ifp->receive_sem, 1, semFlags, 0);
122		if (ifp->flags & DEVICE_CLOSED)
123			return B_INTERRUPTED;
124
125		if (status == B_WOULD_BLOCK) {
126			*numBytes = 0;
127			return B_OK;
128		} else if (status < B_OK)
129			return status;
130
131		IF_DEQUEUE(&ifp->receive_queue, mb);
132	} while (mb == NULL);
133
134	length = min_c(max_c((size_t)mb->m_pkthdr.len, 0), *numBytes);
135
136#if 0
137	mb = m_defrag(mb, 0);
138	if (mb == NULL) {
139		*numBytes = 0;
140		return B_NO_MEMORY;
141	}
142#endif
143
144	m_copydata(mb, 0, length, buffer);
145	*numBytes = length;
146
147	m_freem(mb);
148	return B_OK;
149}
150
151
152static status_t
153compat_write(void *cookie, off_t position, const void *buffer,
154	size_t *numBytes)
155{
156	struct ifnet *ifp = cookie;
157	struct mbuf *mb;
158
159	//if_printf(ifp, "compat_write(%lld, %p, [%lu])\n", position,
160	//	buffer, *numBytes);
161
162	if (*numBytes > MHLEN)
163		mb = m_getcl(0, MT_DATA, M_PKTHDR);
164	else
165		mb = m_gethdr(0, MT_DATA);
166
167	if (mb == NULL)
168		return ENOBUFS;
169
170	// if we waited, check after if the ifp is still valid
171
172	mb->m_pkthdr.len = mb->m_len = min_c(*numBytes, (size_t)MCLBYTES);
173	memcpy(mtod(mb, void *), buffer, mb->m_len);
174
175	return ifp->if_output(ifp, mb, NULL, NULL);
176}
177
178
179static status_t
180compat_control(void *cookie, uint32 op, void *arg, size_t length)
181{
182	struct ifnet *ifp = cookie;
183
184	//if_printf(ifp, "compat_control(op %lu, %p, [%lu])\n", op,
185	//	arg, length);
186
187	switch (op) {
188		case ETHER_INIT:
189			return B_OK;
190
191		case ETHER_GETADDR:
192			return user_memcpy(arg, IF_LLADDR(ifp), ETHER_ADDR_LEN);
193
194		case ETHER_NONBLOCK:
195		{
196			int32 value;
197			if (length < 4)
198				return B_BAD_VALUE;
199			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
200				return B_BAD_ADDRESS;
201			if (value)
202				ifp->flags |= DEVICE_NON_BLOCK;
203			else
204				ifp->flags &= ~DEVICE_NON_BLOCK;
205			return B_OK;
206		}
207
208		case ETHER_SETPROMISC:
209		{
210			int32 value;
211			if (length < 4)
212				return B_BAD_VALUE;
213			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
214				return B_BAD_ADDRESS;
215			if (value)
216				ifp->if_flags |= IFF_PROMISC;
217			else
218				ifp->if_flags &= ~IFF_PROMISC;
219			return ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
220		}
221
222		case ETHER_GETFRAMESIZE:
223		{
224			uint32 frameSize;
225			if (length < 4)
226				return B_BAD_VALUE;
227
228			frameSize = ifp->if_mtu + ETHER_HDR_LEN;
229			return user_memcpy(arg, &frameSize, 4);
230		}
231
232		case ETHER_ADDMULTI:
233		case ETHER_REMMULTI:
234		{
235			struct sockaddr_dl address;
236
237			if ((ifp->if_flags & IFF_MULTICAST) == 0)
238				return B_NOT_SUPPORTED;
239
240			memset(&address, 0, sizeof(address));
241			address.sdl_family = AF_LINK;
242			memcpy(LLADDR(&address), arg, ETHER_ADDR_LEN);
243
244			if (op == ETHER_ADDMULTI)
245				return if_addmulti(ifp, (struct sockaddr *)&address, NULL);
246
247			return if_delmulti(ifp, (struct sockaddr *)&address);
248		}
249
250		case ETHER_GET_LINK_STATE:
251		{
252			struct ifmediareq mediareq;
253			ether_link_state_t state;
254			status_t status;
255
256			if (length < sizeof(ether_link_state_t))
257				return EINVAL;
258
259			memset(&mediareq, 0, sizeof(mediareq));
260			status = ifp->if_ioctl(ifp, SIOCGIFMEDIA, (caddr_t)&mediareq);
261			if (status < B_OK)
262				return status;
263
264			state.media = mediareq.ifm_active;
265			if ((mediareq.ifm_status & IFM_ACTIVE) != 0)
266				state.media |= IFM_ACTIVE;
267			if ((mediareq.ifm_active & IFM_10_T) != 0)
268				state.speed = 10000000;
269			else if ((mediareq.ifm_active & IFM_100_TX) != 0)
270				state.speed = 100000000;
271			else
272				state.speed = 1000000000;
273			state.quality = 1000;
274
275			return user_memcpy(arg, &state, sizeof(ether_link_state_t));
276		}
277
278		case ETHER_SET_LINK_STATE_SEM:
279			if (user_memcpy(&ifp->link_state_sem, arg, sizeof(sem_id)) < B_OK) {
280				ifp->link_state_sem = -1;
281				return B_BAD_ADDRESS;
282			}
283			return B_OK;
284	}
285
286	return wlan_control(cookie, op, arg, length);
287}
288
289
290device_hooks gDeviceHooks = {
291	compat_open,
292	compat_close,
293	compat_free,
294	compat_control,
295	compat_read,
296	compat_write,
297};
298