📄 i2o_core.c
字号:
* Clear and (re)initialize IOP's outbound queue. Returns 0 on * success or a negative errno code on a failure. */ int i2o_init_outbound_q(struct i2o_controller *c){ u8 *status; u32 m; u32 *msg; u32 time; dprintk(KERN_INFO "%s: Initializing Outbound Queue...\n", c->name); m=i2o_wait_message(c, "OutboundInit"); if(m==0xFFFFFFFF) return -ETIMEDOUT; msg=(u32 *)(c->mem_offset+m); status = kmalloc(4,GFP_KERNEL); if (status==NULL) { printk(KERN_ERR "%s: Outbound Queue initialization failed - no free memory.\n", c->name); return -ENOMEM; } memset(status, 0, 4); msg[0]= EIGHT_WORD_MSG_SIZE| TRL_OFFSET_6; msg[1]= I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID; msg[2]= core_context; msg[3]= 0x0106; /* Transaction context */ msg[4]= 4096; /* Host page frame size */ /* Frame size is in words. Pick 128, its what everyone elses uses and other sizes break some adapters. */ msg[5]= MSG_FRAME_SIZE<<16|0x80; /* Outbound msg frame size and Initcode */ msg[6]= 0xD0000004; /* Simple SG LE, EOB */ msg[7]= virt_to_bus(status); i2o_post_message(c,m); barrier(); time=jiffies; while(status[0] < I2O_CMD_REJECTED) { if((jiffies-time)>=30*HZ) { if(status[0]==0x00) printk(KERN_ERR "%s: Ignored queue initialize request.\n", c->name); else printk(KERN_ERR "%s: Outbound queue initialize timeout.\n", c->name); kfree(status); return -ETIMEDOUT; } schedule(); barrier(); } if(status[0] != I2O_CMD_COMPLETED) { printk(KERN_ERR "%s: IOP outbound initialise failed.\n", c->name); kfree(status); return -ETIMEDOUT; } return 0;}/** * i2o_post_outbound_messages - fill message queue * @c: controller * * Allocate a message frame and load the messages into the IOP. The * function returns zero on success or a negative errno code on * failure. */int i2o_post_outbound_messages(struct i2o_controller *c){ int i; u32 m; /* Alloc space for IOP's outbound queue message frames */ c->page_frame = kmalloc(MSG_POOL_SIZE, GFP_KERNEL); if(c->page_frame==NULL) { printk(KERN_ERR "%s: Outbound Q initialize failed; out of memory.\n", c->name); return -ENOMEM; } c->page_frame_map = pci_map_single(c->pdev, c->page_frame, MSG_POOL_SIZE, PCI_DMA_FROMDEVICE); if(c->page_frame_map == 0) { kfree(c->page_frame); printk(KERN_ERR "%s: Unable to map outbound queue.\n", c->name); return -ENOMEM; } m = c->page_frame_map; /* Post frames */ for(i=0; i< NMBR_MSG_FRAMES; i++) { I2O_REPLY_WRITE32(c,m); mb(); m += MSG_FRAME_SIZE; } return 0;}/* * Get the IOP's Logical Configuration Table */int i2o_lct_get(struct i2o_controller *c){ u32 msg[8]; int ret, size = c->status_block->expected_lct_size; do { if (c->lct == NULL) { c->lct = kmalloc(size, GFP_KERNEL); if(c->lct == NULL) { printk(KERN_CRIT "%s: Lct Get failed. Out of memory.\n", c->name); return -ENOMEM; } } memset(c->lct, 0, size); msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6; msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID; /* msg[2] filled in i2o_post_wait */ msg[3] = 0; msg[4] = 0xFFFFFFFF; /* All devices */ msg[5] = 0x00000000; /* Report now */ msg[6] = 0xD0000000|size; msg[7] = virt_to_bus(c->lct); ret=i2o_post_wait_mem(c, msg, sizeof(msg), 120, c->lct, NULL); if(ret == -ETIMEDOUT) { c->lct = NULL; return ret; } if(ret<0) { printk(KERN_ERR "%s: LCT Get failed (status=%#x.\n", c->name, -ret); return ret; } if (c->lct->table_size << 2 > size) { size = c->lct->table_size << 2; kfree(c->lct); c->lct = NULL; } } while (c->lct == NULL); if ((ret=i2o_parse_lct(c)) < 0) return ret; return 0;}/* * Like above, but used for async notification. The main * difference is that we keep track of the CurrentChangeIndiicator * so that we only get updates when it actually changes. * */int i2o_lct_notify(struct i2o_controller *c){ u32 msg[8]; msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6; msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID; msg[2] = core_context; msg[3] = 0xDEADBEEF; msg[4] = 0xFFFFFFFF; /* All devices */ msg[5] = c->dlct->change_ind+1; /* Next change */ msg[6] = 0xD0000000|8192; msg[7] = virt_to_bus(c->dlct); return i2o_post_this(c, msg, sizeof(msg));} /* * Bring a controller online into OPERATIONAL state. */ int i2o_online_controller(struct i2o_controller *iop){ u32 v; if (i2o_systab_send(iop) < 0) return -1; /* In READY state */ dprintk(KERN_INFO "%s: Attempting to enable...\n", iop->name); if (i2o_enable_controller(iop) < 0) return -1; /* In OPERATIONAL state */ dprintk(KERN_INFO "%s: Attempting to get/parse lct...\n", iop->name); if (i2o_lct_get(iop) < 0) return -1; /* Check battery status */ iop->battery = 0; if(i2o_query_scalar(iop, ADAPTER_TID, 0x0000, 4, &v, 4)>=0) { if(v&16) iop->battery = 1; } return 0;}/* * Build system table * * The system table contains information about all the IOPs in the * system (duh) and is used by the Executives on the IOPs to establish * peer2peer connections. We're not supporting peer2peer at the moment, * but this will be needed down the road for things like lan2lan forwarding. */static int i2o_build_sys_table(void){ struct i2o_controller *iop = NULL; struct i2o_controller *niop = NULL; int count = 0; sys_tbl_len = sizeof(struct i2o_sys_tbl) + // Header + IOPs (i2o_num_controllers) * sizeof(struct i2o_sys_tbl_entry); if(sys_tbl) kfree(sys_tbl); sys_tbl = kmalloc(sys_tbl_len, GFP_KERNEL); if(!sys_tbl) { printk(KERN_CRIT "SysTab Set failed. Out of memory.\n"); return -ENOMEM; } memset((void*)sys_tbl, 0, sys_tbl_len); sys_tbl->num_entries = i2o_num_controllers; sys_tbl->version = I2OVERSION; /* TODO: Version 2.0 */ sys_tbl->change_ind = sys_tbl_ind++; for(iop = i2o_controller_chain; iop; iop = niop) { niop = iop->next; /* * Get updated IOP state so we have the latest information * * We should delete the controller at this point if it * doesn't respond since if it's not on the system table * it is techninically not part of the I2O subsy遲em... */ if(i2o_status_get(iop)) { printk(KERN_ERR "%s: Deleting b/c could not get status while" "attempting to build system table\n", iop->name); i2o_delete_controller(iop); sys_tbl->num_entries--; continue; // try the next one } sys_tbl->iops[count].org_id = iop->status_block->org_id; sys_tbl->iops[count].iop_id = iop->unit + 2; sys_tbl->iops[count].seg_num = 0; sys_tbl->iops[count].i2o_version = iop->status_block->i2o_version; sys_tbl->iops[count].iop_state = iop->status_block->iop_state; sys_tbl->iops[count].msg_type = iop->status_block->msg_type; sys_tbl->iops[count].frame_size = iop->status_block->inbound_frame_size; sys_tbl->iops[count].last_changed = sys_tbl_ind - 1; // ?? sys_tbl->iops[count].iop_capabilities = iop->status_block->iop_capabilities; sys_tbl->iops[count].inbound_low = (u32)virt_to_bus(iop->post_port); sys_tbl->iops[count].inbound_high = 0; // TODO: 64-bit support count++; }#ifdef DRIVERDEBUG{ u32 *table; table = (u32*)sys_tbl; for(count = 0; count < (sys_tbl_len >>2); count++) printk(KERN_INFO "sys_tbl[%d] = %0#10x\n", count, table[count]);}#endif return 0;}/* * Run time support routines */ /* * Generic "post and forget" helpers. This is less efficient - we do * a memcpy for example that isnt strictly needed, but for most uses * this is simply not worth optimising */int i2o_post_this(struct i2o_controller *c, u32 *data, int len){ u32 m; u32 *msg; unsigned long t=jiffies; do { mb(); m = I2O_POST_READ32(c); } while(m==0xFFFFFFFF && (jiffies-t)<HZ); if(m==0xFFFFFFFF) { printk(KERN_ERR "%s: Timeout waiting for message frame!\n", c->name); return -ETIMEDOUT; } msg = (u32 *)(c->mem_offset + m); memcpy_toio(msg, data, len); i2o_post_message(c,m); return 0;}/** * i2o_post_wait_mem - I2O query/reply with DMA buffers * @c: controller * @msg: message to send * @len: length of message * @timeout: time in seconds to wait * @mem1: attached memory buffer 1 * @mem2: attached memory buffer 2 * * This core API allows an OSM to post a message and then be told whether * or not the system received a successful reply. * * If the message times out then the value '-ETIMEDOUT' is returned. This * is a special case. In this situation the message may (should) complete * at an indefinite time in the future. When it completes it will use the * memory buffers attached to the request. If -ETIMEDOUT is returned then * the memory buffers must not be freed. Instead the event completion will * free them for you. In all other cases the buffers are your problem. * * Pass NULL for unneeded buffers. */ int i2o_post_wait_mem(struct i2o_controller *c, u32 *msg, int len, int timeout, void *mem1, void *mem2){ DECLARE_WAIT_QUEUE_HEAD(wq_i2o_post); DECLARE_WAITQUEUE(wait, current); int complete = 0; int status; unsigned long flags = 0; struct i2o_post_wait_data *wait_data = kmalloc(sizeof(struct i2o_post_wait_data), GFP_KERNEL); if(!wait_data) return -ENOMEM; /* * Create a new notification object */ wait_data->status = &status; wait_data->complete = &complete; wait_data->mem[0] = mem1; wait_data->mem[1] = mem2; /* * Queue the event with its unique id */ spin_lock_irqsave(&post_wait_lock, flags); wait_data->next = post_wait_queue; post_wait_queue = wait_data; wait_data->id = (++post_wait_id) & 0x7fff; wait_data->wq = &wq_i2o_post; spin_unlock_irqrestore(&post_wait_lock, flags); /* * Fill in the message id */ msg[2] = 0x80000000|(u32)core_context|((u32)wait_data->id<<16); /* * Post the message to the controller. At some point later it * will return. If we time out before it returns then * complete will be zero. From the point post_this returns * the wait_data may have been deleted. */ add_wait_queue(&wq_i2o_post, &wait); set_current_state(TASK_INTERRUPTIBLE); if ((status = i2o_post_this(c, msg, len))==0) { schedule_timeout(HZ * timeout); } else { remove_wait_queue(&wq_i2o_post, &wait); return -EIO; } remove_wait_queue(&wq_i2o_post, &wait); if(signal_pending(current)) status = -EINTR; spin_lock_irqsave(&post_wait_lock, flags); barrier(); /* Be sure we see complete as it is locked */ if(!complete) { /* * Mark the entry dead. We cannot remove it. This is important. * When it does terminate (which it must do if the controller hasnt * died..) then it will otherwise scribble on stuff. * !complete lets us safely check if the entry is still * allocated and thus we can write into it */ wait_data->wq = NULL; status = -ETIMEDOUT; } else { /* Debugging check - remove me soon */ if(status == -ETIMEDOUT) { printk("TIMEDOUT BUG!\n"); status = -EIO; } } /* And the wait_data is not leaked either! */ spin_unlock_irqrestore(&post_wait_lock, flags); return status;}/** * i2o_post_wait - I2O query/reply * @c: controller * @msg: message to send * @len: length of message * @timeout: time in seconds to wait * * This core API allows an OSM to post a message and then be told whether * or not the system received a successful reply. */ int i2o_post_wait(struct i2o_controller *c, u32 *msg, int len, int timeout){ return i2o_post_wait_mem(c, msg, len, timeout, NULL, NULL);}/* * i2o_post_wait is completed and we want to wake up the * sleeping proccess. Called by core's reply handler. */static void i2o_post_wait_complete(u32 context, int status){ struct i2o_post_wait_data **p1, *q; unsigned long flags; /* * We need to search through the post_wait * queue to see if the given message is still * outstanding. If not, it means that the IOP * took longer to respond to the message than we * had allowed and timer has already expired. * Not much we can do about that except log * it for debug purposes, increase timeout, and recompile * * Lock needed to keep anyone from moving queue pointers * around while we're looking through them. */ spin_lock_irqsave(&post_wait_lock, flags); for(p1 = &post_wait_queue; *p1!=NULL; p1 = &((*p1)->next)) { q = (*p1); if(q->id == ((context >> 16) & 0x7fff)) { /* * Delete it */ *p1 = q->next; /* * Live or dead ? */ if(q->wq) { /* Live entry - wakeup and set status */ *q->status = status; *q->complete = 1; wake_up(q->wq); } else { /* * Free resources. Caller is dead */ if(q->mem[0]) kfree(q->mem[0]); if(q->mem[1]) kfree(q->mem[1]); printk(KERN_WARNING "i2o_post_wait event completed after timeout.\n"); } kfree(q); spin_unlock(&post_wait_lock); return; } } spin_unlock(&post_wait_lock); printk(KERN_DEBUG "i2o_post_wait: Bogus reply!\n");}/* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET * * This function can be used for all UtilParamsGet/Set operations. * The OperationList is given in oplist-buffer, * and results are returned in reslist-buffer. * Note that the minimum sized reslist is 8 bytes and contains * ResultCount, ErrorInfoS
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -