Bluetooth: Add debugfs fields for hardware and firmware info
authorMarcel Holtmann <marcel@holtmann.org>
Sun, 17 Jul 2016 17:55:16 +0000 (19:55 +0200)
committerJohan Hedberg <johan.hedberg@intel.com>
Mon, 18 Jul 2016 06:33:28 +0000 (09:33 +0300)
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>
include/net/bluetooth/hci_core.h
net/bluetooth/hci_core.c
net/bluetooth/hci_debugfs.c

index 84d0273..ee7fc47 100644 (file)
@@ -372,6 +372,8 @@ struct hci_dev {
 
        atomic_t                promisc;
 
+       const char              *hw_info;
+       const char              *fw_info;
        struct dentry           *debugfs;
 
        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_recv_frame(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_close(__u16 dev);
 int hci_dev_do_close(struct hci_dev *hdev);
index 98f6c37..ddf8432 100644 (file)
@@ -3163,6 +3163,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
        device_del(&hdev->dev);
 
        debugfs_remove_recursive(hdev->debugfs);
+       kfree_const(hdev->hw_info);
+       kfree_const(hdev->fw_info);
 
        destroy_workqueue(hdev->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);
 
+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 ---- */
 
 int hci_register_cb(struct hci_cb *cb)
index 7db4220..63df63e 100644 (file)
@@ -76,6 +76,30 @@ static const struct file_operations __name ## _fops = {                            \
        .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)
 {
        struct hci_dev *hdev = f->private;
@@ -349,6 +373,9 @@ static const struct file_operations sc_only_mode_fops = {
        .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)
 {
        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))
                debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
                                    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)