1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Marvell Bluetooth driver: debugfs related functions |
4 | * |
5 | * Copyright (C) 2009, Marvell International Ltd. |
6 | **/ |
7 | |
8 | #include <linux/debugfs.h> |
9 | #include <linux/slab.h> |
10 | |
11 | #include <net/bluetooth/bluetooth.h> |
12 | #include <net/bluetooth/hci_core.h> |
13 | |
14 | #include "btmrvl_drv.h" |
15 | |
16 | struct btmrvl_debugfs_data { |
17 | struct dentry *config_dir; |
18 | struct dentry *status_dir; |
19 | }; |
20 | |
21 | static ssize_t btmrvl_hscfgcmd_write(struct file *file, |
22 | const char __user *ubuf, size_t count, loff_t *ppos) |
23 | { |
24 | struct btmrvl_private *priv = file->private_data; |
25 | long result, ret; |
26 | |
27 | ret = kstrtol_from_user(s: ubuf, count, base: 10, res: &result); |
28 | if (ret) |
29 | return ret; |
30 | |
31 | priv->btmrvl_dev.hscfgcmd = result; |
32 | |
33 | if (priv->btmrvl_dev.hscfgcmd) { |
34 | btmrvl_prepare_command(priv); |
35 | wake_up_interruptible(&priv->main_thread.wait_q); |
36 | } |
37 | |
38 | return count; |
39 | } |
40 | |
41 | static ssize_t btmrvl_hscfgcmd_read(struct file *file, char __user *userbuf, |
42 | size_t count, loff_t *ppos) |
43 | { |
44 | struct btmrvl_private *priv = file->private_data; |
45 | char buf[16]; |
46 | int ret; |
47 | |
48 | ret = snprintf(buf, size: sizeof(buf) - 1, fmt: "%d\n" , |
49 | priv->btmrvl_dev.hscfgcmd); |
50 | |
51 | return simple_read_from_buffer(to: userbuf, count, ppos, from: buf, available: ret); |
52 | } |
53 | |
54 | static const struct file_operations btmrvl_hscfgcmd_fops = { |
55 | .read = btmrvl_hscfgcmd_read, |
56 | .write = btmrvl_hscfgcmd_write, |
57 | .open = simple_open, |
58 | .llseek = default_llseek, |
59 | }; |
60 | |
61 | static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf, |
62 | size_t count, loff_t *ppos) |
63 | { |
64 | struct btmrvl_private *priv = file->private_data; |
65 | long result, ret; |
66 | |
67 | ret = kstrtol_from_user(s: ubuf, count, base: 10, res: &result); |
68 | if (ret) |
69 | return ret; |
70 | |
71 | priv->btmrvl_dev.pscmd = result; |
72 | |
73 | if (priv->btmrvl_dev.pscmd) { |
74 | btmrvl_prepare_command(priv); |
75 | wake_up_interruptible(&priv->main_thread.wait_q); |
76 | } |
77 | |
78 | return count; |
79 | |
80 | } |
81 | |
82 | static ssize_t btmrvl_pscmd_read(struct file *file, char __user *userbuf, |
83 | size_t count, loff_t *ppos) |
84 | { |
85 | struct btmrvl_private *priv = file->private_data; |
86 | char buf[16]; |
87 | int ret; |
88 | |
89 | ret = snprintf(buf, size: sizeof(buf) - 1, fmt: "%d\n" , priv->btmrvl_dev.pscmd); |
90 | |
91 | return simple_read_from_buffer(to: userbuf, count, ppos, from: buf, available: ret); |
92 | } |
93 | |
94 | static const struct file_operations btmrvl_pscmd_fops = { |
95 | .read = btmrvl_pscmd_read, |
96 | .write = btmrvl_pscmd_write, |
97 | .open = simple_open, |
98 | .llseek = default_llseek, |
99 | }; |
100 | |
101 | static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf, |
102 | size_t count, loff_t *ppos) |
103 | { |
104 | struct btmrvl_private *priv = file->private_data; |
105 | long result, ret; |
106 | |
107 | ret = kstrtol_from_user(s: ubuf, count, base: 10, res: &result); |
108 | if (ret) |
109 | return ret; |
110 | |
111 | priv->btmrvl_dev.hscmd = result; |
112 | if (priv->btmrvl_dev.hscmd) { |
113 | btmrvl_prepare_command(priv); |
114 | wake_up_interruptible(&priv->main_thread.wait_q); |
115 | } |
116 | |
117 | return count; |
118 | } |
119 | |
120 | static ssize_t btmrvl_hscmd_read(struct file *file, char __user *userbuf, |
121 | size_t count, loff_t *ppos) |
122 | { |
123 | struct btmrvl_private *priv = file->private_data; |
124 | char buf[16]; |
125 | int ret; |
126 | |
127 | ret = snprintf(buf, size: sizeof(buf) - 1, fmt: "%d\n" , priv->btmrvl_dev.hscmd); |
128 | |
129 | return simple_read_from_buffer(to: userbuf, count, ppos, from: buf, available: ret); |
130 | } |
131 | |
132 | static const struct file_operations btmrvl_hscmd_fops = { |
133 | .read = btmrvl_hscmd_read, |
134 | .write = btmrvl_hscmd_write, |
135 | .open = simple_open, |
136 | .llseek = default_llseek, |
137 | }; |
138 | |
139 | void btmrvl_debugfs_init(struct hci_dev *hdev) |
140 | { |
141 | struct btmrvl_private *priv = hci_get_drvdata(hdev); |
142 | struct btmrvl_debugfs_data *dbg; |
143 | |
144 | if (!hdev->debugfs) |
145 | return; |
146 | |
147 | dbg = kzalloc(size: sizeof(*dbg), GFP_KERNEL); |
148 | priv->debugfs_data = dbg; |
149 | |
150 | if (!dbg) { |
151 | BT_ERR("Can not allocate memory for btmrvl_debugfs_data." ); |
152 | return; |
153 | } |
154 | |
155 | dbg->config_dir = debugfs_create_dir(name: "config" , parent: hdev->debugfs); |
156 | |
157 | debugfs_create_u8(name: "psmode" , mode: 0644, parent: dbg->config_dir, |
158 | value: &priv->btmrvl_dev.psmode); |
159 | debugfs_create_file(name: "pscmd" , mode: 0644, parent: dbg->config_dir, |
160 | data: priv, fops: &btmrvl_pscmd_fops); |
161 | debugfs_create_x16(name: "gpiogap" , mode: 0644, parent: dbg->config_dir, |
162 | value: &priv->btmrvl_dev.gpio_gap); |
163 | debugfs_create_u8(name: "hsmode" , mode: 0644, parent: dbg->config_dir, |
164 | value: &priv->btmrvl_dev.hsmode); |
165 | debugfs_create_file(name: "hscmd" , mode: 0644, parent: dbg->config_dir, |
166 | data: priv, fops: &btmrvl_hscmd_fops); |
167 | debugfs_create_file(name: "hscfgcmd" , mode: 0644, parent: dbg->config_dir, |
168 | data: priv, fops: &btmrvl_hscfgcmd_fops); |
169 | |
170 | dbg->status_dir = debugfs_create_dir(name: "status" , parent: hdev->debugfs); |
171 | debugfs_create_u8(name: "curpsmode" , mode: 0444, parent: dbg->status_dir, |
172 | value: &priv->adapter->psmode); |
173 | debugfs_create_u8(name: "psstate" , mode: 0444, parent: dbg->status_dir, |
174 | value: &priv->adapter->ps_state); |
175 | debugfs_create_u8(name: "hsstate" , mode: 0444, parent: dbg->status_dir, |
176 | value: &priv->adapter->hs_state); |
177 | debugfs_create_u8(name: "txdnldready" , mode: 0444, parent: dbg->status_dir, |
178 | value: &priv->btmrvl_dev.tx_dnld_rdy); |
179 | } |
180 | |
181 | void btmrvl_debugfs_remove(struct hci_dev *hdev) |
182 | { |
183 | struct btmrvl_private *priv = hci_get_drvdata(hdev); |
184 | struct btmrvl_debugfs_data *dbg = priv->debugfs_data; |
185 | |
186 | if (!dbg) |
187 | return; |
188 | |
189 | debugfs_remove_recursive(dentry: dbg->config_dir); |
190 | debugfs_remove_recursive(dentry: dbg->status_dir); |
191 | |
192 | kfree(objp: dbg); |
193 | } |
194 | |