1/** 2 * Marvell Bluetooth driver: debugfs related functions 3 * 4 * Copyright (C) 2009, Marvell International Ltd. 5 * 6 * This software file (the "File") is distributed by Marvell International 7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991 8 * (the "License"). You may use, redistribute and/or modify this File in 9 * accordance with the terms and conditions of the License, a copy of which 10 * is available by writing to the Free Software Foundation, Inc., 11 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the 12 * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 13 * 14 * 15 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 17 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 18 * this warranty disclaimer. 19 **/ 20 21#include <linux/debugfs.h> 22#include <linux/slab.h> 23 24#include <net/bluetooth/bluetooth.h> 25#include <net/bluetooth/hci_core.h> 26 27#include "btmrvl_drv.h" 28 29struct btmrvl_debugfs_data { 30 struct dentry *config_dir; 31 struct dentry *status_dir; 32}; 33 34static ssize_t btmrvl_hscfgcmd_write(struct file *file, 35 const char __user *ubuf, size_t count, loff_t *ppos) 36{ 37 struct btmrvl_private *priv = file->private_data; 38 char buf[16]; 39 long result, ret; 40 41 memset(buf, 0, sizeof(buf)); 42 43 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 44 return -EFAULT; 45 46 ret = kstrtol(buf, 10, &result); 47 if (ret) 48 return ret; 49 50 priv->btmrvl_dev.hscfgcmd = result; 51 52 if (priv->btmrvl_dev.hscfgcmd) { 53 btmrvl_prepare_command(priv); 54 wake_up_interruptible(&priv->main_thread.wait_q); 55 } 56 57 return count; 58} 59 60static ssize_t btmrvl_hscfgcmd_read(struct file *file, char __user *userbuf, 61 size_t count, loff_t *ppos) 62{ 63 struct btmrvl_private *priv = file->private_data; 64 char buf[16]; 65 int ret; 66 67 ret = snprintf(buf, sizeof(buf) - 1, "%d\n", 68 priv->btmrvl_dev.hscfgcmd); 69 70 return simple_read_from_buffer(userbuf, count, ppos, buf, ret); 71} 72 73static const struct file_operations btmrvl_hscfgcmd_fops = { 74 .read = btmrvl_hscfgcmd_read, 75 .write = btmrvl_hscfgcmd_write, 76 .open = simple_open, 77 .llseek = default_llseek, 78}; 79 80static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf, 81 size_t count, loff_t *ppos) 82{ 83 struct btmrvl_private *priv = file->private_data; 84 char buf[16]; 85 long result, ret; 86 87 memset(buf, 0, sizeof(buf)); 88 89 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 90 return -EFAULT; 91 92 ret = kstrtol(buf, 10, &result); 93 if (ret) 94 return ret; 95 96 priv->btmrvl_dev.pscmd = result; 97 98 if (priv->btmrvl_dev.pscmd) { 99 btmrvl_prepare_command(priv); 100 wake_up_interruptible(&priv->main_thread.wait_q); 101 } 102 103 return count; 104 105} 106 107static ssize_t btmrvl_pscmd_read(struct file *file, char __user *userbuf, 108 size_t count, loff_t *ppos) 109{ 110 struct btmrvl_private *priv = file->private_data; 111 char buf[16]; 112 int ret; 113 114 ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.pscmd); 115 116 return simple_read_from_buffer(userbuf, count, ppos, buf, ret); 117} 118 119static const struct file_operations btmrvl_pscmd_fops = { 120 .read = btmrvl_pscmd_read, 121 .write = btmrvl_pscmd_write, 122 .open = simple_open, 123 .llseek = default_llseek, 124}; 125 126static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf, 127 size_t count, loff_t *ppos) 128{ 129 struct btmrvl_private *priv = file->private_data; 130 char buf[16]; 131 long result, ret; 132 133 memset(buf, 0, sizeof(buf)); 134 135 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 136 return -EFAULT; 137 138 ret = kstrtol(buf, 10, &result); 139 if (ret) 140 return ret; 141 142 priv->btmrvl_dev.hscmd = result; 143 if (priv->btmrvl_dev.hscmd) { 144 btmrvl_prepare_command(priv); 145 wake_up_interruptible(&priv->main_thread.wait_q); 146 } 147 148 return count; 149} 150 151static ssize_t btmrvl_hscmd_read(struct file *file, char __user *userbuf, 152 size_t count, loff_t *ppos) 153{ 154 struct btmrvl_private *priv = file->private_data; 155 char buf[16]; 156 int ret; 157 158 ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hscmd); 159 160 return simple_read_from_buffer(userbuf, count, ppos, buf, ret); 161} 162 163static const struct file_operations btmrvl_hscmd_fops = { 164 .read = btmrvl_hscmd_read, 165 .write = btmrvl_hscmd_write, 166 .open = simple_open, 167 .llseek = default_llseek, 168}; 169 170static ssize_t btmrvl_fwdump_write(struct file *file, const char __user *ubuf, 171 size_t count, loff_t *ppos) 172{ 173 struct btmrvl_private *priv = file->private_data; 174 char buf[16]; 175 bool result; 176 177 memset(buf, 0, sizeof(buf)); 178 179 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 180 return -EFAULT; 181 182 if (strtobool(buf, &result)) 183 return -EINVAL; 184 185 if (!result) 186 return -EINVAL; 187 188 btmrvl_firmware_dump(priv); 189 190 return count; 191} 192 193static const struct file_operations btmrvl_fwdump_fops = { 194 .write = btmrvl_fwdump_write, 195 .open = simple_open, 196 .llseek = default_llseek, 197}; 198 199void btmrvl_debugfs_init(struct hci_dev *hdev) 200{ 201 struct btmrvl_private *priv = hci_get_drvdata(hdev); 202 struct btmrvl_debugfs_data *dbg; 203 204 if (!hdev->debugfs) 205 return; 206 207 dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); 208 priv->debugfs_data = dbg; 209 210 if (!dbg) { 211 BT_ERR("Can not allocate memory for btmrvl_debugfs_data."); 212 return; 213 } 214 215 dbg->config_dir = debugfs_create_dir("config", hdev->debugfs); 216 217 debugfs_create_u8("psmode", 0644, dbg->config_dir, 218 &priv->btmrvl_dev.psmode); 219 debugfs_create_file("pscmd", 0644, dbg->config_dir, 220 priv, &btmrvl_pscmd_fops); 221 debugfs_create_x16("gpiogap", 0644, dbg->config_dir, 222 &priv->btmrvl_dev.gpio_gap); 223 debugfs_create_u8("hsmode", 0644, dbg->config_dir, 224 &priv->btmrvl_dev.hsmode); 225 debugfs_create_file("hscmd", 0644, dbg->config_dir, 226 priv, &btmrvl_hscmd_fops); 227 debugfs_create_file("hscfgcmd", 0644, dbg->config_dir, 228 priv, &btmrvl_hscfgcmd_fops); 229 debugfs_create_file("fw_dump", 0200, dbg->config_dir, 230 priv, &btmrvl_fwdump_fops); 231 232 dbg->status_dir = debugfs_create_dir("status", hdev->debugfs); 233 debugfs_create_u8("curpsmode", 0444, dbg->status_dir, 234 &priv->adapter->psmode); 235 debugfs_create_u8("psstate", 0444, dbg->status_dir, 236 &priv->adapter->ps_state); 237 debugfs_create_u8("hsstate", 0444, dbg->status_dir, 238 &priv->adapter->hs_state); 239 debugfs_create_u8("txdnldready", 0444, dbg->status_dir, 240 &priv->btmrvl_dev.tx_dnld_rdy); 241} 242 243void btmrvl_debugfs_remove(struct hci_dev *hdev) 244{ 245 struct btmrvl_private *priv = hci_get_drvdata(hdev); 246 struct btmrvl_debugfs_data *dbg = priv->debugfs_data; 247 248 if (!dbg) 249 return; 250 251 debugfs_remove_recursive(dbg->config_dir); 252 debugfs_remove_recursive(dbg->status_dir); 253 254 kfree(dbg); 255} 256