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 27static 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; 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 down_read(&dev->me_clients_rwsem); 42 list_for_each_entry(me_cl, &dev->me_clients, list) 43 bufsz++; 44 45 bufsz *= sizeof(HDR) + 1; 46 buf = kzalloc(bufsz, GFP_KERNEL); 47 if (!buf) { 48 up_read(&dev->me_clients_rwsem); 49 return -ENOMEM; 50 } 51 52 pos += scnprintf(buf + pos, bufsz - pos, HDR); 53 54 /* if the driver is not enabled the list won't be consistent */ 55 if (dev->dev_state != MEI_DEV_ENABLED) 56 goto out; 57 58 list_for_each_entry(me_cl, &dev->me_clients, list) { 59 60 if (mei_me_cl_get(me_cl)) { 61 pos += scnprintf(buf + pos, bufsz - pos, 62 "%2d|%2d|%3d|%pUl|%3d|%7d|%2d|%4d|\n", 63 i++, me_cl->client_id, 64 me_cl->props.fixed_address, 65 &me_cl->props.protocol_name, 66 me_cl->props.max_number_of_connections, 67 me_cl->props.max_msg_length, 68 me_cl->props.single_recv_buf, 69 atomic_read(&me_cl->refcnt.refcount)); 70 71 mei_me_cl_put(me_cl); 72 } 73 } 74 75out: 76 up_read(&dev->me_clients_rwsem); 77 ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); 78 kfree(buf); 79 return ret; 80} 81 82static const struct file_operations mei_dbgfs_fops_meclients = { 83 .open = simple_open, 84 .read = mei_dbgfs_read_meclients, 85 .llseek = generic_file_llseek, 86}; 87 88static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, 89 size_t cnt, loff_t *ppos) 90{ 91 struct mei_device *dev = fp->private_data; 92 struct mei_cl *cl; 93 const size_t bufsz = 1024; 94 char *buf; 95 int i = 0; 96 int pos = 0; 97 int ret; 98 99 if (!dev) 100 return -ENODEV; 101 102 buf = kzalloc(bufsz, GFP_KERNEL); 103 if (!buf) 104 return -ENOMEM; 105 106 pos += scnprintf(buf + pos, bufsz - pos, 107 " |me|host|state|rd|wr|\n"); 108 109 mutex_lock(&dev->device_lock); 110 111 /* if the driver is not enabled the list won't be consistent */ 112 if (dev->dev_state != MEI_DEV_ENABLED) 113 goto out; 114 115 list_for_each_entry(cl, &dev->file_list, link) { 116 117 pos += scnprintf(buf + pos, bufsz - pos, 118 "%2d|%2d|%4d|%5d|%2d|%2d|\n", 119 i, cl->me_client_id, cl->host_client_id, cl->state, 120 !list_empty(&cl->rd_completed), cl->writing_state); 121 i++; 122 } 123out: 124 mutex_unlock(&dev->device_lock); 125 ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); 126 kfree(buf); 127 return ret; 128} 129 130static const struct file_operations mei_dbgfs_fops_active = { 131 .open = simple_open, 132 .read = mei_dbgfs_read_active, 133 .llseek = generic_file_llseek, 134}; 135 136static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, 137 size_t cnt, loff_t *ppos) 138{ 139 struct mei_device *dev = fp->private_data; 140 const size_t bufsz = 1024; 141 char *buf = kzalloc(bufsz, GFP_KERNEL); 142 int pos = 0; 143 int ret; 144 145 if (!buf) 146 return -ENOMEM; 147 148 pos += scnprintf(buf + pos, bufsz - pos, "dev: %s\n", 149 mei_dev_state_str(dev->dev_state)); 150 pos += scnprintf(buf + pos, bufsz - pos, "hbm: %s\n", 151 mei_hbm_state_str(dev->hbm_state)); 152 pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n", 153 mei_pg_is_enabled(dev) ? "ENABLED" : "DISABLED", 154 mei_pg_state_str(mei_pg_state(dev))); 155 ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); 156 kfree(buf); 157 return ret; 158} 159static const struct file_operations mei_dbgfs_fops_devstate = { 160 .open = simple_open, 161 .read = mei_dbgfs_read_devstate, 162 .llseek = generic_file_llseek, 163}; 164 165/** 166 * mei_dbgfs_deregister - Remove the debugfs files and directories 167 * 168 * @dev: the mei device structure 169 */ 170void mei_dbgfs_deregister(struct mei_device *dev) 171{ 172 if (!dev->dbgfs_dir) 173 return; 174 debugfs_remove_recursive(dev->dbgfs_dir); 175 dev->dbgfs_dir = NULL; 176} 177 178/** 179 * mei_dbgfs_register - Add the debugfs files 180 * 181 * @dev: the mei device structure 182 * @name: the mei device name 183 * 184 * Return: 0 on success, <0 on failure. 185 */ 186int mei_dbgfs_register(struct mei_device *dev, const char *name) 187{ 188 struct dentry *dir, *f; 189 190 dir = debugfs_create_dir(name, NULL); 191 if (!dir) 192 return -ENOMEM; 193 194 f = debugfs_create_file("meclients", S_IRUSR, dir, 195 dev, &mei_dbgfs_fops_meclients); 196 if (!f) { 197 dev_err(dev->dev, "meclients: registration failed\n"); 198 goto err; 199 } 200 f = debugfs_create_file("active", S_IRUSR, dir, 201 dev, &mei_dbgfs_fops_active); 202 if (!f) { 203 dev_err(dev->dev, "meclients: registration failed\n"); 204 goto err; 205 } 206 f = debugfs_create_file("devstate", S_IRUSR, dir, 207 dev, &mei_dbgfs_fops_devstate); 208 if (!f) { 209 dev_err(dev->dev, "devstate: registration failed\n"); 210 goto err; 211 } 212 dev->dbgfs_dir = dir; 213 return 0; 214err: 215 mei_dbgfs_deregister(dev); 216 return -ENODEV; 217} 218 219