aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2016-07-17 13:55:16 -0400
committerJohan Hedberg <johan.hedberg@intel.com>2016-07-18 02:33:28 -0400
commit5177a83827cd0b8cf6ce0391b00dd4417352d2f1 (patch)
tree94051d92a967961a962188b74445fdb986faadd6
parentf962fe32f2f85769cd835ddcecbff8c1d34cf561 (diff)
Bluetooth: Add debugfs fields for hardware and firmware info
Some Bluetooth controllers allow for reading hardware and firmware related vendor specific infos. If they are available, then they can be exposed via debugfs now. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
-rw-r--r--include/net/bluetooth/hci_core.h4
-rw-r--r--net/bluetooth/hci_core.c24
-rw-r--r--net/bluetooth/hci_debugfs.c35
3 files changed, 63 insertions, 0 deletions
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 84d0273d826a..ee7fc47680a1 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -372,6 +372,8 @@ struct hci_dev {
372 372
373 atomic_t promisc; 373 atomic_t promisc;
374 374
375 const char *hw_info;
376 const char *fw_info;
375 struct dentry *debugfs; 377 struct dentry *debugfs;
376 378
377 struct device dev; 379 struct device dev;
@@ -1024,6 +1026,8 @@ int hci_resume_dev(struct hci_dev *hdev);
1024int hci_reset_dev(struct hci_dev *hdev); 1026int hci_reset_dev(struct hci_dev *hdev);
1025int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb); 1027int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
1026int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb); 1028int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb);
1029void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...);
1030void hci_set_fw_info(struct hci_dev *hdev, const char *fmt, ...);
1027int hci_dev_open(__u16 dev); 1031int hci_dev_open(__u16 dev);
1028int hci_dev_close(__u16 dev); 1032int hci_dev_close(__u16 dev);
1029int hci_dev_do_close(struct hci_dev *hdev); 1033int hci_dev_do_close(struct hci_dev *hdev);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 98f6c3770736..ddf8432fe8fb 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -3163,6 +3163,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
3163 device_del(&hdev->dev); 3163 device_del(&hdev->dev);
3164 3164
3165 debugfs_remove_recursive(hdev->debugfs); 3165 debugfs_remove_recursive(hdev->debugfs);
3166 kfree_const(hdev->hw_info);
3167 kfree_const(hdev->fw_info);
3166 3168
3167 destroy_workqueue(hdev->workqueue); 3169 destroy_workqueue(hdev->workqueue);
3168 destroy_workqueue(hdev->req_workqueue); 3170 destroy_workqueue(hdev->req_workqueue);
@@ -3266,6 +3268,28 @@ int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb)
3266} 3268}
3267EXPORT_SYMBOL(hci_recv_diag); 3269EXPORT_SYMBOL(hci_recv_diag);
3268 3270
3271void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...)
3272{
3273 va_list vargs;
3274
3275 va_start(vargs, fmt);
3276 kfree_const(hdev->hw_info);
3277 hdev->hw_info = kvasprintf_const(GFP_KERNEL, fmt, vargs);
3278 va_end(vargs);
3279}
3280EXPORT_SYMBOL(hci_set_hw_info);
3281
3282void hci_set_fw_info(struct hci_dev *hdev, const char *fmt, ...)
3283{
3284 va_list vargs;
3285
3286 va_start(vargs, fmt);
3287 kfree_const(hdev->fw_info);
3288 hdev->fw_info = kvasprintf_const(GFP_KERNEL, fmt, vargs);
3289 va_end(vargs);
3290}
3291EXPORT_SYMBOL(hci_set_fw_info);
3292
3269/* ---- Interface to upper protocols ---- */ 3293/* ---- Interface to upper protocols ---- */
3270 3294
3271int hci_register_cb(struct hci_cb *cb) 3295int hci_register_cb(struct hci_cb *cb)
diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c
index 7db4220941cc..63df63ebfb24 100644
--- a/net/bluetooth/hci_debugfs.c
+++ b/net/bluetooth/hci_debugfs.c
@@ -76,6 +76,30 @@ static const struct file_operations __name ## _fops = { \
76 .llseek = default_llseek, \ 76 .llseek = default_llseek, \
77} \ 77} \
78 78
79#define DEFINE_INFO_ATTRIBUTE(__name, __field) \
80static int __name ## _show(struct seq_file *f, void *ptr) \
81{ \
82 struct hci_dev *hdev = f->private; \
83 \
84 hci_dev_lock(hdev); \
85 seq_printf(f, "%s\n", hdev->__field ? : ""); \
86 hci_dev_unlock(hdev); \
87 \
88 return 0; \
89} \
90 \
91static int __name ## _open(struct inode *inode, struct file *file) \
92{ \
93 return single_open(file, __name ## _show, inode->i_private); \
94} \
95 \
96static const struct file_operations __name ## _fops = { \
97 .open = __name ## _open, \
98 .read = seq_read, \
99 .llseek = seq_lseek, \
100 .release = single_release, \
101} \
102
79static int features_show(struct seq_file *f, void *ptr) 103static int features_show(struct seq_file *f, void *ptr)
80{ 104{
81 struct hci_dev *hdev = f->private; 105 struct hci_dev *hdev = f->private;
@@ -349,6 +373,9 @@ static const struct file_operations sc_only_mode_fops = {
349 .llseek = default_llseek, 373 .llseek = default_llseek,
350}; 374};
351 375
376DEFINE_INFO_ATTRIBUTE(hardware_info, hw_info);
377DEFINE_INFO_ATTRIBUTE(firmware_info, fw_info);
378
352void hci_debugfs_create_common(struct hci_dev *hdev) 379void hci_debugfs_create_common(struct hci_dev *hdev)
353{ 380{
354 debugfs_create_file("features", 0444, hdev->debugfs, hdev, 381 debugfs_create_file("features", 0444, hdev->debugfs, hdev,
@@ -382,6 +409,14 @@ void hci_debugfs_create_common(struct hci_dev *hdev)
382 if (lmp_sc_capable(hdev) || lmp_le_capable(hdev)) 409 if (lmp_sc_capable(hdev) || lmp_le_capable(hdev))
383 debugfs_create_file("sc_only_mode", 0444, hdev->debugfs, 410 debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
384 hdev, &sc_only_mode_fops); 411 hdev, &sc_only_mode_fops);
412
413 if (hdev->hw_info)
414 debugfs_create_file("hardware_info", 0444, hdev->debugfs,
415 hdev, &hardware_info_fops);
416
417 if (hdev->fw_info)
418 debugfs_create_file("firmware_info", 0444, hdev->debugfs,
419 hdev, &firmware_info_fops);
385} 420}
386 421
387static int inquiry_cache_show(struct seq_file *f, void *p) 422static int inquiry_cache_show(struct seq_file *f, void *p)