Changeset 657 for trunk/kernel/devices
- Timestamp:
- Mar 18, 2020, 11:16:59 PM (5 years ago)
- Location:
- trunk/kernel/devices
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/kernel/devices/dev_dma.c
r647 r657 2 2 * dev_dma.c - DMA (Interrupt Controler Unit) generic device API implementation. 3 3 * 4 * Authors Alain Greiner (2016,2017,2018,2019 )4 * Authors Alain Greiner (2016,2017,2018,2019,2020) 5 5 * 6 6 * Copyright (c) UPMC Sorbonne Universites … … 84 84 85 85 //////////////////////////////////////////////// 86 error_t dev_dma_ remote_memcpy( xptr_t dst_xp,87 88 86 error_t dev_dma_async_memcpy( xptr_t dst_xp, 87 xptr_t src_xp, 88 uint32_t size ) 89 89 { 90 90 thread_t * this = CURRENT_THREAD; 91 91 92 #if CONGIG_DEBUG_DEV_DMA92 #if DEBUG_DEV_DMA 93 93 uint32_t cycle = (uint32_t)hal_get_cycles(); 94 94 if( CONGIG_DEBUG_DEV_DMA < cycle ) … … 107 107 108 108 // register command in calling thread descriptor 109 this->dma_cmd.sync = false; 109 110 this->dma_cmd.dev_xp = dev_xp; 110 111 this->dma_cmd.dst_xp = dst_xp; … … 112 113 this->dma_cmd.size = size; 113 114 114 // register client thread in waiting queue, activate server thread115 // block client thread on THREAD_BLOCKED_IOand deschedule.116 // it is re-activated by the ISR signaling IO operation completion.115 // register the client thread in waiting queue, activate the server thread, 116 // block the client thread on THREAD_BLOCKED_IO, and deschedule. 117 // it is re-activated by the server thread when the transfer is completed. 117 118 chdev_register_command( dev_xp ); 118 119 119 #if CONGIG_DEBUG_DEV_DMA120 #if DEBUG_DEV_DMA 120 121 cycle = (uint32_t)hal_get_cycles(); 121 122 if( CONGIG_DEBUG_DEV_DMA < cycle ) … … 127 128 return this->dma_cmd.error; 128 129 129 } // dev_dma_ remote_memcpy()130 } // dev_dma_async_memcpy() 130 131 132 ////////////////////////////////////////////// 133 error_t dev_dma_sync_memcpy( xptr_t dst_xp, 134 xptr_t src_xp, 135 uint32_t size ) 136 { 137 thread_t * this = CURRENT_THREAD; 138 139 #if DEBUG_DEV_DMA 140 uint32_t cycle = (uint32_t)hal_get_cycles(); 141 if( CONGIG_DEBUG_DEV_DMA < cycle ) 142 printk("\n[DBG] %s : thread %x enters / dst %l / src %l / size = %x\n", 143 __FUNCTION__ , this, dst_xp, src_xp, size ); 144 #endif 145 146 // select DMA channel corresponding to core lid 147 uint32_t channel = this->core->lid; 148 149 // get extended pointer on selected DMA chdev descriptor 150 xptr_t dev_xp = chdev_dir.dma[channel]; 151 152 // check DMA chdev definition 153 assert( (dev_xp != XPTR_NULL) , "undefined DMA chdev descriptor" ); 154 155 // register command in calling thread descriptor 156 this->dma_cmd.sync = true; 157 this->dma_cmd.dev_xp = dev_xp; 158 this->dma_cmd.dst_xp = dst_xp; 159 this->dma_cmd.src_xp = src_xp; 160 this->dma_cmd.size = size; 161 162 // get driver command function 163 cxy_t dev_cxy = GET_CXY( dev_xp ); 164 chdev_t * dev_ptr = GET_PTR( dev_xp ); 165 dev_cmd_t * cmd = (dev_cmd_t *)hal_remote_lpt( XPTR( dev_cxy , &dev_ptr->cmd ) ); 166 167 // call directly the blocking driver function 168 cmd( XPTR( local_cxy , this ) ); 169 170 #if DEBUG_DEV_DMA 171 cycle = (uint32_t)hal_get_cycles(); 172 if( CONGIG_DEBUG_DEV_DMA < cycle ) 173 printk("\n[DBG] %s : thread %x exit / dst %l / src %l / size = %x\n", 174 __FUNCTION__ , this, dst_xp, src_xp, size ); 175 #endif 176 177 // return I/O operation status from calling thread descriptor 178 return this->dma_cmd.error; 179 180 } // dev_dma_sync_memcpy() 181 -
trunk/kernel/devices/dev_dma.h
r647 r657 2 2 * dev_dma.h - DMA (Direct Memory Access) generic device API definition. 3 3 * 4 * Authors Alain Greiner (2016,2017,2018 )4 * Authors Alain Greiner (2016,2017,2018,2019,2020) 5 5 * 6 6 * Copyright (c) UPMC Sorbonne Universites … … 36 36 * to/from remote clusters. The burst size is defined by the cache line size. 37 37 * Each DMA channel is described by a specific chdev descriptor, handling its private 38 * waiting threads queue. It implement one single command : move data from a (remote) 39 * source buffer to a (remote) destination buffer. 38 * waiting threads queue. It implements two blocking commands : 39 * - move synchronously data from a remote source buffer to a remote destination buffer, 40 * using a polling policy to wait completion (No DMA_IRQ use). 41 * - move synchronously data from a remote source buffer to a remote destination buffer, 42 * using a descheduling policy to wait completion (reactivated bythe IDMA_IRQ). 40 43 ****************************************************************************************/ 41 44 … … 50 53 typedef struct dma_command_s 51 54 { 52 xptr_t dev_xp; /*! extended pointer on the DMA chdev descriptor */ 55 bool_t sync; /*! polling policy if true / descheduling policy if false */ 56 xptr_t dev_xp; /*! extended pointer on the DMA chdev descriptor */ 53 57 xptr_t src_xp; /*! extended pointer on source buffer. */ 54 58 xptr_t dst_xp; /*! extended pointer on destination buffer. */ … … 83 87 /***************************************************************************************** 84 88 * This blocking function register a DMA request in the device queue. 85 * It uses a descheduling policy to wait completion, and return an error status86 * when the transfer is completed.89 * It uses a descheduling policy to wait completion, 90 * It return an error status when the transfer is completed. 87 91 ***************************************************************************************** 88 * @ dst 89 * @ src : extended pointer on Rsource buffer.92 * @ dst_xp : extended pointer on destination buffer. 93 * @ src_xp : extended pointer on source buffer. 90 94 * @ size : number of bytes to move. 91 95 ****************************************************************************************/ 92 error_t dev_dma_remote_memcpy( xptr_t dst_xp, 93 xptr_t src_xp, 94 uint32_t size ); 96 error_t dev_dma_async_memcpy( xptr_t dst_xp, 97 xptr_t src_xp, 98 uint32_t size ); 99 100 /***************************************************************************************** 101 * This blocking function register a DMA request in the device queue. 102 * It uses a polling policy to wait completion. 103 * It return an error status when the transfer is completed. 104 ***************************************************************************************** 105 * @ dst_xp : extended pointer on destination buffer. 106 * @ src_xp : extended pointer on source buffer. 107 * @ size : number of bytes to move. 108 ****************************************************************************************/ 109 error_t dev_dma_sync_memcpy( xptr_t dst_xp, 110 xptr_t src_xp, 111 uint32_t size ); 95 112 96 113 -
trunk/kernel/devices/dev_fbf.c
r647 r657 2 2 * dev_fbf.c - FBF (Frame Buffer) generic device API implementation. 3 3 * 4 * Author Alain Greiner (2016,2017,2018,2019 )4 * Author Alain Greiner (2016,2017,2018,2019,2020) 5 5 * 6 6 * Copyright (c) UPMC Sorbonne Universites … … 26 26 #include <hal_gpt.h> 27 27 #include <hal_drivers.h> 28 #include <hal_irqmask.h> 29 #include <hal_macros.h> 30 #include <hal_uspace.h> 31 #include <hal_vmm.h> 28 32 #include <thread.h> 29 33 #include <printk.h> 30 34 #include <string.h> 35 #include <memcpy.h> 31 36 #include <chdev.h> 32 37 #include <dev_fbf.h> … … 41 46 char * dev_fbf_cmd_str( uint32_t cmd_type ) 42 47 { 43 if ( cmd_type == FBF_READ ) return "READ"; 44 else if( cmd_type == FBF_WRITE ) return "WRITE"; 45 else if( cmd_type == FBF_GET_CONFIG ) return "GET_CONFIG"; 46 else return "undefined"; 48 if ( cmd_type == FBF_GET_CONFIG ) return "GET_CONFIG"; 49 else if( cmd_type == FBF_CREATE_WINDOW ) return "CREATE_WINDOW"; 50 else if( cmd_type == FBF_DELETE_WINDOW ) return "DELETE_WINDOW"; 51 else if( cmd_type == FBF_MOVE_WINDOW ) return "MOVE_WINDOW"; 52 else if( cmd_type == FBF_REFRESH_WINDOW ) return "REFRESH_WINDOW"; 53 else if( cmd_type == FBF_DIRECT_WRITE ) return "DIRECT_WRITE"; 54 else if( cmd_type == FBF_DIRECT_READ ) return "DIRECT_READ"; 55 else return "undefined"; 47 56 } 48 57 … … 50 59 void dev_fbf_init( chdev_t * fbf ) 51 60 { 61 uint32_t wid; 62 52 63 // set chdev name 53 64 strcpy( fbf->name, "fbf" ); 54 65 55 // call driver init function 66 // initialize lock protecting the windows 67 remote_rwlock_init( XPTR( local_cxy , &fbf->ext.fbf.windows_lock ), 68 LOCK_FBF_WINDOWS ); 69 70 // initialize root of windows xlist 71 xlist_root_init( XPTR( local_cxy , &fbf->ext.fbf.windows_root ) ); 72 73 // initialize windows_tbl[] array 74 for( wid = 0 ; wid < CONFIG_FBF_WINDOWS_MAX_NR ; wid++ ) 75 { 76 fbf->ext.fbf.windows_tbl[wid] = XPTR_NULL; 77 } 78 79 // initialize wid allocator bitmap 80 bitmap_init( fbf->ext.fbf.windows_bitmap , CONFIG_FBF_WINDOWS_MAX_NR ); 81 82 // call driver init function to initialize the harware FBF 83 // and initialize the width, height, and subsampling FBF chdev fields 56 84 hal_drivers_fbf_init( fbf ); 57 85 … … 66 94 xptr_t dev_xp = chdev_dir.fbf[0]; 67 95 68 96 assert( (dev_xp != XPTR_NULL) , "undefined FBF chdev descriptor" ); 69 97 70 98 // get FBF chdev cluster and local pointer … … 79 107 } // end dev_fbf_get_config() 80 108 81 ///////////////////////////////////////////////////// 82 error_t dev_fbf_move_data( uint32_t cmd_type, 83 void * user_buffer, 84 uint32_t length, 85 uint32_t offset ) 86 { 87 // get pointer on calling thread 88 thread_t * this = CURRENT_THREAD; 109 /////////////////////////////////////////////// 110 uint32_t dev_fbf_create_window( uint32_t nlines, 111 uint32_t npixels, 112 uint32_t l_min, 113 uint32_t p_min, 114 intptr_t * user_buffer ) 115 { 116 kmem_req_t req; 117 fbf_window_t * window; // window descriptor (created in local cluster) 118 vseg_t * vseg; // vseg descriptor (created in reference cluster) 119 intptr_t vseg_base; // vseg base address in user space 120 121 // get local pointers on calling thread and process 122 thread_t * this = CURRENT_THREAD; 123 process_t * process = this->process; 89 124 90 125 #if DEBUG_DEV_FBF 91 126 uint32_t cycle = (uint32_t)hal_get_cycles(); 92 127 if( DEBUG_DEV_FBF < cycle ) 93 printk("\n[%s] thread[%x,%x] : %s / buffer %x / length %d / offset %x / cycle %d\n", 94 __FUNCTION__ , this->process->pid, this->trdid, 95 dev_fbf_cmd_str(cmd_type), user_buffer, length, offset, cycle ); 128 printk("\n[%s] thread[%x,%x] enter : nlines %d / npixels %d / l_min %d / p_min %d / cycle %d\n", 129 __FUNCTION__ , process->pid, this->trdid, nlines, npixels, l_min, p_min, cycle ); 130 #endif 131 132 // get cluster and pointers on FBF chdev 133 xptr_t fbf_xp = chdev_dir.fbf[0]; 134 cxy_t fbf_cxy = GET_CXY( fbf_xp ); 135 chdev_t * fbf_ptr = GET_PTR( fbf_xp ); 136 137 // check fbf_xp definition 138 assert( (fbf_xp != XPTR_NULL) , "undefined FBF chdev descriptor" ); 139 140 // get FBF width and height 141 uint32_t fbf_width = hal_remote_l32( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.width ) ); 142 uint32_t fbf_height = hal_remote_l32( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.height ) ); 143 144 // check new window size and coordinates 145 if( (((l_min + nlines) > fbf_height) || ((p_min + npixels) > fbf_width)) ) 146 { 147 printk("\n[ERROR] in %s / thread[%x,%x]" 148 "illegal new coordinates (%d,%d) for window (%d,%d) in fbf (%d,%d)\n", 149 process->pid, this->trdid, p_min, l_min, npixels, nlines, fbf_width, fbf_height ); 150 return -1; 151 } 152 153 // build extended pointers on windows lock, root, and wid allocator 154 xptr_t windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock ); 155 xptr_t windows_root_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_root ); 156 xptr_t windows_bitmap_xp = XPTR( fbf_cxy , fbf_ptr->ext.fbf.windows_bitmap ); 157 158 // allocate memory for the window descriptor in local cluster 159 req.type = KMEM_KCM; 160 req.order = bits_log2( sizeof(fbf_window_t) ); 161 req.flags = AF_ZERO | AF_KERNEL; 162 window = kmem_alloc( &req ); 163 164 if( window == NULL ) 165 { 166 printk("\n[ERROR] in %s / thread[%x,%x] cannot allocate window descriptor\n", 167 __FUNCTION__, process->pid, this->trdid ); 168 return -1; 169 } 170 171 #if (DEBUG_DEV_FBF & 1) 172 cycle = (uint32_t)hal_get_cycles(); 173 if( DEBUG_DEV_FBF < cycle ) 174 printk("\n[%s] thread[%x,%x] created window descriptor %x / cycle %d\n", 175 __FUNCTION__ , process->pid, this->trdid, window, cycle ); 176 #endif 177 178 // getpointers on reference process 179 xptr_t ref_xp = process->ref_xp; 180 process_t * ref_ptr = GET_PTR( ref_xp ); 181 cxy_t ref_cxy = GET_CXY( ref_xp ); 182 183 // allocate a new vseg, and introduce it in the reference process VSL 184 if( ref_cxy == local_cxy ) 185 { 186 vseg = vmm_create_vseg( process, // owner process 187 VSEG_TYPE_ANON, // localised, public 188 0, // base, unused for ANON 189 nlines * npixels, // size 190 0, // file_offset, unused for ANON 191 0, // file_size, unused for ANON 192 XPTR_NULL, // mapper_xp, unused for ANON 193 local_cxy ); // mapping cluster 194 } 195 else 196 { 197 rpc_vmm_create_vseg_client( ref_cxy, 198 ref_ptr, 199 VSEG_TYPE_ANON, 200 0, // base, unused for ANON 201 nlines * npixels, // size 202 0, // file_offset, unused for ANON 203 0, // file size, unused for ANON 204 XPTR_NULL, // mapper_xp, unused for ANON 205 local_cxy, 206 &vseg ); 207 } 208 209 if( vseg == NULL ) 210 { 211 printk("\n[ERROR] in %s / thread[%x,%x] cannot create vseg in reference cluster\n", 212 __FUNCTION__, process->pid, this->trdid ); 213 req.ptr = (void *)window; 214 kmem_free( &req ); 215 return -1; 216 } 217 218 // get vseg base 219 vseg_base = (intptr_t)hal_remote_lpt( XPTR( ref_cxy , &vseg->min ) ); 220 221 #if (DEBUG_DEV_FBF & 1) 222 cycle = (uint32_t)hal_get_cycles(); 223 if( DEBUG_DEV_FBF < cycle ) 224 printk("\n[%s] thread[%x,%x] allocated vseg / base %x / cycle %d\n", 225 __FUNCTION__ , process->pid, this->trdid, vseg_base, cycle ); 226 #endif 227 228 // take the lock protecting windows in write mode 229 remote_rwlock_wr_acquire( windows_lock_xp ); 230 231 // allocate a wid from allocator in FBF descriptor extension 232 uint32_t wid = bitmap_remote_alloc( windows_bitmap_xp , CONFIG_FBF_WINDOWS_MAX_NR ); 233 234 if( wid == 0xFFFFFFFF ) 235 { 236 printk("\n[ERROR] in %s / thread[%x,%x] cannot allocate buffer for window\n", 237 __FUNCTION__, process->pid, this->trdid ); 238 req.ptr = (void *)window; 239 kmem_free( &req ); 240 vmm_remove_vseg( process , vseg ); 241 return -1; 242 } 243 244 // initialize window descriptor 245 window->pid = process->pid; 246 window->wid = wid; 247 window->height = nlines; 248 window->width = npixels; 249 window->l_min = l_min; 250 window->p_min = p_min; 251 window->hidden = false; 252 window->buffer = (uint8_t *)vseg_base; 253 254 // register new window in xlist rooted in FBF extension 255 xlist_add_last( windows_root_xp , XPTR( local_cxy , &window->xlist ) ); 256 257 // build extended pointer on relevant entry in windows_tbl[] array 258 xptr_t windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] ); 259 260 // register new window in windows_tbl[] stored in FBF extension 261 hal_remote_s64( windows_tbl_xp , XPTR( local_cxy , window ) ); 262 263 // release the lock protecting windows in write mode 264 remote_rwlock_wr_release( windows_lock_xp ); 265 266 #if DEBUG_DEV_FBF 267 cycle = (uint32_t)hal_get_cycles(); 268 if( DEBUG_DEV_FBF < cycle ) 269 printk("\n[%s] thread[%x,%x] exit / wid %d / buffer %x / cycle %d\n", 270 __FUNCTION__ , this->process->pid, this->trdid, wid , window->buffer, cycle ); 271 #endif 272 273 #if (DEBUG_DEV_FBF & 1) 274 hal_vmm_display( ref_xp , true ); 275 #endif 276 277 // return pointer on allocated buffer 278 *user_buffer = vseg_base; 279 280 return wid; 281 282 } // end dev_fbf_create_window() 283 284 //////////////////////////////////////////////////////////////////////////////////////// 285 // This static function is called by the dev_fbf_display() function. 286 // For a partial FBF line, identified by the <f_line>, <f_p_min>, <f_p_max> arguments 287 // in FBF reference, and for one remote window, identified by the <window_xp> argument, 288 // it updates the target buffer identified by <t_buffer>, and containing exactly 289 // (f_p_max - f_p_min) pixels. 290 // Depending on the actual overlap between the window and the <t_buffer> representing 291 // the partial FBF line, it moves up to (f_p_max - f_p_min) pixels from the window 292 // buffer to the target buffer. The number of moved pixels can be nul if no overlap. 293 //////////////////////////////////////////////////////////////////////////////////////// 294 // @ f_line : [in] line index in FBF reference (from 0 to fbf_height-1). 295 // @ f_p_min : [in] first pixel in FBF line. 296 // @ f_p_max : [in] last pixel in FBF line (excluded). 297 // @ window_xp : [in] extended pointer on checked window . 298 // @ t_buffer : [out] local pointer on target buffer to be updated. 299 /////////////////////////////////////////////////////////////////////////////////////// 300 __attribute__ ((noinline)) static void handle_one_window( uint32_t f_line, 301 uint32_t f_p_min, 302 uint32_t f_p_max, 303 xptr_t window_xp, 304 uint8_t * t_buffer ) 305 { 306 // get remote window descriptor cluster and local pointer 307 cxy_t window_cxy = GET_CXY( window_xp ); 308 fbf_window_t * window_ptr = GET_PTR( window_xp ); 309 310 // get remote window min/max coordinates in FBF reference 311 uint32_t w_l_min = hal_remote_l32( XPTR( window_cxy , &window_ptr->l_min ) ); 312 uint32_t w_p_min = hal_remote_l32( XPTR( window_cxy , &window_ptr->p_min ) ); 313 uint32_t w_height = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) ); 314 uint32_t w_width = hal_remote_l32( XPTR( window_cxy , &window_ptr->width ) ); 315 uint32_t w_l_max = w_l_min + w_height; 316 uint32_t w_p_max = w_p_min + w_width; 317 318 // does nothing if partial FBF line does not overlap the window 319 if( (f_line < w_l_min) || (f_line >= w_l_max) || 320 (f_p_max < w_p_min) || (f_p_min >= w_p_max) ) return; 321 322 // get pointer on window buffer in user space 323 uint8_t * w_buffer = hal_remote_lpt( XPTR( window_cxy , &window_ptr->buffer ) ); 324 325 // get min & max indexes for pixels to be moved in FBF reference 326 uint32_t f_pixel_min = (f_p_min < w_p_min) ? w_p_min : f_p_min; 327 uint32_t f_pixel_max = (f_p_max < w_p_max) ? f_p_max : w_p_max; 328 329 // compute number of pixels to move from w_buffer to f_buffer 330 uint32_t npixels = f_pixel_max - f_pixel_min; 331 332 // compute offset in target buffer 333 uint32_t t_offset = f_pixel_min - f_p_min; 334 335 // compute line index in window 336 uint32_t w_line = f_line - w_l_min; 337 338 // compute offset in window buffer 339 uint32_t w_offset = (w_line * w_height) + f_pixel_min - w_p_min; 340 341 // move pixels from w_buffer (user space) to t_buffer in kernel space 342 hal_copy_from_uspace( XPTR( local_cxy , &t_buffer[t_offset] ), 343 &w_buffer[w_offset], 344 npixels ); 345 346 } // end handle_one_window() 347 348 //////////////////////////////////////////////////////////////////////////////////////// 349 // This static function is called by dev_fbf_refresh_window(), dev_fbf_move_window(), 350 // dev_fbf_resize_window(), and dev_fbf_delete_window(). It updates all lines of the 351 // window identified by the <window_xp>, <line_first>, and <line_last>> arguments. 352 // It scan all registered windows to take into account the overlap priorities defined 353 // by the windows xlist. It does not take the lock protecting the xlist, that must be 354 // taken by the calling function. 355 //////////////////////////////////////////////////////////////////////////////////////// 356 // @ window_xp : [in] extended pointer on window defining the FBF pixels to refresh. 357 // @ line_first : [in] first line index. 358 // @ line_last : [in] last line index (excluded). 359 //////////////////////////////////////////////////////////////////////////////////////// 360 error_t fbf_update( xptr_t window_xp, 361 uint32_t line_first, 362 uint32_t line_last ) 363 { 364 uint32_t line; // iterator to scan the FBF lines 365 uint32_t pixel; // iterator to scan pixels in one FBF line 366 xptr_t iter_xp; // iterator to scan the list of windows 367 error_t error; 368 369 // this intermediate buffer stores one line in 370 // target window, to handle other windows overlap 371 uint8_t line_buffer[CONFIG_FBF_WINDOWS_MAX_WIDTH]; 372 373 // get pointer on calling thread and core lid 374 thread_t * this = CURRENT_THREAD; 375 376 // get window cluster and local pointer 377 cxy_t window_cxy = GET_CXY( window_xp ); 378 fbf_window_t * window_ptr = GET_PTR( window_xp ); 379 380 #if DEBUG_DEV_FBF 381 uint32_t wid = hal_remote_l32( XPTR( window_cxy , &window_ptr->wid ) ); 382 uint32_t lid = this->core->lid; 383 uint32_t cycle = (uint32_t)hal_get_cycles(); 384 if( DEBUG_DEV_FBF < cycle ) 385 printk("\n[%s] core[%x,%d] enter / wid %d / cycle %d\n", 386 __FUNCTION__, local_cxy, lid, wid, cycle ); 96 387 #endif 97 388 … … 101 392 chdev_t * fbf_ptr = GET_PTR( fbf_xp ); 102 393 103 // check fbf_xp definition 104 assert( (fbf_xp != XPTR_NULL) , "undefined FBF chdev descriptor" ); 394 // get frame buffer width 395 uint32_t fbf_width = hal_remote_l32( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.width ) ); 396 397 // get pointer on driver command function 398 dev_cmd_t * cmd = hal_remote_lpt( XPTR( fbf_cxy , &fbf_ptr->cmd ) ); 399 400 // build extended pointers on windows xlist root 401 xptr_t windows_root_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_root ); 402 403 // get window size and coordinates 404 uint32_t p_min = hal_remote_l32( XPTR( window_cxy , &window_ptr->p_min ) ); 405 uint32_t l_min = hal_remote_l32( XPTR( window_cxy , &window_ptr->l_min ) ); 406 uint32_t w_pixels = hal_remote_l32( XPTR( window_cxy , &window_ptr->width ) ); 407 408 error = 0; 409 410 // loop on target window lines (FBF coordinates) 411 for( line = l_min + line_first ; line < (l_min + line_last) ; line++ ) 412 { 413 // reset the line buffer to default value 414 for( pixel = 0 ; pixel < w_pixels ; pixel++ ) line_buffer[pixel] = 127; 415 416 // loop on all windows 417 XLIST_FOREACH( windows_root_xp , iter_xp ) 418 { 419 // get pointers on remote window 420 xptr_t tgt_xp = XLIST_ELEMENT( iter_xp , fbf_window_t , xlist ); 421 fbf_window_t * tgt_ptr = GET_PTR( window_xp ); 422 cxy_t tgt_cxy = GET_CXY( window_xp ); 423 424 bool_t hidden = hal_remote_l32( XPTR( tgt_cxy , &tgt_ptr->hidden ) ); 425 426 // fill the line_buf for this window if not hidden 427 if( hidden == false ) handle_one_window( line, // line index 428 p_min, // pixel_min 429 p_min + w_pixels, // pixel_max 430 tgt_xp, // window_xp 431 line_buffer ); 432 } // end for windows 433 434 // compute offset in FBF 435 uint32_t fbf_offset = p_min + (line * fbf_width); 436 437 // register command in calling thread descriptor 438 this->fbf_cmd.dev_xp = fbf_xp; 439 this->fbf_cmd.type = FBF_DRIVER_KERNEL_WRITE; 440 this->fbf_cmd.buffer = line_buffer; 441 this->fbf_cmd.npixels = w_pixels; 442 this->fbf_cmd.offset = fbf_offset; 443 444 // call driver to display one line 445 cmd( XPTR( local_cxy , this ) ); 446 447 error |= this->fbf_cmd.error; 448 449 } // end for lines 450 451 #if DEBUG_DEV_FBF 452 cycle = (uint32_t)hal_get_cycles(); 453 if( DEBUG_DEV_FBF < cycle ) 454 printk("\n[%s] core[%x,%d] exit / wid %d / cycle %d\n", 455 __FUNCTION__, local_cxy, this->core->lid, wid, cycle ); 456 #endif 457 458 // return I/O operation status 459 return error; 460 461 } // end fbf_update() 462 463 ////////////////////////////////////////////// 464 error_t dev_fbf_delete_window( uint32_t wid ) 465 { 466 kmem_req_t req; 467 468 thread_t * this = CURRENT_THREAD; 469 process_t * process = this->process; 470 471 #if DEBUG_DEV_FBF 472 uint32_t cycle = (uint32_t)hal_get_cycles(); 473 if( DEBUG_DEV_FBF < cycle ) 474 printk("\n[%s] thread[%x,%x] enters : wid %d / cycle %d\n", 475 __FUNCTION__ , process->pid, this->trdid, wid, cycle ); 476 #endif 477 478 // get cluster and pointers on FBF chdev 479 xptr_t fbf_xp = chdev_dir.fbf[0]; 480 cxy_t fbf_cxy = GET_CXY( fbf_xp ); 481 chdev_t * fbf_ptr = GET_PTR( fbf_xp ); 482 483 // build extended pointers on windows lock, and wid allocator 484 xptr_t windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock ); 485 xptr_t wid_bitmap_xp = XPTR( fbf_cxy , fbf_ptr->ext.fbf.windows_bitmap ); 486 487 // build extended pointer on relevant entry in windows_tbl[] array 488 xptr_t windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] ); 489 490 // get extended pointer on remote window descriptor 491 xptr_t window_xp = hal_remote_l64( windows_tbl_xp ); 492 493 if( window_xp == XPTR_NULL ) 494 { 495 printk("\n[ERROR] in %s / thread[%x,%x] / wid %d non registered\n", 496 __FUNCTION__, process->pid, this->trdid, wid ); 497 return -1; 498 } 499 500 // get cluster and local pointer on remote window 501 cxy_t window_cxy = GET_CXY( window_xp ); 502 fbf_window_t * window_ptr = GET_PTR( window_xp ); 503 504 // get process owner PID 505 pid_t owner_pid = hal_remote_l32( XPTR( window_cxy , &window_ptr->pid ) ); 506 507 // check caller PID / owner PID 508 if( owner_pid != process->pid ) 509 { 510 printk("\n[ERROR] in %s : caller PID (%x) != owner PID (%x)\n", 511 __FUNCTION__, process->pid , owner_pid ); 512 return -1; 513 } 514 515 // get associated buffer, and number of lines 516 uint8_t * buffer = hal_remote_lpt( XPTR( window_cxy , &window_ptr->buffer ) ); 517 uint32_t nlines = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) ); 518 519 // 1. take the lock protecting windows in write mode 520 remote_rwlock_wr_acquire( windows_lock_xp ); 521 522 // 2. update the FBF window 523 fbf_update( window_xp , 0 , nlines ); 524 525 // 3. remove the window from windows_tbl[] array 526 hal_remote_s64( windows_tbl_xp , XPTR_NULL ); 527 528 // 4. remove the window from xlist 529 xlist_unlink( XPTR( window_cxy , &window_ptr->xlist ) ); 530 531 // 5. release wid to bitmap 532 bitmap_remote_clear( wid_bitmap_xp , wid ); 533 534 // 6. release the lock protecting windows in write mode 535 remote_rwlock_wr_release( windows_lock_xp ); 536 537 // 7. release memory allocated for window descriptor 538 req.type = KMEM_KCM; 539 req.ptr = window_ptr; 540 kmem_remote_free( window_cxy , &req ); 541 542 // 8. release the associated vseg 543 vmm_global_delete_vseg( process , (intptr_t)buffer ); 544 545 #if DEBUG_DEV_FBF 546 cycle = (uint32_t)hal_get_cycles(); 547 if( DEBUG_DEV_FBF < cycle ) 548 printk("\n[%s] thread[%x,%x] exit / cycle %d\n", 549 __FUNCTION__ , process->pid, this->trdid, cycle ); 550 #endif 551 552 return 0; 553 554 } // end dev_fbf_delete_window() 555 556 //////////////////////////////////////////// 557 error_t dev_fbf_move_window( uint32_t wid, 558 uint32_t l_min, 559 uint32_t p_min ) 560 { 561 thread_t * this = CURRENT_THREAD; 562 process_t * process = this->process; 563 564 #if DEBUG_DEV_FBF 565 uint32_t cycle = (uint32_t)hal_get_cycles(); 566 if( DEBUG_DEV_FBF < cycle ) 567 printk("\n[%s] thread[%x,%x] enters : wid %d / l_min %d / p_min %d / cycle %d\n", 568 __FUNCTION__ , process->pid, this->trdid, wid, l_min, p_min, cycle ); 569 #endif 570 571 // get cluster and pointers on FBF chdev 572 xptr_t fbf_xp = chdev_dir.fbf[0]; 573 cxy_t fbf_cxy = GET_CXY( fbf_xp ); 574 chdev_t * fbf_ptr = GET_PTR( fbf_xp ); 575 576 // build extended pointers on windows lock and root 577 xptr_t windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock ); 578 xptr_t windows_root_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_root ); 579 580 // build extended pointer on relevant entry in windows_tbl[] array 581 xptr_t windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] ); 582 583 // get extended pointer on remote window descriptor 584 xptr_t window_xp = hal_remote_l64( windows_tbl_xp ); 585 586 if( window_xp == XPTR_NULL ) 587 { 588 printk("\n[ERROR] in %s / thread[%x,%x] / wid %d non registered\n", 589 __FUNCTION__, process->pid, this->trdid, wid ); 590 return -1; 591 } 592 593 // get cluster and local pointer for remote window 594 cxy_t window_cxy = GET_CXY( window_xp ); 595 fbf_window_t * window_ptr = GET_PTR( window_xp ); 596 597 // get process owner PID, coordinates, and number of lines 598 pid_t owner_pid = hal_remote_l32( XPTR( window_cxy , &window_ptr->pid ) ); 599 uint32_t p_zero = hal_remote_l32( XPTR( window_cxy , &window_ptr->p_min ) ); 600 uint32_t l_zero = hal_remote_l32( XPTR( window_cxy , &window_ptr->l_min ) ); 601 uint32_t nlines = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) ); 602 603 // check caller PID / owner PID 604 if( owner_pid != process->pid ) 605 { 606 printk("\n[ERROR] in %s : caller PID (%x) != owner PID (%x)\n", 607 __FUNCTION__, process->pid , owner_pid ); 608 return -1; 609 } 610 611 // does nothing if no change 612 if( (p_zero == p_min) && (l_zero == l_min) ) return 0; 613 614 // 1. take the lock protecting windows in write mode 615 remote_rwlock_wr_acquire( windows_lock_xp ); 616 617 #if ( DEBUG_DEV_FBF & 1 ) 618 printk("\n[%s] lock taken\n", __FUNCTION__ ); 619 #endif 620 621 // 2. gives the window the lowest priority 622 xptr_t xlist_entry_xp = XPTR( window_cxy , &window_ptr->xlist ); 623 xlist_unlink( xlist_entry_xp ); 624 xlist_add_first( windows_root_xp , xlist_entry_xp ); 625 626 #if ( DEBUG_DEV_FBF & 1 ) 627 printk("\n[%s] set low priority \n", __FUNCTION__ ); 628 #endif 629 630 // 3. set the "hidden" flag in window descriptor 631 hal_remote_s32( XPTR( window_cxy , &window_ptr->hidden ) , true ); 632 633 #if ( DEBUG_DEV_FBF & 1 ) 634 printk("\n[%s] hidden set\n", __FUNCTION__ ); 635 #endif 636 637 // 4. refresh the FBF for the current window position 638 fbf_update( window_xp , 0 , nlines ); 639 640 #if ( DEBUG_DEV_FBF & 1 ) 641 printk("\n[%s] refreshed old position\n", __FUNCTION__ ); 642 #endif 643 644 // 5. set the new coordinates in the window descriptor, 645 hal_remote_s32( XPTR( window_cxy , &window_ptr->l_min ), l_min ); 646 hal_remote_s32( XPTR( window_cxy , &window_ptr->p_min ), p_min ); 647 648 #if ( DEBUG_DEV_FBF & 1 ) 649 printk("\n[%s] l_min & p_min updated\n", __FUNCTION__ ); 650 #endif 651 652 // 6. gives the window the highest priority 653 xlist_unlink( xlist_entry_xp ); 654 xlist_add_last( windows_root_xp , xlist_entry_xp ); 655 656 #if ( DEBUG_DEV_FBF & 1 ) 657 printk("\n[%s] set high priority\n", __FUNCTION__ ); 658 #endif 659 660 // 7. reset the "hidden" flag in window descriptor 661 hal_remote_s32( XPTR( window_cxy , &window_ptr->hidden ) , false ); 662 663 #if ( DEBUG_DEV_FBF & 1 ) 664 printk("\n[%s] hidden reset\n", __FUNCTION__ ); 665 #endif 666 667 // 8. refresh the FBF for the new window position 668 fbf_update( window_xp , 0 , nlines ); 669 670 #if ( DEBUG_DEV_FBF & 1 ) 671 printk("\n[%s] refresh new position\n", __FUNCTION__ ); 672 #endif 673 674 // 9. release the lock protecting windows in write mode 675 remote_rwlock_wr_release( windows_lock_xp ); 676 677 #if DEBUG_DEV_FBF 678 cycle = (uint32_t)hal_get_cycles(); 679 if( DEBUG_DEV_FBF < cycle ) 680 printk("\n[%s] thread[%x,%x] exit / cycle %d\n", 681 __FUNCTION__ , process->pid, this->trdid, cycle ); 682 #endif 683 684 return 0; 685 686 } // end dev_fbf_move_window() 687 688 ///////////////////////////////////////////// 689 error_t dev_fbf_resize_window( uint32_t wid, 690 uint32_t width, 691 uint32_t height ) 692 { 693 thread_t * this = CURRENT_THREAD; 694 process_t * process = this->process; 695 696 #if DEBUG_DEV_FBF 697 uint32_t cycle = (uint32_t)hal_get_cycles(); 698 if( DEBUG_DEV_FBF < cycle ) 699 printk("\n[%s] thread[%x,%x] enters : wid %d / width %d / height %d / cycle %d\n", 700 __FUNCTION__ , process->pid , this->trdid , wid, width , height , cycle ); 701 #endif 702 703 // get cluster and pointers on FBF chdev 704 xptr_t fbf_xp = chdev_dir.fbf[0]; 705 cxy_t fbf_cxy = GET_CXY( fbf_xp ); 706 chdev_t * fbf_ptr = GET_PTR( fbf_xp ); 707 708 // build extended pointers on windows lock and root 709 xptr_t windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock ); 710 xptr_t windows_root_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_root ); 711 712 // build extended pointer on relevant entry in windows_tbl[] array 713 xptr_t windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] ); 714 715 // get extended pointer on remote window descriptor 716 xptr_t window_xp = hal_remote_l64( windows_tbl_xp ); 717 718 if( window_xp == XPTR_NULL ) 719 { 720 printk("\n[ERROR] in %s / thread[%x,%x] / wid %d non registered\n", 721 __FUNCTION__, process->pid, this->trdid, wid ); 722 return -1; 723 } 724 725 // get cluster and local pointer for remote window 726 cxy_t window_cxy = GET_CXY( window_xp ); 727 fbf_window_t * window_ptr = GET_PTR( window_xp ); 728 729 // get process owner PID, width, height, and buffer 730 pid_t owner_pid = hal_remote_l32( XPTR( window_cxy , &window_ptr->pid ) ); 731 uint32_t nlines = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) ); 732 uint32_t npixels = hal_remote_l32( XPTR( window_cxy , &window_ptr->width ) ); 733 void * base = hal_remote_lpt( XPTR( window_cxy , &window_ptr->buffer ) ); 734 735 // check caller PID / owner PID 736 if( owner_pid != process->pid ) 737 { 738 printk("\n[ERROR] in %s : caller PID (%x) != owner PID (%x)\n", 739 __FUNCTION__, process->pid , owner_pid ); 740 return -1; 741 } 742 743 // does nothing if no change 744 if( (width == npixels) && (height == nlines) ) return 0; 745 746 // compute old_size and new size 747 uint32_t old_size = nlines * npixels; 748 uint32_t new_size = width * height; 749 750 // 1. take the lock protecting windows in write mode 751 remote_rwlock_wr_acquire( windows_lock_xp ); 752 753 #if ( DEBUG_DEV_FBF & 1 ) 754 printk("\n[%s] lock taken\n", __FUNCTION__ ); 755 #endif 756 757 // 2. gives the window the lowest priority (remove, then add first) 758 xptr_t xlist_entry_xp = XPTR( window_cxy , &window_ptr->xlist ); 759 xlist_unlink( xlist_entry_xp ); 760 xlist_add_first( windows_root_xp , xlist_entry_xp ); 761 762 #if ( DEBUG_DEV_FBF & 1 ) 763 printk("\n[%s] set low priority\n", __FUNCTION__ ); 764 #endif 765 766 // 3. set the "hidden" flag in window descriptor 767 hal_remote_s32( XPTR( window_cxy , &window_ptr->hidden ) , true ); 768 769 #if ( DEBUG_DEV_FBF & 1 ) 770 printk("\n[%s] hidden set\n", __FUNCTION__ ); 771 #endif 772 773 // 4. refresh the FBF for the current window size 774 fbf_update( window_xp , 0 , nlines ); 775 776 #if ( DEBUG_DEV_FBF & 1 ) 777 printk("\n[%s] refreshed old window\n", __FUNCTION__ ); 778 #endif 779 780 // 5. set the new width & height in the window descriptor, 781 hal_remote_s32( XPTR( window_cxy , &window_ptr->width ), width ); 782 hal_remote_s32( XPTR( window_cxy , &window_ptr->height ), height ); 783 784 #if ( DEBUG_DEV_FBF & 1 ) 785 printk("\n[%s] width & height updated\n", __FUNCTION__ ); 786 #endif 787 788 // 6. resize vseg if required 789 vmm_global_resize_vseg( process, (intptr_t)base, (intptr_t)base, width * height ); 790 791 #if ( DEBUG_DEV_FBF & 1 ) 792 printk("\n[%s] vseg resized\n", __FUNCTION__ ); 793 #endif 794 795 // 7. fill buffer extension if required 796 if( new_size > old_size ) memset( base + old_size , 0 , new_size - old_size ); 797 798 #if ( DEBUG_DEV_FBF & 1 ) 799 printk("\n[%s] buffer extension initialized\n", __FUNCTION__ ); 800 #endif 801 802 // 8. gives the window the highest priority 803 xlist_unlink( xlist_entry_xp ); 804 xlist_add_last( windows_root_xp , xlist_entry_xp ); 805 806 #if ( DEBUG_DEV_FBF & 1 ) 807 printk("\n[%s] set high priority\n", __FUNCTION__ ); 808 #endif 809 810 // 9. reset the "hidden" flag in window descriptor 811 hal_remote_s32( XPTR( window_cxy , &window_ptr->hidden ) , false ); 812 813 #if ( DEBUG_DEV_FBF & 1 ) 814 printk("\n[%s] hidden reset\n", __FUNCTION__ ); 815 #endif 816 817 // 10. refresh the FBF for the new window position 818 fbf_update( window_xp , 0 , height ); 819 820 #if ( DEBUG_DEV_FBF & 1 ) 821 printk("\n[%s] refresh new position\n", __FUNCTION__ ); 822 #endif 823 824 // 11. release the lock protecting windows in write mode 825 remote_rwlock_wr_release( windows_lock_xp ); 826 827 #if DEBUG_DEV_FBF 828 cycle = (uint32_t)hal_get_cycles(); 829 if( DEBUG_DEV_FBF < cycle ) 830 printk("\n[%s] thread[%x,%x] exit / cycle %d\n", 831 __FUNCTION__ , process->pid, this->trdid, cycle ); 832 #endif 833 834 return 0; 835 836 } // end dev_fbf_resize_window() 837 838 /////////////////////////////////////////////// 839 error_t dev_fbf_refresh_window( uint32_t wid, 840 uint32_t line_first, 841 uint32_t line_last ) 842 { 843 // get local pointers on calling thread and process 844 thread_t * this = CURRENT_THREAD; 845 process_t * process = this->process; 846 847 #if DEBUG_DEV_FBF 848 uint32_t cycle = (uint32_t)hal_get_cycles(); 849 if( DEBUG_DEV_FBF < cycle ) 850 printk("\n[%s] thread[%x,%x] enters for wid %d / first %d / last %d / cycle %d\n", 851 __FUNCTION__ , process->pid, this->trdid, wid, line_first, line_last, cycle ); 852 #endif 853 854 // get cluster and pointers on FBF chdev 855 xptr_t fbf_xp = chdev_dir.fbf[0]; 856 cxy_t fbf_cxy = GET_CXY( fbf_xp ); 857 chdev_t * fbf_ptr = GET_PTR( fbf_xp ); 858 859 // build extended pointer on windows lock 860 xptr_t windows_lock_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_lock ); 861 862 // build extended pointer on relevant entry in windows_tbl[] array 863 xptr_t windows_tbl_xp = XPTR( fbf_cxy , &fbf_ptr->ext.fbf.windows_tbl[wid] ); 864 865 // get pointers on remote window descriptor 866 xptr_t window_xp = hal_remote_l64( windows_tbl_xp ); 867 cxy_t window_cxy = GET_CXY( window_xp ); 868 fbf_window_t * window_ptr = GET_PTR( window_xp ); 869 870 // check <wid> argument 871 if( window_xp == XPTR_NULL ) 872 { 873 printk("\n[ERROR] in %s / thread[%x,%x] / wid %d non registered\n", 874 __FUNCTION__, process->pid, this->trdid, wid ); 875 return -1; 876 } 877 878 // get process owner PID 879 pid_t owner_pid = hal_remote_l32( XPTR( window_cxy , &window_ptr->pid ) ); 880 881 // check caller PID / owner PID 882 if( owner_pid != process->pid ) 883 { 884 printk("\n[ERROR] in %s : caller PID (%x) != owner PID (%x)\n", 885 __FUNCTION__, process->pid , owner_pid ); 886 return -1; 887 } 888 889 // get number of lines in window 890 uint32_t nlines = hal_remote_l32( XPTR( window_cxy , &window_ptr->height ) ); 891 892 // check <line_first> and <line_last> arguments 893 if( (line_first >= nlines) || (line_last > nlines) || (line_first >= line_last) ) 894 { 895 printk("\n[ERROR] in %s : illegal (l_first %d , l_last %d) / height %d\n", 896 __FUNCTION__, line_first, line_last, nlines ); 897 return -1; 898 } 899 900 // take the lock protecting windows xlist in read mode 901 remote_rwlock_rd_acquire( windows_lock_xp ); 902 903 // update FBF 904 fbf_update( window_xp , line_first , line_last ); 905 906 // release the lock protecting windows xlist in write mode 907 remote_rwlock_rd_release( windows_lock_xp ); 908 909 #if DEBUG_DEV_FBF 910 cycle = (uint32_t)hal_get_cycles(); 911 if( DEBUG_DEV_FBF < cycle ) 912 printk("\n[%s] thread[%x,%x] exit for wid %d / cycle %d\n", 913 __FUNCTION__, process->pid, this->trdid, wid, cycle ); 914 #endif 915 916 return 0; 917 918 } // end dev_fbf_refresh_window() 919 920 /////////////////////////////////////////////// 921 // TODO Deprecated : january 2020 [AG] 922 /////////////////////////////////////////////// 923 error_t dev_fbf_move_data( bool_t is_write, 924 void * user_buffer, 925 uint32_t npixels, 926 uint32_t offset ) 927 { 928 // get pointer on calling thread 929 thread_t * this = CURRENT_THREAD; 930 931 #if DEBUG_DEV_FBF 932 uint32_t cycle = (uint32_t)hal_get_cycles(); 933 if( DEBUG_DEV_FBF < cycle ) 934 printk("\n[%s] thread[%x,%x] : buffer %x / npixels %d / offset %x / cycle %d\n", 935 __FUNCTION__ , this->process->pid, this->trdid, 936 user_buffer, npixels, offset, cycle ); 937 #endif 938 939 // get pointers on FBF chdev 940 xptr_t fbf_xp = chdev_dir.fbf[0]; 941 cxy_t fbf_cxy = GET_CXY( fbf_xp ); 942 chdev_t * fbf_ptr = GET_PTR( fbf_xp ); 105 943 106 944 // get frame buffer width and height … … 108 946 uint32_t height = hal_remote_l32 ( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.height ) ); 109 947 110 // check offset and length versus FBF size 111 assert( ((offset + length) <= (width * height)) , 112 "offset %d / length %d / width %d / height %d\n", offset, length, width, height ); 948 // check offset and npixels versus FBF size 949 if( ((offset + npixels) > (width * height)) ) 950 { 951 printk("\n[ERROR] in %s : offset (%d) + npixels (%d) / width (%d) / height (%d)\n", 952 __FUNCTION__, offset, npixels, width, height ); 953 return -1; 954 } 113 955 114 956 // register command in calling thread descriptor 115 957 this->fbf_cmd.dev_xp = fbf_xp; 116 this->fbf_cmd.type = cmd_type;958 this->fbf_cmd.type = is_write ? FBF_DRIVER_USER_WRITE : FBF_DRIVER_USER_READ; 117 959 this->fbf_cmd.buffer = user_buffer; 118 960 this->fbf_cmd.offset = offset; 119 this->fbf_cmd. length = length;961 this->fbf_cmd.npixels = npixels; 120 962 121 963 // get driver command function … … 130 972 cycle = (uint32_t)hal_get_cycles(); 131 973 if( DEBUG_DEV_FBF < cycle ) 132 printk("\n[%s] thread[%x,%x] completes %s / error = %d / cycle %d\n", 133 __FUNCTION__ , this->process->pid, this->trdid, 134 dev_fbf_cmd_str(cmd_type), error , cycle ); 974 printk("\n[%s] thread[%x,%x] exit / cycle %d\n", 975 __FUNCTION__ , this->process->pid, this->trdid, cycle ); 135 976 #endif 136 977 … … 139 980 140 981 } // end dev_fbf_move_data() 982 983 -
trunk/kernel/devices/dev_fbf.h
r647 r657 1 1 /* 2 * dev_fbf.h - FBF ( Block Device Controler) generic device API definition.2 * dev_fbf.h - FBF (Frame Buffer) generic device API definition. 3 3 * 4 * Author Alain Greiner (2016,2017,2018,2019 )4 * Author Alain Greiner (2016,2017,2018,2019,2020) 5 5 * 6 6 * Copyright (c) UPMC Sorbonne Universites … … 27 27 #include <hal_kernel_types.h> 28 28 #include <shared_fbf.h> 29 #include <remote_rwlock.h> 30 #include <bits.h> 29 31 30 32 /**** Forward declarations ****/ … … 33 35 34 36 /***************************************************************************************** 35 * Generic Frame Buffer Controlerdefinition37 * Frame Buffer Controler API definition 36 38 * 37 39 * This device provide access to an external graphic display, that is seen 38 40 * as a fixed size frame buffer, mapped in the kernel address space. 39 * The supported pixel encoding types are defined in the <shared_fbf.h> file. 40 * 41 * It supports three command types: 42 * GET_CONFIG : return frame buffer size and type. 43 * READ : move bytes from frame buffer to memory / deschedule the calling thread. 44 * WRITE : move bytes from memory to frame buffer / deschedule the calling thread. 45 * 46 * The READ and WRITE operations do not use the FBF device waiting queue, 47 * the server thread, and the IOC IRQ. The client thread does not deschedule: 48 * it registers the command in the thread descriptor, and calls directly the FBF driver. 49 * that makes a (user <-> kernel) memcpy. 41 * The only pixel encoding type in the current implementation is one byte per pixel 42 * (256 levels of gray). 43 * 44 * It supports a first API, for the user syscalls, implementing a simple windows manager. 45 * This windows manager allows any process to create and use for display one (or several) 46 * window(s). Each window defines a private buffer, dynamically allocated in user space, 47 * that can be directly accessed by the owner process. 48 * These windows can be moved in the frame buffer, they can be resized, they can overlap 49 * other windows, but a window must be entirely contained in the frame buffer. 50 * 51 * To avoid contention, the window descriptor, and the associated user buffer are not 52 * allocated in the cluster containing the FBF chdev, but are distributed: each window 53 * is allocated in the cluster defined by the thread that required the window creation. 54 * 55 * Each window has a single process owner, but all the windows are registered in the FBF 56 * chdev as a windows_tbl[] array, indexed by the window identifier (wid), and each entry 57 * contains an extended pointer on the window descriptor. All windows are also registered 58 * in a trans-cluster xlist, defining the overlapping order (last window in xlist has the 59 * highest priority). 60 * 61 * To refresh a window <wid>, the owner process calls the dev_fbf_refresh_window() 62 * function, that sends parallel RPC_FBF_DISPLAY requests to other cores. All cores 63 * synchronously execute the dev_fbf_display() function. This function scan all windows 64 * to respect the overlaping order, and updates all pixels of the <wid> window. 50 65 * 51 * Note: As we don't use any external DMA to move data, but a purely software approach, 52 * there is no L2/L3 coherence issue. 66 * 1) This "syscall" API defines five syscalls : 67 * - FBF_GET_CONFIG : returns the FBF width, height, and pixel encoding type. 68 * - FBF_CREATE_WINDOW : create a new window , owned by the calling process. 69 * - FBF_DELETE_WINDOW : delete a registered window. 70 * - FBF_MOVE_WINDOW : move in FBF a registered window. 71 * - FBF_REFRESH_WINDOW : request refresh of a given window. 72 * 73 * These 5 operations do not use the FBF device waiting queue, the associated 74 * device thread and the FBF IRQ, as the client thread does NOT deschedule, 75 * and does NOT call the FBF driver. 76 * 77 * 2) Two extra syscalls exist but are deprecated: 78 * - FBF_DIRECT_WRITE : move synchronously pixels from an user buffer to the FBF. 79 * - FBF_DIRECT_READ : move synchronously pixels from the FBF to an user buffer. 80 * 81 * For these deprecated operations, the client thread calls 82 * directly the driver to move data between the user buffer and the FBF. 83 * 84 * 3) The FBF device defines defines four command types to access FBF driver(s) : 85 * - FBF_DRIVER_KERNEL_WRITE : move pixels from a kernel window to the FBF. 86 * - FBF_DRIVER_KERNEL_READ : move pixels from the FBF to a kernel window. 87 * - FBF_DRIVER_USER_WRITE : move bytes from an user buffer to the FBF. 88 * - FBF_DRIVER_USER_READ : move bytes from the FBF to an user buffer. 89 * 90 * Note: As we don't use any external DMA to move data to or from the frame buffer, 91 * but only software memcpy, there is no L2/L3 coherence issue for this device. 92 * 53 93 *****************************************************************************************/ 54 94 … … 59 99 typedef struct fbf_extend_s 60 100 { 61 uint32_t width; /*! number of pixels per line. */ 62 uint32_t height; /*! total number of lines. */ 63 uint32_t subsampling; /*! pixel encoding type. */ 101 remote_rwlock_t windows_lock; /*! lock protecting windows xlist */ 102 xlist_entry_t windows_root; /*! root of the windows xlist */ 103 104 xptr_t windows_tbl[CONFIG_FBF_WINDOWS_MAX_NR]; /*! window desc. */ 105 bitmap_t windows_bitmap[CONFIG_FBF_WINDOWS_MAX_NR >> 5]; /*! wid allocator */ 106 107 uint32_t width; /*! number of pixels per line. */ 108 uint32_t height; /*! total number of lines. */ 109 uint32_t subsampling; /*! pixel encoding type. */ 64 110 } 65 111 fbf_extend_t; … … 70 116 *****************************************************************************************/ 71 117 72 enum fbf_impl_e 118 typedef enum 73 119 { 74 120 IMPL_FBF_SCL = 0, … … 77 123 fbf_impl_t; 78 124 125 /****************************************************************************************** 126 * This structure defines the FBF command for all drivers implementing the FBF device. 127 *****************************************************************************************/ 128 129 typedef enum 130 { 131 FBF_DRIVER_USER_READ = 1, 132 FBF_DRIVER_USER_WRITE = 2, 133 FBF_DRIVER_KERNEL_READ = 3, 134 FBF_DRIVER_KERNEL_WRITE = 4, 135 } 136 fbf_driver_cmd_type_t; 137 79 138 typedef struct fbf_command_s 80 139 { 81 140 xptr_t dev_xp; /*! extended pointer on device descriptor */ 82 uint32_t type; /*! requested operation type.*/83 uint32_t length;/*! number of bytes. */84 uint32_t offset; /*! offset in frame buffer ( bytes)*/85 void * buffer; /*! pointer on memory buffer in user space*/141 uint32_t type; /*! requested driver operation type. */ 142 uint32_t npixels; /*! number of bytes. */ 143 uint32_t offset; /*! offset in frame buffer (pixels) */ 144 void * buffer; /*! pointer on memory buffer (kernel or user) */ 86 145 uint32_t error; /*! operation status (0 if success) */ 87 146 } 88 147 fbf_command_t; 89 148 90 91 /****************************************************************************************** 92 * This function returns a printable string for a given FBF command <cmd_type>. 93 ****************************************************************************************** 94 * @ cmd_type : FBF command type (defined in shared_fbf.h file). 149 /****************************************************************************************** 150 * This structure defines an FBF window descriptor, allocated to a given user process. 151 * The window descriptor and the associated buffer are allocated in the cluster where 152 * is running the thread requesting the window creation. 153 * The <wid> allocator, the window_tbl[] array, and the root of the xlist of windows are 154 * imlemented in the FBF device extension. 155 *****************************************************************************************/ 156 157 typedef struct fbf_window_s 158 { 159 pid_t pid; /*! owner process identifier */ 160 uint32_t wid; /*! window identifier */ 161 uint32_t height; /*! number of lines in window */ 162 uint32_t width; /*! number of pixels per line in window */ 163 uint32_t l_min; /*! first line index in FBF */ 164 uint32_t p_min; /*! first pixel index in FBF */ 165 uint8_t * buffer; /*! pointer on buffer in user space */ 166 bool_t hidden; /*! no display on FBF when true */ 167 xlist_entry_t xlist; /*! member of registered FBF windows list */ 168 } 169 fbf_window_t; 170 171 /****************************************************************************************** 172 * This function returns a printable string for a given FBF user command <cmd_type>. 173 * WARNING : It must be kept consistent with the enum in the <shared_fbf.h> file 174 ****************************************************************************************** 175 * @ cmd_type : FBF user command type (defined in shared_fbf.h file). 95 176 * @ returns a string pointer. 96 177 *****************************************************************************************/ … … 100 181 * This function completes the FBF chdev descriptor initialisation. 101 182 * It calls the specific driver initialisation function, to initialise the hardware 102 * device and the chdev extension. It must be called by a local thread.183 * device, and the chdev extension. It must be called by a local thread. 103 184 ****************************************************************************************** 104 185 * @ chdev : pointer on FBF chdev descriptor. … … 107 188 108 189 /****************************************************************************************** 109 * This function returns the frame buffer size and type. 190 * This function implements the fbf_get_config() syscall, and returns the FBF 191 * size and type. It can be called by a client thread running in any cluster. 110 192 * It does NOT access the hardware, as the size and type have been registered 111 * in the chdev descriptor extension. 193 * in the chdev descriptor extension by the dev_fbf_init() function. 194 * It can be called by any thread running in any cluster. 112 195 ****************************************************************************************** 113 196 * @ width : [out] number of pixels per line. … … 120 203 121 204 /****************************************************************************************** 122 * This blocking function moves <length> bytes between the frame buffer, starting from 123 * byte defined by <offset>, and an user buffer defined by the <user_buffer> argument. 124 * It can be called by a client thread running in any cluster. 125 * The transfer direction are defined by the <cmd_type> argument. 205 * This function implements the fbf_create_window() syscall. 206 * It registers a new window in the windows_tbl[] array, and the windows list, 207 * registers in the reference cluster an ANON vseg, that will be mapped in local cluster. 208 * The window index <wid> is dynamically allocated. The owner is the calling process. 209 * The FBF window is defined by the <nlines>, <npixels>, <l_min>, <p_min> arguments. 210 * It can be called by any thread running in any cluster. As this vseg is not directly 211 * mapped to the frame buffer, the owner process can access this private buffer without 212 * syscall. As for any vseg, the physical memory is allocated on demand at each page fault. 213 * The created vseg base address in user space is returned in the <user_base> argument. 214 * 215 * Implementation note: 216 * 1. it allocates memory in the local cluster for the window, 217 * 2. it creates in the associated vseg, 218 * 3. it initializes the window descriptor, 219 * 4. it takes the lock protecting the windows in write mode, 220 * 5. it allocates a new <wid>, 221 * 6. it registers the window in the window_tbl[] array, 222 * 7. it registers the window in the windows list, 223 * 8. it releases the lock protecting windows. 224 * It does not call the FBF driver. 225 ****************************************************************************************** 226 * @ nlines : [in] number of lines in window. 227 * @ npixels : [in] number of pixels per line in window. 228 * @ l_min : [in] first pixel index in FBF. 229 * @ p_min : [in] first line index in FBF. 230 * @ user_base : [out] pointer on allocated buffer base in user space. 231 * @ return the <wid> index if success / returns -1 if failure 232 *****************************************************************************************/ 233 uint32_t dev_fbf_create_window( uint32_t nlines, 234 uint32_t npixels, 235 uint32_t l_min, 236 uint32_t p_min, 237 intptr_t * user_base ); 238 239 /****************************************************************************************** 240 * This function implements the fbf_delete_window() syscall to delete a FBF window, 241 * and release all memory allocated for this window and for the associated vseg. 242 * releases the memory allocated for the window buffer and for the window descriptor. 243 * It can be called by any thread running in any cluster. 244 * 245 * Implementation note: 246 * 1. it takes the lock protecting windows in write mode, 247 * 2. it set the hidden flag in deleted window descriptor, 248 * 3. it refresh the FBF window, 249 * 4. it removes the window from windows_tbl[] array, 250 * 5. it removes the window from xlist, 251 * 6. it releases the wid to bitmap, 252 * 7. it releases the lock protecting windows, 253 * 8. it releases the memory allocated for window descriptor, 254 * 9. it deletes the associated vseg in all clusters 255 * It does not call directly the FBF driver. 256 ****************************************************************************************** 257 * @ wid : [in] window index in window_tbl[]. 258 * @ returns 0 if success / returns -1 if wid not registered. 259 *****************************************************************************************/ 260 error_t dev_fbf_delete_window( uint32_t wid ); 261 262 /****************************************************************************************** 263 * This function implements the fbf_move_window() syscall. 264 * It moves a window identified by the <wid> argument to a new position in the FBF, 265 * defined by the <l_min> and <p_min> arguments. 266 * It can be called by any thread running in any cluster. 267 * 268 * Implementation note: 269 * 1. it takes the lock protecting windows in write mode, 270 * 2. it gives the modified window the lowest priority, 271 * 3. it set the "hidden" flag in window descriptor, 272 * 4. it refresh the FBF for the current window position, 273 * 5. it set the new coordinates in the window descriptor, 274 * 6. it gives the modified window the highest priority, 275 * 7. it reset the "hidden" flag in window descriptor, 276 * 8. it refresh the FBF for the new window position, 277 * 9. it releases the lock protecting windows, 278 * It does not call directly the FBF driver. 279 ****************************************************************************************** 280 * @ wid : [in] window index in window_tbl[]. 281 * @ l_min : [in] new first pixel index in FBF. 282 * @ p_min : [in] new first line index in FBF. 283 * @ returns 0 if success / returns -1 if illegal arguments. 284 *****************************************************************************************/ 285 error_t dev_fbf_move_window( uint32_t wid, 286 uint32_t l_min, 287 uint32_t p_min ); 288 289 /****************************************************************************************** 290 * This function implements the fbf_resize_window() syscall. 291 * It changes the <width> and <height> of a window identified by the <wid> argument. 292 * It updates the associated vseg "size" if required, but does not change the vseg "base". 293 * When the new window buffer is larger than the existing one, it is 0 filled. 294 * It can be called by any thread running in any cluster. 295 * 296 * Implementation note: 297 * 1. it takes the lock protecting windows in write mode, 298 * 2. it gives the modified window the lowest priority, 299 * 3. it set the "hidden" flag in window descriptor, 300 * 4. it refresh the FBF for the current window, 301 * 5. it set the new size in the window descriptor, 302 * 6. it resizes the associated vseg if required, 303 * 7. if fill the window buffer extension with 0 if required, 304 * 8. it gives the modified window the highest priority, 305 * 9. it reset the "hidden" flag in window descriptor, 306 * 10. it refresh the FBF for the new window, 307 * 11. it releases the lock protecting windows, 308 * It does not call directly the FBF driver. 309 ****************************************************************************************** 310 * @ wid : [in] window index in window_tbl[]. 311 * @ width : [in] new number of pixels per line. 312 * @ height : [in] new number of lines. 313 * @ returns 0 if success / returns -1 if illegal arguments. 314 *****************************************************************************************/ 315 error_t dev_fbf_resize_window( uint32_t wid, 316 uint32_t width, 317 uint32_t height ); 318 319 /****************************************************************************************** 320 * This function implements the fbf_refresh_window() syscall. 321 * It allows an owner process to signal the windows manager that some lines of a window 322 * identified by the <wid>, <line_min>, and <line_max> argument have been modified, and 323 * must be refreshed in the FBF. It scans all the registered FBF windows to respect the 324 * overlap order defined by the windows xlist. 325 * It can be called by any thread running in any cluster. 326 * 327 * Implementation note: 328 * 1. it takes the lock protecting windows in read mode, 329 * 2. it refresh the FBF, 330 * 3. it releases the lock protecting windows, 331 * It does not call directly the FBF driver. 332 ****************************************************************************************** 333 * @ wid : [in] window index in window_tbl[] 334 * @ line_first : [in] first line index in window. 335 * @ line_last : [in] last line index (excluded). 336 * @ returns 0 if success / returns -1 if wid not registered. 337 *****************************************************************************************/ 338 error_t dev_fbf_refresh_window( uint32_t wid, 339 uint32_t line_first, 340 uint32_t line_last ); 341 342 /****************************************************************************************** 343 * WARNING : This function is deprecated ( january 2020 [AG] ). It was defined 344 * to implement the fbf_read() and fbf_write() deprecated syscalls. 345 * 346 * It moves <length> bytes between the frame buffer, starting from pixel defined 347 * by the <offset> argument, and an user buffer defined by the <user_buffer> argument. 348 * The transfer direction are defined by the <is_write> argument. 349 * An error is returned when <offset> + <npixels> is larger than the FBF size. 126 350 * The request is registered in the client thread descriptor, but the client thread is 127 351 * not descheduled, and calls directly the FBF driver. 128 ****************************************************************************************** 129 * @ cmd_type : FBF_READ / FBF_WRITE / FBF_SYNC_READ / FBF_SYN_WRITE. 130 * @ user_buffer : pointer on memory buffer in user space. 131 * @ length : number of bytes. 132 * @ offset : first byte in frame buffer. 133 * @ returns 0 if success / returns EINVAL if error. 134 *****************************************************************************************/ 135 error_t dev_fbf_move_data( uint32_t cmd_type, 352 * It can be called by a client thread running in any cluster. 353 ****************************************************************************************** 354 * @ is_write : [in] write FBF weh true / read FBF when false 355 * @ user_buffer : [in] pointer on memory buffer in user space. 356 * @ npixels : [in] number of bytes. 357 * @ offset : [in] first byte in frame buffer. 358 * @ returns 0 if success / returns -1 if error. 359 *****************************************************************************************/ 360 error_t dev_fbf_move_data( bool_t is_write, 136 361 void * user_buffer, 137 uint32_t length,362 uint32_t npixels, 138 363 uint32_t offset ); 139 364 -
trunk/kernel/devices/dev_ioc.c
r647 r657 2 2 * dev_ioc.c - IOC (Block Device Controler) generic device API implementation. 3 3 * 4 * Author Alain Greiner (2016,2017,2018,2019 )4 * Author Alain Greiner (2016,2017,2018,2019,2020) 5 5 * 6 6 * Copyright (c) UPMC Sorbonne Universites … … 37 37 extern chdev_directory_t chdev_dir; // allocated in kernel_init.c 38 38 39 //////////////////////////////////////// 40 char * dev_ioc_cmd_str( cmd_type_t cmd )39 //////////////////////////////////////////// 40 char * dev_ioc_cmd_str( ioc_cmd_type_t cmd ) 41 41 { 42 42 if ( cmd == IOC_READ ) return "READ"; … … 91 91 } // end dev_ioc_init() 92 92 93 /////////////////////////////////////////////// 94 error_t dev_ioc_move_data( uint32_t cmd_type, 93 //////////////////////////////////////////////////////////////////////////////////// 94 // This static function executes an asynchronous SYNC_READ or SYNC_WRITE request. 95 // thread in the IOC device waiting queue, activates the server thread, blocks on 96 // the THREAD_BLOCKED_IO condition and deschedules. 97 // The clent is re-activated by the server thread after IO operation completion. 98 //////////////////////////////////////////////////////////////////////////////////// 99 static error_t dev_ioc_move( uint32_t cmd_type, 100 xptr_t buffer_xp, 101 uint32_t lba, 102 uint32_t count ) 103 { 104 thread_t * this = CURRENT_THREAD; // pointer on client thread 105 106 // get extended pointer on IOC chdev descriptor 107 xptr_t ioc_xp = chdev_dir.ioc[0]; 108 109 // check dev_xp 110 assert( (ioc_xp != XPTR_NULL) , "undefined IOC chdev descriptor" ); 111 112 // register command in client thread 113 this->ioc_cmd.dev_xp = ioc_xp; 114 this->ioc_cmd.type = cmd_type; 115 this->ioc_cmd.buf_xp = buffer_xp; 116 this->ioc_cmd.lba = lba; 117 this->ioc_cmd.count = count; 118 119 // register client thread in IOC queue, blocks and deschedules 120 chdev_register_command( ioc_xp ); 121 122 // return I/O operation status 123 return this->ioc_cmd.error; 124 125 } // end dev_ioc_move() 126 127 //////////////////////////////////////////////////////////////////////////////////// 128 // This static function executes a synchronous READ or WRITE request. 129 // It register the command in the client thread descriptor, and calls directly 130 // the driver cmd function. 131 //////////////////////////////////////////////////////////////////////////////////// 132 error_t dev_ioc_sync_move( uint32_t cmd_type, 95 133 xptr_t buffer_xp, 96 134 uint32_t lba, … … 98 136 { 99 137 thread_t * this = CURRENT_THREAD; // pointer on client thread 100 101 #if ( DEBUG_DEV_IOC_RX || DEBUG_DEV_IOC_TX )102 uint32_t cycle = (uint32_t)hal_get_cycles();103 #endif104 105 // software L2/L3 cache coherence for memory buffer106 if( chdev_dir.iob )107 {108 if( (cmd_type == IOC_SYNC_READ) || (cmd_type == IOC_READ) )109 {110 dev_mmc_inval( buffer_xp , count<<9 );111 }112 else // (cmd_type == IOC_SYNC_WRITE) or (cmd_type == IOC_WRITE)113 {114 dev_mmc_sync ( buffer_xp , count<<9 );115 }116 }117 138 118 139 // get extended pointer on IOC chdev descriptor … … 129 150 this->ioc_cmd.count = count; 130 151 131 // for a synchronous acces, the driver is directly called by the client thread 132 if( (cmd_type == IOC_SYNC_READ) || (cmd_type == IOC_SYNC_WRITE) ) 133 { 134 135 #if DEBUG_DEV_IOC_RX 136 uint32_t cycle = (uint32_t)hal_get_cycles(); 137 if( (DEBUG_DEV_IOC_RX < cycle) && (cmd_type == IOC_SYNC_READ) ) 138 printk("\n[%s] thread[%x,%x] enters for SYNC_READ / lba %x / buffer[%x,%x] / cycle %d\n", 139 __FUNCTION__ , this->process->pid, this->trdid, lba, 140 GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle ); 141 #endif 142 143 #if DEBUG_DEV_IOC_TX 144 uint32_t cycle = (uint32_t)hal_get_cycles(); 145 if( (DEBUG_DEV_IOC_TX < cycle) && (cmd_type == IOC_SYNC_WRITE) ) 146 printk("\n[%s] thread[%x,%x] enters for SYNC_WRITE / lba %x / buffer[%x,%x] / cycle %d\n", 147 __FUNCTION__ , this->process->pid, this->trdid, lba, 148 GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle ); 149 #endif 150 // get driver command function 151 cxy_t ioc_cxy = GET_CXY( ioc_xp ); 152 chdev_t * ioc_ptr = GET_PTR( ioc_xp ); 153 dev_cmd_t * cmd = (dev_cmd_t *)hal_remote_lpt( XPTR( ioc_cxy , &ioc_ptr->cmd ) ); 154 155 // call driver function 156 cmd( XPTR( local_cxy , this ) ); 157 158 #if DEBUG_DEV_IOC_RX 159 if( (DEBUG_DEV_IOC_RX < cycle) && (cmd_type == IOC_SYNC_READ) ) 160 printk("\n[%s] thread[%x,%x] resumes for IOC_SYNC_READ\n", 161 __FUNCTION__, this->process->pid , this->trdid ) 162 #endif 163 164 #if DEBUG_DEV_IOC_TX 165 if( (DEBUG_DEV_IOC_RX < cycle) && (cmd_type == IOC_SYNC_WRITE) ) 166 printk("\n[%s] thread[%x,%x] resumes for IOC_SYNC_WRITE\n", 167 __FUNCTION__, this->process->pid , this->trdid ) 168 #endif 169 170 } 171 // for an asynchronous access, the client thread registers in the chdev waiting queue, 172 // activates server thread, blocks on THREAD_BLOCKED_IO and deschedules. 173 // It is re-activated by the server thread after IO operation completion. 174 else // (cmd_type == IOC_READ) || (cmd_type == IOC_WRITE) 175 { 176 177 #if DEBUG_DEV_IOC_RX 178 uint32_t cycle = (uint32_t)hal_get_cycles(); 179 if( (DEBUG_DEV_IOC_RX < cycle) && (cmd_type == IOC_READ) ) 180 printk("\n[%s] thread[%x,%x] enters for READ / lba %x / buffer[%x,%x] / cycle %d\n", 181 __FUNCTION__ , this->process->pid, this->trdid, lba, 182 GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle ); 183 #endif 184 185 #if DEBUG_DEV_IOC_TX 186 uint32_t cycle = (uint32_t)hal_get_cycles(); 187 if( (DEBUG_DEV_IOC_TX < cycle) && (cmd_type == IOC_WRITE) ) 188 printk("\n[%s] thread[%x,%x] enters for WRITE / lba %x / buffer[%x,%x] / cycle %d\n", 189 __FUNCTION__ , this->process->pid, this->trdid, lba, 190 GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle ); 191 #endif 192 chdev_register_command( ioc_xp ); 193 194 #if(DEBUG_DEV_IOC_RX ) 195 if( (DEBUG_DEV_IOC_RX < cycle) && (cmd_type == IOC_READ) ) 196 printk("\n[%s] thread[%x,%x] resumes for IOC_READ\n", 197 __FUNCTION__, this->process->pid , this->trdid ) 198 #endif 199 200 #if(DEBUG_DEV_IOC_TX & 1) 201 if( (DEBUG_DEV_IOC_RX < cycle) && (cmd_type == IOC_WRITE) ) 202 printk("\n[%s] thread[%x,%x] resumes for IOC_WRITE\n", 203 __FUNCTION__, this->process->pid , this->trdid ) 204 #endif 205 206 } 152 // get driver command function 153 cxy_t ioc_cxy = GET_CXY( ioc_xp ); 154 chdev_t * ioc_ptr = GET_PTR( ioc_xp ); 155 dev_cmd_t * cmd = (dev_cmd_t *)hal_remote_lpt( XPTR( ioc_cxy , &ioc_ptr->cmd ) ); 156 157 // call driver function whithout blocking & descheduling 158 cmd( XPTR( local_cxy , this ) ); 207 159 208 160 // return I/O operation status 209 161 return this->ioc_cmd.error; 210 162 211 } // end dev_ioc_move_data() 212 213 163 } // end dev_ioc_sync_move() 164 165 /////////////////////////////////////////// 166 error_t dev_ioc_read( xptr_t buffer_xp, 167 uint32_t lba, 168 uint32_t count ) 169 { 170 171 #if DEBUG_DEV_IOC_RX 172 thread_t * this = CURRENT_THREAD; 173 uint32_t cycle = (uint32_t)hal_get_cycles(); 174 if( DEBUG_DEV_IOC_RX < cycle ) 175 printk("\n[%s] thread[%x,%x] enters IOC_READ / lba %x / buffer[%x,%x] / cycle %d\n", 176 __FUNCTION__, this->process->pid, this->trdid, lba, 177 GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle ); 178 #endif 179 180 // software L2/L3 cache coherence for memory buffer 181 if( chdev_dir.iob ) dev_mmc_inval( buffer_xp , count<<9 ); 182 183 // request an asynchronous transfer 184 error_t error = dev_ioc_move( IOC_READ, 185 buffer_xp, 186 lba, 187 count ); 188 #if(DEBUG_DEV_IOC_RX & 1) 189 cycle = (uint32_t)hal_get_cycles(); 190 if( DEBUG_DEV_IOC_RX < cycle ) 191 printk("\n[%s] thread[%x,%x] exit IOC_READ / cycle %d\n", 192 __FUNCTION__, this->process->pid , this->trdid , cycle ); 193 #endif 194 195 return error; 196 197 } // end dev_ioc_read() 198 199 /////////////////////////////////////////// 200 error_t dev_ioc_write( xptr_t buffer_xp, 201 uint32_t lba, 202 uint32_t count ) 203 { 204 205 #if DEBUG_DEV_IOC_TX 206 thread_t * this = CURRENT_THREAD; 207 uint32_t cycle = (uint32_t)hal_get_cycles(); 208 if( DEBUG_DEV_IOC_TX < cycle ) 209 printk("\n[%s] thread[%x,%x] enters IOC_WRITE / lba %x / buffer[%x,%x] / cycle %d\n", 210 __FUNCTION__, this->process->pid, this->trdid, lba, 211 GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle ); 212 #endif 213 214 // software L2/L3 cache coherence for memory buffer 215 if( chdev_dir.iob ) dev_mmc_sync ( buffer_xp , count<<9 ); 216 217 // request a blocking, but asynchronous, transfer 218 error_t error = dev_ioc_move( IOC_WRITE, 219 buffer_xp, 220 lba, 221 count ); 222 #if(DEBUG_DEV_IOC_TX & 1) 223 cycle = (uint32_t)hal_get_cycles(); 224 if( DEBUG_DEV_IOC_TX < cycle ) 225 printk("\n[%s] thread[%x,%x] exit IOC_WRITE / cycle %d\n", 226 __FUNCTION__, this->process->pid , this->trdid , cycle ); 227 #endif 228 229 return error; 230 231 } // end dev_ioc_write() 232 233 234 /////////////////////////////////////////// 235 error_t dev_ioc_sync_read( xptr_t buffer_xp, 236 uint32_t lba, 237 uint32_t count ) 238 { 239 240 #if DEBUG_DEV_IOC_RX 241 thread_t * this = CURRENT_THREAD; 242 uint32_t cycle = (uint32_t)hal_get_cycles(); 243 if( DEBUG_DEV_IOC_RX < cycle ) 244 printk("\n[%s] thread[%x,%x] enters IOC_SYNC_READ / lba %x / buffer[%x,%x] / cycle %d\n", 245 __FUNCTION__, this->process->pid, this->trdid, lba, 246 GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle ); 247 #endif 248 249 // software L2/L3 cache coherence for memory buffer 250 if( chdev_dir.iob ) dev_mmc_inval( buffer_xp , count<<9 ); 251 252 // request an asynchronous transfer 253 error_t error = dev_ioc_sync_move( IOC_SYNC_READ, 254 buffer_xp, 255 lba, 256 count ); 257 #if(DEBUG_DEV_IOC_RX & 1) 258 cycle = (uint32_t)hal_get_cycles(); 259 if( DEBUG_DEV_IOC_RX < cycle ) 260 printk("\n[%s] thread[%x,%x] exit IOC_SYNC_READ / cycle %d\n", 261 __FUNCTION__, this->process->pid , this->trdid , cycle ); 262 #endif 263 264 return error; 265 266 } // end dev_ioc_sync_read() 267 268 ///////////////////////////////////////////////// 269 error_t dev_ioc_sync_write( xptr_t buffer_xp, 270 uint32_t lba, 271 uint32_t count ) 272 { 273 274 #if DEBUG_DEV_IOC_TX 275 thread_t * this = CURRENT_THREAD; 276 uint32_t cycle = (uint32_t)hal_get_cycles(); 277 if( DEBUG_DEV_IOC_TX < cycle ) 278 printk("\n[%s] thread[%x,%x] enters IOC_SYNC_WRITE / lba %x / buffer[%x,%x] / cycle %d\n", 279 __FUNCTION__, this->process->pid, this->trdid, lba, 280 GET_CXY(buffer_xp), GET_PTR(buffer_xp), cycle ); 281 #endif 282 283 // software L2/L3 cache coherence for memory buffer 284 if( chdev_dir.iob ) dev_mmc_sync ( buffer_xp , count<<9 ); 285 286 // request a blocking, but asynchronous, transfer 287 error_t error = dev_ioc_sync_move( IOC_SYNC_WRITE, 288 buffer_xp, 289 lba, 290 count ); 291 #if(DEBUG_DEV_IOC_TX & 1) 292 cycle = (uint32_t)hal_get_cycles(); 293 if( DEBUG_DEV_IOC_TX < cycle ) 294 printk("\n[%s] thread[%x,%x] exit IOC_SYNC_WRITE / cycle %d\n", 295 __FUNCTION__, this->process->pid , this->trdid , cycle ); 296 #endif 297 298 return error; 299 300 } // end dev_ioc_sync_write() 301 302 -
trunk/kernel/devices/dev_ioc.h
r647 r657 2 2 * dev_ioc.h - IOC (Block Device Controler) generic device API definition. 3 3 * 4 * Author Alain Greiner (2016,2017,2018,2019 )4 * Author Alain Greiner (2016,2017,2018,2019,2020) 5 5 * 6 6 * Copyright (c) UPMC Sorbonne Universites … … 45 45 * - SYNC_WRITE : move blocks from memory to device, with a busy waiting policy. 46 46 * 47 * For the heREAD or WRITE operations, the client thread is descheduled, and the work47 * For the READ or WRITE operations, the client thread is descheduled, and the work 48 48 * is done by the server thread associated to the IOC device: 49 49 * The client thread calls the dev_ioc_move_data() kernel functions that (i) registers … … 93 93 94 94 /****************************************************************************************** 95 * This defines the (implementation independant) command passed to the driver.95 * This structure defines the IOC command for all drivers implementing the IOC device. 96 96 *****************************************************************************************/ 97 97 … … 103 103 IOC_SYNC_WRITE = 3, 104 104 } 105 cmd_type_t;105 ioc_cmd_type_t; 106 106 107 107 typedef struct ioc_command_s … … 111 111 uint32_t lba; /*! first block index */ 112 112 uint32_t count; /*! number of blocks */ 113 xptr_t buf_xp; /*! extended pointer on memory buffer*/113 xptr_t buf_xp; /*! extended pointer on kernel memory buffer */ 114 114 uint32_t error; /*! operation status (0 if success) */ 115 115 } … … 122 122 * @ return pointer on string. 123 123 *****************************************************************************************/ 124 char * dev_ioc_cmd_str( cmd_type_t cmd );124 char * dev_ioc_cmd_str( ioc_cmd_type_t cmd ); 125 125 126 126 /****************************************************************************************** … … 138 138 139 139 /****************************************************************************************** 140 * This blocking function moves <count> contiguous blocks of data between the block device 141 * starting from block defined by the <lba> argument and a kernel memory buffer, defined 142 * by the <buffer_xp> argument. The transfer direction and mode are defined by the 143 * <cmd_type> argument. The request is always registered in the calling thread descriptor. 144 * - In synchronous mode, the calling thread is not descheduled, and directly calls the 145 * IOC driver, polling the IOC status to detect transfer completion. 146 * - In asynchronous mode, the calling thread blocks and deschedules, and the IOC driver 147 * is called by the server thread associated to the IOC device. 148 ****************************************************************************************** 149 * @ cmd_type : IOC_READ / IOC_WRITE / IOC_SYNC_READ / IOC_SYN_WRITE. 150 * @ buffer_xp : extended pointer on kernel buffer in memory (must be block aligned). 151 * @ lba : first block index on device. 152 * @ count : number of blocks to transfer. 153 * @ returns 0 if success / returns -1 if error. 154 *****************************************************************************************/ 155 error_t dev_ioc_move_data( uint32_t cmd_type, 156 xptr_t buffer_xp, 140 * This blocking function register an asynchronous READ request : move <count> contiguous 141 * blocks from the block device, starting from block defined by the <lba> argument, to a 142 * kernel buffer defined by the <buffer_xp> argument. 143 * It register the request in the client thread descriptor, it register the client thread 144 * in the IOC device queue, it blocks on the THREAD_BLOCKED_IO condition, and deschedules. 145 * It will be reactivated by the DEV server thread when the transfer is completed. 146 * It can be executed by a thread running in any cluster. 147 ****************************************************************************************** 148 * @ buffer_xp : extended pointer on kernel buffer in memory (must be block aligned). 149 * @ lba : first block index on device. 150 * @ count : number of blocks to transfer. 151 * @ returns 0 if success / returns -1 if error. 152 *****************************************************************************************/ 153 error_t dev_ioc_read( xptr_t buffer_xp, 154 uint32_t lba, 155 uint32_t count ); 156 157 /****************************************************************************************** 158 * This blocking function register an asynchronous WRITE request : move <count> contiguous 159 * blocks from a kernel buffer defined by the <buffer_xp> argument to the block device, 160 * starting from block defined by the <lba> argument. 161 * It register the request in the client thread descriptor, it register the client thread 162 * in the IOC device queue, it blocks on the THREAD_BLOCKED_IO condition, and deschedules. 163 * It will be reactivated by the DEV server thread when the transfer is completed. 164 * It can be executed by a thread running in any cluster. 165 ****************************************************************************************** 166 * @ buffer_xp : extended pointer on kernel buffer in memory (must be block aligned). 167 * @ lba : first block index on device. 168 * @ count : number of blocks to transfer. 169 * @ returns 0 if success / returns -1 if error. 170 *****************************************************************************************/ 171 error_t dev_ioc_write( xptr_t buffer_xp, 172 uint32_t lba, 173 uint32_t count ); 174 175 /****************************************************************************************** 176 * This blocking function executes a synchronous SYNC_READ request : it moves <count> 177 * contiguous blocks of data from the block device, starting from block defined by the 178 * <lba> argument to a kernel memory buffer, defined by the <buffer_xp> argument. 179 * The request is registered in the calling thread descriptor, but the client thread calls 180 * directly the driver cmd function, that is also a blocking function returning only 181 * when the transfer is completed. 182 * It can be executed by a thread running in any cluster. 183 ****************************************************************************************** 184 * @ buffer_xp : extended pointer on kernel buffer in memory (must be block aligned). 185 * @ lba : first block index on device. 186 * @ count : number of blocks to transfer. 187 * @ returns 0 if success / returns -1 if error. 188 *****************************************************************************************/ 189 error_t dev_ioc_sync_read( xptr_t buffer_xp, 157 190 uint32_t lba, 158 191 uint32_t count ); 159 192 193 /****************************************************************************************** 194 * This blocking function executes a synchronous SYNC_WRITE request : it moves <count> 195 * contiguous blocks of data from a kernel memory buffer, defined by the <buffer_xp> 196 * argument to the block device, starting from block defined by the <lba> argument. 197 * The request is registered in the calling thread descriptor, but the client thread calls 198 * directly the driver cmd() function, that is also a blocking function returning only 199 * when the transfer is completed. 200 * It can be executed by a thread running in any cluster. 201 ****************************************************************************************** 202 * @ buffer_xp : extended pointer on kernel buffer in memory (must be block aligned). 203 * @ lba : first block index on device. 204 * @ count : number of blocks to transfer. 205 * @ returns 0 if success / returns -1 if error. 206 *****************************************************************************************/ 207 error_t dev_ioc_sync_write( xptr_t buffer_xp, 208 uint32_t lba, 209 uint32_t count ); 210 160 211 #endif /* _DEV_IOC_H */ -
trunk/kernel/devices/dev_mmc.c
r647 r657 2 2 * dev_mmc.c - MMC (Memory Cache Controler) generic device API implementation. 3 3 * 4 * Author Alain Greiner (2016,2017,2018 )4 * Author Alain Greiner (2016,2017,2018,2019,2020) 5 5 * 6 6 * Copyright (c) UPMC Sorbonne Universites … … 176 176 177 177 ///////////////////////////////////////// 178 error_t dev_mmc_ set_error( cxy_t cxy,178 error_t dev_mmc_error_set( cxy_t cxy, 179 179 uint32_t index, 180 180 uint32_t wdata ) … … 185 185 // store command arguments in thread descriptor 186 186 this->mmc_cmd.dev_xp = chdev_dir.mmc[cxy]; 187 this->mmc_cmd.type = MMC_ SET_ERROR;187 this->mmc_cmd.type = MMC_ERROR_SET; 188 188 this->mmc_cmd.reg_index = index; 189 189 this->mmc_cmd.reg_ptr = &wdata; … … 194 194 195 195 ////////////////////////////////////////// 196 error_t dev_mmc_ get_error( cxy_t cxy,196 error_t dev_mmc_error_get( cxy_t cxy, 197 197 uint32_t index, 198 198 uint32_t * rdata ) … … 203 203 // store command arguments in thread descriptor 204 204 this->mmc_cmd.dev_xp = chdev_dir.mmc[cxy]; 205 this->mmc_cmd.type = MMC_ GET_ERROR;205 this->mmc_cmd.type = MMC_ERROR_GET; 206 206 this->mmc_cmd.reg_index = index; 207 207 this->mmc_cmd.reg_ptr = rdata; … … 210 210 return dev_mmc_access( this ); 211 211 } 212 213 ////////////////////////////////////////// //////////214 error_t dev_mmc_ get_instrumentation( cxy_t cxy,215 216 212 213 ////////////////////////////////////////// 214 error_t dev_mmc_instr_get( cxy_t cxy, 215 uint32_t index, 216 uint32_t * rdata ) 217 217 { 218 218 // get calling thread local pointer … … 221 221 // store command arguments in thread descriptor 222 222 this->mmc_cmd.dev_xp = chdev_dir.mmc[cxy]; 223 this->mmc_cmd.type = MMC_ GET_INSTRU;223 this->mmc_cmd.type = MMC_INSTR_GET; 224 224 this->mmc_cmd.reg_index = index; 225 225 this->mmc_cmd.reg_ptr = rdata; … … 228 228 return dev_mmc_access( this ); 229 229 } 230 -
trunk/kernel/devices/dev_mmc.h
r565 r657 2 2 * dev_mmc.h - MMC (Generic L2 cache controller) device API definition. 3 3 * 4 * Authors Alain Greiner (2016,2017,2018 )4 * Authors Alain Greiner (2016,2017,2018,2019,2020) 5 5 * 6 6 * Copyright (c) UPMC Sorbonne Universites … … 34 34 * acting in all clusters containing a level 2 cache controller. 35 35 * 36 * It supports five command types: 36 * It supports three different services: 37 * 1) L2/L3 software cache-coherence operations. 38 * 2) error reporting for architecture specific addressing error. 39 * 3) architecture specific intrumentation registers access. 40 * 41 * It supports therefore five command types: 37 42 * - MMC_CC_INVAL : invalidate all cache lines covering a given buffer in L2 cache. 38 43 * - MMC_CC_SYNC : synchronize all cache lines covering a given buffer to L3 cache. 39 * - MMC_ GET_ERROR: return content of a given error signaling register.40 * - MMC_ SET_ERROR: set a given error signaling register.41 * - MMC_ GET_INSTRU: return content of a given instrumentation register.44 * - MMC_ERROR_GET : return content of a given error signaling register. 45 * - MMC_ERROR_SET : set a given error signaling register. 46 * - MMC_INSTR_GET : return content of a given instrumentation register. 42 47 * 43 48 * As all L2 caches can be accessed by any thread running in any cluster, a calling … … 73 78 MMC_CC_INVAL = 0, 74 79 MMC_CC_SYNC = 1, 75 MMC_ GET_ERROR= 2,76 MMC_ SET_ERROR= 3,77 MMC_ GET_INSTRU= 4,80 MMC_ERROR_GET = 2, 81 MMC_ERROR_SET = 3, 82 MMC_INSTR_GET = 4, 78 83 }; 79 84 … … 126 131 127 132 /***************************************************************************************** 128 * This function set a value in one error signaling MMCregister.133 * This function set a value in one (architecture specific) MMC_ERROR register. 129 134 * It can be executed by any thread in any cluster, because it uses remote accesses 130 135 * to access the L2 cache instrumentation interface in any cluster. … … 135 140 * @ return 0 if success / return EINVAL if failure 136 141 ****************************************************************************************/ 137 error_t dev_mmc_ set_error( cxy_t cxy,142 error_t dev_mmc_error_set( cxy_t cxy, 138 143 uint32_t index, 139 144 uint32_t wdata ); 140 145 141 146 /***************************************************************************************** 142 * This function returns the value contained in one error signaling MMC register.143 * It can be executed by any thread in any cluster, because it uses remote accesses144 * to access the L2 cache instrumentation interface in any cluster.147 * This function returns the value contained in one (architecture specific) MMC_ERROR 148 * register. It can be executed by any thread in any cluster, because it uses remote 149 * accesses to access the L2 cache instrumentation interface in any cluster. 145 150 ***************************************************************************************** 146 151 * @ cxy : MMC cluster identifier. … … 149 154 * @ return 0 if success / return EINVAL if failure 150 155 ****************************************************************************************/ 151 error_t dev_mmc_ get_error( cxy_t cxy,156 error_t dev_mmc_error_get( cxy_t cxy, 152 157 uint32_t index, 153 158 uint32_t * rdata ); 154 159 155 156 160 /***************************************************************************************** 157 * This function returns the value contained in one instrumentation MMC register.158 * It can be executed by any thread in any cluster, because it uses remote accesses159 * to access the L2 cache configuration interface in any cluster.161 * This function returns the value contained in one (architecture specific) MMC_INSTR 162 * register. It can be executed by any thread in any cluster, because it uses remote 163 * accesses to access the L2 cache instrumentation interface in any cluster. 160 164 ***************************************************************************************** 161 165 * @ cxy : MMC cluster identifier. 162 * @ index : instrumentationregister index in MMC peripheral.166 * @ index : error register index in MMC peripheral. 163 167 * @ rdata : local pointer on buffer for returned value. 164 168 * @ return 0 if success / return EINVAL if failure 165 169 ****************************************************************************************/ 166 error_t dev_mmc_ get_instrumentation( cxy_t cxy,167 uint32_t index,168 169 170 error_t dev_mmc_instr_get( cxy_t cxy, 171 uint32_t index, 172 uint32_t * rdata ); 173 170 174 #endif /* _DEV_MMC_H_ */ -
trunk/kernel/devices/dev_nic.c
r647 r657 2 2 * dev_nic.c - NIC (Network Controler) generic device API implementation. 3 3 * 4 * Author Alain Greiner (2016,2017,2018,2019 )4 * Author Alain Greiner (2016,2017,2018,2019,2020) 5 5 * 6 6 * Copyright (c) UPMC Sorbonne Universites … … 24 24 #include <hal_kernel_types.h> 25 25 #include <hal_special.h> 26 #include <hal_uspace.h> 27 #include <remote_buf.h> 26 28 #include <printk.h> 27 29 #include <chdev.h> 28 30 #include <thread.h> 31 #include <socket.h> 29 32 #include <hal_drivers.h> 30 33 #include <dev_nic.h> 34 #include <vfs.h> 35 #include <shared_socket.h> 31 36 32 37 ///////////////////////////////////////////////////////////////////////////////////////// 33 // Extern global variables38 // Extern global variables 34 39 ///////////////////////////////////////////////////////////////////////////////////////// 35 40 36 41 extern chdev_directory_t chdev_dir; // allocated in kernel_init.c 42 43 //////////////////////////////////////////////////////////////////////////////////////////// 44 // This static function is used by the dev_nic_rx_handle_tcp() & dev_nic_tx_handle_tcp() 45 // functions to check acceptability of a given sequence number. It returns true when 46 // the <seq> argument is contained in a wrap-around window defined by the <min> and <max> 47 // arguments. The window wrap-around when (min > max). 48 //////////////////////////////////////////////////////////////////////////////////////////// 49 // @ seq : [in] value to be checked. 50 // @ min : [in] first base. 51 // @ max : [in] window size. 52 //////////////////////////////////////////////////////////////////////////////////////////// 53 static inline bool_t is_in_window( uint32_t seq, 54 uint32_t min, 55 uint32_t max ) 56 { 57 if( max >= min ) // no wrap_around => only one window [min,max] 58 { 59 return( (seq >= min) && (seq <= max) ); 60 } 61 else // window wrap-around => two windows [min,0xFFFFFFFF] and [0,max] 62 { 63 return( (seq <= max) || (seq >= min) ); 64 } 65 } 66 67 //////////////////////////////////////////////////////////////////////////////////////////// 68 // this static function compute a channel index in range [0,nic_channelx[ from 69 // a remote IP address and remote port. 70 // TODO this function should be provided by the NIC driver. 71 //////////////////////////////////////////////////////////////////////////////////////////// 72 // @ addr : [in] IP address. 73 // @ port : [in] TCP/UDP port. 74 //////////////////////////////////////////////////////////////////////////////////////////// 75 static inline uint32_t dev_nic_channel_index( uint32_t addr, 76 uint16_t port ) 77 { 78 // get number of NIC channels 79 uint32_t nic_channels = LOCAL_CLUSTER->nb_nic_channels; 80 81 // compute NIC channel index 82 return ( ((addr ) & 0xFF) ^ 83 ((addr > 8 ) & 0xFF) ^ 84 ((addr > 16) & 0xFF) ^ 85 ((addr > 24) & 0xFF) ^ 86 ((port ) & 0xFF) ^ 87 ((port > 8 ) & 0xFF) ) % nic_channels; 88 } 89 90 //////////////////////////////////////////////////////////////////////////////////////// 91 // This static function computes the checksum for an IP packet header. 92 // The "checksum" field itself is not taken into account for this computation. 93 //////////////////////////////////////////////////////////////////////////////////////// 94 // @ buffer : [in] pointer on IP packet header (20 bytes) 95 // @ return the checksum value on 16 bits 96 //////////////////////////////////////////////////////////////////////////////////////// 97 uint16_t dev_nic_ip_checksum( uint8_t * buffer ) 98 { 99 uint32_t i; 100 uint32_t cs; // 32 bits accumulator 101 uint16_t * buf; 102 103 buf = (uint16_t *)buffer; 104 105 // compute checksum 106 for( i = 0 , cs = 0 ; i < 10 ; i++ ) 107 { 108 if( i != 5 ) cs += buf[i]; 109 } 110 111 // one's complement 112 return ~cs; 113 } 114 115 //////////////////////////////////////////////////////////////////////////////////////// 116 // This static function computes the checksum for an UDP packet defined by 117 // the <buffer> and <size> arguments. 118 //////////////////////////////////////////////////////////////////////////////////////// 119 // @ buffer : [in] pointer on UDP packet base. 120 // @ size : [in] number of bytes in this packet (including header). 121 // @ return the checksum value on 16 bits 122 //////////////////////////////////////////////////////////////////////////////////////// 123 uint16_t dev_nic_udp_checksum( uint8_t * buffer, 124 uint32_t size ) 125 { 126 uint32_t i; 127 uint32_t carry; 128 uint32_t cs; // 32 bits accumulator 129 uint16_t * buf; 130 uint32_t max; // number of uint16_t in packet 131 132 // compute max & buf 133 buf = (uint16_t *)buffer; 134 max = size >> 1; 135 136 // extend buffer[] if required 137 if( size & 1 ) 138 { 139 max++; 140 buffer[size] = 0; 141 } 142 143 // compute checksum for UDP packet 144 for( i = 0 , cs = 0 ; i < size ; i++ ) cs += buf[i]; 145 146 // handle carry 147 carry = (cs >> 16); 148 if( carry ) 149 { 150 cs += carry; 151 carry = (cs >> 16); 152 if( carry ) cs += carry; 153 } 154 155 // one's complement 156 return ~cs; 157 } 158 159 //////////////////////////////////////////////////////////////////////////////////////// 160 // This static function computes the checksum for a TCP segment defined by 161 // the <buffer> and <size> arguments. 162 // It includes the pseudo header defined by the <src_ip_addr>, <dst_ip_addr>, 163 // <size> arguments, and by the TCP_PROTOCOL code. 164 //////////////////////////////////////////////////////////////////////////////////////// 165 // @ buffer : [in] pointer on TCP segment base. 166 // @ size : [in] number of bytes in this segment (including header). 167 // @ src_ip_addr : [in] source IP address (pseudo header) 168 // @ dst_ip_addr : [in] destination IP address (pseudo header) 169 // @ return the checksum value on 16 bits 170 //////////////////////////////////////////////////////////////////////////////////////// 171 uint16_t dev_nic_tcp_checksum( uint8_t * buffer, 172 uint32_t size, 173 uint32_t src_ip_addr, 174 uint32_t dst_ip_addr ) 175 { 176 uint32_t i; 177 uint32_t carry; 178 uint32_t cs; // 32 bits accumulator 179 uint16_t * buf; 180 uint32_t max; // number of uint16_t in segment 181 182 // compute max & buf 183 buf = (uint16_t *)buffer; 184 max = size >> 1; 185 186 // extend buffer[] if required 187 if( size & 1 ) 188 { 189 max++; 190 buffer[size] = 0; 191 } 192 193 // compute checksum for TCP segment 194 for( i = 0 , cs = 0 ; i < size ; i++ ) cs += buf[i]; 195 196 // complete checksum for pseudo-header 197 cs += src_ip_addr; 198 cs += dst_ip_addr; 199 cs += PROTOCOL_TCP; 200 cs += size; 201 202 // handle carry 203 carry = (cs >> 16); 204 if( carry ) 205 { 206 cs += carry; 207 carry = (cs >> 16); 208 if( carry ) cs += carry; 209 } 210 211 // one's complement 212 return ~cs; 213 } 37 214 38 215 ////////////////////////////////// … … 80 257 } // end dev_nic_init() 81 258 259 260 ///////////////////////////////////////////////////////////////////////////////////////// 261 // Functions implementing the SOCKET related syscalls 262 ///////////////////////////////////////////////////////////////////////////////////////// 263 264 ////////////////////////////////////// 265 int dev_nic_socket( uint32_t domain, 266 uint32_t type ) 267 { 268 uint32_t fdid; 269 socket_t * socket; 270 error_t error; 271 272 // allocate memory for the file descriptor and for the socket 273 error = socket_create( local_cxy, 274 domain, 275 type, 276 &socket, // unused here 277 &fdid ); 278 279 if( error ) return -1; 280 return fdid; 281 } 282 283 //////////////////////////////// 284 int dev_nic_bind( uint32_t fdid, 285 uint32_t addr, 286 uint16_t port ) 287 { 288 vfs_inode_type_t type; 289 socket_t * socket; 290 uint32_t state; 291 292 thread_t * this = CURRENT_THREAD; 293 process_t * process = this->process; 294 295 // get pointers on file descriptor 296 xptr_t file_xp = process_fd_get_xptr( process , fdid ); 297 vfs_file_t * file_ptr = GET_PTR( file_xp ); 298 cxy_t file_cxy = GET_CXY( file_xp ); 299 300 // check file_xp 301 if( file_xp == XPTR_NULL ) 302 { 303 printk("\n[ERROR] in %s : undefined fdid %d", 304 __FUNCTION__, fdid ); 305 return -1; 306 } 307 308 type = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) ); 309 socket = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) ); 310 311 // check file descriptor type 312 if( type != INODE_TYPE_SOCK ) 313 { 314 printk("\n[ERROR] in %s : illegal file type %s", 315 __FUNCTION__, vfs_inode_type_str( type ) ); 316 return -1; 317 } 318 319 state = (type == SOCK_STREAM) ? TCP_STATE_BOUND : UDP_STATE_BOUND; 320 321 // update the socket descriptor 322 hal_remote_s32( XPTR( file_cxy , &socket->local_addr ) , addr ); 323 hal_remote_s32( XPTR( file_cxy , &socket->local_port ) , port ); 324 hal_remote_s32( XPTR( file_cxy , &socket->state ) , state ); 325 326 return 0; 327 328 } // end dev_nic_bind() 329 330 ////////////////////////////////// 331 int dev_nic_listen( uint32_t fdid, 332 uint32_t max_pending ) 333 { 334 xptr_t file_xp; 335 vfs_file_t * file_ptr; 336 cxy_t file_cxy; 337 vfs_inode_type_t file_type; 338 socket_t * socket_ptr; 339 uint32_t socket_type; 340 uint32_t socket_state; 341 342 thread_t * this = CURRENT_THREAD; 343 process_t * process = this->process; 344 345 if( max_pending != 0 ) 346 { 347 printk("\n[WARNING] in %s : max_pending argument non supported\n", 348 __FUNCTION__ ); 349 } 350 351 // get pointers on file descriptor 352 file_xp = process_fd_get_xptr( process , fdid ); 353 file_ptr = GET_PTR( file_xp ); 354 file_cxy = GET_CXY( file_xp ); 355 356 // check file_xp 357 if( file_xp == XPTR_NULL ) 358 { 359 printk("\n[ERROR] in %s : undefined fdid %d", 360 __FUNCTION__, fdid ); 361 return -1; 362 } 363 364 file_type = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) ); 365 socket_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) ); 366 367 // check file descriptor type 368 if( file_type != INODE_TYPE_SOCK ) 369 { 370 printk("\n[ERROR] in %s : illegal file type %s", 371 __FUNCTION__, vfs_inode_type_str(file_type) ); 372 return -1; 373 } 374 375 // get socket type and state 376 socket_type = hal_remote_l32( XPTR( file_cxy , &socket_ptr->type )); 377 socket_state = hal_remote_l32( XPTR( file_cxy , &socket_ptr->state )); 378 379 // check socket type 380 if( socket_type != SOCK_STREAM ) 381 { 382 printk("\n[ERROR] in %s : illegal socket type", 383 __FUNCTION__ ); 384 return -1; 385 } 386 387 // check socket state 388 if( socket_state != TCP_STATE_BOUND ) 389 { 390 printk("\n[ERROR] in %s : illegal socket state %s", 391 __FUNCTION__, socket_state_str(socket_state) ); 392 return -1; 393 } 394 395 // update socket.state 396 hal_remote_s32( XPTR( file_cxy , &socket_ptr->state ) , TCP_STATE_LISTEN ); 397 398 return 0; 399 400 } // end dev_nic_listen() 401 82 402 /////////////////////////////////// 83 error_t dev_nic_read( pkd_t * pkd ) 84 { 85 error_t error; 86 87 // get pointers on this NIC-RX kernel thread 88 thread_t * thread_ptr = CURRENT_THREAD; 89 xptr_t thread_xp = XPTR( local_cxy , thread_ptr ); 90 91 // get local pointer on core running this kernel thead 92 core_t * core = thread_ptr->core; 93 94 // check thread can yield 95 assert( (thread_ptr->busylocks == 0), 96 "cannot yield : busylocks = %d\n", thread_ptr->busylocks ); 403 int dev_nic_connect( uint32_t fdid, 404 uint32_t remote_addr, 405 uint16_t remote_port ) 406 { 407 vfs_inode_type_t file_type; 408 socket_t * socket; 409 uint32_t socket_state; // socket state 410 uint32_t socket_type; // socket type 411 uint32_t local_addr; // local IP address 412 uint32_t local_port; // local port 413 xptr_t tx_server_xp; // extended pointer on TX server thread 414 thread_t * tx_server_ptr; // local pointer on TX server thread 415 416 thread_t * this = CURRENT_THREAD; 417 process_t * process = this->process; 418 419 // get pointers on file descriptor 420 xptr_t file_xp = process_fd_get_xptr( process , fdid ); 421 vfs_file_t * file_ptr = GET_PTR( file_xp ); 422 cxy_t file_cxy = GET_CXY( file_xp ); 423 424 // check file_xp 425 if( file_xp == XPTR_NULL ) 426 { 427 printk("\n[ERROR] in %s : undefined fdid %d", 428 __FUNCTION__, fdid ); 429 return -1; 430 } 431 432 file_type = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) ); 433 socket = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) ); 434 435 // check file descriptor type 436 if( file_type != INODE_TYPE_SOCK ) 437 { 438 printk("\n[ERROR] in %s : illegal file type %s", 439 __FUNCTION__, vfs_inode_type_str( file_type ) ); 440 return -1; 441 } 442 443 // get relevant socket infos 444 socket_type = hal_remote_l32( XPTR( file_cxy , &socket->type ) ); 445 socket_state = hal_remote_l32( XPTR( file_cxy , &socket->state ) ); 446 local_addr = hal_remote_l32( XPTR( file_cxy , &socket->local_addr ) ); 447 local_port = hal_remote_l32( XPTR( file_cxy , &socket->local_port ) ); 448 449 if( socket_type == SOCK_DGRAM ) // UDP 450 { 451 if( socket_state != UDP_STATE_BOUND ) 452 { 453 printk("\n[ERROR] in %s : illegal socket statea %s for CONNECT", 454 __FUNCTION__, socket_state_str(socket_state) ); 455 return -1; 456 } 457 } 458 else if( socket_type == SOCK_STREAM ) // TCP 459 { 460 if( socket_state != TCP_STATE_BOUND ) 461 { 462 printk("\n[ERROR] in %s : illegal socket state %s for CONNECT", 463 __FUNCTION__, socket_state_str(socket_state) ); 464 return -1; 465 } 466 } 467 else 468 { 469 printk("\n[ERROR] in %s : illegal socket type %d for CONNECT", 470 __FUNCTION__, socket_type ); 471 return -1; 472 } 473 474 // compute nic_channel index from remote_addr and remote_port 475 uint32_t nic_channel = dev_nic_channel_index( remote_addr , remote_port ); 476 477 // link new socket to chdev servers 478 socket_link_to_servers( XPTR( file_cxy , socket ), 479 nic_channel ); 480 481 // update the socket descriptor 482 hal_remote_s32( XPTR( file_cxy , &socket->remote_addr ) , remote_addr ); 483 hal_remote_s32( XPTR( file_cxy , &socket->remote_port ) , remote_port ); 484 hal_remote_s32( XPTR( file_cxy , &socket->nic_channel ) , nic_channel ); 485 486 // the actual connection mechanism depends on socket type 487 // UDP : client thread directly updates the local socket state 488 // TCP : client thread request TX server thread to start the 3 steps handshake 489 490 if( socket_type == SOCK_DGRAM ) // UDP 491 { 492 // directly update the local socket state 493 hal_remote_s32( XPTR( file_cxy , &socket->state ) , UDP_STATE_CONNECT ); 494 } 495 else // TCP 496 { 497 // get pointers on NIC_TX[index] chdev 498 xptr_t tx_chdev_xp = chdev_dir.nic_tx[nic_channel]; 499 chdev_t * tx_chdev_ptr = GET_PTR( tx_chdev_xp ); 500 cxy_t tx_chdev_cxy = GET_CXY( tx_chdev_xp ); 501 502 // get pointers on NIC_TX[channel] server thread 503 tx_server_ptr = hal_remote_lpt( XPTR( tx_chdev_cxy , &tx_chdev_ptr->server )); 504 tx_server_xp = XPTR( tx_chdev_cxy , tx_server_ptr ); 505 506 // register command arguments in socket descriptor 507 hal_remote_s64( XPTR( file_cxy , &socket->tx_cmd ), 508 SOCKET_TX_CONNECT ); 509 510 // update the "tx_client" field in socket descriptor 511 hal_remote_s64( XPTR( file_cxy , &socket->tx_client ), 512 XPTR( local_cxy , this ) ); 513 514 // unblock NIC_TX server thread 515 thread_unblock( tx_server_xp , THREAD_BLOCKED_CLIENT ); 516 517 // block on THREAD_BLOCKED_IO condition and deschedules 518 thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO ); 519 sched_yield( "blocked in connect" ); 520 521 // reset the "tx_client" field in socket descriptor 522 hal_remote_s64( XPTR( file_cxy , &socket->tx_client ), 523 XPTR_NULL ); 524 } 525 526 return 0; 527 528 } // end dev_nic_connect() 529 530 //////////////////////////////////// 531 int dev_nic_accept( uint32_t fdid, 532 uint32_t * remote_addr, 533 uint16_t * remote_port ) 534 { 535 xptr_t file_xp; // extended pointer on remote file 536 vfs_file_t * file_ptr; 537 cxy_t file_cxy; 538 vfs_inode_type_t file_type; // file descriptor type 539 socket_t * socket; // local pointer on remote waiting socket 540 uint32_t socket_type; // waiting socket type 541 uint32_t socket_state; // waiting socket state 542 uint32_t socket_domain; // waiting socket domain 543 uint32_t socket_local_addr; // waiting socket local IP address 544 uint32_t socket_local_port; // waiting socket local port 545 xptr_t crqq_xp; // extended pointer on socket.crqq queue 546 socket_t * new_socket; // local pointer on new socket 547 uint32_t new_fdid; // new socket file descriptor index 548 sockaddr_t new_sockaddr; // one request in crqq queue 549 uint32_t new_remote_addr; // new socket remote IP address 550 uint32_t new_remote_port; // new socket remote port 551 error_t error; 552 553 thread_t * this = CURRENT_THREAD; 554 process_t * process = this->process; 555 556 // get pointers on file descriptor 557 file_xp = process_fd_get_xptr( process , fdid ); 558 file_ptr = GET_PTR( file_xp ); 559 file_cxy = GET_CXY( file_xp ); 560 561 // check file_xp 562 if( file_xp == XPTR_NULL ) 563 { 564 printk("\n[ERROR] in %s : undefined fdid %d", 565 __FUNCTION__, fdid ); 566 return -1; 567 } 568 569 file_type = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) ); 570 socket = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) ); 571 572 // check file descriptor type 573 if( file_type != INODE_TYPE_SOCK ) 574 { 575 printk("\n[ERROR] in %s : illegal file type %s / thread[%x,%x]\n", 576 __FUNCTION__, vfs_inode_type_str(file_type), process->pid, this->trdid ); 577 return -1; 578 } 579 580 // get socket type, domain, state, local_addr and local_port 581 socket_type = hal_remote_l32( XPTR( file_cxy , &socket->type )); 582 socket_state = hal_remote_l32( XPTR( file_cxy , &socket->state )); 583 socket_domain = hal_remote_l32( XPTR( file_cxy , &socket->domain )); 584 socket_local_addr = hal_remote_l32( XPTR( file_cxy , &socket->local_addr )); 585 socket_local_port = hal_remote_l32( XPTR( file_cxy , &socket->local_port )); 586 587 // check socket type 588 if( socket_type != SOCK_STREAM ) 589 { 590 printk("\n[ERROR] in %s : illegal socket type / thread[%x,%x]\n", 591 __FUNCTION__, process->pid , this->trdid ); 592 return -1; 593 } 594 595 // check socket state 596 if( socket_state != TCP_STATE_LISTEN ) 597 { 598 printk("\n[ERROR] in %s : illegal socket state %s / thread[%x,%x]\n", 599 __FUNCTION__, socket_state_str(socket_state), process->pid, this->trdid ); 600 return -1; 601 } 602 603 // select a cluster for the new socket 604 cxy_t new_cxy = cluster_random_select(); 605 606 // allocate memory for the new socket descriptor 607 error = socket_create( new_cxy, 608 socket_domain, 609 socket_type, 610 &new_socket, 611 &new_fdid ); 612 if( error ) 613 { 614 printk("\n[ERROR] in %s : cannot allocate new socket / thread[%x,%x]\n", 615 __FUNCTION__, process->pid, this->trdid ); 616 return -1; 617 } 618 619 // build extended pointer on socket.crqq 620 crqq_xp = XPTR( file_cxy , &socket->crqq ); 621 622 // blocks and deschedules if requests queue empty 623 if( remote_buf_status( crqq_xp ) == 0 ) 624 { 625 thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO ); 626 sched_yield( "socket.crqq queue empty"); 627 } 628 629 // extract first request from the socket.crqq queue 630 remote_buf_get_to_kernel( crqq_xp, 631 (uint8_t *)(&new_sockaddr), 632 sizeof(sockaddr_t) ); 633 634 new_remote_addr = new_sockaddr.s_addr; 635 new_remote_port = new_sockaddr.s_port; 636 637 // compute NIC channel index from remote_addr and remote_port 638 uint32_t nic_channel = dev_nic_channel_index( new_remote_addr , new_remote_port ); 639 640 // update new socket descriptor 641 new_socket->local_addr = hal_remote_l32(XPTR( file_cxy , &socket->local_addr )); 642 new_socket->local_port = hal_remote_l32(XPTR( file_cxy , &socket->local_port )); 643 new_socket->remote_addr = new_remote_addr; 644 new_socket->remote_port = new_remote_port; 645 new_socket->nic_channel = nic_channel; 646 647 // link new socket to chdev servers 648 socket_link_to_servers( XPTR( new_cxy , new_socket ), 649 nic_channel ); 650 // return success 651 *remote_addr = new_remote_addr; 652 *remote_port = new_remote_port; 653 654 return new_fdid; 655 656 } // end dev_nic_accept() 657 658 //////////////////////////////////////////////////////////////////////////////////////// 659 // This static and blocking function is called by the four functions : 660 // dev_nic_send() / dev_nic_recv() / dev_nic_sendto() / dev_nic_recvfrom(). 661 //////////////////////////////////////////////////////////////////////////////////////// 662 // Implementation note 663 // The behavior is very different for SEND & RECV : 664 // - For a SEND, the client thread checks that there is no TX command registered 665 // in the socket. It registers the command arguments in the socket descriptor 666 // (tx_client, tx_cmd, tx_buf, tx_len). Then the client thread unblocks the 667 // TX server thread from the BLOCKED_CLIENT condition, blocks itself on the 668 // BLOCKED_IO condition, and deschedules. It is unblocked by the TX server thread 669 // when the last byte has been sent (for UDP) or acknowledged (for TCP). 670 // When the client thread resumes, it reset the command in socket, and returns. 671 // - For a RECV, the client thread checks that there is no RX command registered 672 // in the socket. It registers itself in socket (rx_client). It checks the status 673 // of the receive buffer. It the rx_buf is empty, it blocks on the BLOCKED_IO 674 // condition, and deschedules. It is unblocked by the RX server thread when an UDP 675 // packet or TCP segment has been writen in the rx_buf. When it resumes, it moves 676 // the available data from the rx_buf to the user buffer, reset its registration 677 // in socket (reset the rx_buf for an UDP socket), and returns. 678 //////////////////////////////////////////////////////////////////////////////////////// 679 int dev_nic_register_cmd( bool_t is_send, 680 uint32_t fdid, 681 uint8_t * u_buf, 682 uint32_t length, 683 bool_t explicit, 684 uint32_t explicit_addr, 685 uint32_t explicit_port ) 686 { 687 vfs_inode_type_t file_type; // file descriptor type 688 socket_t * socket_ptr; // local pointer on socket descriptor 689 uint32_t socket_state; // current socket state 690 uint32_t socket_type; // socket type (UDP/TCP) 691 uint32_t nic_channel; // NIC channel for this socket 692 xptr_t socket_lock_xp; // extended pointer on socket lock 693 xptr_t file_xp; // extended pointer on file descriptor 694 vfs_file_t * file_ptr; 695 cxy_t file_cxy; 696 xptr_t chdev_xp; // extended pointer on NIC_TX[channel] chdev 697 chdev_t * chdev_ptr; 698 cxy_t chdev_cxy; 699 uint32_t remote_addr; 700 uint32_t remote_port; 701 uint32_t status; // number of bytes in rx_buf 702 int32_t moved_bytes; // total number of moved bytes (fot return) 703 xptr_t server_xp; // extended pointer on NIC_TX / NIC_RX server thread 704 thread_t * server_ptr; // local pointer on NIC_TX / NIC_RX server thread 705 706 thread_t * this = CURRENT_THREAD; 707 process_t * process = this->process; 708 709 // get pointers on file descriptor identifying the socket 710 file_xp = process_fd_get_xptr( process , fdid ); 711 file_ptr = GET_PTR( file_xp ); 712 file_cxy = GET_CXY( file_xp ); 713 714 if( file_xp == XPTR_NULL ) 715 { 716 printk("\n[ERROR] in %s : undefined fdid %d / thread%x,%x]\n", 717 __FUNCTION__, fdid , process->pid, this->trdid ); 718 return -1; 719 } 720 721 // get file type and socket pointer 722 file_type = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) ); 723 724 // get local pointer on socket 725 socket_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->socket ) ); 726 727 // check file descriptor type 728 if( file_type != INODE_TYPE_SOCK ) 729 { 730 printk("\n[ERROR] in %s : illegal file type %s / fdid %d / thread%x,%x]\n", 731 __FUNCTION__, vfs_inode_type_str(file_type), fdid, process->pid, this->trdid ); 732 return -1; 733 } 734 735 // build extended pointer on file lock protecting socket 736 socket_lock_xp = XPTR( file_cxy , &file_ptr->lock ); 737 738 // take the socket lock 739 remote_rwlock_wr_acquire( socket_lock_xp ); 740 741 // get socket type, state, and channel 742 socket_type = hal_remote_l32( XPTR( file_cxy , &socket_ptr->type )); 743 socket_state = hal_remote_l32( XPTR( file_cxy , &socket_ptr->state )); 744 nic_channel = hal_remote_l32( XPTR( file_cxy , &socket_ptr->nic_channel )); 745 746 // check socket state / type 747 if( socket_type == SOCK_STREAM ) // TCP socket 748 { 749 if( socket_state != TCP_STATE_ESTAB ) 750 { 751 printk("\n[ERROR] in %s : illegal SEND/RECV for state %s / thread%x,%x]\n", 752 __FUNCTION__, socket_state_str(socket_state), process->pid, this->trdid ); 753 return -1; 754 } 755 756 if( explicit ) 757 { 758 // get remote IP address and type from socket descriptor 759 remote_addr = hal_remote_l32( XPTR( file_cxy , &socket_ptr->remote_addr )); 760 remote_port = hal_remote_l32( XPTR( file_cxy , &socket_ptr->remote_port )); 761 762 if( (remote_addr != explicit_addr) || (remote_port != explicit_port) ) 763 { 764 printk("\n[ERROR] in %s : wrong expliciy access / thread%x,%x]\n", 765 __FUNCTION__, process->pid, this->trdid ); 766 return -1; 767 } 768 } 769 } 770 else // UDP socket 771 { 772 if( explicit ) 773 { 774 if( socket_state == UDP_STATE_UNBOUND ) 775 { 776 printk("\n[ERROR] in %s : illegal SEND/RECV for state %s / thread%x,%x]\n", 777 __FUNCTION__, socket_state_str(socket_state), process->pid, this->trdid ); 778 return -1; 779 } 780 781 // update remote IP address and port into socket descriptor 782 hal_remote_s32( XPTR( file_cxy , &socket_ptr->remote_addr ), explicit_addr ); 783 hal_remote_s32( XPTR( file_cxy , &socket_ptr->remote_port ), explicit_port ); 784 } 785 else 786 { 787 if( socket_state != UDP_STATE_CONNECT ) 788 { 789 printk("\n[ERROR] in %s : illegal SEND/RECV for state %s / thread%x,%x]\n", 790 __FUNCTION__, socket_state_str(socket_state), process->pid, this->trdid ); 791 return -1; 792 } 793 } 794 } 795 796 /////////////////////////////////////////////////////// 797 if( is_send ) // SEND command 798 { 799 // build extended pointer on socket "tx_client" 800 xptr_t client_xp = XPTR( file_cxy , &socket_ptr->tx_client ); 801 802 // check no previous SEND command 803 xptr_t client = hal_remote_l64( client_xp ); 804 805 if( client != XPTR_NULL ) // release socket lock and return error 806 { 807 // release socket lock 808 remote_rwlock_wr_release( socket_lock_xp ); 809 810 // get previous thread cluster & local pointer 811 cxy_t prev_cxy = GET_CXY( client ); 812 thread_t * prev_ptr = GET_PTR( client ); 813 814 // get previous command type and trdid 815 uint32_t prev_cmd = hal_remote_l32( XPTR( prev_cxy , &prev_ptr->nic_cmd.type )); 816 uint32_t prev_tid = hal_remote_l32( XPTR( prev_cxy , &prev_ptr->trdid )); 817 818 printk("\n[ERROR] in %s : previous command %s for thread %x / thread%x,%x]\n", 819 __FUNCTION__, socket_cmd_str(prev_cmd), prev_tid, 820 process->pid, this->trdid ); 821 822 return -1; 823 } 824 825 // client thread registers in socket descriptor 826 hal_remote_s64( client_xp , XPTR( local_cxy , this ) ); 827 hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_cmd ) , SOCKET_TX_SEND ); 828 hal_remote_spt( XPTR( file_cxy , &socket_ptr->tx_buf ) , u_buf ); 829 hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_len ) , length ); 830 hal_remote_s32( XPTR( file_cxy , &socket_ptr->tx_todo ) , length ); 831 832 // release socket lock 833 remote_rwlock_wr_release( socket_lock_xp ); 834 835 // get pointers on relevant chdev 836 chdev_xp = chdev_dir.nic_tx[nic_channel]; 837 chdev_ptr = GET_PTR( chdev_xp ); 838 chdev_cxy = GET_CXY( chdev_xp ); 839 840 // get pointers on NIC_TX[channel] server thread 841 server_ptr = hal_remote_lpt( XPTR( chdev_cxy , &chdev_ptr->server )); 842 server_xp = XPTR( chdev_cxy , server_ptr ); 843 844 // unblocks the NIC_TX server thread 845 thread_unblock( server_xp , THREAD_BLOCKED_CLIENT ); 846 847 // client thread blocks itself and deschedules 848 thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO ); 849 sched_yield( "blocked in nic_io" ); 850 851 // take the socket lock when unblocked 852 remote_rwlock_wr_acquire( socket_lock_xp ); 853 854 // unlink client thread from socket 855 hal_remote_s64( client_xp , XPTR_NULL ); 856 857 // release socket lock 858 remote_rwlock_wr_release( socket_lock_xp ); 859 860 // exit waiting loop and return 861 return length; 862 863 } // end SEND 864 865 //////////////////////////////////////////////////////// 866 else // RECV command 867 { 868 // build extended pointers on socket "rx_client" 869 xptr_t client_xp = XPTR( file_cxy , &socket_ptr->rx_client ); 870 871 // check no previous RECV command 872 xptr_t client = hal_remote_l64( client_xp ); 873 874 if( client != XPTR_NULL ) // release socket lock and return error 875 { 876 // release socket lock 877 remote_rwlock_wr_release( socket_lock_xp ); 878 879 // get previous thread cluster & local pointer 880 cxy_t prev_cxy = GET_CXY( client ); 881 thread_t * prev_ptr = GET_PTR( client ); 882 883 // get previous command type and trdid 884 uint32_t prev_cmd = hal_remote_l32( XPTR( prev_cxy , &prev_ptr->nic_cmd.type )); 885 uint32_t prev_tid = hal_remote_l32( XPTR( prev_cxy , &prev_ptr->trdid )); 886 887 printk("\n[ERROR] in %s : previous command %s for thread %x / thread%x,%x]\n", 888 __FUNCTION__, socket_cmd_str(prev_cmd), prev_tid, process->pid, this->trdid ); 889 return -1; 890 } 891 892 // build extended pointer on "rx_buf" 893 xptr_t rx_buf_xp = XPTR( file_cxy , &socket_ptr->rx_buf ); 894 895 // get rx_buf status from socket 896 status = remote_buf_status( rx_buf_xp ); 897 898 if( status == 0 ) // rx_buf empty => blocks and deschedules 899 { 900 // release socket lock 901 remote_rwlock_wr_release( socket_lock_xp ); 902 903 // client thread blocks itself and deschedules 904 thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_IO ); 905 sched_yield( "blocked in nic_io" ); 906 907 // take socket lock 908 remote_rwlock_wr_release( socket_lock_xp ); 909 } 910 911 // number of moved bytes cannot be larger than u_buf size 912 moved_bytes = ( length < status ) ? length : status; 913 914 // move data from kernel rx_buf to user u_buf 915 remote_buf_get_to_user( rx_buf_xp, 916 u_buf, 917 moved_bytes ); 918 919 // reset rx_buf for an UDP socket 920 if( socket_type == SOCK_DGRAM ) remote_buf_reset( rx_buf_xp ); 921 922 // unlink client thread from socket 923 hal_remote_s64( client_xp , XPTR_NULL ); 924 925 // release socket lock 926 remote_rwlock_wr_release( socket_lock_xp ); 927 928 // exit waiting loop and return 929 return moved_bytes; 930 931 } // end SEND 932 933 } // end dev_nic_register_cmd() 934 935 936 /////////////////////////////////// 937 int dev_nic_send( uint32_t fdid, 938 uint8_t * u_buf, 939 uint32_t length ) 940 { 941 #if DEBUG_DEV_NIC_TX 942 thread_t * this = CURRENT_THREAD; 943 process_t * process = this->process; 944 trdid_t trdid = this->trdid; 945 pid_t pid = process->pid; 946 uint32_t cycle = (uint32_t)hal_get_cycle(); 947 if (DEBUG_DEV_NIC_TX < cycle ) 948 printk("[%s] thread[%x,%x] enters : fdid %d / buf %x / length %d / cycle %d\n", 949 __FUNCTION__, pid, trdid, fdid, u_buf, length, cycle ); 950 #endif 951 952 error_t error = dev_nic_register_cmd( true, // SEND 953 fdid, 954 u_buf, 955 length, 956 false, 0, 0 ); // no explicit remote socket 957 #if DEBUG_DEV_NIC_TX 958 cycle = (uint32_t)hal_get_cycle(); 959 if (DEBUG_DEV_NIC_TX < cycle ) 960 printk("[%s] thread[%x,%x] exit : fdid %d / cycle %d\n", 961 __FUNCTION__, pid, trdid, cycle ); 962 #endif 963 964 return error; 965 966 } // end dev_nic_send() 967 968 /////////////////////////////////// 969 int dev_nic_recv( uint32_t fdid, 970 uint8_t * u_buf, 971 uint32_t length ) 972 { 973 #if DEBUG_DEV_NIC_RX 974 thread_t * this = CURRENT_THREAD; 975 process_t * process = this->process; 976 trdid_t trdid = this->trdid; 977 pid_t pid = process->pid; 978 uint32_t cycle = (uint32_t)hal_get_cycle(); 979 if (DEBUG_DEV_NIC_RX < cycle ) 980 printk("[%s] thread[%x,%x] enters : fdid %d / buf %x / length %d / cycle %d\n", 981 __FUNCTION__, pid, trdid, fdid, u_buf, length, cycle ); 982 #endif 983 984 error_t error = dev_nic_register_cmd( false, // RECV 985 fdid, 986 u_buf, 987 length, 988 false, 0, 0 ); // no explicit remote socket 989 #if DEBUG_DEV_NIC_RX 990 cycle = (uint32_t)hal_get_cycle(); 991 if (DEBUG_DEV_NIC_RX < cycle ) 992 printk("[%s] thread[%x,%x] exit : fdid %d / cycle %d\n", 993 __FUNCTION__, pid, trdid, cycle ); 994 #endif 995 996 return error; 997 998 } // end dev_nic_recv() 999 1000 ///////////////////////////////////// 1001 int dev_nic_sendto( uint32_t fdid, 1002 uint8_t * u_buf, 1003 uint32_t length, 1004 uint32_t remote_addr, 1005 uint32_t remote_port ) 1006 { 1007 #if DEBUG_DEV_NIC_TX 1008 thread_t * this = CURRENT_THREAD; 1009 process_t * process = this->process; 1010 trdid_t trdid = this->trdid; 1011 pid_t pid = process->pid; 1012 uint32_t cycle = (uint32_t)hal_get_cycle(); 1013 if (DEBUG_DEV_NIC_TX < cycle ) 1014 printk("[%s] thread[%x,%x] enters : fdid %d / buf %x / length %d / cycle %d\n", 1015 __FUNCTION__, pid, trdid, fdid, u_buf, length, cycle ); 1016 #endif 1017 1018 error_t error = dev_nic_register_cmd( true, // SEND 1019 fdid, 1020 u_buf, 1021 length, 1022 true, // explicit remote socket 1023 remote_addr, 1024 remote_port ); 1025 #if DEBUG_DEV_NIC_TX 1026 cycle = (uint32_t)hal_get_cycle(); 1027 if (DEBUG_DEV_NIC_TX < cycle ) 1028 printk("[%s] thread[%x,%x] exit : fdid %d / cycle %d\n", 1029 __FUNCTION__, pid, trdid, cycle ); 1030 #endif 1031 1032 return error; 1033 1034 } // end dev_nic_sendto() 1035 1036 /////////////////////////////////////// 1037 int dev_nic_recvfrom( uint32_t fdid, 1038 uint8_t * u_buf, 1039 uint32_t length, 1040 uint32_t remote_addr, 1041 uint32_t remote_port ) 1042 { 1043 #if DEBUG_DEV_NIC_RX 1044 thread_t * this = CURRENT_THREAD; 1045 process_t * process = this->process; 1046 trdid_t trdid = this->trdid; 1047 pid_t pid = process->pid; 1048 uint32_t cycle = (uint32_t)hal_get_cycle(); 1049 if (DEBUG_DEV_NIC_RX < cycle ) 1050 printk("[%s] thread[%x,%x] enters : fdid %d / buf %x / length %d / cycle %d\n", 1051 __FUNCTION__, pid, trdid, fdid, u_buf, length, cycle ); 1052 #endif 1053 1054 error_t error = dev_nic_register_cmd( false, // RECV 1055 fdid, 1056 u_buf, 1057 length, 1058 true, // explicit remote socket 1059 remote_addr, 1060 remote_port ); 1061 #if DEBUG_DEV_NIC_RX 1062 cycle = (uint32_t)hal_get_cycle(); 1063 if (DEBUG_DEV_NIC_RX < cycle ) 1064 printk("[%s] thread[%x,%x] exit : fdid %d / cycle %d\n", 1065 __FUNCTION__, pid, trdid, cycle ); 1066 #endif 1067 1068 return error; 1069 1070 } // end dev_nic_recvfrom() 1071 1072 1073 1074 1075 1076 1077 1078 1079 /////////////////////////////////////////////////////////////////////////////////////////// 1080 // Functions called by the NIC_RX server thread 1081 /////////////////////////////////////////////////////////////////////////////////////////// 1082 1083 ///////////////////////////////////////////////////////////////////////////////////////// 1084 // This static function is called by the NIC_RX[channel] server thread to register 1085 // a send request defined by the <flags> argument in the R2T queue specified by 1086 // the <queue_xp> argument. 1087 ///////////////////////////////////////////////////////////////////////////////////////// 1088 // @ queue_xp : [in] extended pointer on the R2T qeue descriptor. 1089 // @ flags : [in] flags to be set in the TCP segment. 1090 ///////////////////////////////////////////////////////////////////////////////////////// 1091 static void dev_nic_rx_put_r2t_request( xptr_t queue_xp, 1092 uint32_t flags ) 1093 { 1094 while( 1 ) 1095 { 1096 error_t error = remote_buf_put_from_kernel( queue_xp, 1097 (uint8_t *)(&flags), 1098 1 ); 1099 1100 if( error ) sched_yield( "waiting R2T queue" ); 1101 else break; 1102 } 1103 1104 } // end dev_nic_rx_put_r2t_request() 1105 1106 /////////////////////////////////////////////////////////////////////////////////////////// 1107 // This static function is called by the dev_nic_rx_server() function. 1108 // It calls directly the NIC driver (with the READABLE command) and returns the status 1109 // of the NIC_RX queue identified by the <chdev> argument. 1110 // in the <readable> buffer. 1111 /////////////////////////////////////////////////////////////////////////////////////////// 1112 // @ chdev : [in] local pointer on NIC_TX chdev. 1113 // @ readable : [out] zero if queue empty. 1114 // @ returns 0 if success / returns -1 if failure in accessing NIC device. 1115 /////////////////////////////////////////////////////////////////////////////////////////// 1116 error_t dev_nic_rx_queue_readable( chdev_t * chdev, 1117 uint32_t * readable ) 1118 { 1119 thread_t * this = CURRENT_THREAD; 1120 1121 // initialize NIC_READABLE command in thread descriptor 1122 this->nic_cmd.dev_xp = XPTR( local_cxy , chdev ); 1123 this->nic_cmd.type = NIC_CMD_READABLE; 1124 1125 // call driver to test readable 1126 chdev->cmd( XPTR( local_cxy , this ) ); 1127 1128 // return status 1129 *readable = this->nic_cmd.status; 1130 1131 // return error 1132 return this->nic_cmd.error; 1133 } 1134 1135 /////////////////////////////////////////////////////////////////////////////////////////// 1136 // This static function is called by the dev_nic_rx_server() function. 1137 // It moves one Ethernet packet from the NIC_RX_QUEUE identified the <chdev> argument, 1138 // to the 2K bytes kernel buffer identified by the <buffer> argument. The actual 1139 // Ethernet packet length is returned in the <length> argument. 1140 // It calls directly the NIC driver with the READ command, without registering in the 1141 // waiting queue, because only the NIC_RX server thread can access this NIC_RX_QUEUE. 1142 /////////////////////////////////////////////////////////////////////////////////////////// 1143 // @ chdev : [in] local pointer on NIC_TX chdev. 1144 // @ buffer : [in] local pointer on destination kernel buffer. 1145 // @ length : [out] Ethernet packet size in bytes. 1146 // @ returns 0 if success / returns -1 if failure in accessing NIC device. 1147 /////////////////////////////////////////////////////////////////////////////////////////// 1148 error_t dev_nic_rx_move_packet( chdev_t * chdev, 1149 uint8_t * k_buf, 1150 uint32_t * length ) 1151 { 1152 thread_t * this = CURRENT_THREAD; 97 1153 98 1154 #if DEBUG_DEV_NIC_RX 99 1155 uint32_t cycle = (uint32_t)hal_get_cycles(); 100 1156 if( DEBUG_DEV_NIC_RX < cycle ) 101 printk("\n[ DBG] %s : thread %x enters for packet %x in cluster %x\n",102 __FUNCTION__ , thread_ptr , pkd , local_cxy);1157 printk("\n[%s] thread[%x,%x] enters / cycle %d\n", 1158 __FUNCTION__, this->process->pid, this->trdid, cycle ); 103 1159 #endif 104 1160 105 // get pointer on NIC-RX chdev descriptor 106 uint32_t channel = thread_ptr->chdev->channel; 107 xptr_t dev_xp = chdev_dir.nic_rx[channel]; 108 cxy_t dev_cxy = GET_CXY( dev_xp ); 109 chdev_t * dev_ptr = (chdev_t *)GET_PTR( dev_xp ); 110 111 assert( (dev_xp != XPTR_NULL) , "undefined NIC chdev descriptor" ); 112 113 assert( (dev_cxy == local_cxy) , " chdev must be local" ); 114 115 // initialize command in thread descriptor 116 thread_ptr->nic_cmd.dev_xp = dev_xp; 117 118 // call driver to test readable 119 thread_ptr->nic_cmd.cmd = NIC_CMD_READABLE; 120 dev_ptr->cmd( thread_xp ); 1161 // initialize NIC_READ command in thread descriptor 1162 this->nic_cmd.type = NIC_CMD_READ; 1163 this->nic_cmd.buffer = k_buf; 1164 1165 // call NIC driver 1166 chdev->cmd( XPTR( local_cxy , this ) ); 1167 1168 // returns packet length 1169 *length = this->nic_cmd.length; 121 1170 122 1171 // check error 123 error = thread_ptr->nic_cmd.error; 124 if( error ) return error; 125 126 // block and deschedule if queue non readable 127 if( thread_ptr->nic_cmd.status == false ) 128 { 129 // enable NIC-RX IRQ 130 dev_pic_enable_irq( core->lid , dev_xp ); 131 132 // block client thread on THREAD_BLOCKED_IO 133 thread_block( XPTR( local_cxy , thread_ptr ) , THREAD_BLOCKED_IO ); 134 135 // deschedule client thread 136 sched_yield("client blocked on I/O"); 137 138 // disable NIC-RX IRQ 139 dev_pic_disable_irq( core->lid , dev_xp ); 140 } 141 142 // call driver for actual read 143 thread_ptr->nic_cmd.cmd = NIC_CMD_READ; 144 thread_ptr->nic_cmd.buffer = pkd->buffer; 145 dev_ptr->cmd( thread_xp ); 146 147 // check error 148 error = thread_ptr->nic_cmd.error; 149 if( error ) return error; 150 151 // returns packet length 152 pkd->length = thread_ptr->nic_cmd.length; 1172 if( this->nic_cmd.error ) 1173 { 153 1174 154 1175 #if DEBUG_DEV_NIC_RX 155 1176 cycle = (uint32_t)hal_get_cycles(); 156 1177 if( DEBUG_DEV_NIC_RX < cycle ) 157 printk("\n[ DBG] %s : thread %x exit for packet %x in cluster %x\n",158 __FUNCTION__ , thread_ptr , pkd , local_cxy);1178 printk("\n[%s] thread[%x,%x] exit / ERROR in NIC_RX / cycle %d\n", 1179 __FUNCTION__, this->process->pid, this->trdid, cycle ); 159 1180 #endif 160 1181 1182 return -1; 1183 } 1184 else 1185 { 1186 1187 #if DEBUG_DEV_NIC_RX 1188 cycle = (uint32_t)hal_get_cycles(); 1189 if( DEBUG_DEV_NIC_RX < cycle ) 1190 printk("\n[%s] thread[%x,%x] exit / SUCCESS / cycle %d\n", 1191 __FUNCTION__, this->process->pid, this->trdid, cycle ); 1192 #endif 1193 1194 return 0; 1195 } 1196 1197 } // end dev_nic_rx_move_packet() 1198 1199 /////////////////////////////////////////////////////////////////////////////////////////// 1200 // This static function is called by the dev_nic_rx_server() function. 1201 // It analyses an Ethernet frame contained in the kernel buffer defined 1202 // by the <buffer> argument, and returns in the <ip_length> argument the length 1203 // of the IP packet contained in the Ethernet packet payload. 1204 /////////////////////////////////////////////////////////////////////////////////////////// 1205 // @ buffer : [in] pointer on a received Ethernet packet 1206 // @ ip_length : [out] length of IP packet (in bytes). 1207 // @ return 0 if success / return -1 if illegal packet length. 1208 /////////////////////////////////////////////////////////////////////////////////////////// 1209 static error_t dev_nic_rx_check_eth( uint8_t * buffer, 1210 uint32_t * ip_length ) 1211 { 1212 uint32_t length = ((uint32_t)buffer[12] << 8) | (uint32_t)buffer[13]; 1213 1214 *ip_length = length; 1215 161 1216 return 0; 162 163 } // end dev_nic_read() 164 165 166 //////////////////////////////////// 167 error_t dev_nic_write( pkd_t * pkd ) 168 { 169 error_t error; 170 171 // get pointers on the NIC-TX kernel tread 172 thread_t * thread_ptr = CURRENT_THREAD; 173 xptr_t thread_xp = XPTR( local_cxy , thread_ptr ); 174 175 // get local pointer on core running this kernel thead 176 core_t * core = thread_ptr->core; 1217 } 1218 1219 /////////////////////////////////////////////////////////////////////////////////////////// 1220 // This static function analyses the IP packet contained in the kernel buffer 1221 // defined by the <buffer> argument, and returns in the <ip_src_addr>, <ip_dst_addr>, 1222 // <header_length> and <protocol> arguments the informations contained in the IP header. 1223 // It checks the IP packet length versus the value contained in Ethernet header. 1224 // It checks the IP header checksum. 1225 /////////////////////////////////////////////////////////////////////////////////////////// 1226 // @ buffer : [in] pointer on the IP packet. 1227 // @ expected_length : [in] expected IP packet length (from Ethernet header). 1228 // @ ip_src_addr : [out] source IP address. 1229 // @ ip_dst_addr : [out] destination IP address. 1230 // @ protocol : [out] transport protocol type. 1231 // @ return 0 if success / return -1 if illegal packet. 1232 /////////////////////////////////////////////////////////////////////////////////////////// 1233 static error_t dev_nic_rx_check_ip( uint8_t * buffer, 1234 uint32_t expected_length, 1235 uint32_t * ip_src_addr, 1236 uint32_t * ip_dst_addr, 1237 uint32_t * trsp_protocol ) 1238 { 1239 uint32_t length = ((uint32_t)buffer[2] << 8) | (uint32_t)buffer[3]; 1240 1241 // discard packet if eth_length != ip_length 1242 if( length != expected_length ) 1243 { 1244 1245 #if DEBUG_NIC_DEV 1246 thread_t * this = CURRENT_THREAD; 1247 printk("\n[%s] thread[%x,%x] enters : length (%d) != expected_length (%d)\n", 1248 __FUNCTION__, this->process->pid, this->trdid, length, expected_length ); 1249 #endif 1250 1251 return -1; 1252 } 1253 1254 // compute IP header checksum 1255 uint32_t received_cs = (uint32_t)dev_nic_ip_checksum( buffer ); 1256 1257 // extract IP header checksum 1258 uint32_t computed_cs = ((uint32_t)buffer[10] << 8) | ((uint32_t)buffer[11]); 1259 1260 // discard packet if bad checksum 1261 if( received_cs != computed_cs ) 1262 { 1263 1264 #if DEBUG_NIC_DEV 1265 thread_t * this = CURRENT_THREAD; 1266 printk("\n[%s] thread[%x,%x] computed checksum (%d) != received checksum (%d)\n", 1267 __FUNCTION__, this->process->pid, this->trdid, computed_cs, received_cs ); 1268 #endif 1269 1270 return -1; 1271 } 1272 1273 1274 *ip_src_addr = ((uint32_t)buffer[12] << 24) | 1275 ((uint32_t)buffer[13] << 16) | 1276 ((uint32_t)buffer[14] << 8) | 1277 ((uint32_t)buffer[15] ) ; 1278 1279 *ip_dst_addr = ((uint32_t)buffer[16] << 24) | 1280 ((uint32_t)buffer[17] << 16) | 1281 ((uint32_t)buffer[18] << 8) | 1282 ((uint32_t)buffer[19] ) ; 1283 1284 *trsp_protocol = (uint32_t)buffer[9]; 1285 1286 return 0; 1287 } 1288 1289 /////////////////////////////////////////////////////////////////////////////////////////// 1290 // This static function analyses the UDP packet contained in the kernel buffer 1291 // defined by the <k_buf> and <k_length> arguments. 1292 // It checks the UDP checksum, and discard corrupted packets. 1293 // It scans the list of sockets attached to the NIC_RX chdev to find a matching socket, 1294 // and discard the received packet if no UDP socket found. 1295 // Finally, it copies the payload to the socket "rx_buf", as long as the packet payload 1296 // is not larger than the rx_buf. 1297 // It set the "rx_valid" flip-flop, and unblock the client thread when the last expected 1298 // byte has been received. 1299 /////////////////////////////////////////////////////////////////////////////////////////// 1300 // @ chdev : [in] local pointer on local NIC_RX chdev descriptor. 1301 // @ k_buf : [in] pointer on the UDP packet in local kernel buffer. 1302 // @ k_length : [in] number of bytes in buffer (including UDP header). 1303 // @ pkt_src_addr : [in] source IP address (from IP packet header). 1304 // @ pkt_dst_addr : [in] destination IP address (from IP packet header). 1305 /////////////////////////////////////////////////////////////////////////////////////////// 1306 static void dev_nic_rx_handle_udp_packet( chdev_t * chdev, 1307 uint8_t * k_buf, 1308 uint32_t k_length, 1309 uint32_t pkt_src_addr, 1310 uint32_t pkt_dst_addr ) 1311 { 1312 xptr_t root_xp; // extended pointer on attached sockets list root 1313 xptr_t lock_xp; // extended pointer on chdev lock 1314 xptr_t iter_xp; // iterator on socket list 1315 xptr_t socket_xp; // extended pointer on socket descriptor 1316 cxy_t socket_cxy; 1317 socket_t * socket_ptr; 1318 uint32_t socket_type; // socket type 1319 uint32_t socket_state; // socket state 1320 uint32_t local_addr; // local IP address from socket 1321 uint32_t local_port; // local port from socket 1322 uint32_t remote_addr; // remote IP address from socket 1323 uint32_t remote_port; // remote port from socket 1324 bool_t match_socket; // matching socket found 1325 uint16_t checksum; // computed checksum 1326 uint16_t pkt_checksum; // received checksum 1327 xptr_t socket_rbuf_xp; // extended pointer on socket rx_buf 1328 xptr_t socket_lock_xp; // extended pointer on socket lock 1329 xptr_t socket_client_xp; // extended pointer on socket rx_client field 1330 xptr_t client_xp; // extended pointer on client thread descriptor 1331 uint32_t payload; // number of bytes in payload 1332 uint32_t status; // number of bytes in rx_buf 1333 uint32_t space; // number of free slots in rx_buf 1334 uint32_t moved_bytes; // number of bytes actually moved to rx_buf 1335 1336 // build extended pointers on list of sockets attached to NIC_RX chdev 1337 root_xp = XPTR( local_cxy , &chdev->wait_root ); 1338 lock_xp = XPTR( local_cxy , &chdev->wait_lock ); 1339 1340 // compute UDP packet checksum 1341 checksum = dev_nic_udp_checksum( k_buf , k_length ); 1342 1343 // get checksum from received packet header 1344 pkt_checksum = ((uint16_t)k_buf[6] << 8) | (uint16_t)k_buf[7]; 1345 1346 // discard corrupted packet 1347 if( pkt_checksum != checksum ) return; 1348 1349 // get src_port and dst_port from UDP header 1350 uint32_t pkt_src_port = ((uint32_t)k_buf[0] << 8) | (uint32_t)k_buf[1]; 1351 uint32_t pkt_dst_port = ((uint32_t)k_buf[2] << 8) | (uint32_t)k_buf[3]; 1352 1353 // discard unexpected packet 1354 if( xlist_is_empty( root_xp ) ) return; 1355 1356 // take the tock protecting the sockets list 1357 remote_busylock_acquire( lock_xp ); 1358 1359 match_socket = false; 1360 1361 // scan sockets list to find a match 1362 XLIST_FOREACH( root_xp , iter_xp ) 1363 { 1364 // get socket cluster and local pointer 1365 socket_xp = XLIST_ELEMENT( iter_xp , socket_t , rx_list ); 1366 socket_ptr = GET_PTR( socket_xp ); 1367 socket_cxy = GET_CXY( socket_xp ); 1368 1369 // get socket type 1370 socket_type = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->type )); 1371 socket_state = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->state )); 1372 1373 // skip TCP socket 1374 if( socket_type == SOCK_STREAM ) continue; 1375 1376 // get relevant info from socket descriptor 1377 local_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); 1378 remote_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_addr )); 1379 local_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); 1380 remote_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_port )); 1381 1382 // compute matching 1383 bool_t local_match = (local_addr == pkt_dst_addr) && 1384 (local_port == pkt_dst_port); 1385 1386 bool_t remote_match = (remote_addr == pkt_src_addr) && 1387 (remote_port == pkt_src_port); 1388 1389 if (socket_state == UDP_STATE_CONNECT ) match_socket = local_match && remote_match; 1390 else match_socket = local_match; 1391 1392 // exit loop when socket found 1393 if( match_socket ) break; 1394 } 1395 1396 // release the lock protecting the sockets list 1397 remote_busylock_release( lock_xp ); 1398 1399 // discard unexpected packet 1400 if( match_socket == false ) return; 1401 1402 // build extended pointers on various socket fields 1403 socket_rbuf_xp = XPTR( socket_cxy , &socket_ptr->rx_buf ); 1404 socket_lock_xp = XPTR( socket_cxy , &socket_ptr->lock ); 1405 socket_client_xp = XPTR( socket_cxy , &socket_ptr->rx_client ); 1406 1407 // take the lock protecting the socket 1408 remote_rwlock_wr_acquire( socket_lock_xp ); 1409 1410 // get status & space from rx_buf 1411 status = remote_buf_status( socket_rbuf_xp ); 1412 space = NIC_RX_BUF_SIZE - status; 1413 1414 // get client thread 1415 client_xp = hal_remote_l64( socket_client_xp ); 1416 1417 // get number of bytes in payload 1418 payload = k_length - UDP_HEAD_LEN; 1419 1420 // compute number of bytes to move : min (space , seg_payload) 1421 moved_bytes = ( space < payload ) ? space : payload; 1422 1423 // move payload from kernel buffer to socket rx_buf 1424 remote_buf_put_from_kernel( socket_rbuf_xp, 1425 k_buf + UDP_HEAD_LEN, 1426 moved_bytes ); 1427 1428 // unblock client thread if registered 1429 if( client_xp != XPTR_NULL ) 1430 { 1431 thread_unblock( client_xp , THREAD_BLOCKED_IO ); 1432 } 1433 1434 // release the lock protecting the socket 1435 remote_rwlock_wr_release( socket_lock_xp ); 1436 1437 } // end dev_nic_rx_handle_udp_packet() 1438 1439 /////////////////////////////////////////////////////////////////////////////////////////// 1440 // This static function is called by the dev_nic_rx_server() function to handle one RX 1441 // TCP segment contained in a kernel buffer defined by the <k_buf> & <k_length> arguments. 1442 // It the received segment doesn't match an existing local socket, or is corrupted, 1443 // this faulty segment is discarded. 1444 /////////////////////////////////////////////////////////////////////////////////////////// 1445 // Implementation note: 1446 // 1) It checks the TCP checksum, and discard the corrupted segment. 1447 // 2) It scans the list of sockets attached to the RX chdev, to find the socket 1448 // matching the TCP segment header, and discards the segment if no socket found. 1449 // 3) When a socket has been found, it takes the lock protecting the socket state, 1450 // because the socket is accessed by both the NIC_TX and NIC_RX server threads. 1451 // 4) Depending on the socket state, it handle the received segment, including the 1452 // SYN, FIN, ACK and RST flags. It updates the socket state when required, moves 1453 // data to the rx_buf when possible, and registers requests to the TX server 1454 // thread in the R2T queue attached to the socket, to insert control flags in the 1455 // TX stream, as required. 1456 // 5) Finally, it releases the lock protecting the socke and returns. 1457 /////////////////////////////////////////////////////////////////////////////////////////// 1458 // @ chdev : [in] local pointer on local NIC_RX chdev descriptor. 1459 // @ k_buf : [in] pointer on the TCP packet in local kernel buffer. 1460 // @ k_length : [in] number of bytes in buffer (including TCP header). 1461 // @ seg_src_addr : [in] source IP address (from IP packet header). 1462 // @ seg_dst_addr : [in] destination IP address (from IP packet header). 1463 /////////////////////////////////////////////////////////////////////////////////////////// 1464 static void dev_nic_rx_handle_tcp_segment( chdev_t * chdev, 1465 uint8_t * k_buf, 1466 uint32_t k_length, 1467 uint32_t seg_src_addr, 1468 uint32_t seg_dst_addr ) 1469 { 1470 xptr_t root_xp; // extended pointer on attached sockets list root 1471 xptr_t lock_xp; // extended pointer on chdev lock 1472 xptr_t iter_xp; // iterator for these queues 1473 bool_t match_socket; // true if socket found 1474 xptr_t socket_xp; // extended pointer on matching socket descriptor 1475 cxy_t socket_cxy; 1476 socket_t * socket_ptr; 1477 uint32_t local_addr; // local IP address from socket 1478 uint32_t local_port; // local port from socket 1479 uint32_t remote_addr; // remote IP address from socket 1480 uint32_t remote_port; // remote port from socket 1481 uint32_t socket_state; // socket state 1482 uint32_t socket_type; // socket type 1483 uint32_t socket_tx_nxt; // next byte to send in TX stream 1484 uint32_t socket_tx_una; // first unacknowledged byte in TX stream 1485 uint32_t socket_rx_nxt; // next expected byte in RX stream 1486 uint32_t socket_rx_wnd; // current window value in RX stream 1487 xptr_t socket_lock_xp; // extended pointer on lock protecting socket state 1488 xptr_t socket_rx_buf_xp; // extended pointer on socket rx_buf 1489 xptr_t socket_r2tq_xp; // extended pointer on socket r2t queue 1490 xptr_t socket_client_xp; // extended pointer on socket rx_client thread 1491 uint16_t checksum; // computed TCP segment chechsum 1492 1493 // build extended pointer on xlist of all sockets attached to NIC_RX chdev 1494 root_xp = XPTR( local_cxy , &chdev->wait_root ); 1495 lock_xp = XPTR( local_cxy , &chdev->wait_lock ); 1496 1497 // get relevant infos from TCP segment header 1498 uint32_t seg_src_port = ((uint32_t)k_buf[0] << 8) | (uint32_t)k_buf[1]; 1499 uint32_t seg_dst_port = ((uint32_t)k_buf[2] << 8) | (uint32_t)k_buf[3]; 1500 1501 uint32_t seg_seq_num = ((uint32_t)k_buf[4] << 24) | 1502 ((uint32_t)k_buf[5] << 16) | 1503 ((uint32_t)k_buf[6] << 8) | 1504 ((uint32_t)k_buf[7] ); 1505 1506 uint32_t seg_ack_num = ((uint32_t)k_buf[8] << 24) | 1507 ((uint32_t)k_buf[9] << 16) | 1508 ((uint32_t)k_buf[10] << 8) | 1509 ((uint32_t)k_buf[11] ); 1510 1511 uint8_t seg_hlen = k_buf[12] >> 2; // TCP header length in bytes 1512 1513 uint8_t seg_flags = k_buf[13]; 1514 1515 bool_t seg_ack_set = ((seg_flags & TCP_FLAG_ACK) != 0); 1516 bool_t seg_syn_set = ((seg_flags & TCP_FLAG_SYN) != 0); 1517 bool_t seg_fin_set = ((seg_flags & TCP_FLAG_FIN) != 0); 1518 bool_t seg_rst_set = ((seg_flags & TCP_FLAG_RST) != 0); 1519 1520 uint16_t seg_window = ((uint32_t)k_buf[14] << 8) | (uint32_t)k_buf[15]; 1521 1522 uint16_t seg_checksum = ((uint32_t)k_buf[16] << 8) | (uint32_t)k_buf[17]; 1523 1524 uint32_t seg_payload = k_length - seg_hlen; // number of bytes in payload 1525 1526 // 1. compute TCP checksum 1527 checksum = dev_nic_tcp_checksum( k_buf, 1528 k_length, 1529 seg_src_addr, 1530 seg_dst_addr ); 1531 1532 // discard segment if corrupted 1533 if( seg_checksum != checksum ) return; 1534 1535 match_socket = false; 1536 1537 // take the lock protecting the list of sockets 1538 remote_busylock_acquire( lock_xp ); 1539 1540 // 2. scan list of sockets to find a matching socket 1541 XLIST_FOREACH( root_xp , iter_xp ) 1542 { 1543 // get socket cluster and local pointer 1544 socket_xp = XLIST_ELEMENT( iter_xp , socket_t , rx_list ); 1545 socket_ptr = GET_PTR( socket_xp ); 1546 socket_cxy = GET_CXY( socket_xp ); 1547 1548 // get socket type and state 1549 socket_type = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->type )); 1550 socket_state = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->state )); 1551 1552 // skip UDP socket 1553 if( socket_type == SOCK_DGRAM ) continue; 1554 1555 // get relevant socket infos for matching 1556 local_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); 1557 remote_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_addr )); 1558 local_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); 1559 remote_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_port )); 1560 1561 // compute matching condition 1562 // (in LISTEN state, remote_port and remote_addr can be unspecified) 1563 if( socket_state == TCP_STATE_LISTEN ) 1564 { 1565 match_socket = (local_addr == seg_dst_addr) && 1566 (local_port == seg_dst_port) ; 1567 } 1568 else 1569 { 1570 match_socket = (local_addr == seg_dst_addr) && 1571 (local_port == seg_dst_port) && 1572 (remote_addr == seg_src_addr) && 1573 (remote_port == seg_src_port) ; 1574 } 1575 1576 // exit loop if matching 1577 if( match_socket ) break; 1578 1579 } // end loop on sockets 1580 1581 // release the lock protecting the list of sockets 1582 remote_busylock_release( lock_xp ); 1583 1584 // discard segment if no matching socket found 1585 if( match_socket == false ) return; 1586 1587 // From here the actions depend on both the socket state, 1588 // and the received segment flags 1589 // - update socket state, 1590 // - move data to rx_buf, 1591 // - make a R2T request when required 1592 1593 // build extended pointers on various socket fields 1594 socket_lock_xp = XPTR( socket_cxy , &socket_ptr->lock ); 1595 socket_rx_buf_xp = XPTR( socket_cxy , &socket_ptr->rx_buf ); 1596 socket_r2tq_xp = XPTR( socket_cxy , &socket_ptr->r2tq ); 1597 socket_client_xp = XPTR( socket_cxy , &socket_ptr->rx_client ); 1598 1599 // 3. take the lock protecting the matching socket 1600 remote_rwlock_wr_acquire( socket_lock_xp ); 1601 1602 // get relevant socket infos from socket descriptor 1603 socket_state = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->state )); 1604 socket_rx_nxt = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_nxt )); 1605 socket_rx_wnd = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->rx_wnd )); 1606 socket_tx_una = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_una )); 1607 socket_tx_nxt = hal_remote_l32(XPTR( socket_cxy , &socket_ptr->tx_nxt )); 1608 1609 switch( socket_state ) 1610 { 1611 ////////////////////// 1612 case TCP_STATE_LISTEN: 1613 { 1614 // [1] discard segment if RST flag 1615 if( seg_rst_set ) return; 1616 1617 // [2] send a RST & discard segment if ACK flag 1618 if( seg_ack_set ) 1619 { 1620 // set socket.tx_nxt to seg_ack_num 1621 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ), 1622 seg_ack_num ); 1623 1624 // make RST request to R2T queue 1625 dev_nic_rx_put_r2t_request( socket_r2tq_xp, 1626 TCP_FLAG_RST ); 1627 // discard segment 1628 break; 1629 } 1630 1631 // [3] handle SYN flag 1632 if( seg_syn_set ) 1633 { 1634 // set socket.rx_nxt to seg_seq_num + 1 1635 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ), 1636 seg_seq_num + 1 ); 1637 1638 // set socket.tx_nxt to ISS 1639 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ), 1640 TCP_ISS ); 1641 1642 // set socket.rx_irs to seg_seq_num 1643 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_irs ), 1644 seg_seq_num + 1 ); 1645 1646 // make SYN.ACK request to R2T queue 1647 dev_nic_rx_put_r2t_request( socket_r2tq_xp, 1648 TCP_FLAG_SYN | TCP_FLAG_ACK ); 1649 1650 // set socket.tx_nxt to ISS + 1 1651 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ), 1652 TCP_ISS + 1 ); 1653 1654 // set socket.tx_una to ISS 1655 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_una ), 1656 TCP_ISS ); 1657 1658 // update socket.state 1659 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 1660 TCP_STATE_SYN_RCVD ); 1661 1662 // update socket.remote_addr 1663 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->remote_addr ), 1664 seg_src_addr ); 1665 1666 // update socket.remote_port 1667 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->remote_port ), 1668 seg_src_port ); 1669 } 1670 break; 1671 } 1672 //////////////////////// 1673 case TCP_STATE_SYN_SENT: 1674 { 1675 // [1] check ACK flag 1676 if( seg_ack_set ) 1677 { 1678 if( seg_ack_num != TCP_ISS + 1 ) // ACK not acceptable 1679 { 1680 // discard segment if RST 1681 if( seg_rst_set ) break; 1682 1683 // set socket.tx_nxt to seg_ack_num 1684 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ), 1685 seg_ack_num ); 1686 1687 // make an RST request to R2T queue 1688 dev_nic_rx_put_r2t_request( socket_r2tq_xp, 1689 TCP_FLAG_RST ); 1690 // discard segment 1691 break; 1692 } 1693 } 1694 1695 // [2] check RST flag 1696 if( seg_rst_set ) 1697 { 1698 // TODO signal "error: connection reset" to user 1699 1700 // update socket.state 1701 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 1702 TCP_STATE_BOUND ); 1703 1704 // discard segment 1705 break; 1706 } 1707 1708 // [3] handle SYN flag when (no ACK or acceptable ACK, and no RST) 1709 if( seg_syn_set ) 1710 { 1711 // TODO Ne faut-il pas tester seg_seq_num ? 1712 1713 if( seg_ack_set ) // received both SYN and ACK 1714 { 1715 // set socket.rx_nxt to seg_seq_num + 1 1716 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ), 1717 seg_seq_num + 1 ); 1718 1719 // set socket.tx_una to seg_ack_num 1720 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_una ), 1721 seg_ack_num ); 1722 1723 // set socket.rx_irs to seg_seq_num 1724 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_irs ), 1725 seg_seq_num ); 1726 1727 // update socket.state 1728 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 1729 TCP_STATE_ESTAB ); 1730 1731 // make an ACK request to R2T queue 1732 dev_nic_rx_put_r2t_request( socket_r2tq_xp, 1733 TCP_FLAG_ACK ); 1734 } 1735 else // received SYN without ACK 1736 { 1737 // update socket.state 1738 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 1739 TCP_STATE_SYN_RCVD ); 1740 1741 // set socket.tx_nxt to ISS 1742 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ), 1743 TCP_ISS ); 1744 1745 // make a SYN.ACK request to R2T queue 1746 dev_nic_rx_put_r2t_request( socket_r2tq_xp, 1747 TCP_FLAG_SYN | TCP_FLAG_ACK ); 1748 } 1749 } 1750 break; 1751 } 1752 //////////////////////// 1753 case TCP_STATE_SYN_RCVD: 1754 case TCP_STATE_ESTAB: 1755 case TCP_STATE_FIN_WAIT1: 1756 case TCP_STATE_FIN_WAIT2: 1757 case TCP_STATE_CLOSE_WAIT: 1758 case TCP_STATE_CLOSING: 1759 case TCP_STATE_LAST_ACK: 1760 case TCP_STATE_TIME_WAIT: 1761 { 1762 // [1] check sequence number 1763 1764 // compute min & max acceptable sequence numbers 1765 uint32_t seq_min = socket_rx_nxt; 1766 uint32_t seq_max = socket_rx_nxt + socket_rx_wnd - 1; 1767 1768 // compute sequence number for last byte in segment 1769 uint32_t seg_seq_last = seg_seq_num + seg_payload - 1; 1770 1771 if( (seg_seq_num != socket_rx_nxt) || // out_of_order 1772 (is_in_window( seg_seq_last, 1773 seq_min, 1774 seq_max ) == false) ) // out_of_window 1775 { 1776 // discard segment 1777 return; 1778 } 1779 1780 // [2] handle RST flag 1781 1782 if( seg_rst_set ) 1783 { 1784 if( socket_state == TCP_STATE_SYN_RCVD ) 1785 { 1786 // TODO unblock all clients threads with "reset" responses 1787 } 1788 else if( (socket_state == TCP_STATE_ESTAB ) || 1789 (socket_state == TCP_STATE_FIN_WAIT1 ) || 1790 (socket_state == TCP_STATE_FIN_WAIT2 ) || 1791 (socket_state == TCP_STATE_CLOSE_WAIT) ) 1792 { 1793 // TODO all pending send & received commands 1794 // must receive "reset" responses 1795 1796 // TODO destroy the socket 1797 } 1798 else // all other states 1799 { 1800 1801 1802 } 1803 1804 // [3] handle security & precedence TODO ... someday 1805 1806 // [4] handle SYN flag 1807 1808 if( seg_syn_set ) // received SYN 1809 { 1810 // TODO signal error to user 1811 1812 // make an RST request to R2T queue 1813 dev_nic_rx_put_r2t_request( socket_r2tq_xp, 1814 TCP_FLAG_RST ); 1815 // update socket state 1816 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 1817 TCP_STATE_BOUND ); 1818 } 1819 1820 // [5] handle ACK flag 1821 1822 if( seg_ack_set == false ) 1823 { 1824 // discard segment when ACK not set 1825 break; 1826 } 1827 else if( socket_state == TCP_STATE_SYN_RCVD ) 1828 { 1829 if( is_in_window( seg_ack_num , socket_tx_una , socket_tx_nxt ) ) 1830 { 1831 // update socket.state to ESTAB 1832 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 1833 TCP_STATE_ESTAB ); 1834 } 1835 else // unacceptable ACK 1836 { 1837 // set socket.tx_nxt to seg_ack_num 1838 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ), 1839 seg_ack_num ); 1840 1841 // make an RST request to R2T queue 1842 dev_nic_rx_put_r2t_request( socket_r2tq_xp, 1843 TCP_FLAG_RST ); 1844 } 1845 } 1846 else if( (socket_state == TCP_STATE_ESTAB) || 1847 (socket_state == TCP_STATE_FIN_WAIT1) || 1848 (socket_state == TCP_STATE_FIN_WAIT1) || 1849 (socket_state == TCP_STATE_CLOSE_WAIT) || 1850 (socket_state == TCP_STATE_CLOSING) ) 1851 { 1852 if( is_in_window( seg_ack_num + 1 , socket_tx_una , socket_tx_nxt ) ) 1853 { 1854 // update socket.tx_una 1855 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_una ), 1856 seg_ack_num ); 1857 1858 // update socket.tx_wnd 1859 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_wnd ), 1860 seg_window ); 1861 } 1862 else // unacceptable ACK 1863 { 1864 // discard segment 1865 break; 1866 } 1867 1868 // specific for FIN_WAIT1 1869 if( socket_state == TCP_STATE_FIN_WAIT1 ) 1870 { 1871 if( seg_fin_set ) 1872 { 1873 // update socket.state 1874 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 1875 TCP_STATE_FIN_WAIT2 ); 1876 } 1877 } 1878 1879 // specific for CLOSING 1880 if( socket_state == TCP_STATE_CLOSING ) 1881 { 1882 if( seg_ack_set ) 1883 { 1884 // update socket.state 1885 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 1886 TCP_STATE_TIME_WAIT ); 1887 } 1888 else 1889 { 1890 // discard segment 1891 break; 1892 } 1893 } 1894 } 1895 else if( socket_state == TCP_STATE_LAST_ACK ) 1896 { 1897 if( seg_ack_set ) 1898 { 1899 // update socket.state 1900 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 1901 TCP_STATE_TIME_WAIT ); 1902 } 1903 1904 } 1905 1906 // [6] handle URG flag TODO ... someday 1907 1908 // [7] Move DATA to rx_buf and unblock client thread 1909 1910 if( seg_payload ) 1911 { 1912 if( (socket_state == TCP_STATE_ESTAB) || 1913 (socket_state == TCP_STATE_FIN_WAIT1) || 1914 (socket_state == TCP_STATE_FIN_WAIT2) ) 1915 { 1916 // get number of bytes already stored in rx_buf 1917 uint32_t status = remote_buf_status( socket_rx_buf_xp ); 1918 1919 // compute empty space in rx_buf 1920 uint32_t space = NIC_RX_BUF_SIZE - status; 1921 1922 // compute number of bytes to move : min (space , seg_payload) 1923 uint32_t nbytes = ( space < seg_payload ) ? space : seg_payload; 1924 1925 // move payload from k_buf to rx_buf 1926 remote_buf_put_from_kernel( socket_rx_buf_xp, 1927 k_buf + seg_hlen, 1928 nbytes ); 1929 // update socket.rx_nxt 1930 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ), 1931 socket_rx_nxt + nbytes ); 1932 1933 // update socket.rx_wnd 1934 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_wnd ), 1935 socket_rx_wnd - nbytes ); 1936 1937 // make an ACK request to R2T queue 1938 dev_nic_rx_put_r2t_request( socket_r2tq_xp, 1939 TCP_FLAG_ACK ); 1940 1941 // get extended pointer on rx_client thread 1942 xptr_t client_xp = hal_remote_l64( socket_client_xp ); 1943 1944 // unblock client thread 1945 if( client_xp != XPTR_NULL ) 1946 { 1947 thread_unblock( client_xp , THREAD_BLOCKED_IO ); 1948 } 1949 } 1950 } 1951 1952 // [8] handle FIN flag 1953 1954 if( seg_fin_set ) 1955 { 1956 if( (socket_state == TCP_STATE_UNBOUND) || 1957 (socket_state == TCP_STATE_BOUND) || 1958 (socket_state == TCP_STATE_LISTEN) || 1959 (socket_state == TCP_STATE_SYN_SENT) ) 1960 { 1961 // discard segment 1962 break; 1963 } 1964 else // all other states 1965 { 1966 // TODO signal "connection closing" 1967 1968 // make an ACK request to R2T queue 1969 dev_nic_rx_put_r2t_request( socket_r2tq_xp, 1970 TCP_FLAG_ACK ); 1971 1972 // increment socket.rx_nxt 1973 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ), 1974 socket_rx_nxt + 1 ); 1975 1976 if( (socket_state == TCP_STATE_SYN_RCVD) || 1977 (socket_state == TCP_STATE_ESTAB) ) 1978 { 1979 // update socket.state 1980 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 1981 TCP_STATE_TIME_WAIT ); 1982 } 1983 else if( socket_state == TCP_STATE_FIN_WAIT1 ) 1984 { 1985 if( seg_ack_set ) 1986 { 1987 // TODO start "time-wait" timer / turn off others timers 1988 1989 // update socket.state 1990 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 1991 TCP_STATE_TIME_WAIT ); 1992 } 1993 else 1994 { 1995 // update socket.state 1996 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 1997 TCP_STATE_CLOSING ); 1998 } 1999 } 2000 else if( socket_state == TCP_STATE_FIN_WAIT2 ) 2001 { 2002 // TODO start "time-wait" timer / turn off other timers 2003 2004 // update socket.state 2005 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 2006 TCP_STATE_TIME_WAIT ); 2007 } 2008 else if( socket_state == TCP_STATE_TIME_WAIT ) 2009 { 2010 // TODO restart "time_wait" timer 2011 } 2012 } 2013 } // end if FIN 2014 } // end case sockets synchronized 2015 } // end switch socket state 2016 2017 // release the lock protecting socket 2018 remote_rwlock_wr_acquire( socket_lock_xp ); 2019 2020 } // end socket found 2021 2022 } // end dev_nic_rx_handle_tcp_segment() 2023 2024 2025 ///////////////////////////////////////// 2026 void dev_nic_rx_server( chdev_t * chdev ) 2027 { 2028 uint8_t k_buf[2048]; // kernel buffer for one ETH/IP/UDP packet 2029 2030 uint32_t pkt_src_addr; // packet source IP address 2031 uint32_t pkt_dst_addr; // packet destination IP address 2032 uint32_t trsp_protocol; // transport protocol (TCP / UDP) 2033 uint32_t eth_length; // size of Ethernet packet (bytes) 2034 uint32_t ip_length; // size of IP packet in bytes 2035 uint32_t nic_queue_readable; // NIC_RX queue non empty when true 2036 error_t error; 2037 2038 thread_t * this = CURRENT_THREAD; 2039 2040 // check chdev direction and type 2041 assert( (chdev->func == DEV_FUNC_NIC) && (chdev->is_rx == true) , 2042 "illegal chdev type or direction" ); 177 2043 178 2044 // check thread can yield 179 assert( (thread_ptr->busylocks == 0), 180 "cannot yield : busylocks = %d\n", thread_ptr->busylocks ); 2045 assert( (this->busylocks == 0), 2046 "cannot yield : busylocks = %d\n", this->busylocks ); 2047 2048 while( 1 ) 2049 { 2050 // check NIC_RX_QUEUE readable 2051 error = dev_nic_rx_queue_readable( chdev, 2052 &nic_queue_readable ); 2053 if( error ) 2054 { 2055 printk("\n[PANIC] in %s : cannot access NIC_TX[%d] queue\n", 2056 __FUNCTION__, chdev->channel ); 2057 } 2058 2059 if( nic_queue_readable ) // NIC_TX_QUEUE non empty 2060 { 2061 // moves one Ethernet packet to kernel buffer 2062 error = dev_nic_rx_move_packet( chdev, 2063 k_buf, 2064 ð_length ); 2065 if( error ) 2066 { 2067 printk("\n[PANIC] in %s : cannot read the NIC_TX[%d] queue\n", 2068 __FUNCTION__, chdev->channel ); 2069 } 2070 2071 // analyse the ETH header 2072 error = dev_nic_rx_check_eth( k_buf, 2073 &ip_length ); 2074 2075 // discard packet if error reported by Ethernet layer 2076 if( error ) continue; 2077 2078 // analyse the IP header 2079 error = dev_nic_rx_check_ip( k_buf + ETH_HEAD_LEN, 2080 ip_length, 2081 &pkt_src_addr, 2082 &pkt_dst_addr, 2083 &trsp_protocol ); 2084 2085 // discard packet if error reported by IP layer 2086 if( error ) continue; 2087 2088 // call relevant transport protocol 2089 if( trsp_protocol == PROTOCOL_UDP ) 2090 { 2091 dev_nic_rx_handle_udp_packet( chdev, 2092 k_buf + ETH_HEAD_LEN + IP_HEAD_LEN, 2093 ip_length - IP_HEAD_LEN, 2094 pkt_src_addr, 2095 pkt_dst_addr ); 2096 } 2097 else if ( trsp_protocol == PROTOCOL_TCP) 2098 { 2099 dev_nic_rx_handle_tcp_segment( chdev, 2100 k_buf + ETH_HEAD_LEN + IP_HEAD_LEN, 2101 ip_length - IP_HEAD_LEN, 2102 pkt_src_addr, 2103 pkt_dst_addr ); 2104 } 2105 } 2106 else // block and deschedule if NIC_RX_QUEUE empty 2107 { 2108 thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_ISR ); 2109 sched_yield( "waiting RX client" ); 2110 } 2111 2112 } // end of while loop 2113 2114 } // end dev_nic_rx_server() 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 /////////////////////////////////////////////////////////////////////////////////////////// 2126 // Functions used by the NIC_TX server thread 2127 /////////////////////////////////////////////////////////////////////////////////////////// 2128 2129 2130 /////////////////////////////////////////////////////////////////////////////////////////// 2131 // These static functions are called by the NIC_TX server thread to report the 2132 // completion (success or error) of a TX command. 2133 // - it print an error message in case of error. 2134 // - it updates the "tx_error" field in socket descriptor. 2135 // - it unblocks the client thread. 2136 /////////////////////////////////////////////////////////////////////////////////////////// 2137 // @ socket_xp : [in] extended pointer on socket 2138 // @ cmd_type : [in] SOCKET_TX_CONNECT / SOCKET_TX_SEND / SOCKET_TX_CLOSE 2139 // @ socket_state : [in] current socket state 2140 /////////////////////////////////////////////////////////////////////////////////////////// 2141 static void dev_nic_tx_report_error( xptr_t socket_xp, 2142 uint32_t cmd_type, 2143 uint32_t socket_state ) 2144 { 2145 printk("\n[ERROR] in %s : command %s in %s state\n", 2146 __FUNCTION__, socket_cmd_str(cmd_type), socket_state_str(socket_state) ); 2147 2148 // get socket thread cluster and local pointer 2149 socket_t * socket_ptr = GET_PTR( socket_xp ); 2150 cxy_t socket_cxy = GET_CXY( socket_xp ); 2151 2152 // set tx_error field in socket descriptor 2153 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_error ) , 1 ); 2154 2155 // get extended point on client thread 2156 xptr_t client_xp = hal_remote_l64( XPTR( socket_cxy , &socket_ptr->tx_client )); 2157 2158 // unblock the client thread 2159 thread_unblock( client_xp , THREAD_BLOCKED_IO ); 2160 } 2161 2162 //////////////////////////////////////////////////////////// 2163 static void dev_nic_tx_report_success( xptr_t socket_xp ) 2164 { 2165 // get socket thread cluster and local pointer 2166 socket_t * socket_ptr = GET_PTR( socket_xp ); 2167 cxy_t socket_cxy = GET_CXY( socket_xp ); 2168 2169 // set tx_error field in socket descriptor 2170 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_error ) , 0 ); 2171 2172 // get extended point on client thread 2173 xptr_t client_xp = hal_remote_l64( XPTR( socket_cxy , &socket_ptr->tx_client )); 2174 2175 // unblock the client thread 2176 thread_unblock( client_xp , THREAD_BLOCKED_IO ); 2177 } 2178 2179 2180 2181 2182 2183 /////////////////////////////////////////////////////////////////////////////////////////// 2184 // This static function is called by the dev_nic_tx_server() function. 2185 // It calls directly the NIC driver (WRITABLE command) and returns the status 2186 // of the NIC_TX queue identified by the <chdev> argument. 2187 // in the <writable> buffer. 2188 /////////////////////////////////////////////////////////////////////////////////////////// 2189 // @ chdev : [in] local pointer on NIC_TX chdev. 2190 // @ length : [in] packet length in bytes. 2191 // @ writable : [out] zero if queue full. 2192 // @ returns 0 if success / returns -1 if failure in accessing NIC device. 2193 /////////////////////////////////////////////////////////////////////////////////////////// 2194 error_t dev_nic_tx_queue_writable( chdev_t * chdev, 2195 uint32_t length, 2196 uint32_t * writable ) 2197 { 2198 thread_t * this = CURRENT_THREAD; 2199 2200 // initialize READABLE command in thread descriptor 2201 this->nic_cmd.dev_xp = XPTR( local_cxy , chdev ); 2202 this->nic_cmd.type = NIC_CMD_WRITABLE; 2203 this->nic_cmd.length = length; 2204 2205 // call driver to test writable 2206 chdev->cmd( XPTR( local_cxy , this ) ); 2207 2208 // return status 2209 *writable = this->nic_cmd.status; 2210 2211 // return error 2212 return this->nic_cmd.error; 2213 2214 } // end dev_nic_tx_queue_writable 2215 2216 /////////////////////////////////////////////////////////////////////////////////////////// 2217 // This static function is called by the dev_nic_tx_server() function. 2218 // It moves one ETH/IP/UDP packet from the kernel buffer identified by the <buffer> and 2219 // <length> arguments to the NIC_TX_QUEUE identified the <chdev> argument. 2220 // It calls directly the NIC driver, without registering in a waiting queue, because 2221 // only this NIC_TX server thread can access this NIC_TX_QUEUE. 2222 // 1) It checks NIC_TX_QUEUE status in a while loop, using the NIC_CMD_WRITABLE command. 2223 // As long as the queue is not writable, it blocks and deschedules. It is re-activated 2224 // by the NIC-TX ISR as soon as the queue changes status. 2225 // 2) When the queue is writable, it put the ETH/IP/UDP packet into the NIC_TX_QUEUE, 2226 // using the driver NIC_CMD_WRITE command. 2227 // Both commands are successively registered in this NIC-TX server thread descriptor 2228 // to be passed to the driver. 2229 /////////////////////////////////////////////////////////////////////////////////////////// 2230 // @ chdev : [in] local pointer on NIC_TX chdev. 2231 // @ buffer : [in] pointer on a local kernel buffer (2K bytes). 2232 // @ length : [in] actual Ethernet packet length in bytes. 2233 /////////////////////////////////////////////////////////////////////////////////////////// 2234 void dev_nic_tx_move_packet( chdev_t * chdev, 2235 uint8_t * buffer, 2236 uint32_t length ) 2237 { 2238 error_t error; 2239 uint32_t writable; 2240 2241 thread_t * this = CURRENT_THREAD; 2242 2243 // get extended pointers on server tread and chdev 2244 xptr_t thread_xp = XPTR( local_cxy , this ); 2245 xptr_t chdev_xp = XPTR( local_cxy , chdev ); 2246 2247 // get local pointer on core running this server thead 2248 core_t * core = this->core; 2249 2250 // check thread can yield 2251 assert( (this->busylocks == 0), 2252 "cannot yield : busylocks = %d\n", this->busylocks ); 181 2253 182 2254 #if DEBUG_DEV_NIC_RX 183 2255 uint32_t cycle = (uint32_t)hal_get_cycles(); 184 2256 if( DEBUG_DEV_NIC_RX < cycle ) 185 printk("\n[ DBG] %s : thread %x enters for packet %x in cluster %x\n",186 __FUNCTION__ , thread_ptr , pkd , local_cxy);2257 printk("\n[%s] thread[%x,%x] enters for packet %x / cycle %d\n", 2258 __FUNCTION__, this->process->pid, this->trdid, pkd, cycle ); 187 2259 #endif 188 2260 189 // get pointer on NIC-TX chdev descriptor 190 uint32_t channel = thread_ptr->chdev->channel; 191 xptr_t dev_xp = chdev_dir.nic_tx[channel]; 192 cxy_t dev_cxy = GET_CXY( dev_xp ); 193 chdev_t * dev_ptr = (chdev_t *)GET_PTR( dev_xp ); 194 195 assert( (dev_xp != XPTR_NULL) , "undefined NIC chdev descriptor" ); 196 197 assert( (dev_cxy == local_cxy) , " chdev must be local" ); 198 199 // initialize command in thread descriptor 200 thread_ptr->nic_cmd.dev_xp = dev_xp; 201 202 // call driver to test writable 203 thread_ptr->nic_cmd.cmd = NIC_CMD_WRITABLE; 204 dev_ptr->cmd( thread_xp ); 205 206 // check error 207 error = thread_ptr->nic_cmd.error; 208 if( error ) return error; 209 210 // block and deschedule if queue non writable 211 if( thread_ptr->nic_cmd.status == false ) 212 { 213 // enable NIC-TX IRQ 214 dev_pic_enable_irq( core->lid ,dev_xp ); 215 216 // block client thread on THREAD_BLOCKED I/O condition 217 thread_block( XPTR( local_cxy , thread_ptr ) , THREAD_BLOCKED_IO ); 218 219 // deschedule client thread 220 sched_yield("client blocked on I/O"); 221 222 // disable NIC-TX IRQ 223 dev_pic_disable_irq( core->lid , dev_xp ); 224 } 225 226 // call driver for actual write 227 thread_ptr->nic_cmd.cmd = NIC_CMD_WRITE; 228 thread_ptr->nic_cmd.buffer = pkd->buffer; 229 thread_ptr->nic_cmd.length = pkd->length; 230 dev_ptr->cmd( thread_xp ); 231 232 // check error 233 error = thread_ptr->nic_cmd.error; 234 if( error ) return error; 2261 // check NIC_TX_QUEUE writable 2262 while( 1 ) 2263 { 2264 error = dev_nic_tx_queue_writable( chdev, 2265 length, 2266 &writable ); 2267 if( error ) 2268 { 2269 printk("\n[PANIC] in %s : cannot access NIC_TX queue\n", __FUNCTION__ ); 2270 return; 2271 } 2272 2273 if( writable == 0 ) // block & deschedule if non writable 2274 { 2275 // enable NIC-TX IRQ 2276 dev_pic_enable_irq( core->lid , chdev_xp ); 2277 2278 // block TX server thread 2279 thread_block( thread_xp , THREAD_BLOCKED_ISR ); 2280 2281 // deschedule TX server thread 2282 sched_yield("client blocked on NIC_TX queue full"); 2283 2284 // disable NIC-TX IRQ 2285 dev_pic_disable_irq( core->lid , chdev_xp ); 2286 } 2287 else // exit loop if writable 2288 { 2289 break; 2290 } 2291 } 2292 2293 // initialize WRITE command in server thread descriptor 2294 this->nic_cmd.dev_xp = chdev_xp; 2295 this->nic_cmd.type = NIC_CMD_WRITE; 2296 this->nic_cmd.buffer = buffer; 2297 this->nic_cmd.length = length; 2298 2299 // call driver to move packet 2300 chdev->cmd( thread_xp ); 235 2301 236 2302 #if DEBUG_DEV_NIC_RX 237 2303 cycle = (uint32_t)hal_get_cycles(); 238 2304 if( DEBUG_DEV_NIC_RX < cycle ) 239 printk("\n[ DBG] %s : thread %x exit for packet %x in cluster%x\n",240 __FUNCTION__ , th read_ptr , pkd , local_cxy);2305 printk("\n[%s] thread[%x,%x] exit for packet %x\n", 2306 __FUNCTION__ , this->process->pid, this->trdid , pkd ); 241 2307 #endif 242 2308 243 return 0; 244 } // end dev_nic_write() 245 246 247 2309 return; 2310 2311 } // end dev_nic_tx_move_packet() 2312 2313 /////////////////////////////////////////////////////////////////////////////////////////// 2314 // This static function is called by the dev_nic_tx_server() function to build an UDP 2315 // header in the kernel buffer defined by the <k_buf> arguement, as specified by the 2316 // <socket_xp> argument. The <length> argument defines the number of bytes in payload. 2317 // It set the "src_port", "dst_port", "total_length" and "checksum" fields in UDP header. 2318 // The payload must be previouly loaded in the pernel buffer. 2319 /////////////////////////////////////////////////////////////////////////////////////////// 2320 // @ k_buf : [in] pointer on first byte of UDP header in kernel buffer. 2321 // @ socket_xp : [in] extended pointer on socket. 2322 // @ length : [in] number of bytes in payload. 2323 /////////////////////////////////////////////////////////////////////////////////////////// 2324 void dev_nic_tx_build_udp_header( uint8_t * k_buf, 2325 xptr_t socket_xp, 2326 uint32_t length ) 2327 { 2328 uint16_t checksum; // checksum value 2329 uint32_t total_length; // total UDP packet length 2330 uint32_t local_addr; // local IP address 2331 uint32_t remote_addr; // remote IP address 2332 uint32_t local_port; // local port 2333 uint32_t remote_port; // remote port 2334 2335 // get socket cluster an local pointer 2336 socket_t * socket_ptr = GET_PTR( socket_xp ); 2337 cxy_t socket_cxy = GET_CXY( socket_xp ); 2338 2339 // get relevant infos from socket 2340 local_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_addr )); 2341 remote_addr = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_addr )); 2342 local_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->local_port )); 2343 remote_port = hal_remote_l32(XPTR(socket_cxy , &socket_ptr->remote_port )); 2344 2345 // compute UDP packet total length 2346 total_length = length + UDP_HEAD_LEN; 2347 2348 // set src_port and dst_port in header 2349 k_buf[0] = local_port >> 8; 2350 k_buf[1] = local_port; 2351 k_buf[2] = remote_port >> 8; 2352 k_buf[3] = remote_port; 2353 2354 // set packet length in header 2355 k_buf[4] = total_length >> 8; 2356 k_buf[5] = total_length; 2357 2358 // compute UDP packet checksum 2359 checksum = dev_nic_udp_checksum( k_buf , total_length ); 2360 2361 // set checksum 2362 k_buf[6] = checksum >> 8; 2363 k_buf[7] = checksum; 2364 2365 } // end dev_nic_tx_build_udp_header() 2366 2367 /////////////////////////////////////////////////////////////////////////////////////////// 2368 // This static function is called by the dev_nic_tx_server() function. 2369 // It builds a TCP header in the kernel buffer defined by the <k_buf> argument. 2370 // The payload must have been previouly registered in this buffer. 2371 // The "local_addr", "local_port", "remote_addr", "remote_port", seq_num", "ack_num", 2372 // and "window" fields are obtained from the <socket_xp> argument. 2373 // The <length> argument defines the number of bytes in payload, and the <flags> argument 2374 // defines the flags to be set in TCP header. 2375 /////////////////////////////////////////////////////////////////////////////////////////// 2376 // @ k_buf : [in] pointer on first byte of TCP header in kernel buffer. 2377 // @ length : [in] number of bytes in payload. 2378 // @ socket_xp : [in] extended pointer on socket. 2379 // @ flags : [in] flags to be set in TCP header. 2380 /////////////////////////////////////////////////////////////////////////////////////////// 2381 void dev_nic_tx_build_tcp_header( uint8_t * k_buf, 2382 uint32_t length, 2383 xptr_t socket_xp, 2384 uint8_t flags ) 2385 { 2386 uint16_t checksum; // global segment checksum 2387 uint32_t total_length; // total UDP packet length 2388 uint32_t src_addr; // local IP address 2389 uint32_t dst_addr; // remote IP address 2390 uint16_t src_port; // local port 2391 uint16_t dst_port; // remote port 2392 uint32_t seq_num; // first byte of segment in TX stream 2393 uint32_t ack_num; // next expected byte in RX stream 2394 uint16_t window; // window of accepted segments in RX stream 2395 2396 // get socket cluster an local pointer 2397 socket_t * sock_ptr = GET_PTR( socket_xp ); 2398 cxy_t sock_cxy = GET_CXY( socket_xp ); 2399 2400 // get relevant infos from socket 2401 src_addr = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->local_addr )); 2402 dst_addr = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->remote_addr )); 2403 src_port = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->local_port )); 2404 dst_port = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->remote_port )); 2405 seq_num = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->tx_nxt )); 2406 ack_num = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->rx_nxt )); 2407 window = hal_remote_l32(XPTR( sock_cxy , &sock_ptr->rx_wnd )); 2408 2409 // compute TCP segment total length 2410 total_length = length + TCP_HEAD_LEN; 2411 2412 // set "src_port" and "dst_port" 2413 k_buf[0] = src_port >> 8; 2414 k_buf[1] = src_port; 2415 k_buf[2] = dst_port >> 8; 2416 k_buf[3] = dst_port; 2417 2418 // set "seq_num" 2419 k_buf[4] = seq_num >> 24; 2420 k_buf[5] = seq_num >> 16; 2421 k_buf[6] = seq_num >> 8; 2422 k_buf[7] = seq_num; 2423 2424 // set "ack_num" 2425 k_buf[8] = ack_num >> 24; 2426 k_buf[9] = ack_num >> 16; 2427 k_buf[10] = ack_num >> 8; 2428 k_buf[11] = ack_num; 2429 2430 // set "hlen" 2431 k_buf[12] = 5; 2432 2433 // set "flags" 2434 k_buf[13] = flags & 0x3F; 2435 2436 // set "window" 2437 k_buf[14] = window >> 8; 2438 k_buf[15] = window; 2439 2440 // reset "checksum" 2441 k_buf[16] = 0; 2442 k_buf[17] = 0; 2443 2444 // set "urgent_ptr" 2445 k_buf[18] = 0; 2446 k_buf[19] = 0; 2447 2448 // compute TCP segment checksum 2449 checksum = dev_nic_tcp_checksum( k_buf, 2450 total_length, 2451 src_addr, 2452 dst_addr ); 2453 // set "checksum" 2454 k_buf[16] = checksum >> 8; 2455 k_buf[17] = checksum; 2456 2457 } // end dev_nic_tx_build_tcp_header() 2458 2459 2460 /////////////////////////////////////////////////////////////////////////////////////////// 2461 // This static function is called by the dev_nic_tx_server() function. 2462 // It builds the IP header in the 20 first bytes of <buffer>. 2463 /////////////////////////////////////////////////////////////////////////////////////////// 2464 // @ buffer : pointer on first byte of IP header in kernel buffer 2465 // @ src_addr : source IP address. 2466 // @ dst_addr : destination IP address. 2467 // @ length : number of bytes in IP packet payload. 2468 /////////////////////////////////////////////////////////////////////////////////////////// 2469 void dev_nic_tx_build_ip_header( uint8_t * buffer, 2470 uint32_t src_addr, 2471 uint32_t dst_addr, 2472 uint16_t length ) 2473 { 2474 uint16_t hcs; 2475 2476 uint16_t total = length + IP_HEAD_LEN; 2477 2478 buffer[0] = 0x45; // IPV4 / IHL = 20 bytes 2479 buffer[1] = 0; // DSCP / ECN 2480 buffer[2] = total >> 8; 2481 buffer[3] = total; 2482 2483 buffer[4] = 0x40; // Don't Fragment 2484 buffer[5] = 0; 2485 buffer[6] = 0; 2486 buffer[7] = 0; 2487 2488 buffer[8] = 0xFF; // TTL 2489 buffer[9] = 0x11; // UDP protocol 2490 2491 buffer[12] = src_addr >> 24; 2492 buffer[13] = src_addr >> 16; 2493 buffer[14] = src_addr >> 8; 2494 buffer[15] = src_addr; 2495 2496 buffer[16] = dst_addr >> 24; 2497 buffer[17] = dst_addr >> 16; 2498 buffer[18] = dst_addr >> 8; 2499 buffer[19] = dst_addr; 2500 2501 // compute IP header checksum 2502 hcs = dev_nic_ip_checksum( buffer ); 2503 2504 // set checksum 2505 buffer[10] = hcs >> 8; 2506 buffer[11] = hcs; 2507 2508 } // end dev_nic_tx_build_ip_header 2509 2510 /////////////////////////////////////////////////////////////////////////////////////////// 2511 // This static function is called by the dev_nic_tx_server() function. 2512 // It builds the Ethernet header in the 14 first bytes of <buffer>. 2513 /////////////////////////////////////////////////////////////////////////////////////////// 2514 // @ buffer : pointer on first byte of Ethernet header in kernel buffer 2515 // @ src_mac_54 : two MSB bytes in source MAC address. 2516 // @ src_mac_32 : two MED bytes in source MAC address. 2517 // @ src_mac_10 : two LSB bytes in source MAC address. 2518 // @ dst_mac_54 : two MSB bytes in destination MAC address. 2519 // @ dst_mac_32 : two MED bytes in destination MAC address. 2520 // @ dst_mac_10 : two LSB bytes in destination MAC address. 2521 // @ length : number of bytes in Ethernet frame payload. 2522 /////////////////////////////////////////////////////////////////////////////////////////// 2523 void dev_nic_tx_build_eth_header( uint8_t * buffer, 2524 uint16_t src_mac_54, 2525 uint16_t src_mac_32, 2526 uint16_t src_mac_10, 2527 uint16_t dst_mac_54, 2528 uint16_t dst_mac_32, 2529 uint16_t dst_mac_10, 2530 uint32_t length ) 2531 { 2532 buffer[0] = dst_mac_54 >> 8; 2533 buffer[1] = dst_mac_54; 2534 buffer[2] = dst_mac_32 >> 8; 2535 buffer[3] = dst_mac_32; 2536 buffer[4] = dst_mac_10 >> 8; 2537 buffer[5] = dst_mac_10; 2538 2539 buffer[6] = src_mac_54 >> 8; 2540 buffer[7] = src_mac_54; 2541 buffer[8] = src_mac_32 >> 8; 2542 buffer[9] = src_mac_32; 2543 buffer[10] = src_mac_10 >> 8; 2544 buffer[11] = src_mac_10; 2545 2546 buffer[12] = length >> 8; 2547 buffer[13] = length; 2548 2549 } // end dev_nic_tx_build_eth_header() 2550 2551 /////////////////////////////////////////////////////////////////////////////////////////// 2552 // This static function is called by the dev_nic_tx_server() function to handle one 2553 // TX command, or one R2T request, registered in the socket identified by the <socket_xp> 2554 // argument. If there is one valid command, or if the R2T queue is non empty (for a TCP 2555 // socket), it builds an ETH/IP/UDP packet (or a ETH/IP/TCP segment), in the buffer 2556 // defined by the <k_buf> argument, and registers it in the NIC_TX queue defined by the 2557 // <chdev> argument. The supported commands are SOCKET_SEND/SOCKET_CONNECT/SOCKET_CLOSE. 2558 // It unblocks the client thread when the command is completed. 2559 /////////////////////////////////////////////////////////////////////////////////////////// 2560 // When there is a packet to send, it makes the following actions: 2561 // 1) it takes the lock protecting the socket state. 2562 // 2) it get the command arguments from client thread descriptor. 2563 // 3) it build an UDP packet or a TCP segment, depending on both the command type, and 2564 // the socket state, updates the socket state, and unblocks the client thread. 2565 // 4) it release the lock protecting the socket. 2566 // 5) it build the IP header. 2567 // 6) it build the ETH header. 2568 // 7) it copies the packet in the NIC_TX queue. 2569 /////////////////////////////////////////////////////////////////////////////////////////// 2570 // @ socket_xp : [in] extended pointer on client socket. 2571 // @ k_buf : [in] local pointer on kernel buffer (2 Kbytes). 2572 // @ chdev : [in] local pointer on NIC_RX chdev. 2573 /////////////////////////////////////////////////////////////////////////////////////////// 2574 static void dev_nic_tx_handle_one_cmd( xptr_t socket_xp, 2575 uint8_t * k_buf, 2576 chdev_t * chdev ) 2577 { 2578 socket_t * socket_ptr; 2579 cxy_t socket_cxy; 2580 xptr_t client_xp; // extended pointer on client thread 2581 thread_t * client_ptr; 2582 cxy_t client_cxy; 2583 sock_cmd_t cmd; // NIC command type 2584 uint8_t * buf; // pointer on user buffer 2585 uint32_t len; // user buffer length 2586 uint32_t todo; // number of bytes not yet sent 2587 uint32_t socket_type; // socket type (UDP/TCP) 2588 uint32_t socket_state; // socket state 2589 xptr_t socket_lock_xp; // extended pointer on socket lock 2590 xptr_t socket_r2tq_xp; // extended pointer on R2T queue 2591 uint32_t src_ip_addr; // source IP address 2592 uint32_t dst_ip_addr; // destination IP address 2593 uint32_t tx_una; // next byte to be sent 2594 uint32_t tx_nxt; // first unacknowledged byte 2595 uint32_t nbytes; // number of bytes in UDP/TCP packet payload 2596 uint8_t * k_base; // pointer UDP/TCP packet in kernel buffer 2597 uint32_t trsp_length; // length of TCP/UDP packet 2598 uint8_t r2t_flags; // flags defined by one R2T queue request 2599 bool_t do_send; // build & send a packet when true 2600 2601 // get socket cluster and local pointer 2602 socket_cxy = GET_CXY( socket_xp ); 2603 socket_ptr = GET_PTR( socket_xp ); 2604 2605 // build extended pointer on socket lock and r2t queue 2606 socket_lock_xp = XPTR( socket_cxy , &socket_ptr->lock ); 2607 socket_r2tq_xp = XPTR( socket_cxy , &socket_ptr->r2tq ); 2608 2609 // 1. take lock protecting this socket 2610 remote_rwlock_wr_acquire( socket_lock_xp ); 2611 2612 // get pointers on TX client thread from socket 2613 client_xp = hal_remote_l64( XPTR( socket_cxy , &socket_ptr->tx_client )); 2614 client_cxy = GET_CXY( client_xp ); 2615 client_ptr = GET_PTR( client_xp ); 2616 2617 // check valid command 2618 if( client_xp != XPTR_NULL ) // valid command found 2619 { 2620 // 2. get command arguments from socket 2621 cmd = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_cmd )); 2622 buf = hal_remote_lpt( XPTR(socket_cxy , &socket_ptr->tx_buf )); 2623 len = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_len )); 2624 todo = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_todo )); 2625 2626 // get socket type and state 2627 socket_type = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->type )); 2628 socket_state = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->state )); 2629 2630 // 3. UDP : build UDP packet and update UDP socket state 2631 if( socket_type == SOCK_DGRAM ) 2632 { 2633 if( socket_state == UDP_STATE_UNBOUND ) 2634 { 2635 // report illegal command 2636 dev_nic_tx_report_error( socket_xp, cmd, socket_state ); 2637 2638 do_send = false; 2639 } 2640 else // BOUND or CONNECT state 2641 { 2642 if( cmd == SOCKET_TX_SEND ) 2643 { 2644 // compute payload length 2645 nbytes = ( PAYLOAD_MAX_LEN < todo ) ? PAYLOAD_MAX_LEN : todo; 2646 2647 // compute UDP packet base in kernel buffer 2648 k_base = k_buf + ETH_HEAD_LEN + IP_HEAD_LEN; 2649 2650 // move payload to kernel buffer 2651 hal_copy_from_uspace( XPTR(local_cxy , k_base + UDP_HEAD_LEN ), 2652 buf + (len - todo), 2653 nbytes ); 2654 // build UDP header 2655 dev_nic_tx_build_udp_header( k_base, 2656 socket_xp, 2657 nbytes ); 2658 2659 // update "tx_todo" in socket descriptor 2660 hal_remote_s32( XPTR(socket_cxy , socket_ptr->tx_todo), 2661 todo - nbytes ); 2662 2663 // unblock client thread when SEND command completed 2664 if( nbytes == todo ) 2665 { 2666 dev_nic_tx_report_success( socket_xp ); 2667 } 2668 2669 do_send = true; 2670 } 2671 else 2672 { 2673 // report illegal command 2674 dev_nic_tx_report_error( socket_xp, cmd, socket_state ); 2675 2676 do_send = false; 2677 } 2678 } 2679 2680 // compute transport packet length 2681 trsp_length = UDP_HEAD_LEN + nbytes; 2682 2683 } // end UDP 2684 2685 // 3. TCP : build TCP segment and update TCP socket state 2686 if( socket_type == SOCK_STREAM ) 2687 { 2688 // extract one request from TCP socket R2T queue if queue non empty 2689 if( remote_buf_status( socket_r2tq_xp ) ) 2690 { 2691 remote_buf_get_to_kernel( socket_r2tq_xp , &r2t_flags , 1 ); 2692 } 2693 else 2694 { 2695 r2t_flags = 0; 2696 } 2697 2698 ///////////////////////////////////// 2699 if( socket_state == TCP_STATE_ESTAB ) // connected TCP socket 2700 { 2701 if( cmd == SOCKET_TX_SEND ) 2702 { 2703 // get "tx_nxt", and "tx_una" from socket descriptor 2704 tx_nxt = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_nxt )); 2705 tx_una = hal_remote_l32( XPTR(socket_cxy , &socket_ptr->tx_una )); 2706 2707 // compute actual payload length 2708 nbytes = ( PAYLOAD_MAX_LEN < todo ) ? PAYLOAD_MAX_LEN : todo; 2709 2710 // compute TCP segment base in kernel buffer 2711 k_base = k_buf + ETH_HEAD_LEN + IP_HEAD_LEN; 2712 2713 // move payload to kernel buffer 2714 hal_copy_from_uspace( XPTR( local_cxy , k_base + TCP_HEAD_LEN ), 2715 buf + (len - todo), 2716 nbytes ); 2717 2718 // build TCP header 2719 dev_nic_tx_build_tcp_header( k_base, 2720 socket_xp, 2721 nbytes, // payload 2722 TCP_FLAG_ACK | r2t_flags ); // flags 2723 2724 // update "tx_todo" in socket descriptor 2725 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_todo ), 2726 todo - nbytes ); 2727 2728 // update "tx_nxt" in socket descriptor 2729 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ), 2730 tx_nxt + nbytes ); 2731 2732 // unblock client thread when SEND command completed 2733 if( (todo == 0) && (tx_nxt == tx_una) ) 2734 { 2735 dev_nic_tx_report_success( socket_xp ); 2736 } 2737 2738 do_send = true; 2739 } 2740 else if( cmd == SOCKET_TX_CLOSE ) 2741 { 2742 // build TCP FIN segment 2743 dev_nic_tx_build_tcp_header( k_base, 2744 socket_xp, 2745 0, // payload 2746 TCP_FLAG_FIN | r2t_flags ); // flags 2747 // update socket state 2748 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 2749 TCP_STATE_FIN_WAIT1 ); 2750 2751 do_send = true; 2752 } 2753 else // cmd == CONNECT 2754 { 2755 // report illegal command 2756 dev_nic_tx_report_error( socket_xp , cmd , socket_state ); 2757 2758 do_send = false; 2759 } 2760 } 2761 ////////////////////////////////////////// 2762 else if( socket_state == TCP_STATE_BOUND ) // unconnected TCP socket 2763 { 2764 if ( cmd == SOCKET_TX_CONNECT ) 2765 { 2766 // set socket.tx_nxt 2767 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_nxt ), 2768 TCP_ISS ); 2769 2770 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_nxt ), 0 ); 2771 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->rx_wnd ), 2772 NIC_RX_BUF_SIZE); 2773 2774 // build TCP SYN segment 2775 dev_nic_tx_build_tcp_header( k_base, 2776 socket_xp, 2777 0, // payload 2778 TCP_FLAG_SYN ); // flags 2779 // update socket state 2780 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 2781 TCP_STATE_SYN_SENT ); 2782 2783 do_send = true; 2784 } 2785 else // cmd == SEND / CLOSE 2786 { 2787 // report illegal command 2788 dev_nic_tx_report_error( socket_xp, cmd, socket_state ); 2789 2790 do_send = false; 2791 } 2792 } 2793 /////////////////////////////////////////// 2794 else if( socket_state == TCP_STATE_LISTEN ) // server wait connect 2795 { 2796 if( cmd == SOCKET_TX_CONNECT ) 2797 { 2798 // update socket.state 2799 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 2800 TCP_STATE_SYN_SENT ); 2801 2802 // set socket.tx_una 2803 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_una ), 2804 TCP_ISS ); 2805 2806 // set socket.tx_nxt 2807 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->tx_una ), 2808 TCP_ISS + 1 ); 2809 2810 // build TCP SYN segment 2811 dev_nic_tx_build_tcp_header( k_base, 2812 socket_xp, 2813 0, // payload 2814 TCP_FLAG_SYN ); // flags 2815 do_send = true; 2816 } 2817 else // cmd == CLOSE / SEND 2818 { 2819 // report illegal command 2820 dev_nic_tx_report_error( socket_xp, cmd, socket_state ); 2821 2822 do_send = false; 2823 } 2824 } 2825 ///////////////////////////////////////////// 2826 else if( socket_state == TCP_STATE_SYN_RCVD ) // socket wait ACK 2827 { 2828 if( cmd == SOCKET_TX_CLOSE ) 2829 { 2830 // build TCP FIN segment 2831 dev_nic_tx_build_tcp_header( k_base, 2832 socket_xp, 2833 0, // payload 2834 TCP_FLAG_FIN ); // flags 2835 // update socket state 2836 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 2837 TCP_STATE_FIN_WAIT1 ); 2838 2839 do_send = true; 2840 } 2841 else // SEND / CONNECT 2842 { 2843 // report illegal command 2844 dev_nic_tx_report_error( socket_xp, cmd, socket_state ); 2845 2846 do_send = false; 2847 } 2848 } 2849 //////////////////////////////////////////////// 2850 else if( socket_state == TCP_STATE_CLOSE_WAIT ) // wait local close() 2851 { 2852 if( cmd == SOCKET_TX_CLOSE ) 2853 { 2854 // build TCP FIN segment 2855 dev_nic_tx_build_tcp_header( k_base, 2856 socket_xp, 2857 0, // payload 2858 TCP_FLAG_FIN ); // flags 2859 // update socket state 2860 hal_remote_s32( XPTR( socket_cxy , &socket_ptr->state ), 2861 TCP_STATE_LAST_ACK ); 2862 2863 do_send = true; 2864 } 2865 else // SEND / CONNECT 2866 { 2867 // report illegal command 2868 dev_nic_tx_report_error( socket_xp, cmd, socket_state ); 2869 2870 do_send = false; 2871 } 2872 } 2873 //// 2874 else 2875 { 2876 // report illegal command 2877 dev_nic_tx_report_error( socket_xp, cmd, socket_state ); 2878 2879 do_send = false; 2880 } 2881 2882 // compute TCP segment length 2883 trsp_length = TCP_HEAD_LEN + nbytes; 2884 } 2885 } 2886 else // no valid command found 2887 { 2888 if( socket_type == SOCK_DGRAM ) // UDP socket 2889 { 2890 do_send = false; 2891 } 2892 else // TCP socket 2893 { 2894 if( remote_buf_status( socket_r2tq_xp ) == 0 ) // R2T queue empty 2895 { 2896 do_send = false; 2897 } 2898 else // pending request in R2T queue 2899 { 2900 // get one request from R2T queue 2901 remote_buf_get_to_kernel( socket_r2tq_xp , &r2t_flags , 1 ); 2902 2903 // build TCP header for an empty segment 2904 dev_nic_tx_build_tcp_header( k_base, 2905 socket_xp, 2906 0, // payload 2907 r2t_flags ); // flags 2908 do_send = true; 2909 } 2910 } 2911 } 2912 2913 // 4. release the lock protecting the socket 2914 remote_rwlock_wr_release( socket_lock_xp ); 2915 2916 // return if no packet to send 2917 if( do_send == false ) return; 2918 2919 // 5. build IP header 2920 dev_nic_tx_build_ip_header( k_buf + ETH_HEAD_LEN, 2921 src_ip_addr, 2922 dst_ip_addr, 2923 IP_HEAD_LEN + trsp_length ); 2924 2925 // 6. build ETH header 2926 dev_nic_tx_build_eth_header( k_buf, 2927 (uint16_t)SRC_MAC_54, 2928 (uint16_t)SRC_MAC_32, 2929 (uint16_t)SRC_MAC_10, 2930 (uint16_t)DST_MAC_54, 2931 (uint16_t)DST_MAC_32, 2932 (uint16_t)DST_MAC_10, 2933 ETH_HEAD_LEN + IP_HEAD_LEN + trsp_length ); 2934 2935 // 7. move packet to NIC_TX queue 2936 dev_nic_tx_move_packet( chdev, 2937 k_buf, 2938 ETH_HEAD_LEN + IP_HEAD_LEN + trsp_length ); 2939 2940 } // end dev_nic_tx_handle_one_cmd() 2941 2942 ///////////////////////////////////////// 2943 void dev_nic_tx_server( chdev_t * chdev ) 2944 { 2945 uint8_t k_buf[NIC_KERNEL_BUF_SIZE]; // buffer for one packet 2946 2947 xptr_t root_xp; // extended pointer on clients list root 2948 xptr_t lock_xp; // extended pointer on lock protecting this list 2949 xptr_t socket_xp; // extended pointer on on client socket 2950 socket_t * socket_ptr; 2951 cxy_t socket_cxy; 2952 xptr_t entry_xp; // extended pointer on socket tx_list entry 2953 2954 thread_t * this = CURRENT_THREAD; 2955 2956 // check chdev direction and type 2957 assert( (chdev->func == DEV_FUNC_NIC) && (chdev->is_rx == false) , 2958 "illegal chdev type or direction" ); 2959 2960 // check thread can yield 2961 assert( (this->busylocks == 0), 2962 "cannot yield : busylocks = %d\n", this->busylocks ); 2963 2964 // build extended pointer on client sockets lock & root 2965 lock_xp = XPTR( local_cxy , &chdev->wait_lock ); 2966 root_xp = XPTR( local_cxy , &chdev->wait_root ); 2967 2968 while( 1 ) // TX server infinite loop 2969 { 2970 // take the lock protecting the client sockets queue 2971 remote_busylock_acquire( lock_xp ); 2972 2973 /////////////// block and deschedule if no clients 2974 if( xlist_is_empty( root_xp ) == false ) 2975 { 2976 // release the lock protecting the TX client sockets queue 2977 remote_busylock_release( lock_xp ); 2978 2979 // block and deschedule 2980 thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_CLIENT ); 2981 sched_yield( "waiting client" ); 2982 } 2983 ////////////// 2984 else 2985 { 2986 // get first client socket 2987 socket_xp = XLIST_FIRST( root_xp , socket_t , tx_list ); 2988 socket_cxy = GET_CXY( socket_xp ); 2989 socket_ptr = GET_PTR( socket_xp ); 2990 2991 // build extended pointer on socket xlist_entry 2992 entry_xp = XPTR( socket_cxy , &socket_ptr->tx_list ); 2993 2994 // remove this socket from the waiting queue 2995 xlist_unlink( entry_xp ); 2996 2997 // release the lock protecting the client sockets queue 2998 remote_busylock_release( lock_xp ); 2999 3000 // handle this TX client 3001 dev_nic_tx_handle_one_cmd( socket_xp, 3002 k_buf, 3003 chdev ); 3004 3005 // take the lock protecting the client sockets queue 3006 remote_busylock_acquire( lock_xp ); 3007 3008 // add this socket in last position of queue 3009 xlist_add_last( root_xp , entry_xp ); 3010 3011 // release the lock protecting the client sockets queue 3012 remote_busylock_release( lock_xp ); 3013 } 3014 } // end while 3015 } // end dev_nic_tx_server() 3016 3017 -
trunk/kernel/devices/dev_nic.h
r457 r657 2 2 * dev_nic.h - NIC (Network Controler) generic device API definition. 3 3 * 4 * Author Alain Greiner (2016,2017,2018 )4 * Author Alain Greiner (2016,2017,2018,2019,2020) 5 5 * 6 6 * Copyright (c) UPMC Sorbonne Universites … … 27 27 #include <kernel_config.h> 28 28 #include <hal_kernel_types.h> 29 #include <remote_busylock.h> 30 #include <remote_buf.h> 31 #include <xlist.h> 32 33 /**** Forward declarations ****/ 34 35 struct chdev_s; 29 36 30 37 /***************************************************************************************** 31 38 * Generic Network Interface Controler definition 32 39 * 33 * This device provide access to an external Gigabit Ethernet network controler. 34 * It assume that the NIC hardware peripheral handles two packets queues for sent (TX) 35 * and received (RX) packets. Packets are (Ethernet/IPV4). 40 * This device provides access to a generic Gigabit Ethernet network controler. 41 * It assumes that the NIC hardware peripheral handles two packets queues for sent (TX) 42 * and received (RX) packets. 43 * 44 * The supported protocols stack is : Ethernet / IPV4 / TCP or UDP 36 45 * 37 * The NIC device is handling an (infinite) stream of packets to or from the network. 46 * 1) hardware assumptions 47 * 48 * The NIC device is handling two (infinite) streams of packets to or from the network. 38 49 * It is the driver responsibility to move the RX packets from the NIC to the RX queue, 39 50 * and the TX packets from the TX queue to the NIC. 40 51 * 41 * AS the RX and TX queues are independant, there is one NIC-RX device descriptor 42 * to handle RX packets, and another NIC-TX device descriptor to handle TX packets. 43 * In order to improve throughput, the hardware NIC controller can optionnally implement 44 * multiple channels: 45 * - The RX channels are indexed by an hash key derived from the source IP address. 46 * - The TX channels are indexed by an hash key derived from the destination IP address. 47 * These 2*N devices, and 2*N associated server threads, are distributed in 2*N clusters. 48 * The 2*N server threads implement the protocols stack. The RX server threads block 49 * and deschedule when the RX queue is empty. The TX server stack block and deschedule 50 * when the queue is full. 51 * 52 * It is the driver responsibily to re-activate a blocked server thread when 53 * the queue state is modified: not full for TX, or not empty for RX. 52 * AS the RX and TX queues are independant, there is one NIC_RX device descriptor 53 * to handle RX packets, and another NIC_TX device descriptor to handle TX packets. 54 * 55 * In order to improve throughput, the NIC controller can implement multiple (N) channels. 56 * In this case, the channel index is defined by an hash function computed from the remote 57 * IP address and port. This index is computed by the hardware for an RX packet, and is 58 * computed by the kernel for a TX packet, using a specific driver function. TODO ... 59 * The 2*N chdevs, and the associated server threads implementing the protocols stack, 60 * are distributed in 2*N different clusters. 61 * 62 * 2) User API 63 * 64 * On the user side, ALMOS-MKH implements the POSIX socket API. 65 * The kernel functions implementing the socket related syscalls are : 66 * - dev_nic_socket() : create a local socket registered in process fd_array[]. 67 * - dev_nic_bind() : attach a local IP address and port to a local socket. 68 * - dev_nic_listen() : local server makes a passive open. 69 * - dev_nic_connect() : local client makes an active open to a remote server. 70 * - dev_nic_accept() : local server accept a new remote client. 71 * - dev_nic_send() : send data on a connected socket. 72 * - dev_nic_recv() : receive data on a connected socket. 73 * - dev_nic_sendto() : send a packet to a remote (IP address/port). 74 * - dev_nic_recvfrom() : receive a paket from a remote (IP address/port). 75 * - dev_nic_close() : close a socket 76 * 77 * 3) TX stream 78 * 79 * The internal API between the client threads and the TX server thread defines 80 * the 3 following commands: 81 * . SOCKET_TX_CONNECT : request to execute the 3 steps TCP connection handshake. 82 * . SOCKET_TX_SEND : send data to a remote socket (UDP or TCP). 83 * . SOCKET_TX_CLOSE : request to execute the 3 steps TCP close handshake. 84 * 85 * - These 3 commands are blocking for the client thread that registers the command in the 86 * socket descriptor, blocks on the BLOCKED_IO condition, and deschedules. 87 * - The TX server thread is acting as a multiplexer. It scans the list of attached sockets, 88 * to handle all valid commands: one UDP packet or TCP segment per iteration. 89 * It uses the user buffer defined by the client thread, and attached to socket descriptor, 90 * as a retransmission buffer. It blocks and deschedules on the BLOCKED_CLIENT condition, 91 * when there is no more active TX command registered in any socket. It is re-activated 92 * by the first client thread registering a new TX command in the socket descriptor. 93 * It unblocks a client thread only when a command is fully completed. It signals errors 94 * to the client thread using the tx_error field in socket descriptor. 95 * 96 * 4) RX stream 97 * 98 * The communication between the RX server thread and the client threads expecting data 99 * is done through receive buffers (one private buffer per socket) that are handled 100 * as single-writer / single reader-FIFOs, called rx_buf. 101 * - The RX server thread is acting as a demultiplexor: it handle one TCP segment or UDP 102 * packet per iteration, and register the data in the rx_buf of the socket matching 103 * the packet. It simply discard all packets that does not match a registered socket. 104 * When a client thread is registered in the socket descriptor, the RX server thread 105 * unblocks this client thread as soon as there is data available in rx_buf. 106 * It blocks and deschedules on the BLOCKED_ISR condition when there is no more packets 107 * in the NIC_RX queue. It is unblocked by the hardware ISR. 108 * - The client thread simply access the rx_buf attached to socket descriptor, and consumes 109 * the available data when the rx_buf is non empty. It blocks on the BLOCKED_IO condition, 110 * and deschedules when the rx_buf is empty. 111 * 112 * 5) R2T queue 113 * 114 * To implement the TCP "3 steps handshake" protocol, the RX server thread can directly 115 * request the associated TX server thread to send control packets in the TX stream, 116 * using a dedicate R2T (RX to TX) FIFO stored in the socket descriptor. 117 * 118 * 6) NIC driver API 119 * 120 * The generic NIC device "driver" API defines the following commands to the NIC driver: 121 * - READABLE : returns true if at least one RX paquet is available in RX queue. 122 * - WRITABLE : returns true if at least one empty slot is available in TX queue. 123 * - READ : consume one packet from the RX queue. 124 * - WRITE : produce one packet to the TX queue. 125 * All RX or TX paquets are sent or received in standard 2 Kbytes kernel buffers, 126 * that are dynamically allocated by the protocols stack. 127 * 128 * The actual TX an RX queues structures depends on the hardware NIC implementation, 129 * and are defined in the HAL specific driver code. 54 130 * 55 131 * WARNING: the WTI mailboxes used by the driver ro receive events from the hardware 56 132 * (available RX packet, or available free TX slot, for a given channel), must be 57 133 * statically allocated during the kernel initialisation phase, and must be 58 * routed to the cluster containing the associated device descriptor and server thread. 59 * to simplify the server thread re-activation. 60 * 61 * Finally, the generic NIC device API defines the following commands: 62 * - READABLE : returns true if at least one RX paquet is available in RX queue. 63 * - WRITABLE : returns true if atleast one empty slot is available in TX queue. 64 * - READ : consume one packet from the RX queue. 65 * - WRITE : produce one packet fto the TX queue. 66 * All RX or TX paquets are sent or received in standard 2 Kbytes kernel buffers, 67 * that are dynamically allocated by the protocols stack. The structure pkd_t 68 * defining a packet descriptor is defined below, and contain the buffer pointer 69 * and the actual Ethernet packet Length. 70 * 71 * The actual TX an RX queues structures depends on the hardware NIC implementation, 72 * and are defined in the driver code. 134 * routed to the cluster containing the associated TX/RX chdev and server thread. 135 * 73 136 *****************************************************************************************/ 74 137 … … 78 141 79 142 /****************************************************************************************** 80 * This defines the extension for the generic IOC device. 143 * Various constants used by the Protocols stack 144 *****************************************************************************************/ 145 146 #define SRC_MAC_54 0x54 147 #define SRC_MAC_32 0x32 148 #define SRC_MAC_10 0x10 149 #define DST_MAC_54 0x54 150 #define DST_MAC_32 0x32 151 #define DST_MAC_10 0x10 152 153 #define TCP_HEAD_LEN 20 154 #define UDP_HEAD_LEN 8 155 #define IP_HEAD_LEN 20 156 #define ETH_HEAD_LEN 14 157 158 #define PROTOCOL_UDP 0x11 159 #define PROTOCOL_TCP 0x06 160 161 #define TCP_ISS 0x10000 162 163 #define PAYLOAD_MAX_LEN 1500 // max payload for and UDP packet or a TCP segment 164 165 #define TCP_FLAG_FIN 0x01 166 #define TCP_FLAG_SYN 0x02 167 #define TCP_FLAG_RST 0x04 168 #define TCP_FLAG_PSH 0x08 169 #define TCP_FLAG_ACK 0x10 170 #define TCP_FLAG_URG 0x20 171 172 #define NIC_RX_BUF_SIZE 0x100000 // 1 Mbytes 173 #define NIC_R2T_QUEUE_SIZE 0x64 // smallest KCM size 174 #define NIC_CRQ_QUEUE_SIZE 0x8 // 8 * sizeof(sockaddr_t) = smallest KCM size 175 #define NIC_PKT_MAX_SIZE 1500 // for Ethernet 176 #define NIC_KERNEL_BUF_SIZE 2000 // for on ETH/IP/TCP packet 177 178 /***************************************************************************************** 179 * This defines the extension for the generic NIC device. 81 180 * The actual queue descriptor depends on the implementation. 82 *****************************************************************************************/ 181 * 182 * WARNING : for all NIC_TX and NIC_RX chdevs, the xlist rooted in in the chdev 183 * ("wait_root" and "wait_lock" fields) is actually a list of sockets. 184 ****************************************************************************************/ 83 185 84 186 typedef struct nic_extend_s 85 187 { 86 void * queue; /*! local pointer on the packets queue descriptor (RX or TX)*/188 void * queue; /*! local pointer on NIC queue descriptor (RX or TX) */ 87 189 } 88 190 nic_extend_t; 89 191 90 /****************************************************************************************** 91 * This structure defines the Ethernet/IPV4 packet descriptor, that is sent to, 92 * or received from, the protocols stack. 93 *****************************************************************************************/ 94 95 typedef struct pkd_s 96 { 97 char * buffer; /*! local pointer on 2 Kbytes buffer containing packet */ 98 uint32_t length; /*! actual number of bytes */ 99 } 100 pkd_t; 101 102 /****************************************************************************************** 192 /***************************************************************************************** 103 193 * This enum defines the various implementations of the generic NIC peripheral. 104 194 * This array must be kept consistent with the define in the arch_info.h file. 105 **************************************************************************************** */106 107 enum nic_impl_e195 ****************************************************************************************/ 196 197 typedef enum nic_impl_e 108 198 { 109 199 IMPL_NIC_CBF = 0, … … 112 202 nic_impl_t; 113 203 114 /****************************************************************************************** 115 * This defines the (implementation independant) command passed to the NIC driver. 116 *****************************************************************************************/ 204 /**************************************************************************************** 205 * This defines the (implementation independant) commands to access the NIC hardware. 206 * These commands are registered by the NIC_TX and NIC_RX server threads in the 207 * server thread descriptor, to be used by the NIC driver. 208 * The buffer is always a 2K bytes kernel buffer, containing an Ethernet packet. 209 ****************************************************************************************/ 117 210 118 211 typedef enum nic_cmd_e 119 212 { 120 NIC_CMD_WRITABLE = 0, /*! test TX queue not full (for a given length packet)*/121 NIC_CMD_WRITE = 1, /*! put one (given length) packet to TX queue*/122 NIC_CMD_READABLE = 2, /*! test RX queue not empty (for any length packet)*/123 NIC_CMD_READ = 3, /*! get one (any length) packet from RX queue*/213 NIC_CMD_WRITABLE = 10, /*! test TX queue not full (for a given packet length) */ 214 NIC_CMD_WRITE = 11, /*! put one (given length) packet to TX queue */ 215 NIC_CMD_READABLE = 12, /*! test RX queue not empty (for any packet length) */ 216 NIC_CMD_READ = 13, /*! get one (any length) packet from RX queue */ 124 217 } 125 218 nic_cmd_t; … … 127 220 typedef struct nic_command_s 128 221 { 129 xptr_t dev_xp; /*! extended pointer on device descriptor*/130 nic_cmd_t cmd; /*! requested operation type*/131 char * buffer; /*! local pointer on 2 Kbytes buffer containing packet*/132 uint32_t length; /*! actual number of bytes*/133 bool_t status; /*! return true if writable or readable (depend on command)*/134 uint32_t error; /*! return an error from the hardware (0 if no error)*/222 xptr_t dev_xp; /*! extended pointer on NIC chdev descriptor */ 223 nic_cmd_t type; /*! command type */ 224 uint8_t * buffer; /*! local pointer on buffer (kernel or user space) */ 225 uint32_t length; /*! number of bytes in buffer */ 226 uint32_t status; /*! return value (depends on command type) */ 227 uint32_t error; /*! return an error from the hardware (0 if no error) */ 135 228 } 136 229 nic_command_t; 230 231 /***************************************************************************************** 232 * This structure defines a socket descriptor. In order to parallelize the transfers, 233 * the set of all registered sockets is split in several subsets. 234 * The number of subsets is the number of NIC channels. 235 * The distribution key is computed from the (remote_addr/remote_port) couple. 236 * This computation is done by the NIC hardware for RX packets, 237 * and by the dev_nic_connect() function for the TX packets. 238 * 239 * A socket is attached to the NIC_TX[channel] & NIC_RX[channel] chdevs. 240 * Each socket descriptor allows the TX and TX server threads to access various buffers: 241 * - the user "send" buffer contains the data to be send by the TX server thread. 242 * - the kernel "receive" buffer contains the data received by the RX server thread. 243 * - the kernel "r2t" buffer allows the RX server thread to make direct requests 244 * to the associated TX server (to implement the TCP 3 steps handshake). 245 * 246 * The synchronisation mechanism between the clients threads and the servers threads 247 * is different for TX and RX transfers: 248 * 249 * 1) For a TX transfer, it can exist only one client thread for a given socket, 250 * the transfer is always initiated by the local process, and all TX commands 251 * (CONNECT/SEND/CLOSE) are blocking for the client thread. The user buffer is 252 * used by TCP to handle retransmissions when required.in case of re 253 * The client thread registers the command in the thread descriptor, registers itself 254 * in the socket descriptor, unblocks the TX server thread from the BLOCKED_CLIENT 255 * condition, blocks itself on the BLOCKED_IO condition, and deschedules. 256 * When the command is completed, the TX server thread unblocks the client thread. 257 * The TX server blocks itself on the BLOCKED_CLIENT condition, when there is no 258 * pending commands and the R2T queue is empty. It is unblocked when a client 259 * register a new command, or when the TX server thread register a mew request 260 * in the R2T queue. 261 * The tx_valid flip-flop is SET by the client thread to signal a valid command. 262 * It is RESET by the server thread when the command is completed: For a SEND, 263 * all bytes have been sent (UDP) or acknowledged (TCP). 264 * 265 * 2) For an RX transfer, it can exist only one client thread for a given socket, 266 * but the transfer is initiated by the remote process, and the RECV command 267 * is not really blocking: the data can arrive before the local RECV command is 268 * executed, and the server thread does not wait to receive all requested data 269 * to deliver data to client thread. Therefore each socket contains a receive 270 * buffer (rx_buf) handled as a single-writer/single-reader fifo. 271 * The client thread consumes data from the rx_buf when possible. It blocks on the 272 * BLOCKED_IO condition and deschedules when the rx_buf is empty. 273 * It is unblocked by the RX server thread when new data is available in the rx_buf. 274 * The RX server blocks itself on the BLOCKED_ISR condition When the NIC_RX packets 275 * queue is empty. It is unblocked by the hardware when new packets are available. 276 * 277 * Note : the socket domains and types are defined in the "shared_socket.h" file. 278 ****************************************************************************************/ 279 280 /****************************************************************************************** 281 * This function returns a printable string for a given NIC command <type>. 282 ****************************************************************************************** 283 * @ type : NIC command type 284 *****************************************************************************************/ 285 char * nic_cmd_str( uint32_t type ); 286 287 /****************************************************************************************** 288 * This function returns a printable string for a given socket <state>. 289 ****************************************************************************************** 290 * @ state : socket state 291 *****************************************************************************************/ 292 char * socket_state_str( uint32_t state ); 137 293 138 294 /****************************************************************************************** … … 143 299 * device and the specific data structures when required. 144 300 * It creates the associated server thread and allocates a WTI from local ICU. 301 * For a TX_NIC chedv, it allocates and initializes the R2T waiting queue used by the 302 * NIC_RX[channel] server to send direct requests to the NIC_TX[channel] server. 145 303 * It must de executed by a local thread. 146 304 ****************************************************************************************** … … 149 307 void dev_nic_init( struct chdev_s * chdev ); 150 308 151 /****************************************************************************************** 152 * This blocking function must be called by the kernel thread running in the cluster 153 * containing the NIC_RX channel device descriptor. 154 * It read one packet (Ethernet/IPV4) from the NIC_RX queue associated to the NIC channel. 155 * It calls directly the NIC driver, without registering in a waiting queue, because 156 * only this NIC_RX thread can access this packets queue. 157 * 1) It test the packets queue status, using the NIC_CMD_WRITABLE command. 158 * If it is empty, it unmask the NIC-RX channel IRQ, blocks and deschedule. 159 * It is re-activated by the NIC-RX ISR (generated by the NIC) as soon as the queue 160 * becomes not empty. 161 * 2) if the queue is not empty, it get one packet, using the driver NIC_CMD_READ command. 162 * Both commands are successively registered in the NIC-RX server thread descriptor 163 * to be passed to the driver. 164 * 165 * WARNING : for a RX packet the initiator is the NIC hardware, and the protocols 166 * stack is traversed upward, from the point of view of function calls. 167 ****************************************************************************************** 168 * @ pkd : pointer on packet descriptor (expected). 169 * @ returns 0 if success / returns non zero if ENOMEM, or error reported from NIC. 170 *****************************************************************************************/ 171 error_t dev_nic_read( pkd_t * pkd ); 172 173 /****************************************************************************************** 174 * This blocking function must be called by the kernel thread running in the cluster 175 * containing the NIC_TX channel device descriptor. 176 * It writes one packet (Ethernet/IPV4) to the NIC_RX queue associated to the NIC channel. 177 * It calls directly the NIC driver, without registering in a waiting queue, because 178 * only this NIC_TX thread can access this packets queue. 179 * 1) It test the packets queue status, using the NIC_CMD_READABLE command. 180 * If it is full, it unmask the NIC-TX channel IRQ, blocks and deschedule. 181 * It is re-activated by the NIC-TX ISR (generated by the NIC) as soon as the queue 182 * is not full. 183 * 2) If the queue is not empty, it put one packet, using the driver NIC_CMD_WRITE command. 184 * Both commands are successively registered in the NIC-TX server thread descriptor 185 * to be passed to the driver. 186 * 187 * WARNING : for a TX packet the initiator is the "client" thread, and the protocols 188 * stack is traversed downward from the point of view of function calls. 189 ****************************************************************************************** 190 * @ pkd : pointer on packet descriptor (to be filed). 191 * @ returns 0 if success / returns if length > 2K, undefined key, or error from NIC. 192 *****************************************************************************************/ 193 error_t dev_nic_write( pkd_t * pkd ); 194 195 196 /****************************************************************************************** 197 * This function is executed by the server thread associated to a NIC channel device 198 * descriptor (RX or TX). This thread is created by the dev_nic_init() function. 199 * It executes an infinite loop, handling one packet per iteration. 200 * 201 * -- For a TX channel -- 202 * 1) It allocates a 2 Kbytes buffer. 203 * 2) It copies the client TCP/UDP packet in this buffer. 204 * 3) It calls the IP layer to add the IP header. 205 * 4) It calls the ETH layer to add the ETH header. 206 * 5) It calls the dev_nic_write() blocking function to move the packet to the TX queue. 207 * 6) It releases the 2 Kbytes buffer. 208 * 209 * When the waiting threads queue is empty, it blocks on the THREAD_BLOCKED_IO_CMD 210 * condition and deschedule. It is re-activated by a client thread registering a command. 211 * 212 * -- For a RX channel -- 213 * 1) It allocates a 2 Kbytes buffer. 214 * 2 It calls the dev_nic_read() blocking function to move the ETH packet to this buffer. 215 * 3) It calls the ETH layer to analyse the ETH header. 216 * 4) It calls the IP layer to analyse the IP header. TODO ??? 217 * 5) It calls the transport (TCP/UDP) layer. TODO ??? 218 * 5) It deliver the packet to the client thread. TODO ??? 219 * 6) It releases the 2 Kbytes buffer. 220 * 221 * When the RX packets queue is empty, it blocks on the THREAD_BLOCKED_IO_CMD 222 * condition and deschedule. It is re-activated by the NIC driver when this queue 223 * becomes non empty. 224 ****************************************************************************************** 225 * @ dev : local pointer on NIC chdev descriptor. 226 *****************************************************************************************/ 227 void dev_nic_server( struct chdev_s * chdev ); 228 309 310 /* functions implementing the socket API */ 311 312 /**************************************************************************************** 313 * This function implements the socket() syscall. 314 * This function allocates and intializes in the calling thread cluster: 315 * - a new socket descriptor, defined by the <domain> and <type> arguments, 316 * - a new file descriptor, associated to this socket, 317 * It registers the file descriptor in the reference process fd_array[], set 318 * the socket state to IDLE, and returns the <fdid> value. 319 **************************************************************************************** 320 * @ domain : [in] socket protocol family (AF_UNIX / AF_INET) 321 * @ type : [in] socket type (SOCK_DGRAM / SOCK_STREAM). 322 * @ return a file descriptor <fdid> if success / return -1 if failure. 323 ***************************************************************************************/ 324 int dev_nic_socket( uint32_t domain, 325 uint32_t type ); 326 327 /**************************************************************************************** 328 * This function implements the bind() syscall. 329 * It initializes the "local_addr" and "local_port" fields in the socket 330 * descriptor identified by the <fdid> argument and set the socket state to BOUND. 331 * It can be called by a thread running in any cluster. 332 **************************************************************************************** 333 * @ fdid : [in] file descriptor identifying the socket. 334 * @ addr : [in] local IP address. 335 * @ port : [in] local port. 336 * @ return 0 if success / return -1 if failure. 337 ***************************************************************************************/ 338 int dev_nic_bind( uint32_t fdid, 339 uint32_t addr, 340 uint16_t port ); 341 342 /**************************************************************************************** 343 * This function implements the listen() syscall(). 344 * It is called by a (local) server process to specify the max size of the queue 345 * registering the (remote) client process connections, and set the socket identified 346 * by the <fdid> argument to LISTEN state. It applies only to sockets of type TCP. 347 * It can be called by a thread running in any cluster. 348 * TODO handle the <max_pending> argument... 349 **************************************************************************************** 350 * @ fdid : [in] file descriptor identifying the local server socket. 351 * @ max_pending : [in] max number of accepted remote client connections. 352 ***************************************************************************************/ 353 int dev_nic_listen( uint32_t fdid, 354 uint32_t max_pending ); 355 356 /**************************************************************************************** 357 * This function implements the connect() syscall. 358 * It is used by a (local) client process to connect a local socket identified by 359 * the <fdid> argument, to a remote socket identified by the <remote_addr> and 360 * <remote_port> arguments. It can be used for both UDP and TCP sockets. 361 * It computes the nic_channel index from <remote_addr> and <remote_port> values, 362 * and initializes "remote_addr","remote_port", "nic_channel" in local socket. 363 * It registers the socket in the two lists of clients rooted in the NIC_RX[channel] 364 * and NIC_TX[channel] chdevs. It can be called by a thread running in any cluster. 365 * WARNING : the clients are the socket descriptors, and NOT the threads descriptors. 366 **************************************************************************************** 367 * Implementation Note: 368 * - For a TCP socket, it updates the "remote_addr", "remote_port", "nic_channel" fields 369 * in the socket descriptor defined by the <fdid> argument, and register this socket, 370 * in the lists of sockets attached to the NIC_TX and NIC_RX chdevs. 371 * Then, it registers a CONNECT command in the "nic_cmd" field ot the client thread 372 * descriptor to request the NIC_TX server thread to execute the 3 steps handshake, 373 * and updates the "tx_client" field in the socket descriptor. It unblocks the NIC_TX 374 * server thread, blocks on the THREAD_BLOCKED_IO condition and deschedules. 375 * - For an UDP socket, it simply updates "remote_addr", "remote_port", "nic_channel" 376 * in the socket descriptor defined by the <fdid> argument, and register this socket, 377 * in the lists of sockets attached to the NIC_TX and NIC_RX chdevs. 378 * Then, it set the socket state to CONNECT, without unblocking the NIC_TX server 379 * thread, and without blocking itself. 380 * TODO : the nic_channel index computation must be done by a driver specific function. 381 **************************************************************************************** 382 * @ fdid : [in] file descriptor identifying the socket. 383 * @ remote_addr : [in] remote IP address. 384 * @ remote_port : [in] remote port. 385 * @ return 0 if success / return -1 if failure. 386 ***************************************************************************************/ 387 int dev_nic_connect( uint32_t fdid, 388 uint32_t remote_addr, 389 uint16_t remote_port ); 390 391 /**************************************************************************************** 392 * This function implements the accept() syscall(). 393 * It is executed by a server process, waiting for one (or several) client process(es) 394 * requesting a connection on a socket identified by the <fdid> argument. 395 * This socket was previouly created with socket(), bound to a local address with bind(), 396 * and is listening for connections after a listen(). 397 * This function extracts the first connection request on the CRQQ queue of pending 398 * requests, creates a new socket with the same properties as the existing socket, 399 * and allocates a new file descriptor for this new socket. 400 * If no pending connections are present on the queue, it blocks the caller until a 401 * connection is present. 402 * The new socket cannot accept more connections, but the original socket remains open. 403 * It returns the new socket <fdid>, and register in the <address> an <port> arguments 404 * the remote client IP address & port. It applies only to sockets of type SOCK_STREAM. 405 **************************************************************************************** 406 * @ fdid : [in] file descriptor identifying the listening socket. 407 * @ address : [out] server IP address. 408 * @ port : [out] server port address length in bytes. 409 * @ return the new socket <fdid> if success / return -1 if failure 410 ***************************************************************************************/ 411 int dev_nic_accept( uint32_t fdid, 412 uint32_t * address, 413 uint16_t * port ); 414 415 /**************************************************************************************** 416 * This blocking function implements the send() syscall. 417 * It is used to send data stored in the user buffer, identified the <u_buf> and <length> 418 * arguments, to a connected (TCP or UDP) socket, identified by the <fdid> argument. 419 * The work is actually done by the NIC_TX server thread, and the synchronisation 420 * between the client and the server threads uses the "rx_valid" set/reset flip-flop: 421 * The client thread registers itself in the socket descriptor, registers in the queue 422 * rooted in the NIC_TX[index] chdev, set "rx_valid", unblocks the server thread, and 423 * finally blocks on THREAD_BLOCKED_IO, and deschedules. 424 * When the TX server thread completes the command (all data has been sent for an UDP 425 * socket, or acknowledeged for a TCP socket), the server thread reset "rx_valid" and 426 * unblocks the client thread. 427 * This function can be called by a thread running in any cluster. 428 * WARNING : This implementation does not support several concurent SEND/SENDTO commands 429 * on the same socket, as only one TX thread can register in a given socket. 430 **************************************************************************************** 431 * @ fdid : [in] file descriptor identifying the socket. 432 * @ u_buf : [in] pointer on buffer containing packet in user space. 433 * @ length : [in] packet size in bytes. 434 * @ return number of sent bytes if success / return -1 if failure. 435 ***************************************************************************************/ 436 int dev_nic_send( uint32_t fdid, 437 uint8_t * u_buf, 438 uint32_t length ); 439 440 /**************************************************************************************** 441 * This blocking function implements the sendto() syscall. 442 * It registers the <remote_addr> and <remote_port> arguments in the local socket 443 * descriptor, and does the same thing as the dev_nic_send() function above, 444 * but can be called on an unconnected UDP socket. 445 **************************************************************************************** 446 * @ fdid : [in] file descriptor identifying the socket. 447 * @ u_buf : [in] pointer on buffer containing packet in user space. 448 * @ length : [in] packet size in bytes. 449 * @ remote_addr : [in] destination IP address. 450 * @ remote_port : [in] destination port. 451 * @ return number of sent bytes if success / return -1 if failure. 452 ***************************************************************************************/ 453 int dev_nic_sendto( uint32_t fdid, 454 uint8_t * u_buf, 455 uint32_t length, 456 uint32_t remote_addr, 457 uint32_t remote_port ); 458 459 /**************************************************************************************** 460 * This blocking function implements the recv() syscall. 461 * It is used to receive data that has been stored by the NIC_RX server thread in the 462 * rx_buf of a connected (TCP or UDP) socket, identified by the <fdid> argument. 463 * The synchronisation between the client and the server threads uses the "rx_valid" 464 * set/reset flip-flop: If "rx_valid" is set, the client simply moves the available 465 * data from the "rx_buf" to the user buffer identified by the <u_buf> and <length> 466 * arguments, and reset the "rx_valid" flip_flop. If "rx_valid" is not set, the client 467 * thread register itself in the socket descriptor, registers in the clients queue rooted 468 * in the NIC_RX[index] chdev, and finally blocks on THREAD_BLOCKED_IO, and deschedules. 469 * The client thread is re-activated by the RX server, that set the "rx_valid" flip-flop 470 * as soon as data is available in the "rcv_buf" (can be less than the user buffer size). 471 * This function can be called by a thread running in any cluster. 472 * WARNING : This implementation does not support several concurent RECV/RECVFROM 473 * commands on the same socket, as only one RX thread can register in a given socket. 474 **************************************************************************************** 475 * @ fdid : [in] file descriptor identifying the socket. 476 * @ u_buf : [in] pointer on buffer in user space. 477 * @ length : [in] buffer size in bytes. 478 * @ return number of received bytes if success / return -1 if failure. 479 ***************************************************************************************/ 480 int dev_nic_recv( uint32_t fdid, 481 uint8_t * u_buf, 482 uint32_t length ); 483 484 /**************************************************************************************** 485 * This blocking function implements the recvfrom() syscall. 486 * It registers the <remote_addr> and <remote_port> arguments in the local socket 487 * descriptor, and does the same thing as the dev_nic_recv() function above, 488 * but can be called on an unconnected UDP socket. 489 **************************************************************************************** 490 * @ fdid : [in] file descriptor identifying the socket. 491 * @ u_buf : [in] pointer on buffer containing packet in user space. 492 * @ length : [in] packet size in bytes. 493 * @ remote_addr : [in] destination IP address. 494 * @ remote_port : [in] destination port. 495 * @ return number of received bytes if success / return -1 if failure. 496 ***************************************************************************************/ 497 int dev_nic_recvfrom( uint32_t fdid, 498 uint8_t * u_buf, 499 uint32_t length, 500 uint32_t remote_addr, 501 uint32_t remote_port ); 502 503 504 /* Instrumentation functions */ 505 506 507 /****************************************************************************************** 508 * This instrumentation function displays on the TXT0 kernel terminal the content 509 * of the instrumentation registers contained in the NIC device. 510 *****************************************************************************************/ 511 void dev_nic_print_stats( void ); 512 513 /****************************************************************************************** 514 * This instrumentation function reset all instrumentation registers contained 515 * in the NIC device. 516 *****************************************************************************************/ 517 void dev_nic_clear_stats( void ); 518 519 520 /* Functions executed by the TX and RX server threads */ 521 522 /****************************************************************************************** 523 * This function is executed by the server thread associated to a NIC_TX[channel] chdev. 524 * This TX server thread is created by the dev_nic_init() function. 525 * It build and send UDP packets or TCP segments for all clients threads registered in 526 * the NIC_TX[channel] chdev. The command types are (CONNECT / SEND / CLOSE), and the 527 * priority between clients is round-robin. It takes into account the request registered 528 * by the RX server thread in the R2T queue associated to the involved socket. 529 * When a command is completed, it unblocks the client thread. For a SEND command, the 530 * last byte must have been sent for an UDP socket, and it must have been acknowledged 531 * for a TCP socket. 532 * When the TX client threads queue is empty, it blocks on THREAD_BLOCKED_CLIENT 533 * condition and deschedules. It is re-activated by a client thread registering a command. 534 ****************************************************************************************** 535 * Implementation note: 536 * It execute an infinite loop in which it takes the lock protecting the clients list 537 * to build a "kleenex" list of currently registered clients. 538 * For each client registered in this "kleenex" list, it takes the lock protecting the 539 * socket state, build one packet/segment in a local 2K bytes kernel buffer, calls the 540 * transport layer to add the UDP/TCP header, calls the IP layer to add the IP header, 541 * calls the ETH layer to add the ETH header, and moves the packet to the NIC_TX_QUEUE. 542 * Finally, it updates the socket state, and release the socket lock. 543 ****************************************************************************************** 544 * @ chdev : [in] local pointer on one local NIC_TX[channel] chdev descriptor. 545 *****************************************************************************************/ 546 void dev_nic_tx_server( struct chdev_s * chdev ); 547 548 549 /****************************************************************************************** 550 * This function is executed by the server thread associated to a NIC_RX[channel] chdev. 551 * This RX server thread is created by the dev_nic_init() function. 552 * It handles all UDP packets or TCP segments received by the sockets attached to 553 * the NIC_RX[channel] chdev. It writes the received data in the socket rcv_buf, and 554 * unblocks the client thread waiting on a RECV command. 555 * To implement the three steps handshahke required by a TCP connection, it posts direct 556 * requests to the TX server, using the R2T queue attached to the involved socket. 557 * It blocks on the THREAD_BLOCKED_ISR condition and deschedules when the NIC_RX_QUEUE 558 * is empty. It is re-activated by the NIC_RX_ISR, when the queue becomes non empty. 559 ****************************************************************************************** 560 * Implementation note: 561 * It executes an infinite loop in which it extracts one packet from the NIC_RX_QUEUE 562 * of received packets, copies this packet in a local 2 kbytes kernel buffer, checks 563 * the Ethernet header, checks the IP header, calls the relevant (TCP or UDP) transport 564 * protocol that search a matching socket for the received packet. It copies the payload 565 * to the relevant socket rcv_buf when the packet is acceptable, and unblocks the client 566 * thread. It discard the packet if no socket found. 567 ****************************************************************************************** 568 * @ chdev : [in] local pointer on one local NIC_RX[channel] chdev descriptor. 569 *****************************************************************************************/ 570 void dev_nic_rx_server( struct chdev_s * chdev ); 229 571 230 572 #endif /* _DEV_NIC_H */ -
trunk/kernel/devices/dev_txt.c
r647 r657 133 133 } // end dev_txt_init() 134 134 135 //////////////////////////////////////////////////////////////////////////////////136 // This static function is called by dev_txt_read(), dev_txt_write() functions.137 ////////////////////////////////////i/////////////////////////////////////////////138 static error_t dev_txt_access( uint32_t type,139 uint32_t channel,140 char * buffer,141 uint32_t count )142 {143 xptr_t dev_xp;144 thread_t * this = CURRENT_THREAD;145 146 // check channel argument147 assert( (channel < CONFIG_MAX_TXT_CHANNELS) , "illegal channel index" );148 149 // get extended pointer on remote TXT chdev descriptor150 if( type == TXT_WRITE ) dev_xp = chdev_dir.txt_tx[channel];151 else dev_xp = chdev_dir.txt_rx[channel];152 153 assert( (dev_xp != XPTR_NULL) , "undefined TXT chdev descriptor" );154 155 // register command in calling thread descriptor156 this->txt_cmd.dev_xp = dev_xp;157 this->txt_cmd.type = type;158 this->txt_cmd.buf_xp = XPTR( local_cxy , buffer );159 this->txt_cmd.count = count;160 161 // register client thread in waiting queue, activate server thread162 // block client thread on THREAD_BLOCKED_IO and deschedule.163 // it is re-activated by the ISR signaling IO operation completion.164 chdev_register_command( dev_xp );165 166 // return I/O operation status from calling thread descriptor167 return this->txt_cmd.error;168 169 } // end dev_txt_access()170 171 135 ///////////////////////////////////////// 172 136 error_t dev_txt_write( uint32_t channel, … … 180 144 #endif 181 145 146 thread_t * this = CURRENT_THREAD; 147 182 148 #if DEBUG_DEV_TXT_TX 183 thread_t * this = CURRENT_THREAD;184 149 uint32_t cycle = (uint32_t)hal_get_cycles(); 185 150 if( DEBUG_DEV_TXT_TX < cycle ) … … 188 153 #endif 189 154 155 // check channel argument 156 assert( (channel < CONFIG_MAX_TXT_CHANNELS) , "illegal channel index" ); 157 158 // get pointers on chdev 159 xptr_t dev_xp = chdev_dir.txt_tx[channel]; 160 cxy_t dev_cxy = GET_CXY( dev_xp ); 161 chdev_t * dev_ptr = GET_PTR( dev_xp ); 162 163 // check dev_xp 164 assert( (dev_xp != XPTR_NULL) , "undefined TXT chdev descriptor" ); 165 190 166 // If we use MTTY (vci_multi_tty), we do a synchronous write on TXT[0] 191 167 // If we use TTY (vci_tty_tsar), we do a standard asynchronous write 192 168 // TODO this is not very clean ... [AG] 193 169 194 // get pointers on chdev 195 xptr_t dev_xp = chdev_dir.txt_tx[0]; 196 cxy_t dev_cxy = GET_CXY( dev_xp ); 197 chdev_t * dev_ptr = GET_PTR( dev_xp ); 198 199 if( dev_ptr->impl == IMPL_TXT_MTY ) 170 if( dev_ptr->impl == IMPL_TXT_MTY ) 200 171 { 201 172 // get driver command function … … 216 187 else 217 188 { 218 // register command in chdev queue for an asynchronous access 219 error = dev_txt_access( TXT_WRITE , channel , buffer , count ); 189 // register command in calling thread descriptor 190 this->txt_cmd.dev_xp = dev_xp; 191 this->txt_cmd.type = TXT_WRITE; 192 this->txt_cmd.buf_xp = XPTR( local_cxy , buffer ); 193 this->txt_cmd.count = count; 194 195 // register client thread in waiting queue, activate server thread 196 // block client thread on THREAD_BLOCKED_IO and deschedule. 197 // it is re-activated by the ISR signaling IO operation completion. 198 chdev_register_command( dev_xp ); 199 200 // get I/O operation status from calling thread descriptor 201 error = this->txt_cmd.error; 220 202 221 203 if( error ) … … 251 233 #endif 252 234 235 thread_t * this = CURRENT_THREAD; 236 253 237 #if DEBUG_DEV_TXT_RX 254 thread_t * this = CURRENT_THREAD;255 238 uint32_t cycle = (uint32_t)hal_get_cycles(); 256 239 if( DEBUG_DEV_TXT_RX < cycle ) … … 259 242 #endif 260 243 261 // register command in chdev queue for an asynchronous access 262 error = dev_txt_access( TXT_READ , channel , buffer , 1 ); 244 // check channel argument 245 assert( (channel < CONFIG_MAX_TXT_CHANNELS) , "illegal channel index" ); 246 247 // get pointers on chdev 248 xptr_t dev_xp = chdev_dir.txt_rx[channel]; 249 250 // check dev_xp 251 assert( (dev_xp != XPTR_NULL) , "undefined TXT chdev descriptor" ); 252 253 // register command in calling thread descriptor 254 this->txt_cmd.dev_xp = dev_xp; 255 this->txt_cmd.type = TXT_READ; 256 this->txt_cmd.buf_xp = XPTR( local_cxy , buffer ); 257 this->txt_cmd.count = 1; 258 259 // register client thread in waiting queue, activate server thread 260 // block client thread on THREAD_BLOCKED_IO and deschedule. 261 // it is re-activated by the ISR signaling IO operation completion. 262 chdev_register_command( dev_xp ); 263 264 // get I/O operation status from calling thread descriptor 265 error = this->txt_cmd.error; 263 266 264 267 if( error ) 265 268 { 266 printk("\n[ERROR] in %s : cannot get character/ cycle %d\n",267 __FUNCTION__, (uint32_t)hal_get_cycles() );269 printk("\n[ERROR] in %s : cannot write string %s / cycle %d\n", 270 __FUNCTION__, buffer, (uint32_t)hal_get_cycles() ); 268 271 } 269 272
Note: See TracChangeset
for help on using the changeset viewer.