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>
This commit is contained in:
Marcel Holtmann 2016-07-17 19:55:16 +02:00 committed by Johan Hedberg
parent f962fe32f2
commit 5177a83827
3 changed files with 63 additions and 0 deletions

View File

@ -372,6 +372,8 @@ struct hci_dev {
atomic_t promisc; atomic_t promisc;
const char *hw_info;
const char *fw_info;
struct dentry *debugfs; struct dentry *debugfs;
struct device dev; struct device dev;
@ -1024,6 +1026,8 @@ int hci_resume_dev(struct hci_dev *hdev);
int hci_reset_dev(struct hci_dev *hdev); int hci_reset_dev(struct hci_dev *hdev);
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb);
void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...);
void hci_set_fw_info(struct hci_dev *hdev, const char *fmt, ...);
int hci_dev_open(__u16 dev); int hci_dev_open(__u16 dev);
int hci_dev_close(__u16 dev); int hci_dev_close(__u16 dev);
int hci_dev_do_close(struct hci_dev *hdev); int hci_dev_do_close(struct hci_dev *hdev);

View File

@ -3163,6 +3163,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
device_del(&hdev->dev); device_del(&hdev->dev);
debugfs_remove_recursive(hdev->debugfs); debugfs_remove_recursive(hdev->debugfs);
kfree_const(hdev->hw_info);
kfree_const(hdev->fw_info);
destroy_workqueue(hdev->workqueue); destroy_workqueue(hdev->workqueue);
destroy_workqueue(hdev->req_workqueue); destroy_workqueue(hdev->req_workqueue);
@ -3266,6 +3268,28 @@ int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb)
} }
EXPORT_SYMBOL(hci_recv_diag); EXPORT_SYMBOL(hci_recv_diag);
void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...)
{
va_list vargs;
va_start(vargs, fmt);
kfree_const(hdev->hw_info);
hdev->hw_info = kvasprintf_const(GFP_KERNEL, fmt, vargs);
va_end(vargs);
}
EXPORT_SYMBOL(hci_set_hw_info);
void hci_set_fw_info(struct hci_dev *hdev, const char *fmt, ...)
{
va_list vargs;
va_start(vargs, fmt);
kfree_const(hdev->fw_info);
hdev->fw_info = kvasprintf_const(GFP_KERNEL, fmt, vargs);
va_end(vargs);
}
EXPORT_SYMBOL(hci_set_fw_info);
/* ---- Interface to upper protocols ---- */ /* ---- Interface to upper protocols ---- */
int hci_register_cb(struct hci_cb *cb) int hci_register_cb(struct hci_cb *cb)

View File

@ -76,6 +76,30 @@ static const struct file_operations __name ## _fops = { \
.llseek = default_llseek, \ .llseek = default_llseek, \
} \ } \
#define DEFINE_INFO_ATTRIBUTE(__name, __field) \
static int __name ## _show(struct seq_file *f, void *ptr) \
{ \
struct hci_dev *hdev = f->private; \
\
hci_dev_lock(hdev); \
seq_printf(f, "%s\n", hdev->__field ? : ""); \
hci_dev_unlock(hdev); \
\
return 0; \
} \
\
static int __name ## _open(struct inode *inode, struct file *file) \
{ \
return single_open(file, __name ## _show, inode->i_private); \
} \
\
static const struct file_operations __name ## _fops = { \
.open = __name ## _open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
} \
static int features_show(struct seq_file *f, void *ptr) static int features_show(struct seq_file *f, void *ptr)
{ {
struct hci_dev *hdev = f->private; struct hci_dev *hdev = f->private;
@ -349,6 +373,9 @@ static const struct file_operations sc_only_mode_fops = {
.llseek = default_llseek, .llseek = default_llseek,
}; };
DEFINE_INFO_ATTRIBUTE(hardware_info, hw_info);
DEFINE_INFO_ATTRIBUTE(firmware_info, fw_info);
void hci_debugfs_create_common(struct hci_dev *hdev) void hci_debugfs_create_common(struct hci_dev *hdev)
{ {
debugfs_create_file("features", 0444, hdev->debugfs, hdev, debugfs_create_file("features", 0444, hdev->debugfs, hdev,
@ -382,6 +409,14 @@ void hci_debugfs_create_common(struct hci_dev *hdev)
if (lmp_sc_capable(hdev) || lmp_le_capable(hdev)) if (lmp_sc_capable(hdev) || lmp_le_capable(hdev))
debugfs_create_file("sc_only_mode", 0444, hdev->debugfs, debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
hdev, &sc_only_mode_fops); hdev, &sc_only_mode_fops);
if (hdev->hw_info)
debugfs_create_file("hardware_info", 0444, hdev->debugfs,
hdev, &hardware_info_fops);
if (hdev->fw_info)
debugfs_create_file("firmware_info", 0444, hdev->debugfs,
hdev, &firmware_info_fops);
} }
static int inquiry_cache_show(struct seq_file *f, void *p) static int inquiry_cache_show(struct seq_file *f, void *p)