diff options
Diffstat (limited to 'drivers/misc/mei/main.c')
-rw-r--r-- | drivers/misc/mei/main.c | 536 |
1 files changed, 135 insertions, 401 deletions
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 43fb52ff98a..903f809b21f 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -37,79 +37,11 @@ #include <linux/interrupt.h> #include <linux/miscdevice.h> -#include "mei_dev.h" #include <linux/mei.h> -#include "interface.h" - -/* AMT device is a singleton on the platform */ -static struct pci_dev *mei_pdev; - -/* 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)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G965)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GM965)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GME965)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q35)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82G33)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q33)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82X38)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_3200)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_6)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_7)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_8)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_9)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_10)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_2)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_3)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_2)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_3)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_2)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_CPT_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PBG_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)}, - - /* required last entry */ - {0, } -}; - -MODULE_DEVICE_TABLE(pci, mei_pci_tbl); -static DEFINE_MUTEX(mei_mutex); - - -/** - * find_read_list_entry - find read list entry - * - * @dev: device structure - * @file: pointer to file structure - * - * returns cb on success, NULL on error - */ -static struct mei_cl_cb *find_read_list_entry( - struct mei_device *dev, - struct mei_cl *cl) -{ - 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(pos, next, &dev->read_list.list, list) - if (mei_cl_cmp_id(cl, pos->cl)) - return pos; - return NULL; -} +#include "mei_dev.h" +#include "hw-me.h" +#include "client.h" /** * mei_open - the open function @@ -121,16 +53,20 @@ static struct mei_cl_cb *find_read_list_entry( */ static int mei_open(struct inode *inode, struct file *file) { + struct miscdevice *misc = file->private_data; + struct pci_dev *pdev; struct mei_cl *cl; struct mei_device *dev; - unsigned long cl_id; + int err; err = -ENODEV; - if (!mei_pdev) + if (!misc->parent) goto out; - dev = pci_get_drvdata(mei_pdev); + pdev = container_of(misc->parent, struct pci_dev, dev); + + dev = pci_get_drvdata(pdev); if (!dev) goto out; @@ -153,24 +89,9 @@ static int mei_open(struct inode *inode, struct file *file) goto out_unlock; } - cl_id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX); - if (cl_id >= MEI_CLIENTS_MAX) { - dev_err(&dev->pdev->dev, "client_id exceded %d", - MEI_CLIENTS_MAX) ; + err = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); + if (err) 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); - cl->state = MEI_FILE_INITIALIZING; - cl->sm_state = 0; file->private_data = cl; mutex_unlock(&dev->device_lock); @@ -216,7 +137,7 @@ static int mei_release(struct inode *inode, struct file *file) "ME client = %d\n", cl->host_client_id, cl->me_client_id); - rets = mei_disconnect_host_client(dev, cl); + rets = mei_cl_disconnect(cl); } mei_cl_flush_queues(cl); dev_dbg(&dev->pdev->dev, "remove client host client = %d, ME client = %d\n", @@ -227,12 +148,13 @@ static int mei_release(struct inode *inode, struct file *file) clear_bit(cl->host_client_id, dev->host_clients_map); dev->open_handle_count--; } - mei_me_cl_unlink(dev, cl); + mei_cl_unlink(cl); + /* free read cb */ cb = NULL; if (cl->read_cb) { - cb = find_read_list_entry(dev, cl); + cb = mei_cl_find_read_cb(cl); /* Remove entry from read list */ if (cb) list_del(&cb->list); @@ -322,7 +244,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - err = mei_start_read(dev, cl); + err = mei_cl_read_start(cl); if (err && err != -EBUSY) { dev_dbg(&dev->pdev->dev, "mei start read failure with status = %d\n", err); @@ -393,14 +315,13 @@ copy_buffer: goto out; free: - cb_pos = find_read_list_entry(dev, cl); + cb_pos = mei_cl_find_read_cb(cl); /* Remove entry from read list */ if (cb_pos) list_del(&cb_pos->list); mei_io_cb_free(cb); cl->reading_state = MEI_IDLE; cl->read_cb = NULL; - cl->read_pending = 0; out: dev_dbg(&dev->pdev->dev, "end mei read rets= %d\n", rets); mutex_unlock(&dev->device_lock); @@ -475,16 +396,15 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, /* free entry used in read */ if (cl->reading_state == MEI_READ_COMPLETE) { *offset = 0; - write_cb = find_read_list_entry(dev, cl); + write_cb = mei_cl_find_read_cb(cl); if (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; - cl->read_pending = 0; } - } else if (cl->reading_state == MEI_IDLE && !cl->read_pending) + } else if (cl->reading_state == MEI_IDLE) *offset = 0; @@ -519,7 +439,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, if (rets) { dev_err(&dev->pdev->dev, - "amthi write failed with status = %d\n", rets); + "amthif write failed with status = %d\n", rets); goto err; } mutex_unlock(&dev->device_lock); @@ -530,20 +450,20 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, dev_dbg(&dev->pdev->dev, "host client = %d, ME client = %d\n", cl->host_client_id, cl->me_client_id); - rets = mei_flow_ctrl_creds(dev, cl); + rets = mei_cl_flow_ctrl_creds(cl); if (rets < 0) goto err; - if (rets == 0 || dev->mei_host_buffer_is_empty == false) { + if (rets == 0 || !dev->hbuf_is_ready) { write_cb->buf_idx = 0; mei_hdr.msg_complete = 0; cl->writing_state = MEI_WRITING; goto out; } - dev->mei_host_buffer_is_empty = false; - if (length > mei_hbuf_max_data(dev)) { - mei_hdr.length = mei_hbuf_max_data(dev); + dev->hbuf_is_ready = false; + if (length > mei_hbuf_max_len(dev)) { + mei_hdr.length = mei_hbuf_max_len(dev); mei_hdr.msg_complete = 0; } else { mei_hdr.length = length; @@ -552,10 +472,10 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, 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)) { + + dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n", + MEI_HDR_PRM(&mei_hdr)); + if (mei_write_message(dev, &mei_hdr, write_cb->request_buffer.data)) { rets = -ENODEV; goto err; } @@ -564,7 +484,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, out: if (mei_hdr.msg_complete) { - if (mei_flow_ctrl_reduce(dev, cl)) { + if (mei_cl_flow_ctrl_reduce(cl)) { rets = -ENODEV; goto err; } @@ -582,6 +502,103 @@ err: return rets; } +/** + * mei_ioctl_connect_client - the connect to fw client IOCTL function + * + * @dev: the device structure + * @data: IOCTL connect data, input and output parameters + * @file: private data of the file object + * + * Locking: called under "dev->device_lock" lock + * + * returns 0 on success, <0 on failure. + */ +static int mei_ioctl_connect_client(struct file *file, + struct mei_connect_client_data *data) +{ + struct mei_device *dev; + struct mei_client *client; + struct mei_cl *cl; + int i; + int rets; + + cl = file->private_data; + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + if (dev->dev_state != MEI_DEV_ENABLED) { + rets = -ENODEV; + goto end; + } + + if (cl->state != MEI_FILE_INITIALIZING && + cl->state != MEI_FILE_DISCONNECTED) { + rets = -EBUSY; + goto end; + } + + /* find ME client we're trying to connect to */ + i = mei_me_cl_by_uuid(dev, &data->in_client_uuid); + if (i >= 0 && !dev->me_clients[i].props.fixed_address) { + cl->me_client_id = dev->me_clients[i].client_id; + cl->state = MEI_FILE_CONNECTING; + } + + dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n", + cl->me_client_id); + dev_dbg(&dev->pdev->dev, "FW Client - Protocol Version = %d\n", + dev->me_clients[i].props.protocol_version); + dev_dbg(&dev->pdev->dev, "FW Client - Max Msg Len = %d\n", + dev->me_clients[i].props.max_msg_length); + + /* if we're connecting to amthif client then we will use the + * existing connection + */ + if (uuid_le_cmp(data->in_client_uuid, mei_amthif_guid) == 0) { + dev_dbg(&dev->pdev->dev, "FW Client is amthi\n"); + if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) { + rets = -ENODEV; + goto end; + } + clear_bit(cl->host_client_id, dev->host_clients_map); + mei_cl_unlink(cl); + + kfree(cl); + cl = NULL; + file->private_data = &dev->iamthif_cl; + + client = &data->out_client_properties; + client->max_msg_length = + dev->me_clients[i].props.max_msg_length; + client->protocol_version = + dev->me_clients[i].props.protocol_version; + rets = dev->iamthif_cl.status; + + goto end; + } + + if (cl->state != MEI_FILE_CONNECTING) { + rets = -ENODEV; + goto end; + } + + + /* prepare the output buffer */ + client = &data->out_client_properties; + client->max_msg_length = dev->me_clients[i].props.max_msg_length; + client->protocol_version = dev->me_clients[i].props.protocol_version; + dev_dbg(&dev->pdev->dev, "Can connect?\n"); + + + rets = mei_cl_connect(cl, file); + +end: + dev_dbg(&dev->pdev->dev, "free connect cb memory."); + return rets; +} + /** * mei_ioctl - the IOCTL function @@ -630,6 +647,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) rets = -EFAULT; goto out; } + rets = mei_ioctl_connect_client(file, connect_data); /* if all is ok, copying the data back to user. */ @@ -726,7 +744,6 @@ static const struct file_operations mei_fops = { .llseek = no_llseek }; - /* * Misc Device Struct */ @@ -736,300 +753,17 @@ static struct miscdevice mei_misc_device = { .minor = MISC_DYNAMIC_MINOR, }; -/** - * mei_quirk_probe - probe for devices that doesn't valid ME interface - * @pdev: PCI device structure - * @ent: entry into pci_device_table - * - * returns true if ME Interface is valid, false otherwise - */ -static bool mei_quirk_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +int mei_register(struct device *dev) { - u32 reg; - if (ent->device == MEI_DEV_ID_PBG_1) { - pci_read_config_dword(pdev, 0x48, ®); - /* make sure that bit 9 is up and bit 10 is down */ - if ((reg & 0x600) == 0x200) { - dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n"); - return false; - } - } - return true; -} -/** - * mei_probe - Device Initialization Routine - * - * @pdev: PCI device structure - * @ent: entry in kcs_pci_tbl - * - * returns 0 on success, <0 on failure. - */ -static int mei_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct mei_device *dev; - int err; - - mutex_lock(&mei_mutex); - - if (!mei_quirk_probe(pdev, ent)) { - err = -ENODEV; - goto end; - } - - if (mei_pdev) { - err = -EEXIST; - goto end; - } - /* enable pci dev */ - err = pci_enable_device(pdev); - if (err) { - dev_err(&pdev->dev, "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, KBUILD_MODNAME); - if (err) { - dev_err(&pdev->dev, "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) { - dev_err(&pdev->dev, "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, - IRQF_ONESHOT, KBUILD_MODNAME, dev); - else - err = request_threaded_irq(pdev->irq, - mei_interrupt_quick_handler, - mei_interrupt_thread_handler, - IRQF_SHARED, KBUILD_MODNAME, dev); - - if (err) { - dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n", - pdev->irq); - 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; - goto release_irq; - } - - err = misc_register(&mei_misc_device); - if (err) - goto release_irq; - - mei_pdev = pdev; - pci_set_drvdata(pdev, dev); - - - schedule_delayed_work(&dev->timer_work, HZ); - - mutex_unlock(&mei_mutex); - - pr_debug("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); -disable_msi: - pci_disable_msi(pdev); - 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); - dev_err(&pdev->dev, "initialization failed.\n"); - return err; + mei_misc_device.parent = dev; + return misc_register(&mei_misc_device); } -/** - * 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 mei_remove(struct pci_dev *pdev) +void mei_deregister(void) { - struct mei_device *dev; - - if (mei_pdev != pdev) - return; - - dev = pci_get_drvdata(pdev); - if (!dev) - return; - - mutex_lock(&dev->device_lock); - - cancel_delayed_work(&dev->timer_work); - - mei_wd_stop(dev); - - mei_pdev = 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_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; - - 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); - misc_deregister(&mei_misc_device); -} -#ifdef CONFIG_PM -static int mei_pci_suspend(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - struct mei_device *dev = pci_get_drvdata(pdev); - int err; - - if (!dev) - return -ENODEV; - mutex_lock(&dev->device_lock); - - cancel_delayed_work(&dev->timer_work); - - /* Stop watchdog if exists */ - err = mei_wd_stop(dev); - /* Set new mei state */ - if (dev->dev_state == MEI_DEV_ENABLED || - dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) { - dev->dev_state = MEI_DEV_POWER_DOWN; - mei_reset(dev, 0); - } - mutex_unlock(&dev->device_lock); - - free_irq(pdev->irq, dev); - pci_disable_msi(pdev); - - return err; + mei_misc_device.parent = NULL; } -static int mei_pci_resume(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - struct mei_device *dev; - int err; - - dev = pci_get_drvdata(pdev); - if (!dev) - return -ENODEV; - - 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, - IRQF_ONESHOT, KBUILD_MODNAME, dev); - else - err = request_threaded_irq(pdev->irq, - mei_interrupt_quick_handler, - mei_interrupt_thread_handler, - IRQF_SHARED, KBUILD_MODNAME, dev); - - if (err) { - dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n", - pdev->irq); - return err; - } - - mutex_lock(&dev->device_lock); - dev->dev_state = MEI_DEV_POWER_UP; - mei_reset(dev, 1); - mutex_unlock(&dev->device_lock); - - /* Start timer if stopped in suspend */ - schedule_delayed_work(&dev->timer_work, HZ); - - return err; -} -static SIMPLE_DEV_PM_OPS(mei_pm_ops, mei_pci_suspend, mei_pci_resume); -#define MEI_PM_OPS (&mei_pm_ops) -#else -#define MEI_PM_OPS NULL -#endif /* CONFIG_PM */ -/* - * PCI driver structure - */ -static struct pci_driver mei_driver = { - .name = KBUILD_MODNAME, - .id_table = mei_pci_tbl, - .probe = mei_probe, - .remove = mei_remove, - .shutdown = mei_remove, - .driver.pm = MEI_PM_OPS, -}; - -module_pci_driver(mei_driver); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) Management Engine Interface"); MODULE_LICENSE("GPL v2"); + |