/** * mei_wd_host_init - connect to the watchdog client * * @dev: the device structure * @me_cl: me client * * Return: -ENOTTY if wd client cannot be found * -EIO if write has failed * 0 on success */ int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl) { struct mei_cl *cl = &dev->wd_cl; int ret; mei_cl_init(cl, dev); dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; dev->wd_state = MEI_WD_IDLE; ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); if (ret < 0) { dev_info(dev->dev, "wd: failed link client\n"); return ret; } ret = mei_cl_connect(cl, me_cl, NULL); if (ret) { dev_err(dev->dev, "wd: failed to connect = %d\n", ret); mei_cl_unlink(cl); return ret; } ret = mei_watchdog_register(dev); if (ret) { mei_cl_disconnect(cl); mei_cl_unlink(cl); } return ret; }
static void mei_nfc_free(struct mei_nfc_dev *ndev) { if (ndev->cl) { list_del(&ndev->cl->device_link); mei_cl_unlink(ndev->cl); kfree(ndev->cl); } if (ndev->cl_info) { list_del(&ndev->cl_info->device_link); mei_cl_unlink(ndev->cl_info); kfree(ndev->cl_info); } }
/** * mei_reset - resets host and fw. * * @dev: the device structure * @interrupts_enabled: if interrupt should be enabled after reset. */ void mei_reset(struct mei_device *dev, int interrupts_enabled) { bool unexpected; if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) return; unexpected = (dev->dev_state != MEI_DEV_INITIALIZING && dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN && dev->dev_state != MEI_DEV_POWER_UP); mei_hw_reset(dev, interrupts_enabled); if (dev->dev_state != MEI_DEV_INITIALIZING) { if (dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN) dev->dev_state = MEI_DEV_RESETING; mei_cl_all_disconnect(dev); /* remove entry if already in list */ dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); mei_cl_unlink(&dev->wd_cl); if (dev->open_handle_count > 0) dev->open_handle_count--; mei_cl_unlink(&dev->iamthif_cl); if (dev->open_handle_count > 0) dev->open_handle_count--; mei_amthif_reset_params(dev); memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); } dev->me_clients_num = 0; dev->rd_msg_hdr = 0; dev->wd_pending = false; if (unexpected) dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", mei_dev_state_str(dev->dev_state)); /* wake up all readings so they can be interrupted */ mei_cl_all_read_wakeup(dev); /* remove all waiting requests */ mei_cl_all_write_clear(dev); }
/** * mei_release - the release function * * @inode: pointer to inode structure * @file: pointer to file structure * * Return: 0 on success, <0 on error */ static int mei_release(struct inode *inode, struct file *file) { struct mei_cl *cl = file->private_data; struct mei_device *dev; int rets; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; dev = cl->dev; mutex_lock(&dev->device_lock); if (cl == &dev->iamthif_cl) { rets = mei_amthif_release(dev, file); goto out; } rets = mei_cl_disconnect(cl); mei_cl_flush_queues(cl, file); cl_dbg(dev, cl, "removing\n"); mei_cl_unlink(cl); file->private_data = NULL; kfree(cl); out: mutex_unlock(&dev->device_lock); return rets; }
/** * mei_wd_host_init - connect to the watchdog client * * @dev: the device structure * * returns -ENENT if wd client cannot be found * -EIO if write has failed * 0 on success */ int mei_wd_host_init(struct mei_device *dev) { struct mei_cl *cl = &dev->wd_cl; int i; int ret; mei_cl_init(cl, dev); dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; dev->wd_state = MEI_WD_IDLE; /* check for valid client id */ i = mei_me_cl_by_uuid(dev, &mei_wd_guid); if (i < 0) { dev_info(&dev->pdev->dev, "wd: failed to find the client\n"); return -ENOENT; } cl->me_client_id = dev->me_clients[i].client_id; ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); if (ret < 0) { dev_info(&dev->pdev->dev, "wd: failed link client\n"); return -ENOENT; } cl->state = MEI_FILE_CONNECTING; ret = mei_cl_connect(cl, NULL); if (ret) { dev_err(&dev->pdev->dev, "wd: failed to connect = %d\n", ret); mei_cl_unlink(cl); return ret; } ret = mei_watchdog_register(dev); if (ret) { mei_cl_disconnect(cl); mei_cl_unlink(cl); } return ret; }
/** * mei_release - the release function * * @inode: pointer to inode structure * @file: pointer to file structure * * returns 0 on success, <0 on error */ static int mei_release(struct inode *inode, struct file *file) { struct mei_cl *cl = file->private_data; struct mei_cl_cb *cb; struct mei_device *dev; int rets = 0; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; dev = cl->dev; mutex_lock(&dev->device_lock); 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_cl_disconnect(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); mei_cl_unlink(cl); /* free read cb */ cb = NULL; if (cl->read_cb) { cb = mei_cl_find_read_cb(cl); /* Remove entry from read list */ if (cb) list_del(&cb->list); cb = cl->read_cb; cl->read_cb = NULL; } file->private_data = NULL; mei_io_cb_free(cb); kfree(cl); out: mutex_unlock(&dev->device_lock); 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) { dev_dbg(&dev->pdev->dev, "Cannot connect to FW Client UUID = %pUl\n", &data->in_client_uuid); rets = -ENOTTY; goto end; } cl->me_client_id = dev->me_clients[i].client_id; 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; } mei_cl_unlink(cl); kfree(cl); cl = NULL; dev->iamthif_open_count++; 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; } /* 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: return rets; }
/** * mei_reset - resets host and fw. * * @dev: the device structure */ int mei_reset(struct mei_device *dev) { enum mei_dev_state state = dev->dev_state; bool interrupts_enabled; int ret; if (state != MEI_DEV_INITIALIZING && state != MEI_DEV_DISABLED && state != MEI_DEV_POWER_DOWN && state != MEI_DEV_POWER_UP) dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", mei_dev_state_str(state)); /* we're already in reset, cancel the init timer * if the reset was called due the hbm protocol error * we need to call it before hw start * so the hbm watchdog won't kick in */ mei_hbm_idle(dev); /* enter reset flow */ interrupts_enabled = state != MEI_DEV_POWER_DOWN; dev->dev_state = MEI_DEV_RESETTING; dev->reset_count++; if (dev->reset_count > MEI_MAX_CONSEC_RESET) { dev_err(&dev->pdev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); dev->dev_state = MEI_DEV_DISABLED; return -ENODEV; } ret = mei_hw_reset(dev, interrupts_enabled); /* fall through and remove the sw state even if hw reset has failed */ /* no need to clean up software state in case of power up */ if (state != MEI_DEV_INITIALIZING && state != MEI_DEV_POWER_UP) { /* remove all waiting requests */ mei_cl_all_write_clear(dev); mei_cl_all_disconnect(dev); /* wake up all readers and writers so they can be interrupted */ mei_cl_all_wakeup(dev); /* remove entry if already in list */ dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); mei_cl_unlink(&dev->wd_cl); mei_cl_unlink(&dev->iamthif_cl); mei_amthif_reset_params(dev); } dev->me_clients_num = 0; dev->rd_msg_hdr = 0; dev->wd_pending = false; if (ret) { dev_err(&dev->pdev->dev, "hw_reset failed ret = %d\n", ret); return ret; } if (state == MEI_DEV_POWER_DOWN) { dev_dbg(&dev->pdev->dev, "powering down: end of reset\n"); dev->dev_state = MEI_DEV_DISABLED; return 0; } ret = mei_hw_start(dev); if (ret) { dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret); return ret; } dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); dev->dev_state = MEI_DEV_INIT_CLIENTS; ret = mei_hbm_start_req(dev); if (ret) { dev_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret); dev->dev_state = MEI_DEV_RESETTING; return ret; } return 0; }
/** * mei_ioctl_connect_client - the connect to fw client IOCTL function * * @file: private data of the file object * @data: IOCTL connect data, input and output parameters * * Locking: called under "dev->device_lock" lock * * Return: 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_me_client *me_cl; struct mei_cl *cl; int rets; cl = file->private_data; dev = cl->dev; if (dev->dev_state != MEI_DEV_ENABLED) return -ENODEV; if (cl->state != MEI_FILE_INITIALIZING && cl->state != MEI_FILE_DISCONNECTED) return -EBUSY; /* find ME client we're trying to connect to */ me_cl = mei_me_cl_by_uuid(dev, &data->in_client_uuid); if (!me_cl) { dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n", &data->in_client_uuid); rets = -ENOTTY; goto end; } if (me_cl->props.fixed_address) { bool forbidden = dev->override_fixed_address ? !dev->allow_fixed_address : !dev->hbm_f_fa_supported; if (forbidden) { dev_dbg(dev->dev, "Connection forbidden to FW Client UUID = %pUl\n", &data->in_client_uuid); rets = -ENOTTY; goto end; } } dev_dbg(dev->dev, "Connect to FW Client ID = %d\n", me_cl->client_id); dev_dbg(dev->dev, "FW Client - Protocol Version = %d\n", me_cl->props.protocol_version); dev_dbg(dev->dev, "FW Client - Max Msg Len = %d\n", me_cl->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->dev, "FW Client is amthi\n"); if (!mei_cl_is_connected(&dev->iamthif_cl)) { rets = -ENODEV; goto end; } mei_cl_unlink(cl); kfree(cl); cl = NULL; dev->iamthif_open_count++; file->private_data = &dev->iamthif_cl; client = &data->out_client_properties; client->max_msg_length = me_cl->props.max_msg_length; client->protocol_version = me_cl->props.protocol_version; rets = dev->iamthif_cl.status; goto end; } /* prepare the output buffer */ client = &data->out_client_properties; client->max_msg_length = me_cl->props.max_msg_length; client->protocol_version = me_cl->props.protocol_version; dev_dbg(dev->dev, "Can connect?\n"); rets = mei_cl_connect(cl, me_cl, file); end: mei_me_cl_put(me_cl); return rets; }
/** * mei_reset - resets host and fw. * * @dev: the device structure * @interrupts_enabled: if interrupt should be enabled after reset. */ void mei_reset(struct mei_device *dev, int interrupts_enabled) { bool unexpected; unexpected = (dev->dev_state != MEI_DEV_INITIALIZING && dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN && dev->dev_state != MEI_DEV_POWER_UP); mei_hw_reset(dev, interrupts_enabled); dev->hbm_state = MEI_HBM_IDLE; if (dev->dev_state != MEI_DEV_INITIALIZING && dev->dev_state != MEI_DEV_POWER_UP) { if (dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN) dev->dev_state = MEI_DEV_RESETTING; mei_cl_all_disconnect(dev); /* remove entry if already in list */ dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); mei_cl_unlink(&dev->wd_cl); if (dev->open_handle_count > 0) dev->open_handle_count--; mei_cl_unlink(&dev->iamthif_cl); if (dev->open_handle_count > 0) dev->open_handle_count--; mei_amthif_reset_params(dev); memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); } dev->me_clients_num = 0; dev->rd_msg_hdr = 0; dev->wd_pending = false; if (unexpected) dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", mei_dev_state_str(dev->dev_state)); if (!interrupts_enabled) { dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); return; } mei_hw_start(dev); dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); /* link is established * start sending messages. */ dev->dev_state = MEI_DEV_INIT_CLIENTS; mei_hbm_start_req(dev); /* wake up all readings so they can be interrupted */ mei_cl_all_read_wakeup(dev); /* remove all waiting requests */ mei_cl_all_write_clear(dev); }