Changeset 23 for trunk/kernel/devices
- Timestamp:
- Jun 18, 2017, 10:06:41 PM (8 years ago)
- Location:
- trunk/kernel/devices
- Files:
-
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/kernel/devices/dev_dma.c
r3 r23 45 45 void dev_dma_init( chdev_t * chdev ) 46 46 { 47 // get implementation index from DMA chdev descriptor 48 uint32_t impl = chdev->impl; 47 // get implementation & channel from DMA chdev descriptor 48 uint32_t impl = chdev->impl; 49 uint32_t channel = chdev->channel; 50 51 // set chdev name 52 snprintf( chdev->name , 16 , "dma_%d_%x" , channel , local_cxy ); 49 53 50 54 // set field "cmd", "isr", and call the relevant driver init function … … 59 63 assert( false , __FUNCTION__ , "undefined DMA implementation" ); 60 64 } 61 62 // get DMA channel index63 uint32_t channel = chdev->channel;64 65 65 66 // get DMA HWI IRQ index -
trunk/kernel/devices/dev_fbf.c
r14 r23 57 57 uint32_t impl = chdev->impl; 58 58 59 // set chdev name 60 strcpy( chdev->name, "fbf" ); 59 61 // call driver init function 60 62 if( impl == IMPL_FBF_SCL ) … … 136 138 error_t error; 137 139 paddr_t buf_paddr; 138 bool_t ident = CONFIG_KERNEL_IDENTITY;139 140 140 141 thread_t * this = CURRENT_THREAD; // pointer on client thread 141 142 142 143 // Get buffer physical address 143 error = vmm_v2p_translate( ident, buffer , &buf_paddr );144 error = vmm_v2p_translate( CONFIG_KERNEL_IDENTITY_MAP , buffer , &buf_paddr ); 144 145 145 146 if( error ) -
trunk/kernel/devices/dev_icu.c
r14 r23 47 47 uint32_t pti_nr ) 48 48 { 49 // set chdev name 50 snprintf( icu->name , 16 , "icu_%x" , local_cxy ); 51 49 52 // set ICU chdev extension fields 50 53 icu->ext.icu.hwi_nr = hwi_nr; -
trunk/kernel/devices/dev_iob.c
r14 r23 36 36 // get implementation 37 37 uint32_t impl = chdev->impl; 38 39 // set chdev name 40 strcpy( chdev->name , "iob" ); 38 41 39 42 // call driver init function -
trunk/kernel/devices/dev_ioc.c
r14 r23 55 55 uint32_t channel = chdev->channel; 56 56 57 // set chdev name 58 strcpy( chdev->name , "ioc" ); 59 57 60 // set driver specific fields in chdev descriptor and call driver init function 58 61 if( impl == IMPL_IOC_BDV ) … … 64 67 else if( impl == IMPL_IOC_HBA ) 65 68 { 66 chdev->cmd = &soclib_hba_c ommand;69 chdev->cmd = &soclib_hba_cmd; 67 70 chdev->isr = &soclib_hba_isr; 68 71 soclib_hba_init( chdev ); … … 109 112 ////////////////////////////////////////////////////////////////////////////////// 110 113 // This static function is called by dev_ioc_read() & dev_ioc_write() functions. 111 // It builds and registers the command in the calling thread descriptor, after 112 // translation of buffer virtual address to physical address. 114 // It builds and registers the command in the calling thread descriptor. 113 115 // Then, it registers the calling thead in chdev waiting queue. 114 116 // Finally it blocks on the THREAD_BLOCKED_DEV condition and deschedule. 115 117 ////////////////////////////////////i///////////////////////////////////////////// 116 static error_t dev_ioc_access( bool_t to_mem,117 char* buffer,118 uint32_t lba,119 uint32_t count )118 static error_t dev_ioc_access( uint32_t cmd_type, 119 uint8_t * buffer, 120 uint32_t lba, 121 uint32_t count ) 120 122 { 121 123 thread_t * this = CURRENT_THREAD; // pointer on client thread 122 124 123 error_t error;124 paddr_t buf_paddr;125 126 // Get buffer physical address127 error = vmm_v2p_translate( CONFIG_KERNEL_IDENTITY , buffer , &buf_paddr );128 129 if( error ) return EINVAL;130 131 125 ioc_dmsg("\n[INFO] in %s : thread %x in process %x" 132 " for lba = %x / vaddr = %x / paddr = %l/ at cycle %d\n",126 " for lba = %x / buffer = %x / at cycle %d\n", 133 127 __FUNCTION__ , this->trdid , this->process->pid , 134 lba , ( uint32_t)buffer , buf_paddr , hal_time_stamp() );128 lba , (intptr_t)buffer , hal_time_stamp() ); 135 129 136 130 #if USE_IOB // software L2/L3 cache coherence for memory buffer 137 131 138 if ( t o_mem ) dev_mmc_inval( buf_paddr, count<<9 );139 else dev_mmc_sync( buf_paddr, count<<9 );132 if ( type == IOC_READ ) dev_mmc_inval( XPTR( local_cxy , buffer ) , count<<9 ); 133 else dev_mmc_sync ( XPTR( local_cxy , buffer ) , count<<9 ); 140 134 141 135 #endif // end software L2/L3 cache coherence … … 148 142 // register command in calling thread descriptor 149 143 this->command.ioc.dev_xp = dev_xp; 150 this->command.ioc.t o_mem = to_mem;144 this->command.ioc.type = cmd_type; 151 145 this->command.ioc.buf_xp = XPTR( local_cxy , buffer ); 152 146 this->command.ioc.lba = lba; … … 169 163 170 164 //////////////////////////////////////////// 171 error_t dev_ioc_read( char* buffer,165 error_t dev_ioc_read( uint8_t * buffer, 172 166 uint32_t lba, 173 167 uint32_t count ) 174 168 { 175 return dev_ioc_access( true, buffer , lba , count );169 return dev_ioc_access( IOC_READ , buffer , lba , count ); 176 170 } 177 171 178 172 //////////////////////////////////////////// 179 error_t dev_ioc_write( char* buffer,180 uint32_t 181 uint32_t 182 { 183 return dev_ioc_access( false, buffer , lba , count );173 error_t dev_ioc_write( uint8_t * buffer, 174 uint32_t lba, 175 uint32_t count ) 176 { 177 return dev_ioc_access( IOC_WRITE , buffer , lba , count ); 184 178 } 179 180 ///////////////////////////////////////////// 181 error_t dev_ioc_sync_read( uint8_t * buffer, 182 uint32_t lba, 183 uint32_t count ) 184 { 185 // get pointer on calling thread 186 thread_t * this = CURRENT_THREAD; 187 188 #if USE_IOB // software L2/L3 cache coherence for memory buffer 189 190 dev_mmc_inval( XPTR( local_cxy , buffer ) , count<<9 ); 191 192 #endif // end software L2/L3 cache coherence 193 194 // get extended pointer on IOC[0] chdev 195 xptr_t dev_xp = chdev_dir.ioc[0]; 196 197 assert( (dev_xp != XPTR_NULL) , __FUNCTION__ , "undefined IOC chdev descriptor" ); 198 199 // register command in calling thread descriptor 200 this->command.ioc.dev_xp = dev_xp; 201 this->command.ioc.type = IOC_SYNC_READ; 202 this->command.ioc.buf_xp = XPTR( local_cxy , buffer ); 203 this->command.ioc.lba = lba; 204 this->command.ioc.count = count; 205 206 // get driver command function 207 cxy_t dev_cxy = GET_CXY( dev_xp ); 208 chdev_t * dev_ptr = (chdev_t *)GET_PTR( dev_xp ); 209 dev_cmd_t * cmd = (dev_cmd_t *)hal_remote_lpt( XPTR( dev_cxy , &dev_ptr->cmd ) ); 210 211 // call directly driver command 212 cmd( XPTR( local_cxy , this ) ); 213 214 // return I/O operation status from calling thread descriptor 215 return this->command.ioc.error; 216 } 217 -
trunk/kernel/devices/dev_ioc.h
r14 r23 38 38 * magnetic hard disk or a SD card, that can store blocks of data in a linear array 39 39 * of sectors indexed by a simple lba (logic block address). 40 * It supports two command types: 41 * - READ : move a given number of contiguous blocks from device to a memory buffer. 42 * - WRITE : move a given number of contiguous blocks from a memory buffer to device. 43 * 44 * An I/O operation requires dynamic ressource allocation, and is always blocking for 45 * the client thread. The general scenario is detailed below. 40 * It supports three command types: 41 * - READ : move blocks from device to memory, with a descheduling policy. 42 * - WRITE : move blocks from memory to device, with a descheduling policy. 43 * - SYNC_READ : move blocks from device to memory, with a busy waiting policy. 44 45 * A READ or WRITE operation requires dynamic ressource allocation. The calling thread 46 * is descheduled, and the work is done by the server thread associated to IOC device. 47 * The general scenario is detailed below. 46 48 * A) the client thread start the I/O operation, by calling the dev_ioc_read() 47 49 * or the dev_ioc_write() kernel functions that perform the following actions: … … 50 52 * 3) it access the PIC to link the WTI mailbox to the IOC IRQ. 51 53 * 4) it builds the command descriptor. 52 * 5) it registers in the IOC device waiting queue.54 * 5) it registers in the IOC device waiting queue. 53 55 * 6) itblock on the THREAD_BLOCKED_IO condition and deschedule. 54 56 * B) The server thread attached to the IOC device descriptor handles the commands … … 61 63 * 2) disable the WTI IRQ in the client cluster ICU and update interrupt vector. 62 64 * 3) release the WTI mailbox to the client cluster WTI allocator. 65 * 66 * The SYNC_READ operation is used by the kernel in the initialisation phase. It does 67 * not uses the IOC device waiting queue and server thread, and does not use the IOC IRQ, 68 * but implement a busy-waiting policy for the calling thread. 63 69 *****************************************************************************************/ 64 70 … … 93 99 *****************************************************************************************/ 94 100 101 enum 102 { 103 IOC_READ = 0, 104 IOC_WRITE = 1, 105 IOC_SYNC_READ = 2, 106 }; 107 95 108 typedef struct ioc_command_s 96 109 { 97 xptr_t dev_xp; 98 uint32_t t o_mem; /*! requested operation (WRITE if zero / READ if non-zero)*/110 xptr_t dev_xp; /*! extended pointer on device descriptor */ 111 uint32_t type; /*! IOC_READ / IOC_WRITE / IOC_SYNC_READ */ 99 112 uint32_t lba; /*! first block index */ 100 113 uint32_t count; /*! number of blocks */ … … 119 132 /****************************************************************************************** 120 133 * This blocking function try to tranfer one or several contiguous blocks of data 121 * from the block device to a memory buffer. The corresponding request is actually134 * from the block device to a local memory buffer. The corresponding request is actually 122 135 * registered in the device pending request queue, and the calling thread is descheduled, 123 136 * waiting on transfer completion. It will be resumed by the IRQ signaling completion. 124 137 * It must be called in the client cluster. 125 138 ****************************************************************************************** 126 * @ buffer : local pointer on target buffer in memory .139 * @ buffer : local pointer on target buffer in memory (must be block aligned). 127 140 * @ lba : first block index on device. 128 141 * @ count : number of blocks to transfer. 129 142 * @ returns 0 if success / returns EINVAL if error. 130 143 *****************************************************************************************/ 131 error_t dev_ioc_read( char* buffer,144 error_t dev_ioc_read( uint8_t * buffer, 132 145 uint32_t lba, 133 146 uint32_t count ); … … 135 148 /****************************************************************************************** 136 149 * This blocking function try to tranfer one or several contiguous blocks of data 137 * from a memory buffer to the block device. The corresponding request is actually150 * from a local memory buffer to the block device. The corresponding request is actually 138 151 * registered in the device pending request queue, and the calling thread is descheduled, 139 152 * waiting on transfer completion. It will be resumed by the IRQ signaling completion. 140 153 * It must be called in the client cluster. 141 154 ****************************************************************************************** 142 * @ buffer : local pointer on source buffer in memory .155 * @ buffer : local pointer on source buffer in memory (must be block aligned). 143 156 * @ lba : first block index on device. 144 157 * @ count : number of blocks to transfer. 145 158 * @ returns 0 if success / returns EINVAL if error. 146 159 *****************************************************************************************/ 147 error_t dev_ioc_write( char* buffer,160 error_t dev_ioc_write( uint8_t * buffer, 148 161 uint32_t lba, 149 162 uint32_t count ); 150 163 164 /****************************************************************************************** 165 * This blocking function try to tranfer one or several contiguous blocks of data 166 * from the block device to a memory buffer. 167 * It does not uses the IOC device waiting queue and server thread, and does not use 168 * the IOC IRQ, but call directly the relevant OIC driver, implementing a busy-waiting 169 * policy for the calling thread. 170 * It must be called in the client cluster. 171 ****************************************************************************************** 172 * @ buffer : local pointer on target buffer in memory (must be block aligned). 173 * @ lba : first block index on device. 174 * @ count : number of blocks to transfer. 175 * @ returns 0 if success / returns EINVAL if error. 176 *****************************************************************************************/ 177 error_t dev_ioc_sync_read( uint8_t * buffer, 178 uint32_t lba, 179 uint32_t count ); 180 151 181 #endif /* _DEV_IOC_H */ -
trunk/kernel/devices/dev_mmc.c
r14 r23 43 43 // get implementation from device descriptor 44 44 uint32_t impl = chdev->impl; 45 46 // set chdev name 47 snprintf( chdev->name , 16 , "mmc_%x" , local_cxy ); 45 48 46 49 // set driver specific fields in device descriptor and call driver init function … … 65 68 66 69 ///////////////////////////////////////////////////////////////////////////// 67 // This static function makes some checking, takes the lock granting 68 // exclusive access to MMC peripheral, call the driver to execute the command 70 // This static function is called by all MMC device functions. 71 // It makes some checking, takes the lock granting exclusive 72 // access to MMC peripheral, call the driver to execute the command 69 73 // registered in the calling thread descriptor, and releases the lock. 70 74 ///////////////////////////////////////////////////////////////////////////// 71 static void dev_mmc_access( thread_t * this ) 72 { 73 mmc_dmsg("\n[INFO] %s enters for thread %x in process %x : command = %d\n", 74 __FUNCTION__ , this->trdid , this->process->pid , this->command.mmc.type ); 75 76 // get extended pointer on MMC device 77 xptr_t dev_xp = this->command.mmc.dev_xp; 78 79 assert( (dev_xp != XPTR_NULL) , __FUNCTION__ , "undefined MMC device descriptor" ); 75 static error_t dev_mmc_access( thread_t * this ) 76 { 77 // get extended pointer on MMC device descriptor 78 xptr_t dev_xp = this->command.mmc.dev_xp; 79 80 assert( (dev_xp != XPTR_NULL) , __FUNCTION__ , "target MMC device undefined" ); 80 81 81 82 // get MMC device cluster identifier & local pointer … … 95 96 remote_spinlock_unlock( XPTR( dev_cxy , &dev_ptr->wait_lock ) ); 96 97 97 mmc_dmsg("\n[INFO] %s completes for thread %x in process %x / error = %d\n",98 __FUNCTION__ , this->trdid , this->process->pid , this->dev.ioc.error );98 // return operation status 99 return this->command.mmc.error; 99 100 100 101 } // end dev_mmc_access() 101 102 102 ///////////////////////////////////////// ///103 error_t dev_mmc_inval( paddr_t buf_paddr,103 ///////////////////////////////////////// 104 error_t dev_mmc_inval( xptr_t buf_xp, 104 105 uint32_t buf_size ) 105 106 { 106 // get calling thread local pointer 107 thread_t * this = CURRENT_THREAD; 108 109 // get target cluster from paddr 110 cxy_t cxy = CXY_FROM_PADDR( buf_paddr ); 111 112 assert( ((buf_paddr & (CONFIG_CACHE_LINE_SIZE -1)) == 0) , __FUNCTION__ , 107 error_t error; 108 109 // get calling thread local pointer 110 thread_t * this = CURRENT_THREAD; 111 112 mmc_dmsg("\n[INFO] %s enters for thread %x in process %x / buf_xp = %l\n", 113 __FUNCTION__ , this->trdid , this->process->pid , buf_xp ); 114 115 // get buffer cluster and local pointer 116 cxy_t buf_cxy = GET_CXY( buf_xp ); 117 void * buf_ptr = GET_PTR( buf_xp ); 118 119 assert( (((intptr_t)buf_ptr & (CONFIG_CACHE_LINE_SIZE -1)) == 0) , __FUNCTION__ , 113 120 "buffer not aligned on cache line" ); 114 121 115 // get extended pointer on MMC device descriptor 116 xptr_t dev_xp = chdev_dir.mmc[cxy]; 117 118 // store command arguments in thread descriptor 119 this->command.mmc.dev_xp = dev_xp; 122 // get buffer physical address 123 paddr_t buf_paddr; 124 error = vmm_v2p_translate( CONFIG_KERNEL_IDENTITY_MAP , buf_ptr , &buf_paddr ); 125 126 assert( (error == 0) , __FUNCTION__ , "cannot get buffer paddr" ); 127 128 // store command arguments in thread descriptor 129 this->command.mmc.dev_xp = chdev_dir.mmc[buf_cxy]; 120 130 this->command.mmc.type = MMC_CC_INVAL; 121 131 this->command.mmc.buf_paddr = buf_paddr; 122 132 this->command.mmc.buf_size = buf_size; 123 133 124 // execute operation 125 dev_mmc_access( this ); 126 127 // return operation status 128 return this->command.mmc.error; 129 } 130 131 ///////////////////////////////////// 132 error_t dev_mmc_sync( paddr_t buf_paddr, 134 // call MMC driver 135 error = dev_mmc_access( this ); 136 137 mmc_dmsg("\n[INFO] %s completes for thread %x in process %x / error = %d\n", 138 __FUNCTION__ , this->trdid , this->process->pid , error ); 139 140 return error; 141 } 142 143 //////////////////////////////////////// 144 error_t dev_mmc_sync( xptr_t buf_xp, 133 145 uint32_t buf_size ) 134 146 { 135 // get calling thread local pointer 136 thread_t * this = CURRENT_THREAD; 137 138 // get target cluster from paddr 139 cxy_t cxy = CXY_FROM_PADDR( buf_paddr ); 140 141 assert( ((buf_paddr & (CONFIG_CACHE_LINE_SIZE -1)) == 0) , __FUNCTION__ , 147 error_t error; 148 149 // get calling thread local pointer 150 thread_t * this = CURRENT_THREAD; 151 152 mmc_dmsg("\n[INFO] %s enters for thread %x in process %x / buf_xp = %l\n", 153 __FUNCTION__ , this->trdid , this->process->pid , buf_xp ); 154 155 // get buffer cluster and local pointer 156 cxy_t buf_cxy = GET_CXY( buf_xp ); 157 void * buf_ptr = GET_PTR( buf_xp ); 158 159 assert( (((intptr_t)buf_ptr & (CONFIG_CACHE_LINE_SIZE -1)) == 0) , __FUNCTION__ , 142 160 "buffer not aligned on cache line" ); 143 161 144 // store command arguments in thread descriptor 145 this->command.mmc.dev_xp = chdev_dir.mmc[cxy]; 162 // get buffer physical address 163 paddr_t buf_paddr; 164 error = vmm_v2p_translate( CONFIG_KERNEL_IDENTITY_MAP , buf_ptr , &buf_paddr ); 165 166 assert( (error == 0) , __FUNCTION__ , "cannot get buffer paddr" ); 167 168 // store command arguments in thread descriptor 169 this->command.mmc.dev_xp = chdev_dir.mmc[buf_cxy]; 146 170 this->command.mmc.type = MMC_CC_SYNC; 147 171 this->command.mmc.buf_paddr = buf_paddr; 148 172 this->command.mmc.buf_size = buf_size; 149 173 150 // execute operation 151 dev_mmc_access( this ); 152 153 // return operation status 154 return this->command.mmc.error; 155 } 174 // call MMC driver 175 error = dev_mmc_access( this ); 176 177 mmc_dmsg("\n[INFO] %s completes for thread %x in process %x / error = %d\n", 178 __FUNCTION__ , this->trdid , this->process->pid , error ); 179 180 return error; 181 } 156 182 157 183 ///////////////////////////////////////// … … 170 196 171 197 // execute operation 172 dev_mmc_access( this ); 173 174 // return operation status 175 return this->command.mmc.error; 198 return dev_mmc_access( this ); 176 199 } 177 200 … … 191 214 192 215 // execute operation 193 dev_mmc_access( this ); 194 195 // return operation status 196 return this->command.mmc.error; 216 return dev_mmc_access( this ); 197 217 } 198 218 … … 212 232 213 233 // execute operation 214 dev_mmc_access( this ); 215 216 // return operation status 217 return this->command.mmc.error; 218 } 219 234 return dev_mmc_access( this ); 235 } 236 -
trunk/kernel/devices/dev_mmc.h
r14 r23 81 81 typedef struct mmc_command_s 82 82 { 83 xptr_t dev_xp; /*! extended pointer on t he relevant MMC device descriptor*/83 xptr_t dev_xp; /*! extended pointer on target MMC device descriptor */ 84 84 uint32_t type; /*! CC_INVAL / CC_SYNC / GET_ERROR / SET_ERROR / GET_INSTRU */ 85 paddr_t buf_paddr; /*! buffer physical address (used by INVAL/SYNC)*/86 uint32_t buf_size; /*! buffer size in bytes (used by INVAL/SYNC)*/87 uint32_t reg_index; /*! register index in MMC peripheral (used by SET/GET)*/88 uint32_t * reg_ptr; /*! local pointer on src/dst buffer (used by SET/GET)*/85 paddr_t buf_paddr; /*! physical address of memory buffer (used by INVAL/SYNC) */ 86 uint32_t buf_size; /*! buffer size in bytes (used by INVAL/SYNC) */ 87 uint32_t reg_index; /*! register index in MMC peripheral (used by SET/GET) */ 88 uint32_t * reg_ptr; /*! local pointer on src/dst buffer (used by SET/GET) */ 89 89 error_t error; /*! operation status (0 if success) */ 90 90 } … … 106 106 * to access both the MMC device descriptor, and the L2 cache configuration interface. 107 107 ***************************************************************************************** 108 * @ buf_ paddr : buffer local physical addresse.108 * @ buf_xp : extended pointer on memory buffer. 109 109 * @ buf_size : buffer size (bytes). 110 110 * @ return 0 if success / return EINVAL if failure 111 111 ****************************************************************************************/ 112 error_t dev_mmc_inval( paddr_t buf_paddr,113 uint32_t buf_size );112 error_t dev_mmc_inval( xptr_t buf_xp, 113 uint32_t buf_size ); 114 114 115 115 /***************************************************************************************** … … 119 119 * to access both the MMC device descriptor, and the L2 cache configuration interface. 120 120 ***************************************************************************************** 121 * @ buf_ paddr : buffer local physical addresse.121 * @ buf_xp : extended pointer on memory buffer. 122 122 * @ buf_size : buffer size (bytes). 123 123 * @ return 0 if success / return EINVAL if failure 124 124 ****************************************************************************************/ 125 error_t dev_mmc_sync( paddr_t buf_paddr,125 error_t dev_mmc_sync( xptr_t buf_xp, 126 126 uint32_t buf_size ); 127 127 -
trunk/kernel/devices/dev_nic.c
r3 r23 51 51 uint32_t channel = chdev->channel; 52 52 53 // set chdev name 54 snprintf( chdev->name , 16 , "nic_%d" , chdev->channel ); 55 53 56 // set driver specific fields in chdev descriptor and call driver init function 54 57 if( impl == IMPL_NIC_SOC ) -
trunk/kernel/devices/dev_pic.c
r3 r23 49 49 uint32_t impl = chdev->impl; 50 50 51 // set chdev name 52 strcpy( chdev->name , "pic" ); 53 51 54 // call the relevant driver init function 52 55 if( impl == IMPL_PIC_SOC ) -
trunk/kernel/devices/dev_txt.c
r16 r23 50 50 uint32_t impl = chdev->impl; 51 51 uint32_t channel = chdev->channel; 52 53 // set chdev name 54 snprintf( chdev->name , 16 , "txt_%d" , chdev->channel ); 52 55 53 56 // set fields "cmd", "isr", and call driver init function … … 164 167 xptr_t dev_xp = chdev_dir.txt[channel]; 165 168 169 assert( (dev_xp != XPTR_NULL) , __FUNCTION__ , "undefined TXT0 chdev descriptor" ); 170 166 171 // register command in calling thread 167 172 this->command.txt.dev_xp = dev_xp; -
trunk/kernel/devices/dev_txt.h
r14 r23 106 106 107 107 /****************************************************************************************** 108 * This blocking function writes a character stringon the terminal identified108 * This blocking function writes characters on the terminal identified 109 109 * by the "channel" argument. The corresponding request is actually registered in the 110 110 * chdev requests queue, and the calling thread is descheduled, blocked until
Note: See TracChangeset
for help on using the changeset viewer.