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