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