diff options
Diffstat (limited to 'drivers/misc/mei/main.c')
-rw-r--r-- | drivers/misc/mei/main.c | 462 |
1 files changed, 145 insertions, 317 deletions
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index e8b0858132c..43fb52ff98a 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -90,93 +90,6 @@ static DEFINE_MUTEX(mei_mutex); /** - * mei_clear_list - removes all callbacks associated with file - * from mei_cb_list - * - * @dev: device structure. - * @file: file structure - * @mei_cb_list: callbacks list - * - * mei_clear_list is called to clear resources associated with file - * when application calls close function or Ctrl-C was pressed - * - * returns true if callback removed from the list, false otherwise - */ -static bool mei_clear_list(struct mei_device *dev, - struct file *file, struct list_head *mei_cb_list) -{ - struct mei_cl_cb *cb_pos = NULL; - struct mei_cl_cb *cb_next = NULL; - struct file *file_temp; - bool removed = false; - - /* list all list member */ - list_for_each_entry_safe(cb_pos, cb_next, mei_cb_list, cb_list) { - file_temp = (struct file *)cb_pos->file_object; - /* check if list member associated with a file */ - if (file_temp == file) { - /* remove member from the list */ - list_del(&cb_pos->cb_list); - /* check if cb equal to current iamthif cb */ - if (dev->iamthif_current_cb == cb_pos) { - dev->iamthif_current_cb = NULL; - /* send flow control to iamthif client */ - mei_send_flow_control(dev, &dev->iamthif_cl); - } - /* free all allocated buffers */ - mei_free_cb_private(cb_pos); - cb_pos = NULL; - removed = true; - } - } - return removed; -} - -/** - * mei_clear_lists - removes all callbacks associated with file - * - * @dev: device structure - * @file: file structure - * - * mei_clear_lists is called to clear resources associated with file - * when application calls close function or Ctrl-C was pressed - * - * returns true if callback removed from the list, false otherwise - */ -static bool mei_clear_lists(struct mei_device *dev, struct file *file) -{ - bool removed = false; - - /* remove callbacks associated with a file */ - mei_clear_list(dev, file, &dev->amthi_cmd_list.mei_cb.cb_list); - if (mei_clear_list(dev, file, - &dev->amthi_read_complete_list.mei_cb.cb_list)) - removed = true; - - mei_clear_list(dev, file, &dev->ctrl_rd_list.mei_cb.cb_list); - - if (mei_clear_list(dev, file, &dev->ctrl_wr_list.mei_cb.cb_list)) - removed = true; - - if (mei_clear_list(dev, file, &dev->write_waiting_list.mei_cb.cb_list)) - removed = true; - - if (mei_clear_list(dev, file, &dev->write_list.mei_cb.cb_list)) - removed = true; - - /* check if iamthif_current_cb not NULL */ - if (dev->iamthif_current_cb && !removed) { - /* check file and iamthif current cb association */ - if (dev->iamthif_current_cb->file_object == file) { - /* remove cb */ - mei_free_cb_private(dev->iamthif_current_cb); - dev->iamthif_current_cb = NULL; - removed = true; - } - } - return removed; -} -/** * find_read_list_entry - find read list entry * * @dev: device structure @@ -192,14 +105,9 @@ static struct mei_cl_cb *find_read_list_entry( struct mei_cl_cb *next = NULL; 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)) + list_for_each_entry_safe(pos, next, &dev->read_list.list, list) + if (mei_cl_cmp_id(cl, pos->cl)) return pos; - } return NULL; } @@ -297,67 +205,51 @@ static int mei_release(struct inode *inode, struct file *file) dev = cl->dev; mutex_lock(&dev->device_lock); - if (cl != &dev->iamthif_cl) { - if (cl->state == MEI_FILE_CONNECTED) { - cl->state = MEI_FILE_DISCONNECTING; - dev_dbg(&dev->pdev->dev, - "disconnecting client host client = %d, " - "ME client = %d\n", - cl->host_client_id, - cl->me_client_id); - rets = mei_disconnect_host_client(dev, cl); - } - mei_cl_flush_queues(cl); - dev_dbg(&dev->pdev->dev, "remove client host client = %d, ME client = %d\n", + if (cl == &dev->iamthif_cl) { + rets = mei_amthif_release(dev, file); + goto out; + } + if (cl->state == MEI_FILE_CONNECTED) { + cl->state = MEI_FILE_DISCONNECTING; + dev_dbg(&dev->pdev->dev, + "disconnecting client host client = %d, " + "ME client = %d\n", cl->host_client_id, cl->me_client_id); + rets = mei_disconnect_host_client(dev, cl); + } + mei_cl_flush_queues(cl); + dev_dbg(&dev->pdev->dev, "remove client host client = %d, ME client = %d\n", + cl->host_client_id, + cl->me_client_id); + + if (dev->open_handle_count > 0) { + clear_bit(cl->host_client_id, dev->host_clients_map); + dev->open_handle_count--; + } + mei_me_cl_unlink(dev, cl); - if (dev->open_handle_count > 0) { - 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); - - /* free read cb */ - cb = NULL; - if (cl->read_cb) { - cb = find_read_list_entry(dev, cl); - /* Remove entry from read list */ - if (cb) - list_del(&cb->cb_list); - - cb = cl->read_cb; - cl->read_cb = NULL; - } - - file->private_data = NULL; - - if (cb) { - mei_free_cb_private(cb); - cb = NULL; - } + /* free read cb */ + cb = NULL; + if (cl->read_cb) { + cb = find_read_list_entry(dev, cl); + /* Remove entry from read list */ + if (cb) + list_del(&cb->list); - kfree(cl); - } else { - if (dev->open_handle_count > 0) - dev->open_handle_count--; - - if (dev->iamthif_file_object == file && - dev->iamthif_state != MEI_IAMTHIF_IDLE) { - - dev_dbg(&dev->pdev->dev, "amthi canceled iamthif state %d\n", - dev->iamthif_state); - dev->iamthif_canceled = true; - if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) { - dev_dbg(&dev->pdev->dev, "run next amthi iamthif cb\n"); - mei_run_next_iamthif_cmd(dev); - } - } + cb = cl->read_cb; + cl->read_cb = NULL; + } - if (mei_clear_lists(dev, file)) - dev->iamthif_state = MEI_IAMTHIF_IDLE; + file->private_data = NULL; + if (cb) { + mei_io_cb_free(cb); + cb = NULL; } + + kfree(cl); +out: mutex_unlock(&dev->device_lock); return rets; } @@ -411,20 +303,19 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, } if (cl == &dev->iamthif_cl) { - rets = amthi_read(dev, file, ubuf, length, offset); + rets = mei_amthif_read(dev, file, ubuf, length, offset); goto out; } - if (cl->read_cb && cl->read_cb->information > *offset) { + if (cl->read_cb && cl->read_cb->buf_idx > *offset) { cb = cl->read_cb; goto copy_buffer; - } else if (cl->read_cb && cl->read_cb->information > 0 && - cl->read_cb->information <= *offset) { + } else if (cl->read_cb && cl->read_cb->buf_idx > 0 && + cl->read_cb->buf_idx <= *offset) { cb = cl->read_cb; rets = 0; goto free; - } else if ((!cl->read_cb || !cl->read_cb->information) && - *offset > 0) { + } else if ((!cl->read_cb || !cl->read_cb->buf_idx) && *offset > 0) { /*Offset needs to be cleaned for contiguous reads*/ *offset = 0; rets = 0; @@ -481,16 +372,15 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, copy_buffer: dev_dbg(&dev->pdev->dev, "cb->response_buffer size - %d\n", cb->response_buffer.size); - dev_dbg(&dev->pdev->dev, "cb->information - %lu\n", - cb->information); - if (length == 0 || ubuf == NULL || *offset > cb->information) { + dev_dbg(&dev->pdev->dev, "cb->buf_idx - %lu\n", cb->buf_idx); + if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) { rets = -EMSGSIZE; goto free; } - /* length is being truncated to PAGE_SIZE, however, */ - /* information size may be longer */ - length = min_t(size_t, length, (cb->information - *offset)); + /* length is being truncated to PAGE_SIZE, + * however buf_idx may point beyond that */ + length = min_t(size_t, length, cb->buf_idx - *offset); if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) { rets = -EFAULT; @@ -499,15 +389,15 @@ copy_buffer: rets = length; *offset += length; - if ((unsigned long)*offset < cb->information) + if ((unsigned long)*offset < cb->buf_idx) goto out; free: cb_pos = find_read_list_entry(dev, cl); /* Remove entry from read list */ if (cb_pos) - list_del(&cb_pos->cb_list); - mei_free_cb_private(cb); + list_del(&cb_pos->list); + mei_io_cb_free(cb); cl->reading_state = MEI_IDLE; cl->read_cb = NULL; cl->read_pending = 0; @@ -516,7 +406,6 @@ out: mutex_unlock(&dev->device_lock); return rets; } - /** * mei_write - the write function. * @@ -546,23 +435,39 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { - mutex_unlock(&dev->device_lock); - return -ENODEV; + rets = -ENODEV; + goto err; } + i = mei_me_cl_by_id(dev, cl->me_client_id); + if (i < 0) { + rets = -ENODEV; + goto err; + } + if (length > dev->me_clients[i].props.max_msg_length || length <= 0) { + rets = -EMSGSIZE; + goto err; + } + + if (cl->state != MEI_FILE_CONNECTED) { + rets = -ENODEV; + dev_err(&dev->pdev->dev, "host client = %d, is not connected to ME client = %d", + cl->host_client_id, cl->me_client_id); + goto err; + } if (cl == &dev->iamthif_cl) { - write_cb = find_amthi_read_list_entry(dev, file); + write_cb = mei_amthif_find_read_list_entry(dev, file); if (write_cb) { timeout = write_cb->read_time + - msecs_to_jiffies(IAMTHIF_READ_TIMER); + mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); if (time_after(jiffies, timeout) || - cl->reading_state == MEI_READ_COMPLETE) { - *offset = 0; - list_del(&write_cb->cb_list); - mei_free_cb_private(write_cb); - write_cb = NULL; + cl->reading_state == MEI_READ_COMPLETE) { + *offset = 0; + list_del(&write_cb->list); + mei_io_cb_free(write_cb); + write_cb = NULL; } } } @@ -572,8 +477,8 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, *offset = 0; write_cb = find_read_list_entry(dev, cl); if (write_cb) { - list_del(&write_cb->cb_list); - mei_free_cb_private(write_cb); + list_del(&write_cb->list); + mei_io_cb_free(write_cb); write_cb = NULL; cl->reading_state = MEI_IDLE; cl->read_cb = NULL; @@ -583,24 +488,21 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, *offset = 0; - write_cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); + write_cb = mei_io_cb_init(cl, file); if (!write_cb) { - mutex_unlock(&dev->device_lock); - return -ENOMEM; + dev_err(&dev->pdev->dev, "write cb allocation failed\n"); + rets = -ENOMEM; + goto err; } + rets = mei_io_cb_alloc_req_buf(write_cb, length); + if (rets) + goto err; - write_cb->file_object = file; - write_cb->file_private = cl; - write_cb->request_buffer.data = kmalloc(length, GFP_KERNEL); - rets = -ENOMEM; - if (!write_cb->request_buffer.data) - goto unlock_dev; - - dev_dbg(&dev->pdev->dev, "length =%d\n", (int) length); + dev_dbg(&dev->pdev->dev, "cb request size = %zd\n", length); - rets = -EFAULT; - if (copy_from_user(write_cb->request_buffer.data, ubuf, length)) - goto unlock_dev; + rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); + if (rets) + goto err; cl->sm_state = 0; if (length == 4 && @@ -612,139 +514,71 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, write_cb->request_buffer.data, 4) == 0))) cl->sm_state |= MEI_WD_STATE_INDEPENDENCE_MSG_SENT; - INIT_LIST_HEAD(&write_cb->cb_list); if (cl == &dev->iamthif_cl) { - write_cb->response_buffer.data = - kmalloc(dev->iamthif_mtu, GFP_KERNEL); - if (!write_cb->response_buffer.data) { - rets = -ENOMEM; - goto unlock_dev; - } - if (dev->dev_state != MEI_DEV_ENABLED) { - rets = -ENODEV; - goto unlock_dev; - } - i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id); - if (i < 0) { - rets = -ENODEV; - goto unlock_dev; - } - if (length > dev->me_clients[i].props.max_msg_length || - length <= 0) { - rets = -EMSGSIZE; - goto unlock_dev; - } + rets = mei_amthif_write(dev, write_cb); - write_cb->response_buffer.size = dev->iamthif_mtu; - write_cb->major_file_operations = MEI_IOCTL; - write_cb->information = 0; - write_cb->request_buffer.size = length; - if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) { - rets = -ENODEV; - goto unlock_dev; - } - - if (!list_empty(&dev->amthi_cmd_list.mei_cb.cb_list) || - dev->iamthif_state != MEI_IAMTHIF_IDLE) { - dev_dbg(&dev->pdev->dev, "amthi_state = %d\n", - (int) dev->iamthif_state); - dev_dbg(&dev->pdev->dev, "add amthi cb to amthi cmd waiting list\n"); - list_add_tail(&write_cb->cb_list, - &dev->amthi_cmd_list.mei_cb.cb_list); - rets = length; - } else { - dev_dbg(&dev->pdev->dev, "call amthi write\n"); - rets = amthi_write(dev, write_cb); - - if (rets) { - dev_dbg(&dev->pdev->dev, "amthi write failed with status = %d\n", - rets); - goto unlock_dev; - } - rets = length; + if (rets) { + dev_err(&dev->pdev->dev, + "amthi write failed with status = %d\n", rets); + goto err; } mutex_unlock(&dev->device_lock); - return rets; + return length; } - write_cb->major_file_operations = MEI_WRITE; - /* make sure information is zero before we start */ - - write_cb->information = 0; - write_cb->request_buffer.size = length; + write_cb->fop_type = MEI_FOP_WRITE; dev_dbg(&dev->pdev->dev, "host client = %d, ME client = %d\n", cl->host_client_id, cl->me_client_id); - if (cl->state != MEI_FILE_CONNECTED) { - rets = -ENODEV; - dev_dbg(&dev->pdev->dev, "host client = %d, is not connected to ME client = %d", - cl->host_client_id, - cl->me_client_id); - goto unlock_dev; - } - i = mei_me_cl_by_id(dev, cl->me_client_id); - if (i < 0) { - rets = -ENODEV; - goto unlock_dev; - } - if (length > dev->me_clients[i].props.max_msg_length || length <= 0) { - rets = -EINVAL; - goto unlock_dev; - } - write_cb->file_private = cl; - rets = mei_flow_ctrl_creds(dev, cl); if (rets < 0) - goto unlock_dev; + goto err; - if (rets && dev->mei_host_buffer_is_empty) { - rets = 0; - dev->mei_host_buffer_is_empty = false; - if (length > mei_hbuf_max_data(dev)) { - mei_hdr.length = mei_hbuf_max_data(dev); - mei_hdr.msg_complete = 0; - } else { - mei_hdr.length = length; - mei_hdr.msg_complete = 1; - } - mei_hdr.host_addr = cl->host_client_id; - mei_hdr.me_addr = cl->me_client_id; - mei_hdr.reserved = 0; - dev_dbg(&dev->pdev->dev, "call mei_write_message header=%08x.\n", - *((u32 *) &mei_hdr)); - if (mei_write_message(dev, &mei_hdr, - (unsigned char *) (write_cb->request_buffer.data), - mei_hdr.length)) { - rets = -ENODEV; - goto unlock_dev; - } + if (rets == 0 || dev->mei_host_buffer_is_empty == false) { + write_cb->buf_idx = 0; + mei_hdr.msg_complete = 0; cl->writing_state = MEI_WRITING; - write_cb->information = mei_hdr.length; - if (mei_hdr.msg_complete) { - if (mei_flow_ctrl_reduce(dev, cl)) { - rets = -ENODEV; - goto unlock_dev; - } - list_add_tail(&write_cb->cb_list, - &dev->write_waiting_list.mei_cb.cb_list); - } else { - list_add_tail(&write_cb->cb_list, - &dev->write_list.mei_cb.cb_list); - } + goto out; + } + dev->mei_host_buffer_is_empty = false; + if (length > mei_hbuf_max_data(dev)) { + mei_hdr.length = mei_hbuf_max_data(dev); + mei_hdr.msg_complete = 0; } else { + mei_hdr.length = length; + mei_hdr.msg_complete = 1; + } + mei_hdr.host_addr = cl->host_client_id; + mei_hdr.me_addr = cl->me_client_id; + mei_hdr.reserved = 0; + dev_dbg(&dev->pdev->dev, "call mei_write_message header=%08x.\n", + *((u32 *) &mei_hdr)); + if (mei_write_message(dev, &mei_hdr, + write_cb->request_buffer.data, mei_hdr.length)) { + rets = -ENODEV; + goto err; + } + cl->writing_state = MEI_WRITING; + write_cb->buf_idx = mei_hdr.length; - write_cb->information = 0; - cl->writing_state = MEI_WRITING; - list_add_tail(&write_cb->cb_list, - &dev->write_list.mei_cb.cb_list); +out: + if (mei_hdr.msg_complete) { + if (mei_flow_ctrl_reduce(dev, cl)) { + rets = -ENODEV; + goto err; + } + list_add_tail(&write_cb->list, &dev->write_waiting_list.list); + } else { + list_add_tail(&write_cb->list, &dev->write_list.list); } + mutex_unlock(&dev->device_lock); return length; -unlock_dev: +err: mutex_unlock(&dev->device_lock); - mei_free_cb_private(write_cb); + mei_io_cb_free(write_cb); return rets; } @@ -860,15 +694,7 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) if (cl == &dev->iamthif_cl) { - mutex_unlock(&dev->device_lock); - poll_wait(file, &dev->iamthif_cl.wait, wait); - mutex_lock(&dev->device_lock); - if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE && - dev->iamthif_file_object == file) { - mask |= (POLLIN | POLLRDNORM); - dev_dbg(&dev->pdev->dev, "run next amthi cb\n"); - mei_run_next_iamthif_cmd(dev); - } + mask = mei_amthif_poll(dev, file, wait); goto out; } @@ -917,7 +743,7 @@ static struct miscdevice mei_misc_device = { * * returns true if ME Interface is valid, false otherwise */ -static bool __devinit mei_quirk_probe(struct pci_dev *pdev, +static bool mei_quirk_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { u32 reg; @@ -939,7 +765,7 @@ static bool __devinit mei_quirk_probe(struct pci_dev *pdev, * * returns 0 on success, <0 on failure. */ -static int __devinit mei_probe(struct pci_dev *pdev, +static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct mei_device *dev; @@ -1003,6 +829,8 @@ static int __devinit mei_probe(struct pci_dev *pdev, goto disable_msi; } INIT_DELAYED_WORK(&dev->timer_work, mei_timer); + INIT_WORK(&dev->init_work, mei_host_client_init); + if (mei_hw_init(dev)) { dev_err(&pdev->dev, "init hw failure.\n"); err = -ENODEV; @@ -1054,7 +882,7 @@ end: * 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) +static void mei_remove(struct pci_dev *pdev) { struct mei_device *dev; @@ -1087,8 +915,8 @@ static void __devexit mei_remove(struct pci_dev *pdev) /* 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); + mei_me_cl_unlink(dev, &dev->wd_cl); + mei_me_cl_unlink(dev, &dev->iamthif_cl); dev->iamthif_current_cb = NULL; dev->me_clients_num = 0; @@ -1195,8 +1023,8 @@ static struct pci_driver mei_driver = { .name = KBUILD_MODNAME, .id_table = mei_pci_tbl, .probe = mei_probe, - .remove = __devexit_p(mei_remove), - .shutdown = __devexit_p(mei_remove), + .remove = mei_remove, + .shutdown = mei_remove, .driver.pm = MEI_PM_OPS, }; |