1 /* 2 * Copyright 2006, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 */ 8 9 10 #include "stack_private.h" 11 #include "utility.h" 12 13 #include <net_buffer.h> 14 #include <util/AutoLock.h> 15 16 #include <ByteOrder.h> 17 #include <KernelExport.h> 18 19 20 static struct list sTimers; 21 static benaphore sTimerLock; 22 static sem_id sTimerWaitSem; 23 static thread_id sTimerThread; 24 static bigtime_t sTimerTimeout; 25 26 27 uint16 28 compute_checksum(uint8 *_buffer, size_t length) 29 { 30 uint16 *buffer = (uint16 *)_buffer; 31 uint32 sum = 0; 32 33 // TODO: unfold loop for speed 34 // TODO: write processor dependent version for speed 35 while (length >= 2) { 36 sum += *buffer++; 37 length -= 2; 38 } 39 40 if (length) { 41 // give the last byte it's proper endian-aware treatment 42 #if B_HOST_IS_LENDIAN 43 sum += *(uint8 *)buffer; 44 #else 45 uint8 ordered[2]; 46 ordered[0] = *(uint8 *)buffer; 47 ordered[1] = 0; 48 sum += *(uint16 *)ordered; 49 #endif 50 } 51 52 while (sum >> 16) { 53 sum = (sum & 0xffff) + (sum >> 16); 54 } 55 56 return sum; 57 } 58 59 60 uint16 61 checksum(uint8 *buffer, size_t length) 62 { 63 return ~compute_checksum(buffer, length); 64 } 65 66 67 // #pragma mark - Notifications 68 69 70 status_t 71 notify_socket(net_socket *socket, uint8 event, int32 value) 72 { 73 return gNetSocketModule.notify(socket, event, value); 74 } 75 76 77 // #pragma mark - FIFOs 78 79 80 status_t 81 init_fifo(net_fifo *fifo, const char *name, size_t maxBytes) 82 { 83 status_t status = benaphore_init(&fifo->lock, name); 84 if (status < B_OK) 85 return status; 86 87 fifo->notify = create_sem(1, name); 88 if (fifo->notify < B_OK) { 89 benaphore_destroy(&fifo->lock); 90 return fifo->notify; 91 } 92 93 fifo->max_bytes = maxBytes; 94 fifo->current_bytes = 0; 95 fifo->waiting = 0; 96 97 list_init(&fifo->buffers); 98 99 return B_OK; 100 } 101 102 103 void 104 uninit_fifo(net_fifo *fifo) 105 { 106 clear_fifo(fifo); 107 108 benaphore_destroy(&fifo->lock); 109 delete_sem(fifo->notify); 110 } 111 112 113 status_t 114 fifo_enqueue_buffer(net_fifo *fifo, net_buffer *buffer) 115 { 116 BenaphoreLocker locker(fifo->lock); 117 118 if (fifo->max_bytes > 0 && fifo->current_bytes + buffer->size > fifo->max_bytes) 119 return ENOBUFS; 120 121 list_add_item(&fifo->buffers, buffer); 122 fifo->current_bytes += buffer->size; 123 124 if (fifo->waiting > 0) { 125 fifo->waiting--; 126 release_sem_etc(fifo->notify, 1, B_DO_NOT_RESCHEDULE); 127 // we still hold the benaphore lock, so it makes no sense 128 // to reschedule after having released the sync semaphore 129 } 130 131 return B_OK; 132 } 133 134 135 /*! 136 Gets the first buffer from the FIFO. If there is no buffer, it 137 will wait depending on the \a flags and \a timeout. 138 The following flags are supported (the rest is ignored): 139 MSG_DONTWAIT - ignores the timeout and never wait for a buffer; if your 140 socket is O_NONBLOCK, you should specify this flag. A \a timeout of 141 zero is equivalent to this flag, though. 142 MSG_PEEK - returns a clone of the buffer and keep the original 143 in the FIFO. 144 */ 145 ssize_t 146 fifo_dequeue_buffer(net_fifo *fifo, uint32 flags, bigtime_t timeout, 147 net_buffer **_buffer) 148 { 149 benaphore_lock(&fifo->lock); 150 bool dontWait = (flags & MSG_DONTWAIT) != 0 || timeout == 0; 151 status_t status; 152 153 while (true) { 154 net_buffer *buffer = (net_buffer *)list_get_first_item(&fifo->buffers); 155 if (buffer != NULL) { 156 if ((flags & MSG_PEEK) != 0) { 157 // we need to clone the buffer for inspection; we can't give a 158 // handle to a buffer that we're still using 159 buffer = gNetBufferModule.clone(buffer, false); 160 if (buffer == NULL) { 161 status = B_NO_MEMORY; 162 break; 163 } 164 } else 165 list_remove_item(&fifo->buffers, buffer); 166 167 *_buffer = buffer; 168 status = B_OK; 169 break; 170 } 171 172 if (!dontWait) 173 fifo->waiting++; 174 175 // we need to wait until a new buffer becomes available 176 benaphore_unlock(&fifo->lock); 177 178 if (dontWait) 179 return B_WOULD_BLOCK; 180 181 status = acquire_sem_etc(fifo->notify, 1, 182 B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, timeout); 183 if (status < B_OK) 184 return status; 185 186 // try again 187 benaphore_lock(&fifo->lock); 188 } 189 190 if ((flags & MSG_PEEK) != 0 && fifo->waiting > 0) { 191 // another thread is waiting for data, since we didn't eat the 192 // buffer, it gets it 193 fifo->waiting--; 194 release_sem_etc(fifo->notify, 1, B_DO_NOT_RESCHEDULE); 195 } 196 197 benaphore_unlock(&fifo->lock); 198 return status; 199 } 200 201 202 status_t 203 clear_fifo(net_fifo *fifo) 204 { 205 BenaphoreLocker locker(fifo->lock); 206 207 while (true) { 208 net_buffer *buffer = (net_buffer *)list_remove_head_item(&fifo->buffers); 209 if (buffer == NULL) 210 break; 211 212 gNetBufferModule.free(buffer); 213 } 214 215 fifo->current_bytes = 0; 216 return B_OK; 217 } 218 219 220 // #pragma mark - Timer 221 222 223 static status_t 224 timer_thread(void * /*data*/) 225 { 226 status_t status = B_OK; 227 228 do { 229 bigtime_t timeout = B_INFINITE_TIMEOUT; 230 231 if (status == B_TIMED_OUT || status == B_OK) { 232 // scan timers for new timeout and/or execute a timer 233 if (benaphore_lock(&sTimerLock) < B_OK) 234 return B_OK; 235 236 struct net_timer *timer = NULL; 237 while (true) { 238 timer = (net_timer *)list_get_next_item(&sTimers, timer); 239 if (timer == NULL) 240 break; 241 242 if (timer->due < system_time()) { 243 // execute timer 244 list_remove_item(&sTimers, timer); 245 timer->due = -1; 246 247 benaphore_unlock(&sTimerLock); 248 timer->hook(timer, timer->data); 249 benaphore_lock(&sTimerLock); 250 251 timer = NULL; 252 // restart scanning as we unlocked the list 253 } else { 254 // calculate new timeout 255 if (timer->due < timeout) 256 timeout = timer->due; 257 } 258 } 259 260 sTimerTimeout = timeout; 261 benaphore_unlock(&sTimerLock); 262 } 263 264 status = acquire_sem_etc(sTimerWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout); 265 // the wait sem normally can't be acquired, so we 266 // have to look at the status value the call returns: 267 // 268 // B_OK - a new timer has been added or canceled 269 // B_TIMED_OUT - look for timers to be executed 270 // B_BAD_SEM_ID - we are asked to quit 271 } while (status != B_BAD_SEM_ID); 272 273 return B_OK; 274 } 275 276 277 /*! 278 Initializes a timer before use. You can also use this function to change 279 a timer later on, but make sure you have canceled it before using set_timer(). 280 */ 281 void 282 init_timer(net_timer *timer, net_timer_func hook, void *data) 283 { 284 timer->hook = hook; 285 timer->data = data; 286 timer->due = 0; 287 } 288 289 290 /*! 291 Sets or cancels a timer. When the \a delay is below zero, an eventually running 292 timer is canceled, if not, it is scheduled to be executed after the specified 293 \a delay. 294 You need to have initialized the timer before calling this function. 295 In case you need to change a running timer, you have to cancel it first, before 296 making any changes. 297 */ 298 void 299 set_timer(net_timer *timer, bigtime_t delay) 300 { 301 BenaphoreLocker locker(sTimerLock); 302 303 if (timer->due > 0 && delay < 0) { 304 // this timer is scheduled, cancel it 305 list_remove_item(&sTimers, timer); 306 timer->due = 0; 307 } 308 309 if (delay >= 0) { 310 // reschedule or add this timer 311 if (timer->due <= 0) 312 list_add_item(&sTimers, timer); 313 314 timer->due = system_time() + delay; 315 316 // notify timer about the change if necessary 317 if (sTimerTimeout > timer->due) 318 release_sem(sTimerWaitSem); 319 } 320 } 321 322 323 bool 324 cancel_timer(struct net_timer *timer) 325 { 326 BenaphoreLocker locker(sTimerLock); 327 328 if (timer->due <= 0) 329 return false; 330 331 // this timer is scheduled, cancel it 332 list_remove_item(&sTimers, timer); 333 timer->due = 0; 334 return true; 335 } 336 337 338 bool 339 is_timer_active(net_timer *timer) 340 { 341 return timer->due > 0; 342 } 343 344 345 status_t 346 init_timers(void) 347 { 348 list_init(&sTimers); 349 sTimerTimeout = B_INFINITE_TIMEOUT; 350 351 status_t status = benaphore_init(&sTimerLock, "net timer"); 352 if (status < B_OK) 353 return status; 354 355 sTimerWaitSem = create_sem(0, "net timer wait"); 356 if (sTimerWaitSem < B_OK) { 357 status = sTimerWaitSem; 358 goto err1; 359 } 360 361 sTimerThread = spawn_kernel_thread(timer_thread, "net timer", 362 B_NORMAL_PRIORITY, NULL); 363 if (sTimerThread < B_OK) { 364 status = sTimerThread; 365 goto err2; 366 } 367 368 return resume_thread(sTimerThread); 369 370 err1: 371 benaphore_destroy(&sTimerLock); 372 err2: 373 delete_sem(sTimerWaitSem); 374 return status; 375 } 376 377 378 void 379 uninit_timers(void) 380 { 381 benaphore_destroy(&sTimerLock); 382 delete_sem(sTimerWaitSem); 383 384 status_t status; 385 wait_for_thread(sTimerThread, &status); 386 } 387 388