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