static int mei_osver(struct mei_cl_device *cldev) { const size_t size = MKHI_OSVER_BUF_LEN; char buf[MKHI_OSVER_BUF_LEN]; struct mkhi_msg *req; struct mkhi_fwcaps *fwcaps; struct mei_os_ver *os_ver; unsigned int mode = MEI_CL_IO_TX_BLOCKING | MEI_CL_IO_TX_INTERNAL; memset(buf, 0, size); req = (struct mkhi_msg *)buf; req->hdr.group_id = MKHI_FWCAPS_GROUP_ID; req->hdr.command = MKHI_FWCAPS_SET_OS_VER_APP_RULE_CMD; fwcaps = (struct mkhi_fwcaps *)req->data; fwcaps->id.rule_type = 0x0; fwcaps->id.feature_id = MKHI_FEATURE_PTT; fwcaps->len = sizeof(*os_ver); os_ver = (struct mei_os_ver *)fwcaps->data; os_ver->os_type = OSTYPE_LINUX; return __mei_cl_send(cldev->cl, buf, size, mode); }
/** * whitelist - forcefully whitelist client * * @cldev: me clients device */ static void whitelist(struct mei_cl_device *cldev) { dev_dbg(&cldev->dev, "running hook %s\n", __func__); cldev->do_match = 1; } #define OSTYPE_LINUX 2 struct mei_os_ver { __le16 build; __le16 reserved1; u8 os_type; u8 major; u8 minor; u8 reserved2; } __packed; #define MKHI_FEATURE_PTT 0x10 struct mkhi_rule_id { __le16 rule_type; u8 feature_id; u8 reserved; } __packed; struct mkhi_fwcaps { struct mkhi_rule_id id; u8 len; u8 data[0]; } __packed; struct mkhi_fw_ver_block { u16 minor; u8 major; u8 platform; u16 buildno; u16 hotfix; } __packed; struct mkhi_fw_ver { struct mkhi_fw_ver_block ver[MEI_MAX_FW_VER_BLOCKS]; } __packed; #define MKHI_FWCAPS_GROUP_ID 0x3 #define MKHI_FWCAPS_SET_OS_VER_APP_RULE_CMD 6 #define MKHI_GEN_GROUP_ID 0xFF #define MKHI_GEN_GET_FW_VERSION_CMD 0x2 struct mkhi_msg_hdr { u8 group_id; u8 command; u8 reserved; u8 result; } __packed; struct mkhi_msg { struct mkhi_msg_hdr hdr; u8 data[0]; } __packed; #define MKHI_OSVER_BUF_LEN (sizeof(struct mkhi_msg_hdr) + \ sizeof(struct mkhi_fwcaps) + \ sizeof(struct mei_os_ver)) static int mei_osver(struct mei_cl_device *cldev) { const size_t size = MKHI_OSVER_BUF_LEN; char buf[MKHI_OSVER_BUF_LEN]; struct mkhi_msg *req; struct mkhi_fwcaps *fwcaps; struct mei_os_ver *os_ver; unsigned int mode = MEI_CL_IO_TX_BLOCKING | MEI_CL_IO_TX_INTERNAL; memset(buf, 0, size); req = (struct mkhi_msg *)buf; req->hdr.group_id = MKHI_FWCAPS_GROUP_ID; req->hdr.command = MKHI_FWCAPS_SET_OS_VER_APP_RULE_CMD; fwcaps = (struct mkhi_fwcaps *)req->data; fwcaps->id.rule_type = 0x0; fwcaps->id.feature_id = MKHI_FEATURE_PTT; fwcaps->len = sizeof(*os_ver); os_ver = (struct mei_os_ver *)fwcaps->data; os_ver->os_type = OSTYPE_LINUX; return __mei_cl_send(cldev->cl, buf, size, mode); } #define MKHI_FWVER_BUF_LEN (sizeof(struct mkhi_msg_hdr) + \ sizeof(struct mkhi_fw_ver)) #define MKHI_FWVER_LEN(__num) (sizeof(struct mkhi_msg_hdr) + \ sizeof(struct mkhi_fw_ver_block) * (__num)) #define MKHI_RCV_TIMEOUT 500 /* receive timeout in msec */ static int mei_fwver(struct mei_cl_device *cldev) { char buf[MKHI_FWVER_BUF_LEN]; struct mkhi_msg *req; struct mkhi_fw_ver *fwver; int bytes_recv, ret, i; memset(buf, 0, sizeof(buf)); req = (struct mkhi_msg *)buf; req->hdr.group_id = MKHI_GEN_GROUP_ID; req->hdr.command = MKHI_GEN_GET_FW_VERSION_CMD; ret = __mei_cl_send(cldev->cl, buf, sizeof(struct mkhi_msg_hdr), MEI_CL_IO_TX_BLOCKING); if (ret < 0) { dev_err(&cldev->dev, "Could not send ReqFWVersion cmd\n"); return ret; } ret = 0; bytes_recv = __mei_cl_recv(cldev->cl, buf, sizeof(buf), 0, MKHI_RCV_TIMEOUT); if (bytes_recv < 0 || (size_t)bytes_recv < MKHI_FWVER_LEN(1)) { /* * Should be at least one version block, * error out if nothing found */ dev_err(&cldev->dev, "Could not read FW version\n"); return -EIO; } fwver = (struct mkhi_fw_ver *)req->data; memset(cldev->bus->fw_ver, 0, sizeof(cldev->bus->fw_ver)); for (i = 0; i < MEI_MAX_FW_VER_BLOCKS; i++) { if ((size_t)bytes_recv < MKHI_FWVER_LEN(i + 1)) break; dev_dbg(&cldev->dev, "FW version%d %d:%d.%d.%d.%d\n", i, fwver->ver[i].platform, fwver->ver[i].major, fwver->ver[i].minor, fwver->ver[i].hotfix, fwver->ver[i].buildno); cldev->bus->fw_ver[i].platform = fwver->ver[i].platform; cldev->bus->fw_ver[i].major = fwver->ver[i].major; cldev->bus->fw_ver[i].minor = fwver->ver[i].minor; cldev->bus->fw_ver[i].hotfix = fwver->ver[i].hotfix; cldev->bus->fw_ver[i].buildno = fwver->ver[i].buildno; } return ret; }
static int mei_nfc_if_version(struct mei_nfc_dev *ndev) { struct mei_device *dev; struct mei_cl *cl; struct mei_nfc_cmd cmd; struct mei_nfc_reply *reply = NULL; struct mei_nfc_if_version *version; size_t if_version_length; int bytes_recv, ret; cl = ndev->cl_info; dev = cl->dev; memset(&cmd, 0, sizeof(struct mei_nfc_cmd)); cmd.command = MEI_NFC_CMD_MAINTENANCE; cmd.data_size = 1; cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION; ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd)); if (ret < 0) { dev_err(dev->dev, "Could not send IF version cmd\n"); return ret; } /* to be sure on the stack we alloc memory */ if_version_length = sizeof(struct mei_nfc_reply) + sizeof(struct mei_nfc_if_version); reply = kzalloc(if_version_length, GFP_KERNEL); if (!reply) return -ENOMEM; bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length); if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { dev_err(dev->dev, "Could not read IF version\n"); ret = -EIO; goto err; } version = (struct mei_nfc_if_version *)reply->data; ndev->fw_ivn = version->fw_ivn; ndev->vendor_id = version->vendor_id; ndev->radio_type = version->radio_type; err: kfree(reply); return ret; }
static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length) { struct mei_device *dev; struct mei_nfc_dev *ndev; struct mei_nfc_hci_hdr *hdr; u8 *mei_buf; int err; ndev = (struct mei_nfc_dev *) cldev->priv_data; dev = ndev->cl->dev; mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL); if (!mei_buf) return -ENOMEM; hdr = (struct mei_nfc_hci_hdr *) mei_buf; hdr->cmd = MEI_NFC_CMD_HCI_SEND; hdr->status = 0; hdr->req_id = ndev->req_id; hdr->reserved = 0; hdr->data_size = length; memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); err = __mei_cl_send(ndev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE); if (err < 0) return err; kfree(mei_buf); if (!wait_event_interruptible_timeout(ndev->send_wq, ndev->recv_req_id == ndev->req_id, HZ)) { dev_err(&dev->pdev->dev, "NFC MEI command timeout\n"); err = -ETIMEDOUT; } else { ndev->req_id++; } return err; }
static int mei_nfc_connect(struct mei_nfc_dev *ndev) { struct mei_device *dev; struct mei_cl *cl; struct mei_nfc_cmd *cmd, *reply; struct mei_nfc_connect *connect; struct mei_nfc_connect_resp *connect_resp; size_t connect_length, connect_resp_length; int bytes_recv, ret; cl = ndev->cl; dev = cl->dev; connect_length = sizeof(struct mei_nfc_cmd) + sizeof(struct mei_nfc_connect); connect_resp_length = sizeof(struct mei_nfc_cmd) + sizeof(struct mei_nfc_connect_resp); cmd = kzalloc(connect_length, GFP_KERNEL); if (!cmd) return -ENOMEM; connect = (struct mei_nfc_connect *)cmd->data; reply = kzalloc(connect_resp_length, GFP_KERNEL); if (!reply) { kfree(cmd); return -ENOMEM; } connect_resp = (struct mei_nfc_connect_resp *)reply->data; cmd->command = MEI_NFC_CMD_MAINTENANCE; cmd->data_size = 3; cmd->sub_command = MEI_NFC_SUBCMD_CONNECT; connect->fw_ivn = ndev->fw_ivn; connect->vendor_id = ndev->vendor_id; ret = __mei_cl_send(cl, (u8 *)cmd, connect_length); if (ret < 0) { dev_err(dev->dev, "Could not send connect cmd\n"); goto err; } bytes_recv = __mei_cl_recv(cl, (u8 *)reply, connect_resp_length); if (bytes_recv < 0) { dev_err(dev->dev, "Could not read connect response\n"); ret = bytes_recv; goto err; } dev_info(dev->dev, "IVN 0x%x Vendor ID 0x%x\n", connect_resp->fw_ivn, connect_resp->vendor_id); dev_info(dev->dev, "ME FW %d.%d.%d.%d\n", connect_resp->me_major, connect_resp->me_minor, connect_resp->me_hotfix, connect_resp->me_build); ret = 0; err: kfree(reply); kfree(cmd); return ret; }