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