1 /* 2 * 3 * Intel Management Engine Interface (Intel MEI) Linux driver 4 * Copyright (c) 2012-2013, Intel Corporation. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms and conditions of the GNU General Public License, 8 * version 2, as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 * 15 */ 16 #include <linux/slab.h> 17 #include <linux/kernel.h> 18 #include <linux/device.h> 19 #include <linux/debugfs.h> 20 21 #include <linux/mei.h> 22 23 #include "mei_dev.h" 24 #include "client.h" 25 #include "hw.h" 26 27 static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, 28 size_t cnt, loff_t *ppos) 29 { 30 struct mei_device *dev = fp->private_data; 31 struct mei_me_client *me_cl, *n; 32 size_t bufsz = 1; 33 char *buf; 34 int i = 0; 35 int pos = 0; 36 int ret; 37 38 #define HDR \ 39 " |id|fix| UUID |con|msg len|sb|refc|\n" 40 41 mutex_lock(&dev->device_lock); 42 43 list_for_each_entry(me_cl, &dev->me_clients, list) 44 bufsz++; 45 46 bufsz *= sizeof(HDR) + 1; 47 buf = kzalloc(bufsz, GFP_KERNEL); 48 if (!buf) { 49 mutex_unlock(&dev->device_lock); 50 return -ENOMEM; 51 } 52 53 pos += scnprintf(buf + pos, bufsz - pos, HDR); 54 55 /* if the driver is not enabled the list won't be consistent */ 56 if (dev->dev_state != MEI_DEV_ENABLED) 57 goto out; 58 59 list_for_each_entry_safe(me_cl, n, &dev->me_clients, list) { 60 61 me_cl = mei_me_cl_get(me_cl); 62 if (me_cl) { 63 pos += scnprintf(buf + pos, bufsz - pos, 64 "%2d|%2d|%3d|%pUl|%3d|%7d|%2d|%4d|\n", 65 i++, me_cl->client_id, 66 me_cl->props.fixed_address, 67 &me_cl->props.protocol_name, 68 me_cl->props.max_number_of_connections, 69 me_cl->props.max_msg_length, 70 me_cl->props.single_recv_buf, 71 atomic_read(&me_cl->refcnt.refcount)); 72 } 73 74 mei_me_cl_put(me_cl); 75 } 76 out: 77 mutex_unlock(&dev->device_lock); 78 ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); 79 kfree(buf); 80 return ret; 81 } 82 83 static const struct file_operations mei_dbgfs_fops_meclients = { 84 .open = simple_open, 85 .read = mei_dbgfs_read_meclients, 86 .llseek = generic_file_llseek, 87 }; 88 89 static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, 90 size_t cnt, loff_t *ppos) 91 { 92 struct mei_device *dev = fp->private_data; 93 struct mei_cl *cl; 94 const size_t bufsz = 1024; 95 char *buf; 96 int i = 0; 97 int pos = 0; 98 int ret; 99 100 if (!dev) 101 return -ENODEV; 102 103 buf = kzalloc(bufsz, GFP_KERNEL); 104 if (!buf) 105 return -ENOMEM; 106 107 pos += scnprintf(buf + pos, bufsz - pos, 108 " |me|host|state|rd|wr|\n"); 109 110 mutex_lock(&dev->device_lock); 111 112 /* if the driver is not enabled the list won't be consistent */ 113 if (dev->dev_state != MEI_DEV_ENABLED) 114 goto out; 115 116 list_for_each_entry(cl, &dev->file_list, link) { 117 118 pos += scnprintf(buf + pos, bufsz - pos, 119 "%2d|%2d|%4d|%5d|%2d|%2d|\n", 120 i, cl->me_client_id, cl->host_client_id, cl->state, 121 cl->reading_state, cl->writing_state); 122 i++; 123 } 124 out: 125 mutex_unlock(&dev->device_lock); 126 ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); 127 kfree(buf); 128 return ret; 129 } 130 131 static const struct file_operations mei_dbgfs_fops_active = { 132 .open = simple_open, 133 .read = mei_dbgfs_read_active, 134 .llseek = generic_file_llseek, 135 }; 136 137 static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, 138 size_t cnt, loff_t *ppos) 139 { 140 struct mei_device *dev = fp->private_data; 141 const size_t bufsz = 1024; 142 char *buf = kzalloc(bufsz, GFP_KERNEL); 143 int pos = 0; 144 int ret; 145 146 if (!buf) 147 return -ENOMEM; 148 149 pos += scnprintf(buf + pos, bufsz - pos, "dev: %s\n", 150 mei_dev_state_str(dev->dev_state)); 151 pos += scnprintf(buf + pos, bufsz - pos, "hbm: %s\n", 152 mei_hbm_state_str(dev->hbm_state)); 153 pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n", 154 mei_pg_is_enabled(dev) ? "ENABLED" : "DISABLED", 155 mei_pg_state_str(mei_pg_state(dev))); 156 ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); 157 kfree(buf); 158 return ret; 159 } 160 static const struct file_operations mei_dbgfs_fops_devstate = { 161 .open = simple_open, 162 .read = mei_dbgfs_read_devstate, 163 .llseek = generic_file_llseek, 164 }; 165 166 /** 167 * mei_dbgfs_deregister - Remove the debugfs files and directories 168 * 169 * @dev: the mei device structure 170 */ 171 void mei_dbgfs_deregister(struct mei_device *dev) 172 { 173 if (!dev->dbgfs_dir) 174 return; 175 debugfs_remove_recursive(dev->dbgfs_dir); 176 dev->dbgfs_dir = NULL; 177 } 178 179 /** 180 * mei_dbgfs_register - Add the debugfs files 181 * 182 * @dev: the mei device structure 183 * @name: the mei device name 184 * 185 * Return: 0 on success, <0 on failure. 186 */ 187 int mei_dbgfs_register(struct mei_device *dev, const char *name) 188 { 189 struct dentry *dir, *f; 190 191 dir = debugfs_create_dir(name, NULL); 192 if (!dir) 193 return -ENOMEM; 194 195 f = debugfs_create_file("meclients", S_IRUSR, dir, 196 dev, &mei_dbgfs_fops_meclients); 197 if (!f) { 198 dev_err(dev->dev, "meclients: registration failed\n"); 199 goto err; 200 } 201 f = debugfs_create_file("active", S_IRUSR, dir, 202 dev, &mei_dbgfs_fops_active); 203 if (!f) { 204 dev_err(dev->dev, "meclients: registration failed\n"); 205 goto err; 206 } 207 f = debugfs_create_file("devstate", S_IRUSR, dir, 208 dev, &mei_dbgfs_fops_devstate); 209 if (!f) { 210 dev_err(dev->dev, "devstate: registration failed\n"); 211 goto err; 212 } 213 dev->dbgfs_dir = dir; 214 return 0; 215 err: 216 mei_dbgfs_deregister(dev); 217 return -ENODEV; 218 } 219 220