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