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