#include <linux/bitops.h>
 #include <linux/completion.h>
+#include <linux/debugfs.h>
 #include <linux/hid.h>
 #include <linux/hwmon.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
 #define LABEL_LENGTH           11
 #define REQ_TIMEOUT            300
 
+#define CTL_GET_FW_VER         0x02    /* returns the firmware version in bytes 1-3 */
+#define CTL_GET_BL_VER         0x06    /* returns the bootloader version in bytes 1-2 */
 #define CTL_GET_TMP_CNCT       0x10    /*
                                         * returns in bytes 1-4 for each temp sensor:
                                         * 0 not connected
 struct ccp_device {
        struct hid_device *hdev;
        struct device *hwmon_dev;
+       struct dentry *debugfs;
        /* For reinitializing the completion below */
        spinlock_t wait_input_report_lock;
        struct completion wait_input_report;
        DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS);
        DECLARE_BITMAP(fan_cnct, NUM_FANS);
        char fan_label[6][LABEL_LENGTH];
+       u8 firmware_ver[3];
+       u8 bootloader_ver[2];
 };
 
 /* converts response error in buffer to errno */
        return 0;
 }
 
+/* read firmware version */
+static int get_fw_version(struct ccp_device *ccp)
+{
+       int ret;
+
+       ret = send_usb_cmd(ccp, CTL_GET_FW_VER, 0, 0, 0);
+       if (ret) {
+               hid_notice(ccp->hdev, "Failed to read firmware version.\n");
+               return ret;
+       }
+       ccp->firmware_ver[0] = ccp->buffer[1];
+       ccp->firmware_ver[1] = ccp->buffer[2];
+       ccp->firmware_ver[2] = ccp->buffer[3];
+
+       return 0;
+}
+
+/* read bootloader version */
+static int get_bl_version(struct ccp_device *ccp)
+{
+       int ret;
+
+       ret = send_usb_cmd(ccp, CTL_GET_BL_VER, 0, 0, 0);
+       if (ret) {
+               hid_notice(ccp->hdev, "Failed to read bootloader version.\n");
+               return ret;
+       }
+       ccp->bootloader_ver[0] = ccp->buffer[1];
+       ccp->bootloader_ver[1] = ccp->buffer[2];
+
+       return 0;
+}
+
+static int firmware_show(struct seq_file *seqf, void *unused)
+{
+       struct ccp_device *ccp = seqf->private;
+
+       seq_printf(seqf, "%d.%d.%d\n",
+                  ccp->firmware_ver[0],
+                  ccp->firmware_ver[1],
+                  ccp->firmware_ver[2]);
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(firmware);
+
+static int bootloader_show(struct seq_file *seqf, void *unused)
+{
+       struct ccp_device *ccp = seqf->private;
+
+       seq_printf(seqf, "%d.%d\n",
+                  ccp->bootloader_ver[0],
+                  ccp->bootloader_ver[1]);
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(bootloader);
+
+static void ccp_debugfs_init(struct ccp_device *ccp)
+{
+       char name[32];
+       int ret;
+
+       scnprintf(name, sizeof(name), "corsaircpro-%s", dev_name(&ccp->hdev->dev));
+       ccp->debugfs = debugfs_create_dir(name, NULL);
+
+       ret = get_fw_version(ccp);
+       if (!ret)
+               debugfs_create_file("firmware_version", 0444,
+                                   ccp->debugfs, ccp, &firmware_fops);
+
+       ret = get_bl_version(ccp);
+       if (!ret)
+               debugfs_create_file("bootloader_version", 0444,
+                                   ccp->debugfs, ccp, &bootloader_fops);
+}
+
 static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        struct ccp_device *ccp;
        ret = get_fan_cnct(ccp);
        if (ret)
                goto out_hw_close;
+
+       ccp_debugfs_init(ccp);
+
        ccp->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsaircpro",
                                                         ccp, &ccp_chip_info, NULL);
        if (IS_ERR(ccp->hwmon_dev)) {
 {
        struct ccp_device *ccp = hid_get_drvdata(hdev);
 
+       debugfs_remove_recursive(ccp->debugfs);
        hwmon_device_unregister(ccp->hwmon_dev);
        hid_hw_close(hdev);
        hid_hw_stop(hdev);