diff options
Diffstat (limited to 'drivers/staging/mei')
-rw-r--r-- | drivers/staging/mei/init.c | 49 | ||||
-rw-r--r-- | drivers/staging/mei/interface.c | 8 | ||||
-rw-r--r-- | drivers/staging/mei/interface.h | 11 | ||||
-rw-r--r-- | drivers/staging/mei/interrupt.c | 322 | ||||
-rw-r--r-- | drivers/staging/mei/iorw.c | 77 | ||||
-rw-r--r-- | drivers/staging/mei/main.c | 576 | ||||
-rw-r--r-- | drivers/staging/mei/mei.txt | 226 | ||||
-rw-r--r-- | drivers/staging/mei/mei_dev.h | 13 | ||||
-rw-r--r-- | drivers/staging/mei/wd.c | 32 |
9 files changed, 582 insertions, 732 deletions
diff --git a/drivers/staging/mei/init.c b/drivers/staging/mei/init.c index 8bf34794489c..4ac3696883cb 100644 --- a/drivers/staging/mei/init.c +++ b/drivers/staging/mei/init.c @@ -38,7 +38,6 @@ void mei_io_list_init(struct mei_io_list *list) { /* initialize our queue list */ INIT_LIST_HEAD(&list->mei_cb.cb_list); - list->status = 0; } /** @@ -49,22 +48,15 @@ void mei_io_list_init(struct mei_io_list *list) */ void mei_io_list_flush(struct mei_io_list *list, struct mei_cl *cl) { - struct mei_cl_cb *cb_pos = NULL; - struct mei_cl_cb *cb_next = NULL; + struct mei_cl_cb *pos; + struct mei_cl_cb *next; - if (list->status != 0) - return; - - if (list_empty(&list->mei_cb.cb_list)) - return; - - list_for_each_entry_safe(cb_pos, cb_next, - &list->mei_cb.cb_list, cb_list) { - if (cb_pos) { + list_for_each_entry_safe(pos, next, &list->mei_cb.cb_list, cb_list) { + if (pos->file_private) { struct mei_cl *cl_tmp; - cl_tmp = (struct mei_cl *)cb_pos->file_private; + cl_tmp = (struct mei_cl *)pos->file_private; if (mei_cl_cmp_id(cl, cl_tmp)) - list_del(&cb_pos->cb_list); + list_del(&pos->cb_list); } } } @@ -338,16 +330,10 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) } } /* remove all waiting requests */ - if (dev->write_list.status == 0 && - !list_empty(&dev->write_list.mei_cb.cb_list)) { - list_for_each_entry_safe(cb_pos, cb_next, - &dev->write_list.mei_cb.cb_list, cb_list) { - if (cb_pos) { - list_del(&cb_pos->cb_list); - mei_free_cb_private(cb_pos); - cb_pos = NULL; - } - } + list_for_each_entry_safe(cb_pos, cb_next, + &dev->write_list.mei_cb.cb_list, cb_list) { + list_del(&cb_pos->cb_list); + mei_free_cb_private(cb_pos); } } @@ -380,8 +366,7 @@ void mei_host_start_message(struct mei_device *dev) host_start_req->host_version.major_version = HBM_MAJOR_VERSION; host_start_req->host_version.minor_version = HBM_MINOR_VERSION; dev->recvd_msg = false; - if (!mei_write_message(dev, mei_hdr, - (unsigned char *) (host_start_req), + if (!mei_write_message(dev, mei_hdr, (unsigned char *)host_start_req, mei_hdr->length)) { dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n"); dev->mei_state = MEI_RESETING; @@ -414,8 +399,7 @@ void mei_host_enum_clients_message(struct mei_device *dev) host_enum_req = (struct hbm_host_enum_request *) &dev->wr_msg_buf[1]; memset(host_enum_req, 0, sizeof(struct hbm_host_enum_request)); host_enum_req->cmd.cmd = HOST_ENUM_REQ_CMD; - if (!mei_write_message(dev, mei_hdr, - (unsigned char *) (host_enum_req), + if (!mei_write_message(dev, mei_hdr, (unsigned char *)host_enum_req, mei_hdr->length)) { dev->mei_state = MEI_RESETING; dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n"); @@ -605,15 +589,10 @@ void mei_host_init_iamthif(struct mei_device *dev) return; } - /* Do not render the system unusable when iamthif_mtu is not equal to - the value received from ME. - Assign iamthif_mtu to the value received from ME in order to solve the - hardware macro incompatibility. */ + /* Assign iamthif_mtu to the value received from ME */ - dev_dbg(&dev->pdev->dev, "[DEFAULT] IAMTHIF = %d\n", dev->iamthif_mtu); dev->iamthif_mtu = dev->me_clients[i].props.max_msg_length; - dev_dbg(&dev->pdev->dev, - "IAMTHIF = %d\n", + dev_dbg(&dev->pdev->dev, "IAMTHIF_MTU = %d\n", dev->me_clients[i].props.max_msg_length); kfree(dev->iamthif_msg_buf); diff --git a/drivers/staging/mei/interface.c b/drivers/staging/mei/interface.c index a65dacf00213..eb5df7fc2269 100644 --- a/drivers/staging/mei/interface.c +++ b/drivers/staging/mei/interface.c @@ -128,9 +128,9 @@ int mei_count_empty_write_slots(struct mei_device *dev) * returns 1 if success, 0 - otherwise. */ int mei_write_message(struct mei_device *dev, - struct mei_msg_hdr *header, - unsigned char *write_buffer, - unsigned long write_length) + struct mei_msg_hdr *header, + unsigned char *write_buffer, + unsigned long write_length) { u32 temp_msg = 0; unsigned long bytes_written = 0; @@ -216,7 +216,7 @@ int mei_count_full_read_slots(struct mei_device *dev) * @buffer_length: message size will be read */ void mei_read_slots(struct mei_device *dev, - unsigned char *buffer, unsigned long buffer_length) + unsigned char *buffer, unsigned long buffer_length) { u32 i = 0; unsigned char temp_buf[sizeof(u32)]; diff --git a/drivers/staging/mei/interface.h b/drivers/staging/mei/interface.h index 7bd38ae2c233..aeae511419c7 100644 --- a/drivers/staging/mei/interface.h +++ b/drivers/staging/mei/interface.h @@ -52,6 +52,17 @@ int mei_wd_send(struct mei_device *dev); int mei_wd_stop(struct mei_device *dev, bool preserve); bool mei_wd_host_init(struct mei_device *dev); void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout); +/* + * mei_watchdog_register - Registering watchdog interface + * once we got connection to the WD Client + * @dev - mei device + */ +void mei_watchdog_register(struct mei_device *dev); +/* + * mei_watchdog_unregister - Uegistering watchdog interface + * @dev - mei device + */ +void mei_watchdog_unregister(struct mei_device *dev); int mei_flow_ctrl_reduce(struct mei_device *dev, struct mei_cl *cl); diff --git a/drivers/staging/mei/interrupt.c b/drivers/staging/mei/interrupt.c index 882d106d54e5..3544fee34e48 100644 --- a/drivers/staging/mei/interrupt.c +++ b/drivers/staging/mei/interrupt.c @@ -198,8 +198,7 @@ static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list, unsigned char *buffer = NULL; dev_dbg(&dev->pdev->dev, "start client msg\n"); - if (!(dev->read_list.status == 0 && - !list_empty(&dev->read_list.mei_cb.cb_list))) + if (list_empty(&dev->read_list.mei_cb.cb_list)) goto quit; list_for_each_entry_safe(cb_pos, cb_next, @@ -210,9 +209,6 @@ static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list, buffer = (unsigned char *) (cb_pos->response_buffer.data + cb_pos->information); - BUG_ON(cb_pos->response_buffer.size < - mei_hdr->length + - cb_pos->information); if (cb_pos->response_buffer.size < mei_hdr->length + cb_pos->information) { @@ -390,24 +386,10 @@ static void mei_client_connect_response(struct mei_device *dev, /* if WD or iamthif client treat specially */ if (is_treat_specially_client(&(dev->wd_cl), rs)) { - dev_dbg(&dev->pdev->dev, "dev->wd_timeout =%d.\n", - dev->wd_timeout); - - dev->wd_due_counter = (dev->wd_timeout) ? 1 : 0; - dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n"); + mei_watchdog_register(dev); - /* Registering watchdog interface device once we got connection - to the WD Client - */ - if (watchdog_register_device(&amt_wd_dev)) { - printk(KERN_ERR "mei: unable to register watchdog device.\n"); - dev->wd_interface_reg = false; - } else { - dev_dbg(&dev->pdev->dev, "successfully register watchdog interface.\n"); - dev->wd_interface_reg = true; - } - + /* next step in the state maching */ mei_host_init_iamthif(dev); return; } @@ -416,22 +398,20 @@ static void mei_client_connect_response(struct mei_device *dev, dev->iamthif_state = MEI_IAMTHIF_IDLE; return; } - if (!dev->ctrl_rd_list.status && - !list_empty(&dev->ctrl_rd_list.mei_cb.cb_list)) { - list_for_each_entry_safe(cb_pos, cb_next, - &dev->ctrl_rd_list.mei_cb.cb_list, cb_list) { - cl = (struct mei_cl *)cb_pos->file_private; - if (!cl) { + list_for_each_entry_safe(cb_pos, cb_next, + &dev->ctrl_rd_list.mei_cb.cb_list, cb_list) { + + cl = (struct mei_cl *)cb_pos->file_private; + if (!cl) { + list_del(&cb_pos->cb_list); + return; + } + if (MEI_IOCTL == cb_pos->major_file_operations) { + if (is_treat_specially_client(cl, rs)) { list_del(&cb_pos->cb_list); - return; - } - if (MEI_IOCTL == cb_pos->major_file_operations) { - if (is_treat_specially_client(cl, rs)) { - list_del(&cb_pos->cb_list); - cl->status = 0; - cl->timer_count = 0; - break; - } + cl->status = 0; + cl->timer_count = 0; + break; } } } @@ -458,29 +438,26 @@ static void mei_client_disconnect_response(struct mei_device *dev, rs->host_addr, rs->status); - if (!dev->ctrl_rd_list.status && - !list_empty(&dev->ctrl_rd_list.mei_cb.cb_list)) { - list_for_each_entry_safe(cb_pos, cb_next, - &dev->ctrl_rd_list.mei_cb.cb_list, cb_list) { - cl = (struct mei_cl *)cb_pos->file_private; + list_for_each_entry_safe(cb_pos, cb_next, + &dev->ctrl_rd_list.mei_cb.cb_list, cb_list) { + cl = (struct mei_cl *)cb_pos->file_private; - if (!cl) { - list_del(&cb_pos->cb_list); - return; - } + if (!cl) { + list_del(&cb_pos->cb_list); + return; + } - dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n"); - if (cl->host_client_id == rs->host_addr && - cl->me_client_id == rs->me_addr) { + dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n"); + if (cl->host_client_id == rs->host_addr && + cl->me_client_id == rs->me_addr) { - list_del(&cb_pos->cb_list); - if (!rs->status) - cl->state = MEI_FILE_DISCONNECTED; + list_del(&cb_pos->cb_list); + if (!rs->status) + cl->state = MEI_FILE_DISCONNECTED; - cl->status = 0; - cl->timer_count = 0; - break; - } + cl->status = 0; + cl->timer_count = 0; + break; } } } @@ -718,7 +695,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev, case CLIENT_DISCONNECT_RES_CMD: disconnect_res = (struct hbm_client_connect_response *) mei_msg; - mei_client_disconnect_response(dev, disconnect_res); + mei_client_disconnect_response(dev, disconnect_res); dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n"); wake_up(&dev->wait_recvd_msg); break; @@ -736,7 +713,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev, mei_reset(dev, 1); return; } - if (dev->me_clients[dev->me_client_presentation_num] + if (dev->me_clients[dev->me_client_presentation_num] .client_id == props_res->address) { dev->me_clients[dev->me_client_presentation_num].props @@ -1228,7 +1205,7 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list, { struct mei_cl *cl; - struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; + struct mei_cl_cb *pos = NULL, *next = NULL; struct mei_io_list *list; int ret; @@ -1241,36 +1218,31 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list, dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n"); list = &dev->write_waiting_list; - if (!list->status && !list_empty(&list->mei_cb.cb_list)) { - list_for_each_entry_safe(cb_pos, cb_next, - &list->mei_cb.cb_list, cb_list) { - cl = (struct mei_cl *)cb_pos->file_private; - if (cl) { - cl->status = 0; - list_del(&cb_pos->cb_list); - if (MEI_WRITING == cl->writing_state && - (cb_pos->major_file_operations == - MEI_WRITE) && - (cl != &dev->iamthif_cl)) { - dev_dbg(&dev->pdev->dev, - "MEI WRITE COMPLETE\n"); - cl->writing_state = - MEI_WRITE_COMPLETE; - list_add_tail(&cb_pos->cb_list, - &cmpl_list->mei_cb.cb_list); - } - if (cl == &dev->iamthif_cl) { - dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n"); - if (dev->iamthif_flow_control_pending) { - ret = - _mei_irq_thread_iamthif_read( - dev, slots); - if (ret) - return ret; - } - } + list_for_each_entry_safe(pos, next, + &list->mei_cb.cb_list, cb_list) { + cl = (struct mei_cl *)pos->file_private; + if (cl == NULL) + continue; + + cl->status = 0; + list_del(&pos->cb_list); + if (MEI_WRITING == cl->writing_state && + (pos->major_file_operations == MEI_WRITE) && + (cl != &dev->iamthif_cl)) { + dev_dbg(&dev->pdev->dev, + "MEI WRITE COMPLETE\n"); + cl->writing_state = MEI_WRITE_COMPLETE; + list_add_tail(&pos->cb_list, + &cmpl_list->mei_cb.cb_list); + } + if (cl == &dev->iamthif_cl) { + dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n"); + if (dev->iamthif_flow_control_pending) { + ret = _mei_irq_thread_iamthif_read( + dev, slots); + if (ret) + return ret; } - } } @@ -1317,101 +1289,88 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list, return ~ENODEV; /* complete control write list CB */ - if (!dev->ctrl_wr_list.status) { - /* complete control write list CB */ - dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); - list_for_each_entry_safe(cb_pos, cb_next, + dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); + list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.mei_cb.cb_list, cb_list) { - cl = (struct mei_cl *) - cb_pos->file_private; - if (!cl) { - list_del(&cb_pos->cb_list); - return -ENODEV; - } - switch (cb_pos->major_file_operations) { - case MEI_CLOSE: - /* send disconnect message */ - ret = _mei_irq_thread_close(dev, slots, - cb_pos, cl, cmpl_list); - if (ret) - return ret; - - break; - case MEI_READ: - /* send flow control message */ - ret = _mei_irq_thread_read(dev, slots, - cb_pos, cl, cmpl_list); - if (ret) - return ret; + cl = (struct mei_cl *) pos->file_private; + if (!cl) { + list_del(&pos->cb_list); + return -ENODEV; + } + switch (pos->major_file_operations) { + case MEI_CLOSE: + /* send disconnect message */ + ret = _mei_irq_thread_close(dev, slots, pos, cl, cmpl_list); + if (ret) + return ret; - break; - case MEI_IOCTL: - /* connect message */ - if (!mei_other_client_is_connecting(dev, - cl)) - continue; - ret = _mei_irq_thread_ioctl(dev, slots, - cb_pos, cl, cmpl_list); - if (ret) - return ret; + break; + case MEI_READ: + /* send flow control message */ + ret = _mei_irq_thread_read(dev, slots, pos, cl, cmpl_list); + if (ret) + return ret; - break; + break; + case MEI_IOCTL: + /* connect message */ + if (mei_other_client_is_connecting(dev, cl)) + continue; + ret = _mei_irq_thread_ioctl(dev, slots, pos, cl, cmpl_list); + if (ret) + return ret; - default: - BUG(); - } + break; + default: + BUG(); } + } /* complete write list CB */ - if (!dev->write_list.status && - !list_empty(&dev->write_list.mei_cb.cb_list)) { - dev_dbg(&dev->pdev->dev, "complete write list cb.\n"); - list_for_each_entry_safe(cb_pos, cb_next, - &dev->write_list.mei_cb.cb_list, cb_list) { - cl = (struct mei_cl *)cb_pos->file_private; - - if (cl) { - if (cl != &dev->iamthif_cl) { - if (!mei_flow_ctrl_creds(dev, - cl)) { - dev_dbg(&dev->pdev->dev, - "No flow control" - " credentials for client" - " %d, not sending.\n", - cl->host_client_id); - continue; - } - ret = _mei_irq_thread_cmpl(dev, slots, - cb_pos, - cl, cmpl_list); - if (ret) - return ret; - - } else if (cl == &dev->iamthif_cl) { - /* IAMTHIF IOCTL */ - dev_dbg(&dev->pdev->dev, "complete amthi write cb.\n"); - if (!mei_flow_ctrl_creds(dev, - cl)) { - dev_dbg(&dev->pdev->dev, - "No flow control" - " credentials for amthi" - " client %d.\n", - cl->host_client_id); - continue; - } - ret = _mei_irq_thread_cmpl_iamthif(dev, - slots, - cb_pos, - cl, - cmpl_list); - if (ret) - return ret; - - } + dev_dbg(&dev->pdev->dev, "complete write list cb.\n"); + list_for_each_entry_safe(pos, next, + &dev->write_list.mei_cb.cb_list, cb_list) { + cl = (struct mei_cl *)pos->file_private; + if (cl == NULL) + continue; + + if (cl != &dev->iamthif_cl) { + if (!mei_flow_ctrl_creds(dev, cl)) { + dev_dbg(&dev->pdev->dev, + "No flow control" + " credentials for client" + " %d, not sending.\n", + cl->host_client_id); + continue; + } + ret = _mei_irq_thread_cmpl(dev, slots, + pos, + cl, cmpl_list); + if (ret) + return ret; + + } else if (cl == &dev->iamthif_cl) { + /* IAMTHIF IOCTL */ + dev_dbg(&dev->pdev->dev, "complete amthi write cb.\n"); + if (!mei_flow_ctrl_creds(dev, cl)) { + dev_dbg(&dev->pdev->dev, + "No flow control" + " credentials for amthi" + " client %d.\n", + cl->host_client_id); + continue; } + ret = _mei_irq_thread_cmpl_iamthif(dev, + slots, + pos, + cl, + cmpl_list); + if (ret) + return ret; } + } return 0; } @@ -1502,18 +1461,13 @@ void mei_timer(struct work_struct *work) amthi_complete_list = &dev->amthi_read_complete_list. mei_cb.cb_list; - if (!list_empty(amthi_complete_list)) { - - list_for_each_entry_safe(cb_pos, cb_next, - amthi_complete_list, - cb_list) { + list_for_each_entry_safe(cb_pos, cb_next, amthi_complete_list, cb_list) { - cl_pos = cb_pos->file_object->private_data; + cl_pos = cb_pos->file_object->private_data; - /* Finding the AMTHI entry. */ - if (cl_pos == &dev->iamthif_cl) - list_del(&cb_pos->cb_list); - } + /* Finding the AMTHI entry. */ + if (cl_pos == &dev->iamthif_cl) + list_del(&cb_pos->cb_list); } if (dev->iamthif_current_cb) mei_free_cb_private(dev->iamthif_current_cb); @@ -1527,8 +1481,8 @@ void mei_timer(struct work_struct *work) } } out: - schedule_delayed_work(&dev->timer_work, 2 * HZ); - mutex_unlock(&dev->device_lock); + schedule_delayed_work(&dev->timer_work, 2 * HZ); + mutex_unlock(&dev->device_lock); } /** @@ -1624,7 +1578,7 @@ end: wake_up_interruptible(&dev->wait_recvd_msg); bus_message_received = false; } - if (complete_list.status || list_empty(&complete_list.mei_cb.cb_list)) + if (list_empty(&complete_list.mei_cb.cb_list)) return IRQ_HANDLED; diff --git a/drivers/staging/mei/iorw.c b/drivers/staging/mei/iorw.c index 8a61d1266515..0752ead4269a 100644 --- a/drivers/staging/mei/iorw.c +++ b/drivers/staging/mei/iorw.c @@ -228,18 +228,15 @@ struct mei_cl_cb *find_amthi_read_list_entry( struct file *file) { struct mei_cl *cl_temp; - struct mei_cl_cb *cb_pos = NULL; - struct mei_cl_cb *cb_next = NULL; - - if (!dev->amthi_read_complete_list.status && - !list_empty(&dev->amthi_read_complete_list.mei_cb.cb_list)) { - list_for_each_entry_safe(cb_pos, cb_next, - &dev->amthi_read_complete_list.mei_cb.cb_list, cb_list) { - cl_temp = (struct mei_cl *)cb_pos->file_private; - if (cl_temp && cl_temp == &dev->iamthif_cl && - cb_pos->file_object == file) - return cb_pos; - } + struct mei_cl_cb *pos = NULL; + struct mei_cl_cb *next = NULL; + + list_for_each_entry_safe(pos, next, + &dev->amthi_read_complete_list.mei_cb.cb_list, cb_list) { + cl_temp = (struct mei_cl *)pos->file_private; + if (cl_temp && cl_temp == &dev->iamthif_cl && + pos->file_object == file) + return pos; } return NULL; } @@ -262,7 +259,7 @@ struct mei_cl_cb *find_amthi_read_list_entry( * negative on failure. */ int amthi_read(struct mei_device *dev, struct file *file, - char __user *ubuf, size_t length, loff_t *offset) + char __user *ubuf, size_t length, loff_t *offset) { int rets; int wait_ret; @@ -334,8 +331,7 @@ int amthi_read(struct mei_device *dev, struct file *file, } } /* if the whole message will fit remove it from the list */ - if (cb->information >= *offset && - length >= (cb->information - *offset)) + if (cb->information >= *offset && length >= (cb->information - *offset)) list_del(&cb->cb_list); else if (cb->information > 0 && cb->information <= *offset) { /* end of the message has been reached */ @@ -356,9 +352,7 @@ int amthi_read(struct mei_device *dev, struct file *file, * the information may be longer */ length = min_t(size_t, length, (cb->information - *offset)); - if (copy_to_user(ubuf, - cb->response_buffer.data + *offset, - length)) + if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) rets = -EFAULT; else { rets = length; @@ -427,7 +421,7 @@ int mei_start_read(struct mei_device *dev, struct mei_cl *cl) cb->response_buffer.size = dev->me_clients[i].props.max_msg_length; cb->response_buffer.data = - kmalloc(cb->response_buffer.size, GFP_KERNEL); + kmalloc(cb->response_buffer.size, GFP_KERNEL); if (!cb->response_buffer.data) { rets = -ENOMEM; goto unlock; @@ -448,8 +442,7 @@ int mei_start_read(struct mei_device *dev, struct mei_cl *cl) &dev->read_list.mei_cb.cb_list); } } else { - list_add_tail(&cb->cb_list, - &dev->ctrl_wr_list.mei_cb.cb_list); + list_add_tail(&cb->cb_list, &dev->ctrl_wr_list.mei_cb.cb_list); } return rets; unlock: @@ -482,7 +475,7 @@ int amthi_write(struct mei_device *dev, struct mei_cl_cb *cb) dev->iamthif_ioctl = true; dev->iamthif_msg_buf_size = cb->request_buffer.size; memcpy(dev->iamthif_msg_buf, cb->request_buffer.data, - cb->request_buffer.size); + cb->request_buffer.size); ret = mei_flow_ctrl_creds(dev, &dev->iamthif_cl); if (ret < 0) @@ -534,8 +527,7 @@ int amthi_write(struct mei_device *dev, struct mei_cl_cb *cb) dev_dbg(&dev->pdev->dev, "No flow control credentials, " "so add iamthif cb to write list.\n"); - list_add_tail(&cb->cb_list, - &dev->write_list.mei_cb.cb_list); + list_add_tail(&cb->cb_list, &dev->write_list.mei_cb.cb_list); } return 0; } @@ -550,8 +542,8 @@ int amthi_write(struct mei_device *dev, struct mei_cl_cb *cb) void mei_run_next_iamthif_cmd(struct mei_device *dev) { struct mei_cl *cl_tmp; - struct mei_cl_cb *cb_pos = NULL; - struct mei_cl_cb *cb_next = NULL; + struct mei_cl_cb *pos = NULL; + struct mei_cl_cb *next = NULL; int status; if (!dev) @@ -565,25 +557,22 @@ void mei_run_next_iamthif_cmd(struct mei_device *dev) dev->iamthif_timer = 0; dev->iamthif_file_object = NULL; - if (dev->amthi_cmd_list.status == 0 && - !list_empty(&dev->amthi_cmd_list.mei_cb.cb_list)) { - dev_dbg(&dev->pdev->dev, "complete amthi cmd_list cb.\n"); - - list_for_each_entry_safe(cb_pos, cb_next, - &dev->amthi_cmd_list.mei_cb.cb_list, cb_list) { - list_del(&cb_pos->cb_list); - cl_tmp = (struct mei_cl *)cb_pos->file_private; - - if (cl_tmp && cl_tmp == &dev->iamthif_cl) { - status = amthi_write(dev, cb_pos); - if (status) { - dev_dbg(&dev->pdev->dev, - "amthi write failed status = %d\n", - status); - return; - } - break; + dev_dbg(&dev->pdev->dev, "complete amthi cmd_list cb.\n"); + + list_for_each_entry_safe(pos, next, + &dev->amthi_cmd_list.mei_cb.cb_list, cb_list) { + list_del(&pos->cb_list); + cl_tmp = (struct mei_cl *)pos->file_private; + + if (cl_tmp && cl_tmp == &dev->iamthif_cl) { + status = amthi_write(dev, pos); + if (status) { + dev_dbg(&dev->pdev->dev, + "amthi write failed status = %d\n", + status); + return; } + break; } } } diff --git a/drivers/staging/mei/main.c b/drivers/staging/mei/main.c index eb05c36f45d4..1e1a9f996e7c 100644 --- a/drivers/staging/mei/main.c +++ b/drivers/staging/mei/main.c @@ -33,6 +33,7 @@ #include <linux/compat.h> #include <linux/jiffies.h> #include <linux/interrupt.h> +#include <linux/miscdevice.h> #include "mei_dev.h" #include "mei.h" @@ -51,18 +52,10 @@ static char mei_driver_name[] = MEI_DRIVER_NAME; static const char mei_driver_string[] = "Intel(R) Management Engine Interface"; static const char mei_driver_version[] = MEI_DRIVER_VERSION; -/* mei char device for registration */ -static struct cdev mei_cdev; - -/* major number for device */ -static int mei_major; /* The device pointer */ /* Currently this driver works as long as there is only a single AMT device. */ struct pci_dev *mei_device; -static struct class *mei_class; - - /* mei_pci_tbl - PCI Device ID Table */ static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)}, @@ -105,173 +98,6 @@ MODULE_DEVICE_TABLE(pci, mei_pci_tbl); static DEFINE_MUTEX(mei_mutex); -/** - * mei_probe - Device Initialization Routine - * - * @pdev: PCI device structure - * @ent: entry in kcs_pci_tbl - * - * returns 0 on success, <0 on failure. - */ -static int __devinit mei_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct mei_device *dev; - int err; - - mutex_lock(&mei_mutex); - if (mei_device) { - err = -EEXIST; - goto end; - } - /* enable pci dev */ - err = pci_enable_device(pdev); - if (err) { - printk(KERN_ERR "mei: Failed to enable pci device.\n"); - goto end; - } - /* set PCI host mastering */ - pci_set_master(pdev); - /* pci request regions for mei driver */ - err = pci_request_regions(pdev, mei_driver_name); - if (err) { - printk(KERN_ERR "mei: Failed to get pci regions.\n"); - goto disable_device; - } - /* allocates and initializes the mei dev structure */ - dev = mei_device_init(pdev); - if (!dev) { - err = -ENOMEM; - goto release_regions; - } - /* mapping IO device memory */ - dev->mem_addr = pci_iomap(pdev, 0, 0); - if (!dev->mem_addr) { - printk(KERN_ERR "mei: mapping I/O device memory failure.\n"); - err = -ENOMEM; - goto free_device; - } - pci_enable_msi(pdev); - - /* request and enable interrupt */ - if (pci_dev_msi_enabled(pdev)) - err = request_threaded_irq(pdev->irq, - NULL, - mei_interrupt_thread_handler, - 0, mei_driver_name, dev); - else - err = request_threaded_irq(pdev->irq, - mei_interrupt_quick_handler, - mei_interrupt_thread_handler, - IRQF_SHARED, mei_driver_name, dev); - - if (err) { - printk(KERN_ERR "mei: request_threaded_irq failure. irq = %d\n", - pdev->irq); - goto unmap_memory; - } - INIT_DELAYED_WORK(&dev->timer_work, mei_timer); - if (mei_hw_init(dev)) { - printk(KERN_ERR "mei: Init hw failure.\n"); - err = -ENODEV; - goto release_irq; - } - mei_device = pdev; - pci_set_drvdata(pdev, dev); - schedule_delayed_work(&dev->timer_work, HZ); - - mutex_unlock(&mei_mutex); - - pr_debug("mei: Driver initialization successful.\n"); - - return 0; - -release_irq: - /* disable interrupts */ - dev->host_hw_state = mei_hcsr_read(dev); - mei_disable_interrupts(dev); - flush_scheduled_work(); - free_irq(pdev->irq, dev); - pci_disable_msi(pdev); -unmap_memory: - pci_iounmap(pdev, dev->mem_addr); -free_device: - kfree(dev); -release_regions: - pci_release_regions(pdev); -disable_device: - pci_disable_device(pdev); -end: - mutex_unlock(&mei_mutex); - printk(KERN_ERR "mei: Driver initialization failed.\n"); - return err; -} - -/** - * mei_remove - Device Removal Routine - * - * @pdev: PCI device structure - * - * mei_remove is called by the PCI subsystem to alert the driver - * that it should release a PCI device. - */ -static void __devexit mei_remove(struct pci_dev *pdev) -{ - struct mei_device *dev; - - if (mei_device != pdev) - return; - - dev = pci_get_drvdata(pdev); - if (!dev) - return; - - mutex_lock(&dev->device_lock); - - mei_wd_stop(dev, false); - - mei_device = NULL; - - if (dev->iamthif_cl.state == MEI_FILE_CONNECTED) { - dev->iamthif_cl.state = MEI_FILE_DISCONNECTING; - mei_disconnect_host_client(dev, &dev->iamthif_cl); - } - if (dev->wd_cl.state == MEI_FILE_CONNECTED) { - dev->wd_cl.state = MEI_FILE_DISCONNECTING; - mei_disconnect_host_client(dev, &dev->wd_cl); - } - - /* Unregistering watchdog device */ - if (dev->wd_interface_reg) - watchdog_unregister_device(&amt_wd_dev); - - /* remove entry if already in list */ - dev_dbg(&pdev->dev, "list del iamthif and wd file list.\n"); - mei_remove_client_from_file_list(dev, dev->wd_cl.host_client_id); - mei_remove_client_from_file_list(dev, dev->iamthif_cl.host_client_id); - - dev->iamthif_current_cb = NULL; - dev->me_clients_num = 0; - - mutex_unlock(&dev->device_lock); - - flush_scheduled_work(); - - /* disable interrupts */ - mei_disable_interrupts(dev); - - free_irq(pdev->irq, dev); - pci_disable_msi(pdev); - pci_set_drvdata(pdev, NULL); - - if (dev->mem_addr) - pci_iounmap(pdev, dev->mem_addr); - - kfree(dev); - - pci_release_regions(pdev); - pci_disable_device(pdev); -} /** * mei_clear_list - removes all callbacks associated with file @@ -372,21 +198,17 @@ static struct mei_cl_cb *find_read_list_entry( struct mei_device *dev, struct mei_cl *cl) { - struct mei_cl_cb *cb_pos = NULL; - struct mei_cl_cb *cb_next = NULL; - - if (!dev->read_list.status && - !list_empty(&dev->read_list.mei_cb.cb_list)) { + struct mei_cl_cb *pos = NULL; + struct mei_cl_cb *next = NULL; - dev_dbg(&dev->pdev->dev, "remove read_list CB\n"); - list_for_each_entry_safe(cb_pos, cb_next, - &dev->read_list.mei_cb.cb_list, cb_list) { - struct mei_cl *cl_temp; - cl_temp = (struct mei_cl *)cb_pos->file_private; + dev_dbg(&dev->pdev->dev, "remove read_list CB\n"); + list_for_each_entry_safe(pos, next, + &dev->read_list.mei_cb.cb_list, cb_list) { + struct mei_cl *cl_temp; + cl_temp = (struct mei_cl *)pos->file_private; - if (mei_cl_cmp_id(cl, cl_temp)) - return cb_pos; - } + if (mei_cl_cmp_id(cl, cl_temp)) + return pos; } return NULL; } @@ -402,15 +224,16 @@ static struct mei_cl_cb *find_read_list_entry( static int mei_open(struct inode *inode, struct file *file) { struct mei_cl *cl; - int if_num = iminor(inode), err; struct mei_device *dev; + unsigned long cl_id; + int err; err = -ENODEV; if (!mei_device) goto out; dev = pci_get_drvdata(mei_device); - if (if_num != MEI_MINOR_NUMBER || !dev) + if (!dev) goto out; mutex_lock(&dev->device_lock); @@ -429,14 +252,16 @@ static int mei_open(struct inode *inode, struct file *file) if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) goto out_unlock; - cl->host_client_id = find_first_zero_bit(dev->host_clients_map, - MEI_CLIENTS_MAX); - if (cl->host_client_id > MEI_CLIENTS_MAX) + cl_id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX); + if (cl_id >= MEI_CLIENTS_MAX) goto out_unlock; + cl->host_client_id = cl_id; + dev_dbg(&dev->pdev->dev, "client_id = %d\n", cl->host_client_id); dev->open_handle_count++; + list_add_tail(&cl->link, &dev->file_list); set_bit(cl->host_client_id, dev->host_clients_map); @@ -446,7 +271,7 @@ static int mei_open(struct inode *inode, struct file *file) file->private_data = cl; mutex_unlock(&dev->device_lock); - return 0; + return nonseekable_open(inode, file); out_unlock: mutex_unlock(&dev->device_lock); @@ -492,8 +317,7 @@ static int mei_release(struct inode *inode, struct file *file) cl->me_client_id); if (dev->open_handle_count > 0) { - clear_bit(cl->host_client_id, - dev->host_clients_map); + clear_bit(cl->host_client_id, dev->host_clients_map); dev->open_handle_count--; } mei_remove_client_from_file_list(dev, cl->host_client_id); @@ -554,7 +378,7 @@ static int mei_release(struct inode *inode, struct file *file) * returns >=0 data length on success , <0 on error */ static ssize_t mei_read(struct file *file, char __user *ubuf, - size_t length, loff_t *offset) + size_t length, loff_t *offset) { struct mei_cl *cl = file->private_data; struct mei_cl_cb *cb_pos = NULL; @@ -673,9 +497,7 @@ copy_buffer: /* information size may be longer */ length = min_t(size_t, length, (cb->information - *offset)); - if (copy_to_user(ubuf, - cb->response_buffer.data + *offset, - length)) { + if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) { rets = -EFAULT; goto free; } @@ -711,7 +533,7 @@ out: * returns >=0 data length on success , <0 on error */ static ssize_t mei_write(struct file *file, const char __user *ubuf, - size_t length, loff_t *offset) + size_t length, loff_t *offset) { struct mei_cl *cl = file->private_data; struct mei_cl_cb *write_cb = NULL; @@ -762,8 +584,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, cl->read_cb = NULL; cl->read_pending = 0; } - } else if (cl->reading_state == MEI_IDLE && - !cl->read_pending) + } else if (cl->reading_state == MEI_IDLE && !cl->read_pending) *offset = 0; @@ -1034,7 +855,7 @@ out: */ #ifdef CONFIG_COMPAT static long mei_compat_ioctl(struct file *file, - unsigned int cmd, unsigned long data) + unsigned int cmd, unsigned long data) { return mei_ioctl(file, cmd, (unsigned long)compat_ptr(data)); } @@ -1090,6 +911,206 @@ out: return mask; } +/* + * file operations structure will be used for mei char device. + */ +static const struct file_operations mei_fops = { + .owner = THIS_MODULE, + .read = mei_read, + .unlocked_ioctl = mei_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = mei_compat_ioctl, +#endif + .open = mei_open, + .release = mei_release, + .write = mei_write, + .poll = mei_poll, + .llseek = no_llseek +}; + + +/* + * Misc Device Struct + */ +static struct miscdevice mei_misc_device = { + .name = MEI_DRIVER_NAME, + .fops = &mei_fops, + .minor = MISC_DYNAMIC_MINOR, +}; + +/** + * mei_probe - Device Initialization Routine + * + * @pdev: PCI device structure + * @ent: entry in kcs_pci_tbl + * + * returns 0 on success, <0 on failure. + */ +static int __devinit mei_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct mei_device *dev; + int err; + + mutex_lock(&mei_mutex); + if (mei_device) { + err = -EEXIST; + goto end; + } + /* enable pci dev */ + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR "mei: Failed to enable pci device.\n"); + goto end; + } + /* set PCI host mastering */ + pci_set_master(pdev); + /* pci request regions for mei driver */ + err = pci_request_regions(pdev, mei_driver_name); + if (err) { + printk(KERN_ERR "mei: Failed to get pci regions.\n"); + goto disable_device; + } + /* allocates and initializes the mei dev structure */ + dev = mei_device_init(pdev); + if (!dev) { + err = -ENOMEM; + goto release_regions; + } + /* mapping IO device memory */ + dev->mem_addr = pci_iomap(pdev, 0, 0); + if (!dev->mem_addr) { + printk(KERN_ERR "mei: mapping I/O device memory failure.\n"); + err = -ENOMEM; + goto free_device; + } + pci_enable_msi(pdev); + + /* request and enable interrupt */ + if (pci_dev_msi_enabled(pdev)) + err = request_threaded_irq(pdev->irq, + NULL, + mei_interrupt_thread_handler, + 0, mei_driver_name, dev); + else + err = request_threaded_irq(pdev->irq, + mei_interrupt_quick_handler, + mei_interrupt_thread_handler, + IRQF_SHARED, mei_driver_name, dev); + + if (err) { + printk(KERN_ERR "mei: request_threaded_irq failure. irq = %d\n", + pdev->irq); + goto unmap_memory; + } + INIT_DELAYED_WORK(&dev->timer_work, mei_timer); + if (mei_hw_init(dev)) { + printk(KERN_ERR "mei: Init hw failure.\n"); + err = -ENODEV; + goto release_irq; + } + + err = misc_register(&mei_misc_device); + if (err) + goto release_irq; + + mei_device = pdev; + pci_set_drvdata(pdev, dev); + + + schedule_delayed_work(&dev->timer_work, HZ); + + mutex_unlock(&mei_mutex); + + pr_debug("mei: Driver initialization successful.\n"); + + return 0; + +release_irq: + /* disable interrupts */ + dev->host_hw_state = mei_hcsr_read(dev); + mei_disable_interrupts(dev); + flush_scheduled_work(); + free_irq(pdev->irq, dev); + pci_disable_msi(pdev); +unmap_memory: + pci_iounmap(pdev, dev->mem_addr); +free_device: + kfree(dev); +release_regions: + pci_release_regions(pdev); +disable_device: + pci_disable_device(pdev); +end: + mutex_unlock(&mei_mutex); + printk(KERN_ERR "mei: Driver initialization failed.\n"); + return err; +} + +/** + * mei_remove - Device Removal Routine + * + * @pdev: PCI device structure + * + * mei_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. + */ +static void __devexit mei_remove(struct pci_dev *pdev) +{ + struct mei_device *dev; + + if (mei_device != pdev) + return; + + dev = pci_get_drvdata(pdev); + if (!dev) + return; + + mutex_lock(&dev->device_lock); + + mei_wd_stop(dev, false); + + mei_device = NULL; + + if (dev->iamthif_cl.state == MEI_FILE_CONNECTED) { + dev->iamthif_cl.state = MEI_FILE_DISCONNECTING; + mei_disconnect_host_client(dev, &dev->iamthif_cl); + } + if (dev->wd_cl.state == MEI_FILE_CONNECTED) { + dev->wd_cl.state = MEI_FILE_DISCONNECTING; + mei_disconnect_host_client(dev, &dev->wd_cl); + } + + /* Unregistering watchdog device */ + mei_watchdog_unregister(dev); + + /* remove entry if already in list */ + dev_dbg(&pdev->dev, "list del iamthif and wd file list.\n"); + mei_remove_client_from_file_list(dev, dev->wd_cl.host_client_id); + mei_remove_client_from_file_list(dev, dev->iamthif_cl.host_client_id); + + dev->iamthif_current_cb = NULL; + dev->me_clients_num = 0; + + mutex_unlock(&dev->device_lock); + + flush_scheduled_work(); + + /* disable interrupts */ + mei_disable_interrupts(dev); + + free_irq(pdev->irq, dev); + pci_disable_msi(pdev); + pci_set_drvdata(pdev, NULL); + + if (dev->mem_addr) + pci_iounmap(pdev, dev->mem_addr); + + kfree(dev); + + pci_release_regions(pdev); + pci_disable_device(pdev); +} #ifdef CONFIG_PM static int mei_pci_suspend(struct device *device) { @@ -1173,131 +1194,6 @@ static struct pci_driver mei_driver = { .driver.pm = MEI_PM_OPS, }; -/* - * file operations structure will be used for mei char device. - */ -static const struct file_operations mei_fops = { - .owner = THIS_MODULE, - .read = mei_read, - .unlocked_ioctl = mei_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = mei_compat_ioctl, -#endif - .open = mei_open, - .release = mei_release, - .write = mei_write, - .poll = mei_poll, -}; - -/** - * mei_registration_cdev - sets up the cdev structure for mei device. - * - * @dev: char device struct - * @hminor: minor number for registration char device - * @fops: file operations structure - * - * returns 0 on success, <0 on failure. - */ -static int mei_registration_cdev(struct cdev *dev, int hminor, - const struct file_operations *fops) -{ - int ret, devno = MKDEV(mei_major, hminor); - - cdev_init(dev, fops); - dev->owner = THIS_MODULE; - ret = cdev_add(dev, devno, 1); - /* Fail gracefully if need be */ - if (ret) - printk(KERN_ERR "mei: Error %d registering mei device %d\n", - ret, hminor); - return ret; -} - -/** - * mei_register_cdev - registers mei char device - * - * returns 0 on success, <0 on failure. - */ -static int mei_register_cdev(void) -{ - int ret; - dev_t dev; - - /* registration of char devices */ - ret = alloc_chrdev_region(&dev, MEI_MINORS_BASE, MEI_MINORS_COUNT, - MEI_DRIVER_NAME); - if (ret) { - printk(KERN_ERR "mei: Error allocating char device region.\n"); - return ret; - } - - mei_major = MAJOR(dev); - - ret = mei_registration_cdev(&mei_cdev, MEI_MINOR_NUMBER, - &mei_fops); - if (ret) - unregister_chrdev_region(MKDEV(mei_major, MEI_MINORS_BASE), - MEI_MINORS_COUNT); - - return ret; -} - -/** - * mei_unregister_cdev - unregisters mei char device - */ -static void mei_unregister_cdev(void) -{ - cdev_del(&mei_cdev); - unregister_chrdev_region(MKDEV(mei_major, MEI_MINORS_BASE), - MEI_MINORS_COUNT); -} - -/** - * mei_sysfs_device_create - adds device entry to sysfs - * - * returns 0 on success, <0 on failure. - */ -static int mei_sysfs_device_create(void) -{ - struct class *class; - void *tmphdev; - int err; - - class = class_create(THIS_MODULE, MEI_DRIVER_NAME); - if (IS_ERR(class)) { - err = PTR_ERR(class); - printk(KERN_ERR "mei: Error creating mei class.\n"); - goto err_out; - } - - tmphdev = device_create(class, NULL, mei_cdev.dev, NULL, - MEI_DEV_NAME); - if (IS_ERR(tmphdev)) { - err = PTR_ERR(tmphdev); - goto err_destroy; - } - - mei_class = class; - return 0; - -err_destroy: - class_destroy(class); -err_out: - return err; -} - -/** - * mei_sysfs_device_remove - unregisters the device entry on sysfs - */ -static void mei_sysfs_device_remove(void) -{ - if (IS_ERR_OR_NULL(mei_class)) - return; - - device_destroy(mei_class, mei_cdev.dev); - class_destroy(mei_class); -} - /** * mei_init_module - Driver Registration Routine * @@ -1314,26 +1210,9 @@ static int __init mei_init_module(void) mei_driver_string, mei_driver_version); /* init pci module */ ret = pci_register_driver(&mei_driver); - if (ret < 0) { + if (ret < 0) printk(KERN_ERR "mei: Error registering driver.\n"); - goto end; - } - ret = mei_register_cdev(); - if (ret) - goto unregister_pci; - - ret = mei_sysfs_device_create(); - if (ret) - goto unregister_cdev; - - return ret; - -unregister_cdev: - mei_unregister_cdev(); -unregister_pci: - pci_unregister_driver(&mei_driver); -end: return ret; } @@ -1347,8 +1226,7 @@ module_init(mei_init_module); */ static void __exit mei_exit_module(void) { - mei_sysfs_device_remove(); - mei_unregister_cdev(); + misc_deregister(&mei_misc_device); pci_unregister_driver(&mei_driver); pr_debug("mei: Driver unloaded successfully.\n"); diff --git a/drivers/staging/mei/mei.txt b/drivers/staging/mei/mei.txt index 17302ad2531f..516bfe7319a6 100644 --- a/drivers/staging/mei/mei.txt +++ b/drivers/staging/mei/mei.txt @@ -1,78 +1,74 @@ -Intel MEI +Intel(R) Management Engine Interface (Intel(R) MEI) ======================= Introduction ======================= -The Intel Management Engine (Intel ME) is an isolated and -protected computing resource (Coprocessor) residing inside -Intel chipsets. The Intel ME provides support for computer/IT -management features. -The Feature set depends on the Intel chipset SKU. +The Intel Management Engine (Intel ME) is an isolated andprotected computing +resource (Co-processor) residing inside certain Intel chipsets. The Intel ME +provides support for computer/IT management features. The feature set +depends on the Intel chipset SKU. -The Intel Management Engine Interface (Intel MEI, previously known -as HECI) is the interface between the Host and Intel ME. -This interface is exposed to the host as a PCI device. -The Intel MEI Driver is in charge of the communication channel -between a host application and the ME feature. +The Intel Management Engine Interface (Intel MEI, previously known as HECI) +is the interface between the Host and Intel ME. This interface is exposed +to the host as a PCI device. The Intel MEI Driver is in charge of the +communication channel between a host application and the Intel ME feature. -Each Intel ME feature (Intel ME Client) is addressed by -GUID/UUID and each feature defines its own protocol. -The protocol is message-based with a header and payload up to -512 bytes. +Each Intel ME feature (Intel ME Client) is addressed by a GUID/UUID and +each client has its own protocol. The protocol is message-based with a +header and payload up to 512 bytes. -[place holder to URL to protocol definitions] - -Prominent usage of the Interface is to communicate with -Intel Active Management Technology (Intel AMT) -implemented in firmware running on the Intel ME. +Prominent usage of the Intel ME Interface is to communicate with Intel(R) +Active Management Technology (Intel AMT)implemented in firmware running on +the Intel ME. Intel AMT provides the ability to manage a host remotely out-of-band (OOB) -even when the host processor has crashed or is in a sleep state. +even when the operating system running on the host processor has crashed or +is in a sleep state. Some examples of Intel AMT usage are: - Monitoring hardware state and platform components - - Remote power off/on (useful for green computing or overnight IT maintenance) + - Remote power off/on (useful for green computing or overnight IT + maintenance) - OS updates - Storage of useful platform information such as software assets - - built-in hardware KVM - - selective network isolation of Ethernet and IP protocol flows based on - policies set by a remote management console + - Built-in hardware KVM + - Selective network isolation of Ethernet and IP protocol flows based + on policies set by a remote management console - IDE device redirection from remote management console Intel AMT (OOB) communication is based on SOAP (deprecated -starting with Release 6.0) over HTTP/HTTPS or WS-Management protocol -over HTTP and HTTPS that are received from a remote -management console application. +starting with Release 6.0) over HTTP/S or WS-Management protocol over +HTTP/S that are received from a remote management console application. For more information about Intel AMT: -http://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/WordDocuments/aboutintelamt.htm - +http://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide -MEI Driver +Intel MEI Driver ======================= -The driver exposes a character device called /dev/mei. +The driver exposes a misc device called /dev/mei. -An application maintains communication with an ME feature while -/dev/mei is open. The binding to a specific features is performed -by calling MEI_CONNECT_CLIENT_IOCTL, which passes the desired UUID. -The number of instances of an ME feature that can be opened -at the same time depends on the ME feature, but most of the +An application maintains communication with an Intel ME feature while +/dev/mei is open. The binding to a specific features is performed by calling +MEI_CONNECT_CLIENT_IOCTL, which passes the desired UUID. +The number of instances of an Intel ME feature that can be opened +at the same time depends on the Intel ME feature, but most of the features allow only a single instance. - -The Intel AMT Host Interface (AMTHI) feature requires multiple -simultaneous user applications, therefore the MEI driver handles +The Intel AMT Host Interface (Intel AMTHI) feature supports multiple +simultaneous user applications. Therefore, the Intel MEI driver handles this internally by maintaining request queues for the applications. -The driver is oblivious to data that are passed between +The driver is oblivious to data that is passed between firmware feature +and host application. -Because some of the ME features can change the system -configuration, the driver by default allows only privileged +Because some of the Intel ME features can change the system +configuration, the driver by default allows only a privileged user to access it. -A Code snippet for application communicating with AMTHI client: +A code snippet for an application communicating with +Intel AMTHI client: struct mei_connect_client_data data; fd = open(MEI_DEVICE); @@ -80,7 +76,7 @@ A Code snippet for application communicating with AMTHI client: ioctl(fd, IOCTL_MEI_CONNECT_CLIENT, &data); - printf(“Ver=%d, MaxLen=%ld\n”, + printf("Ver=%d, MaxLen=%ld\n", data.d.in_client_uuid.protocol_version, data.d.in_client_uuid.max_msg_length); @@ -95,76 +91,106 @@ A Code snippet for application communicating with AMTHI client: [...] close(fd); -ME Applications: +IOCTL: +====== +The Intel MEI Driver supports the following IOCTL command: + IOCTL_MEI_CONNECT_CLIENT Connect to firmware Feature (client). + + usage: + struct mei_connect_client_data clientData; + ioctl(fd, IOCTL_MEI_CONNECT_CLIENT, &clientData); + + inputs: + mei_connect_client_data struct contain the following + input field: + + in_client_uuid - UUID of the FW Feature that needs + to connect to. + outputs: + out_client_properties - Client Properties: MTU and Protocol Version. + + error returns: + EINVAL Wrong IOCTL Number + ENODEV Device or Connection is not initialized or ready. + (e.g. Wrong UUID) + ENOMEM Unable to allocate memory to client internal data. + EFAULT Fatal Error (e.g. Unable to access user input data) + EBUSY Connection Already Open + + Notes: + max_msg_length (MTU) in client properties describes the maximum + data that can be sent or received. (e.g. if MTU=2K, can send + requests up to bytes 2k and received responses upto 2k bytes). + +Intel ME Applications: ============== 1) Intel Local Management Service (Intel LMS) - Applications running locally on the platform communicate with - Intel AMT Release 2.0 and later releases in the same way - that network applications do via SOAP over HTTP (deprecated - starting with Release 6.0) or with WS-Management over SOAP over - HTTP. which means that some Intel AMT feature can be access - from a local application using same Network interface as for - remote application. - - When a local application sends a message addressed to the local - Intel AMT host name, the Local Manageability Service (LMS), - which listens for traffic directed to the host name, intercepts - the message and routes it to the Intel Management Engine Interface. + + Applications running locally on the platform communicate with Intel AMT Release + 2.0 and later releases in the same way that network applications do via SOAP + over HTTP (deprecated starting with Release 6.0) or with WS-Management over + SOAP over HTTP. This means that some Intel AMT features can be accessed from a + local application using the same network interface as a remote application + communicating with Intel AMT over the network. + + When a local application sends a message addressed to the local Intel AMT host + name, the Intel LMS, which listens for traffic directed to the host name, + intercepts the message and routes it to the Intel MEI. For more information: - http://software.intel.com/sites/manageability/AMT_Implementation_and_ - Reference_Guide/WordDocuments/localaccess1.htm - - The LMS opens a connection using the MEI driver to the LMS - FW feature using a defined UUID and then communicates with the - feature using a protocol - called Intel(R) AMT Port Forwarding Protocol (APF protocol). - The protocol is used to maintain multiple sessions with - Intel AMT from a single application. - See the protocol specification in - the Intel(R) AMT Implementation and Reference Guide - http://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/HTMLDocuments/MPSDocuments/Intel%20AMT%20Port%20Forwarding%20Protocol%20Reference%20Manual.pdf - - 2) Intel AMT Remote configuration using a Local Agent: + http://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide + Under "About Intel AMT" => "Local Access" + + For downloading Intel LMS: + http://software.intel.com/en-us/articles/download-the-latest-intel-amt-open-source-drivers/ + + The Intel LMS opens a connection using the Intel MEI driver to the Intel LMS + firmware feature using a defined UUID and then communicates with the feature + using a protocol called Intel AMT Port Forwarding Protocol(Intel APF protocol). + The protocol is used to maintain multiple sessions with Intel AMT from a + single application. + + See the protocol specification in the Intel AMT Software Development Kit(SDK) + http://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide + Under "SDK Resources" => "Intel(R) vPro(TM) Gateway(MPS)" + => "Information for Intel(R) vPro(TM) Gateway Developers" + => "Description of the Intel AMT Port Forwarding (APF)Protocol" + + 2) Intel AMT Remote configuration using a Local Agent A Local Agent enables IT personnel to configure Intel AMT out-of-the-box - without requiring installing additional data to enable setup. - The remote configuration process may involve an ISV-developed remote - configuration agent that runs on the host. + without requiring installing additional data to enable setup. The remote + configuration process may involve an ISV-developed remote configuration + agent that runs on the host. For more information: - http://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/WordDocuments/remoteconfigurationwithalocalagent.htm + http://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide + Under "Setup and Configuration of Intel AMT" => + "SDK Tools Supporting Setup and Configuration" => + "Using the Local Agent Sample" + + An open source Intel AMT configuration utility, implementing a local agent + that accesses the Intel MEI driver, can be found here: + http://software.intel.com/en-us/articles/download-the-latest-intel-amt-open-source-drivers/ - How the Local Agent Works (including Command structs): - http://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/WordDocuments/howthelocalagentsampleworks.htm Intel AMT OS Health Watchdog: ============================= The Intel AMT Watchdog is an OS Health (Hang/Crash) watchdog. Whenever the OS hangs or crashes, Intel AMT will send an event -to whoever subscribed to this event. This mechanism means that -IT knows when a platform crashes even when there is a hard failure -on the host. -The AMT Watchdog is composed of two parts: - 1) FW Feature - that receives the heartbeats - and sends an event when the heartbeats stop. - 2) MEI driver – connects to the watchdog (WD) feature, - configures the watchdog and sends the heartbeats. - -The MEI driver configures the Watchdog to expire by default -every 120sec unless set by the user using module parameters. -The Driver then sends heartbeats every 2sec. +to any subsciber to this event. This mechanism means that +IT knows when a platform crashes even when there is a hard failureon the host. -If WD feature does not exist (i.e. the connection failed), -the MEI driver will disable the sending of heartbeats. +The Intel AMT Watchdog is composed of two parts: + 1) Firmware feature - receives the heartbeats + and sends an event when the heartbeats stop. + 2) Intel MEI driver - connects to the watchdog feature, configures the + watchdog and sends the heartbeats. -Module Parameters -================= -watchdog_timeout - the user can use this module parameter -to change the watchdog timeout setting. +The Intel MEI driver uses the kernel watchdog to configure the Intel AMT +Watchdog and to send heartbeats to it. The default timeout of the +watchdog is 120 seconds. -This value sets the Intel AMT watchdog timeout interval in seconds; -the default value is 120sec. -in order to disable the watchdog activites set the value to 0. -Normal values should be between 120 and 65535 +If the Intel AMT Watchdog feature does not exist (i.e. the connection failed), +the Intel MEI driver will disable the sending of heartbeats. Supported Chipsets: ================== diff --git a/drivers/staging/mei/mei_dev.h b/drivers/staging/mei/mei_dev.h index af4b1af9eeac..82bacfc624c5 100644 --- a/drivers/staging/mei/mei_dev.h +++ b/drivers/staging/mei/mei_dev.h @@ -23,13 +23,6 @@ #include "hw.h" /* - * MEI Char Driver Minors - */ -#define MEI_MINORS_BASE 1 -#define MEI_MINORS_COUNT 1 -#define MEI_MINOR_NUMBER 1 - -/* * watch dog definition */ #define MEI_WATCHDOG_DATA_SIZE 16 @@ -42,11 +35,6 @@ */ extern struct pci_dev *mei_device; -/* - * AMT Watchdog Device - */ -#define INTEL_AMT_WATCHDOG_ID "INTCAMT" -extern struct watchdog_device amt_wd_dev; /* * AMTHI Client UUID @@ -175,7 +163,6 @@ struct mei_cl { struct mei_io_list { struct mei_cl_cb mei_cb; - int status; }; /* MEI private device struct */ diff --git a/drivers/staging/mei/wd.c b/drivers/staging/mei/wd.c index ffca7ca32658..8094941a98f1 100644 --- a/drivers/staging/mei/wd.c +++ b/drivers/staging/mei/wd.c @@ -35,12 +35,16 @@ const u8 mei_wd_state_independence_msg[3][4] = { {0x07, 0x02, 0x01, 0x10} }; +/* + * AMT Watchdog Device + */ +#define INTEL_AMT_WATCHDOG_ID "INTCAMT" + /* UUIDs for AMT F/W clients */ const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB); - void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) { dev_dbg(&dev->pdev->dev, "timeout=%d.\n", timeout); @@ -331,14 +335,14 @@ static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, unsigned int t /* * Watchdog Device structs */ -const struct watchdog_ops wd_ops = { +static const struct watchdog_ops wd_ops = { .owner = THIS_MODULE, .start = mei_wd_ops_start, .stop = mei_wd_ops_stop, .ping = mei_wd_ops_ping, .set_timeout = mei_wd_ops_set_timeout, }; -const struct watchdog_info wd_info = { +static const struct watchdog_info wd_info = { .identity = INTEL_AMT_WATCHDOG_ID, .options = WDIOF_KEEPALIVEPING, }; @@ -352,3 +356,25 @@ struct watchdog_device amt_wd_dev = { }; +void mei_watchdog_register(struct mei_device *dev) +{ + dev_dbg(&dev->pdev->dev, "dev->wd_timeout =%d.\n", dev->wd_timeout); + + dev->wd_due_counter = !!dev->wd_timeout; + + if (watchdog_register_device(&amt_wd_dev)) { + dev_err(&dev->pdev->dev, "unable to register watchdog device.\n"); + dev->wd_interface_reg = false; + } else { + dev_dbg(&dev->pdev->dev, "successfully register watchdog interface.\n"); + dev->wd_interface_reg = true; + } +} + +void mei_watchdog_unregister(struct mei_device *dev) +{ + if (dev->wd_interface_reg) + watchdog_unregister_device(&amt_wd_dev); + dev->wd_interface_reg = false; +} + |