diff --git a/README.md b/README.md
index 6147ee362157f0f94be6c2dc0c5e3eb3624e76cf..57ca21d2b64a4eb30275719d02a929d2d7d69247 100644
--- a/README.md
+++ b/README.md
@@ -125,6 +125,7 @@ The devices below are fully feature supported by OpenRazer, which means all avai
 | Razer DeathAdder V2 Pro (Wireless)            |  1532:007D  |
 | Razer Basilisk X HyperSpeed                   |  1532:0083  |
 | Razer DeathAdder V2                           |  1532:0084  |
+| Razer Basilisk V2                             |  1532:0085  |
 | Razer Viper Mini                              |  1532:008A  |
 | Razer DeathAdder V2 Mini                      |  1532:008C  |
 | Razer Naga Left-Handed Edition                |  1532:008D  |
diff --git a/daemon/openrazer_daemon/hardware/mouse.py b/daemon/openrazer_daemon/hardware/mouse.py
index 1d44be233fdfd364145c8485a0e2d2560e7b88fd..52721ef0883a3c6a4883032ec3e5e09e449dcca2 100644
--- a/daemon/openrazer_daemon/hardware/mouse.py
+++ b/daemon/openrazer_daemon/hardware/mouse.py
@@ -2071,6 +2071,59 @@ class RazerBasilisk(__RazerDeviceSpecialBrightnessSuspend):
         self.disable_notify = False
 
 
+class RazerBasiliskV2(__RazerDeviceSpecialBrightnessSuspend):
+    """
+    Class for the Razer Basilisk V2
+    """
+    EVENT_FILE_REGEX = re.compile(r'.*Razer_Basilisk_V2-if0(1|2)-event-kbd')
+
+    USB_VID = 0x1532
+    USB_PID = 0x0085
+    HAS_MATRIX = True
+    MATRIX_DIMS = [1, 1]
+    METHODS = ['get_device_type_mouse', 'max_dpi', 'get_dpi_xy', 'set_dpi_xy', 'get_poll_rate', 'set_poll_rate',
+               'get_logo_brightness', 'set_logo_brightness', 'get_scroll_brightness', 'set_scroll_brightness',
+               # Logo
+               'set_logo_static_naga_hex_v2', 'set_logo_spectrum_naga_hex_v2', 'set_logo_none_naga_hex_v2', 'set_logo_reactive_naga_hex_v2', 'set_logo_breath_random_naga_hex_v2', 'set_logo_breath_single_naga_hex_v2', 'set_logo_breath_dual_naga_hex_v2',
+               # Scroll wheel
+               'set_scroll_static_naga_hex_v2', 'set_scroll_spectrum_naga_hex_v2', 'set_scroll_none_naga_hex_v2', 'set_scroll_reactive_naga_hex_v2', 'set_scroll_breath_random_naga_hex_v2', 'set_scroll_breath_single_naga_hex_v2', 'set_scroll_breath_dual_naga_hex_v2',
+               # Can set LOGO and Scroll with custom
+               'set_custom_effect', 'set_key_row']
+
+    DEVICE_IMAGE = "https://assets.razerzone.com/eeimages/support/products/1617/1617_basilisk-v2.png"
+
+    DPI_MAX = 20000
+
+    def _suspend_device(self):
+        """
+        Suspend the device
+
+        Get the current brightness level, store it for later and then set the brightness to 0
+        """
+        self.suspend_args.clear()
+        self.suspend_args['brightness'] = (_da_get_logo_brightness(self), _da_get_scroll_brightness(self))
+
+        # Todo make it context?
+        self.disable_notify = True
+        _da_set_logo_brightness(self, 0)
+        _da_set_scroll_brightness(self, 0)
+        self.disable_notify = False
+
+    def _resume_device(self):
+        """
+        Resume the device
+
+        Get the last known brightness and then set the brightness
+        """
+        logo_brightness = self.suspend_args.get('brightness', (100, 100))[0]
+        scroll_brightness = self.suspend_args.get('brightness', (100, 100))[1]
+
+        self.disable_notify = True
+        _da_set_logo_brightness(self, logo_brightness)
+        _da_set_scroll_brightness(self, scroll_brightness)
+        self.disable_notify = False
+
+
 class RazerDeathAdderV2(__RazerDeviceSpecialBrightnessSuspend):
     """
     Class for the Razer DeathAdder V2
diff --git a/driver/razermouse_driver.c b/driver/razermouse_driver.c
index 0a7a16974f250ac1c8ff5b6d97a42dbfdab569d0..2dab2f45b18f7897c1c7f67f3bd0cb87163986da 100644
--- a/driver/razermouse_driver.c
+++ b/driver/razermouse_driver.c
@@ -24,6 +24,7 @@
 #include <linux/init.h>
 #include <linux/usb/input.h>
 #include <linux/hid.h>
+#include <linux/hrtimer.h>
 #include <linux/random.h>
 
 #include "razermouse_driver.h"
@@ -394,6 +395,10 @@ static ssize_t razer_attr_read_device_type(struct device *dev, struct device_att
         device_type = "Razer Basilisk\n";
         break;
 
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
+        device_type = "Razer Basilisk V2\n";
+        break;
+
     case USB_DEVICE_ID_RAZER_DEATHADDER_V2:
         device_type = "Razer DeathAdder V2\n";
         break;
@@ -456,6 +461,7 @@ static ssize_t razer_attr_read_get_firmware_version(struct device *dev, struct d
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
     case USB_DEVICE_ID_RAZER_ATHERIS_RECEIVER:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report.transaction_id.id = 0x1f;
         break;
 
@@ -567,6 +573,7 @@ static ssize_t razer_attr_write_mode_custom(struct device *dev, struct device_at
 
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report = razer_chroma_extended_matrix_effect_custom_frame();
         report.transaction_id.id = 0x1f;
         break;
@@ -801,6 +808,7 @@ static ssize_t razer_attr_read_get_serial(struct device *dev, struct device_attr
 
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_ATHERIS_RECEIVER:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report.transaction_id.id = 0x1f;
         break;
     }
@@ -978,6 +986,7 @@ static ssize_t razer_attr_read_poll_rate(struct device *dev, struct device_attri
 
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_ATHERIS_RECEIVER:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report.transaction_id.id = 0x1f;
         break;
     }
@@ -1047,6 +1056,7 @@ static ssize_t razer_attr_write_poll_rate(struct device *dev, struct device_attr
 
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_ATHERIS_RECEIVER:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report.transaction_id.id = 0x1f;
         break;
     }
@@ -1279,6 +1289,7 @@ static ssize_t razer_attr_write_mouse_dpi(struct device *dev, struct device_attr
         case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
         case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
         case USB_DEVICE_ID_RAZER_ATHERIS_RECEIVER:
+        case USB_DEVICE_ID_RAZER_BASILISK_V2:
             report.transaction_id.id = 0x1f;
             break;
         }
@@ -1339,6 +1350,7 @@ static ssize_t razer_attr_read_mouse_dpi(struct device *dev, struct device_attri
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
     case USB_DEVICE_ID_RAZER_ATHERIS_RECEIVER:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report = razer_chroma_misc_get_dpi_xy(NOSTORE);
         report.transaction_id.id = 0x1f;
         break;
@@ -1389,6 +1401,54 @@ static ssize_t razer_attr_read_mouse_dpi(struct device *dev, struct device_attri
     return sprintf(buf, "%u:%u\n", dpi_x, dpi_y);
 }
 
+static ssize_t razer_attr_write_tilt_hwheel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct razer_mouse_device *device = dev_get_drvdata(dev);
+    unsigned int tilt_hwheel;
+    if (kstrtouint(buf, 0, &tilt_hwheel) < 0)
+        return -EINVAL;
+    device->tilt_hwheel = !!tilt_hwheel;
+    return count;
+}
+
+static ssize_t razer_attr_read_tilt_hwheel(struct device *dev, struct device_attribute *attr, char *buf)
+{
+    struct razer_mouse_device *device = dev_get_drvdata(dev);
+    return sprintf(buf, "%u\n", device->tilt_hwheel);
+}
+
+static ssize_t razer_attr_write_tilt_repeat(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct razer_mouse_device *device = dev_get_drvdata(dev);
+    unsigned int tilt_repeat;
+    if (kstrtouint(buf, 0, &tilt_repeat) < 0)
+        return -EINVAL;
+    device->tilt_repeat = tilt_repeat;
+    return count;
+}
+
+static ssize_t razer_attr_read_tilt_repeat(struct device *dev, struct device_attribute *attr, char *buf)
+{
+    struct razer_mouse_device *device = dev_get_drvdata(dev);
+    return sprintf(buf, "%u\n", device->tilt_repeat);
+}
+
+static ssize_t razer_attr_write_tilt_repeat_delay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+    struct razer_mouse_device *device = dev_get_drvdata(dev);
+    unsigned int tilt_repeat_delay;
+    if (kstrtouint(buf, 0, &tilt_repeat_delay) < 0)
+        return -EINVAL;
+    device->tilt_repeat_delay = tilt_repeat_delay;
+    return count;
+}
+
+static ssize_t razer_attr_read_tilt_repeat_delay(struct device *dev, struct device_attribute *attr, char *buf)
+{
+    struct razer_mouse_device *device = dev_get_drvdata(dev);
+    return sprintf(buf, "%u\n", device->tilt_repeat_delay);
+}
+
 /**
  * Write device file "dpi_stages"
  *
@@ -1673,6 +1733,11 @@ static ssize_t razer_attr_write_set_key_row(struct device *dev, struct device_at
             report = razer_chroma_extended_matrix_set_custom_frame(row_id, start_col, stop_col, (unsigned char*)&buf[offset]);
             break;
 
+        case USB_DEVICE_ID_RAZER_BASILISK_V2:
+            report = razer_chroma_extended_matrix_set_custom_frame(row_id, start_col, stop_col, (unsigned char*)&buf[offset]);
+            report.transaction_id.id = 0x1f;
+            break;
+
         case USB_DEVICE_ID_RAZER_MAMBA_WIRED:
         case USB_DEVICE_ID_RAZER_MAMBA_WIRELESS:
             report = razer_chroma_misc_one_row_set_custom_frame(start_col, stop_col, (unsigned char*)&buf[offset]);
@@ -1720,6 +1785,7 @@ static ssize_t razer_attr_write_device_mode(struct device *dev, struct device_at
         case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
         case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
         case USB_DEVICE_ID_RAZER_ATHERIS_RECEIVER:
+        case USB_DEVICE_ID_RAZER_BASILISK_V2:
             report.transaction_id.id = 0x1f;
             break;
 
@@ -1788,6 +1854,7 @@ static ssize_t razer_attr_read_device_mode(struct device *dev, struct device_att
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
     case USB_DEVICE_ID_RAZER_ATHERIS_RECEIVER:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report.transaction_id.id = 0x1f;
         break;
     }
@@ -1817,6 +1884,7 @@ static ssize_t razer_attr_read_scroll_led_brightness(struct device *dev, struct
 
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report = razer_chroma_extended_matrix_get_brightness(VARSTORE, SCROLL_WHEEL_LED);
         report.transaction_id.id = 0x1f;
         break;
@@ -1864,6 +1932,7 @@ static ssize_t razer_attr_write_scroll_led_brightness(struct device *dev, struct
 
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report = razer_chroma_extended_matrix_brightness(VARSTORE, SCROLL_WHEEL_LED, brightness);
         report.transaction_id.id = 0x1f;
         break;
@@ -1911,6 +1980,7 @@ static ssize_t razer_attr_read_logo_led_brightness(struct device *dev, struct de
 
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report = razer_chroma_extended_matrix_get_brightness(VARSTORE, LOGO_LED);
         report.transaction_id.id = 0x1f;
         break;
@@ -1967,6 +2037,7 @@ static ssize_t razer_attr_write_logo_led_brightness(struct device *dev, struct d
 
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report = razer_chroma_extended_matrix_brightness(VARSTORE, LOGO_LED, brightness);
         report.transaction_id.id = 0x1f;
         break;
@@ -2436,6 +2507,7 @@ static ssize_t razer_attr_write_scroll_mode_spectrum(struct device *dev, struct
 
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report = razer_chroma_extended_matrix_effect_spectrum(VARSTORE, SCROLL_WHEEL_LED);
         report.transaction_id.id = 0x1f;
         break;
@@ -2485,6 +2557,7 @@ static ssize_t razer_attr_write_scroll_mode_reactive(struct device *dev, struct
 
         case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
         case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+        case USB_DEVICE_ID_RAZER_BASILISK_V2:
             report = razer_chroma_extended_matrix_effect_reactive(VARSTORE, SCROLL_WHEEL_LED, speed, (struct razer_rgb*)&buf[1]);
             report.transaction_id.id = 0x1f;
             break;
@@ -2545,6 +2618,7 @@ static ssize_t razer_attr_write_scroll_mode_breath(struct device *dev, struct de
     case USB_DEVICE_ID_RAZER_MAMBA_WIRELESS_RECEIVER:
     case USB_DEVICE_ID_RAZER_MAMBA_WIRELESS_WIRED:
     case USB_DEVICE_ID_RAZER_BASILISK:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
     case USB_DEVICE_ID_RAZER_DEATHADDER_V2:
         switch(count) {
         case 3: // Single colour mode
@@ -2565,6 +2639,7 @@ static ssize_t razer_attr_write_scroll_mode_breath(struct device *dev, struct de
     switch(usb_dev->descriptor.idProduct) {
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report.transaction_id.id = 0x1f;
         break;
 
@@ -2613,6 +2688,7 @@ static ssize_t razer_attr_write_scroll_mode_static(struct device *dev, struct de
 
         case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
         case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+        case USB_DEVICE_ID_RAZER_BASILISK_V2:
             report = razer_chroma_extended_matrix_effect_static(VARSTORE, SCROLL_WHEEL_LED, (struct razer_rgb*)&buf[0]);
             report.transaction_id.id = 0x1f;
             break;
@@ -2666,6 +2742,7 @@ static ssize_t razer_attr_write_scroll_mode_none(struct device *dev, struct devi
 
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report = razer_chroma_extended_matrix_effect_none(VARSTORE, SCROLL_WHEEL_LED);
         report.transaction_id.id = 0x1f;
         break;
@@ -2756,6 +2833,7 @@ static ssize_t razer_attr_write_logo_mode_spectrum(struct device *dev, struct de
 
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report = razer_chroma_extended_matrix_effect_spectrum(VARSTORE, LOGO_LED);
         report.transaction_id.id = 0x1f;
         break;
@@ -2813,6 +2891,7 @@ static ssize_t razer_attr_write_logo_mode_reactive(struct device *dev, struct de
 
         case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
         case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+        case USB_DEVICE_ID_RAZER_BASILISK_V2:
             report = razer_chroma_extended_matrix_effect_reactive(VARSTORE, LOGO_LED, speed, (struct razer_rgb*)&buf[1]);
             report.transaction_id.id = 0x1f;
             break;
@@ -2879,6 +2958,7 @@ static ssize_t razer_attr_write_logo_mode_breath(struct device *dev, struct devi
     case USB_DEVICE_ID_RAZER_VIPER_ULTIMATE_WIRED:
     case USB_DEVICE_ID_RAZER_VIPER_ULTIMATE_WIRELESS:
     case USB_DEVICE_ID_RAZER_BASILISK:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
     case USB_DEVICE_ID_RAZER_DEATHADDER_V2:
     case USB_DEVICE_ID_RAZER_DEATHADDER_V2_PRO_WIRED:
     case USB_DEVICE_ID_RAZER_DEATHADDER_V2_PRO_WIRELESS:
@@ -2902,6 +2982,7 @@ static ssize_t razer_attr_write_logo_mode_breath(struct device *dev, struct devi
     switch(usb_dev->descriptor.idProduct) {
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report.transaction_id.id = 0x1f;
         break;
 
@@ -2958,6 +3039,7 @@ static ssize_t razer_attr_write_logo_mode_static(struct device *dev, struct devi
 
         case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
         case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+        case USB_DEVICE_ID_RAZER_BASILISK_V2:
             report = razer_chroma_extended_matrix_effect_static(VARSTORE, LOGO_LED, (struct razer_rgb*)&buf[0]);
             report.transaction_id.id = 0x1f;
             break;
@@ -3018,6 +3100,7 @@ static ssize_t razer_attr_write_logo_mode_none(struct device *dev, struct device
 
     case USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020:
     case USB_DEVICE_ID_RAZER_MAMBA_ELITE:
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
         report = razer_chroma_extended_matrix_effect_none(VARSTORE, LOGO_LED);
         report.transaction_id.id = 0x1f;
         break;
@@ -3411,6 +3494,10 @@ static DEVICE_ATTR(device_mode,               0660, razer_attr_read_device_mode,
 static DEVICE_ATTR(device_serial,             0440, razer_attr_read_get_serial,            NULL);
 static DEVICE_ATTR(device_idle_time,          0660, razer_attr_read_get_idle_time,         razer_attr_write_set_idle_time);
 
+static DEVICE_ATTR(tilt_hwheel,               0660, razer_attr_read_tilt_hwheel,           razer_attr_write_tilt_hwheel);
+static DEVICE_ATTR(tilt_repeat,               0660, razer_attr_read_tilt_repeat,           razer_attr_write_tilt_repeat);
+static DEVICE_ATTR(tilt_repeat_delay,         0660, razer_attr_read_tilt_repeat_delay,     razer_attr_write_tilt_repeat_delay);
+
 static DEVICE_ATTR(charge_level,              0440, razer_attr_read_get_battery,           NULL);
 static DEVICE_ATTR(charge_status,             0440, razer_attr_read_is_charging,           NULL);
 static DEVICE_ATTR(charge_effect,             0220, NULL,                                  razer_attr_write_set_charging_effect);
@@ -3476,6 +3563,200 @@ static DEVICE_ATTR(right_matrix_effect_none,        0220, NULL,
 static DEVICE_ATTR(backlight_led_state,            0660, razer_attr_read_backlight_led_state, razer_attr_write_backlight_led_state);
 
 
+#define REP4_DPI_UP  0x20
+#define REP4_DPI_DN  0x21
+#define REP4_TILT_L  0x22
+#define REP4_TILT_R  0x23
+#define REP4_PROFILE 0x50
+#define REP4_SNIPER  0x51
+
+#define BIT_TILT_L 5
+#define BIT_TILT_R 6
+
+/**
+ * Map "Report 4" codes to evdev key codes
+ */
+static const __u16 rep4_key_codes[] = {
+    [REP4_TILT_L]  = BTN_BACK,          /* BTN_MOUSE + 6 */
+    [REP4_TILT_R]  = BTN_FORWARD,       /* BTN_MOUSE + 5 */
+    [REP4_SNIPER]  = BTN_TASK,          /* BTN_MOUSE + 7 */
+    [REP4_DPI_UP]  = BTN_MOUSE + 8,
+    [REP4_DPI_DN]  = BTN_MOUSE + 9,
+    [REP4_PROFILE] = BTN_MOUSE + 10,
+    /* NOTE: Highest legal mouse button is BTN_MOUSE + 15 */
+};
+
+struct button_mapping {
+    u8 bit;
+    __u16 code;          /* when tilt_hwheel == 0 */
+    __s32 hwheel_value;         /* when tilt_hwheel == 1 */
+};
+
+/**
+ * Map bits in the first byte of the mouse report to evdev keycodes
+ * and REL_HWHEEL values
+ */
+static const struct button_mapping button_mappings[] = {
+    {BIT_TILT_L, BTN_BACK, -1},
+    {BIT_TILT_R, BTN_FORWARD, 1},
+};
+
+/**
+ * Convert an evdev mouse button code to the corresponding HID usage
+ */
+u32 mouse_button_to_usage(__u16 code)
+{
+    return HID_UP_BUTTON + (code - BTN_MOUSE) + 1;
+}
+
+/**
+ * Send the MSC_SCAN event for the usage code associated with an evdev
+ * mouse button code
+ */
+void input_button_msc_scan(struct input_dev *input, __u16 button)
+{
+    input_event(input, EV_MSC, MSC_SCAN, mouse_button_to_usage(button));
+}
+
+/**
+ * Look up and send the evdev key associated with the Razer "report 4"
+ * code
+ */
+void input_rep4_code(struct input_dev *input, u8 code, __s32 value)
+{
+    if (code < ARRAY_SIZE(rep4_key_codes) && rep4_key_codes[code]) {
+        unsigned int button = rep4_key_codes[code];
+        input_button_msc_scan(input, button);
+        input_report_key(input, button, value);
+        input_sync(input);
+    }
+}
+
+/**
+ * Timer callback for wheel tilt repeating
+ */
+static enum hrtimer_restart wheel_tilt_repeat(struct hrtimer *timer)
+{
+    struct razer_mouse_device *dev =
+        container_of(timer, struct razer_mouse_device, repeat_timer);
+    input_report_rel(dev->input, REL_HWHEEL, dev->hwheel_value);
+    input_sync(dev->input);
+    if (dev->tilt_repeat)
+        hrtimer_forward_now(timer, ms_to_ktime(dev->tilt_repeat));
+    return HRTIMER_RESTART;
+}
+
+/**
+ * Send a tilt-wheel event and, if configured, start the key-repeat timer
+ */
+static void tilt_hwheel_start(struct razer_mouse_device *rdev,
+                              __s32 rel_value)
+{
+    input_report_rel(rdev->input, REL_HWHEEL, rel_value);
+    input_sync(rdev->input);
+
+    if (rdev->tilt_repeat && rdev->tilt_repeat_delay) {
+        rdev->hwheel_value = rel_value;
+        hrtimer_start_range_ns(
+            &rdev->repeat_timer, ms_to_ktime(rdev->tilt_repeat_delay),
+            1000, HRTIMER_MODE_REL);
+    }
+}
+
+/**
+ * Stop the tilt wheel key-repeat timer
+ */
+static void tilt_hwheel_stop(struct razer_mouse_device *rdev)
+{
+    hrtimer_cancel(&rdev->repeat_timer);
+}
+
+/**
+ * Test if a device is a HID device
+ */
+static int dev_is_on_bus(struct device *dev, void *data)
+{
+    return (dev->bus == data);
+}
+
+/**
+ * Find an interface on a usb_device with the specified protocol
+ */
+struct usb_interface *find_intf_with_proto(struct usb_device *usbdev, u8 proto)
+{
+    struct usb_interface *intf;
+    int i;
+
+    for (i = 0; i < usbdev->actconfig->desc.bNumInterfaces; i++) {
+        intf = usb_ifnum_to_if(usbdev, i);
+        if (intf && intf->cur_altsetting->desc.bInterfaceProtocol == proto)
+            return intf;
+    }
+
+    return NULL;
+}
+
+/**
+ * Walk up the device tree from an interface to the device it is a
+ * part of, then back down through the interface with protocol == MOUSE
+ * to the razer_mouse_device associated with it
+ */
+static struct razer_mouse_device *find_mouse(struct hid_device *hdev)
+{
+    struct bus_type *hid_bus_type = hdev->dev.bus;
+    struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+    struct usb_device *usbdev = interface_to_usbdev(intf);
+    struct usb_interface *m_intf = find_intf_with_proto(usbdev, USB_INTERFACE_PROTOCOL_MOUSE);
+    struct device *dev;
+    struct razer_mouse_device *rdev;
+
+    if (!m_intf)
+        return NULL;
+
+    dev = device_find_child(&m_intf->dev, hid_bus_type, dev_is_on_bus);
+    if (!dev)
+        return NULL;
+
+    rdev = dev_get_drvdata(dev);
+    put_device(dev);
+    return rdev;
+}
+
+/**
+ * Test if a bit is cleared in 'prev' and set in 'cur'
+ */
+static int rising_bit(u8 prev, u8 cur, u8 mask)
+{
+    return !(prev & mask) && cur & mask;
+}
+
+/**
+ * Test if a bit is set in 'prev' and cleared in 'cur'
+ */
+static int falling_bit(u8 prev, u8 cur, u8 mask)
+{
+    return prev & mask && !(cur & mask);
+}
+
+/**
+ * Test if a bit is different between 'prev' and 'cur'
+ */
+static int edge_bit(u8 prev, u8 cur, u8 mask)
+{
+    return (prev & mask) != (cur & mask);
+}
+
+/**
+ * Search a byte array for a value
+ */
+static int search(u8 *array, u8 value, unsigned n)
+{
+    while (n--) {
+        if (*array++ == value)
+            return 1;
+    }
+    return 0;
+}
 
 /**
  * Raw event function
@@ -3483,41 +3764,138 @@ static DEVICE_ATTR(backlight_led_state,            0660, razer_attr_read_backlig
 static int razer_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
 {
     struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+    struct razer_mouse_device *rdev = hid_get_drvdata(hdev);
+
+    switch (hdev->product) {
+    case USB_DEVICE_ID_RAZER_BASILISK_V2:
+        /* Detect wheel tilt edges */
+        if(intf->cur_altsetting->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) {
+            int i;
+            for (i = 0; i < ARRAY_SIZE(button_mappings); i++) {
+                const struct button_mapping *mapping = &button_mappings[i];
+                u8 mask = 1 << mapping->bit;
+                if (mapping->hwheel_value && rdev->tilt_hwheel) {
+                    __s32 rel_value = mapping->hwheel_value;
+                    if (rising_bit(rdev->button_byte, data[0], mask))
+                        tilt_hwheel_start(rdev, rel_value);
+                    if (falling_bit(rdev->button_byte, data[0], mask))
+                        tilt_hwheel_stop(rdev);
+                } else if (edge_bit(rdev->button_byte, data[0], mask)) {
+                    unsigned int code = mapping->code;
+                    input_button_msc_scan(rdev->input, code);
+                    input_report_key(rdev->input, code, !!(data[0] & mask));
+                    input_sync(rdev->input);
+                }
+            }
+            rdev->button_byte = data[0];
+        }
 
-    // The event were looking for is 16 bytes long and starts with 0x04
-    if(intf->cur_altsetting->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD && size == 16 && data[0] == 0x04) {
-        // Convert 04... to 0100...
-        int index = size-1; // This way we start at 2nd last value, does subtract 1 from the 15key rollover though (not an issue cmon)
-        u8 cur_value = 0x00;
+        /* Detect buttons reported on the keyboard interface */
+        if(intf->cur_altsetting->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD && size == 16 && data[0] == 0x04) {
+            struct razer_mouse_device *m_rdev = find_mouse(hdev);
+            int i;
 
-        while(--index > 0) {
-            cur_value = data[index];
-            if(cur_value == 0x00) { // Skip 0x00
-                continue;
+            if (!m_rdev) {
+                printk(KERN_WARNING "razermouse: Couldn't find mouse intf from kbd intf");
+                return 1;
             }
 
-            switch(cur_value) {
-            case 0x20: // DPI Up
-                cur_value = 0x68; // F13
-                break;
-            case 0x21: // DPI Down
-                cur_value = 0x69; // F14
-                break;
-            case 0x22: // Wheel Left
-                cur_value = 0x6A; // F15
-                break;
-            case 0x23: // Wheel Right
-                cur_value = 0x6B; // F16
-                break;
+            for (i = 1; i < size; i++) {
+                if (!search(rdev->rep4 + 1, data[i], size - 1))
+                    input_rep4_code(m_rdev->input, data[i], 1);
+                if (!search(data + 1, rdev->rep4[i], size - 1))
+                    input_rep4_code(m_rdev->input, rdev->rep4[i], 0);
+            }
+            memcpy(rdev->rep4, data, 16);
+            return 1;
+        }
+        break;
+    default:
+        // The event were looking for is 16 bytes long and starts with 0x04
+        if(intf->cur_altsetting->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD && size == 16 && data[0] == 0x04) {
+            // Convert 04... to 0100...
+            int index = size-1; // This way we start at 2nd last value, does subtract 1 from the 15key rollover though (not an issue cmon)
+            u8 cur_value = 0x00;
+
+            while(--index > 0) {
+                cur_value = data[index];
+                if(cur_value == 0x00) { // Skip 0x00
+                    continue;
+                }
+
+                switch(cur_value) {
+                case 0x20: // DPI Up
+                    cur_value = 0x68; // F13
+                    break;
+                case 0x21: // DPI Down
+                    cur_value = 0x69; // F14
+                    break;
+                case 0x22: // Wheel Left
+                    cur_value = 0x6A; // F15
+                    break;
+                case 0x23: // Wheel Right
+                    cur_value = 0x6B; // F16
+                    break;
+                }
+
+                data[index+1] = cur_value;
             }
 
-            data[index+1] = cur_value;
+
+            data[0] = 0x01;
+            data[1] = 0x00;
+            return 1;
         }
+        break;
+    }
 
+    return 0;
+}
+
+/**
+ * Input mapping function
+ */
+static int
+razer_input_mapping(struct hid_device *hdev, struct hid_input *hidinput,
+                    struct hid_field *field, struct hid_usage *usage,
+                    unsigned long **bit, int *max)
+{
+    /* Some higher nonstandard mouse buttons are reported in
+     * 15-element arrays on reports 4 and 5 with usage 0x10003. If
+     * hid-core tries to interpret this misshapen descriptor it will
+     * botch it and add spurious event codes to input->evkey. */
+    if (field->application == HID_UP_GENDESK
+        && usage->hid == (HID_UP_GENDESK | 0x0003)) {
+        return -1;
+    }
+    return 0;
+}
 
-        data[0] = 0x01;
-        data[1] = 0x00;
-        return 1;
+/**
+ * Input configured function
+ */
+static int razer_input_configured(struct hid_device *hdev,
+                                  struct hid_input *hidinput)
+{
+    struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+    struct razer_mouse_device *dev = hid_get_drvdata(hdev);
+
+    dev->input = hidinput->input;
+
+    if (intf->cur_altsetting->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) {
+        switch (hdev->product) {
+        case USB_DEVICE_ID_RAZER_BASILISK_V2:
+            /* Linux HID doesn't detect the Basilisk V2's tilt wheel
+             * or buttons beyond the first 5 */
+            input_set_capability(hidinput->input, EV_REL, REL_HWHEEL);
+            input_set_capability(hidinput->input, EV_KEY, BTN_FORWARD);
+            input_set_capability(hidinput->input, EV_KEY, BTN_BACK);
+            input_set_capability(hidinput->input, EV_KEY, BTN_TASK);
+            input_set_capability(hidinput->input, EV_KEY, BTN_MOUSE + 8);
+            input_set_capability(hidinput->input, EV_KEY, BTN_MOUSE + 9);
+            input_set_capability(hidinput->input, EV_KEY, BTN_MOUSE + 10);
+            break;
+        }
     }
 
     return 0;
@@ -3553,6 +3931,13 @@ static void razer_mouse_init(struct razer_mouse_device *dev, struct usb_interfac
     dev->da3_5g.dpi = 1; // 3500 DPI
     dev->da3_5g.profile = 1; // Profile 1
     dev->da3_5g.poll = 1; // Poll rate 1000
+
+    // Setup tilt wheel HWHEEL emulation
+    hrtimer_init(&dev->repeat_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+    dev->repeat_timer.function = wheel_tilt_repeat;
+    dev->tilt_hwheel = 1;
+    dev->tilt_repeat_delay = 250;
+    dev->tilt_repeat = 33;
 }
 
 /**
@@ -3654,6 +4039,11 @@ static int razer_mouse_probe(struct hid_device *hdev, const struct hid_device_id
             CREATE_DEVICE_FILE(&hdev->dev, &dev_attr_matrix_custom_frame);
             break;
 
+        case USB_DEVICE_ID_RAZER_BASILISK_V2:
+            CREATE_DEVICE_FILE(&hdev->dev, &dev_attr_tilt_hwheel);
+            CREATE_DEVICE_FILE(&hdev->dev, &dev_attr_tilt_repeat_delay);
+            CREATE_DEVICE_FILE(&hdev->dev, &dev_attr_tilt_repeat);
+        /* Fall through */
         case USB_DEVICE_ID_RAZER_DEATHADDER_ELITE:
         case USB_DEVICE_ID_RAZER_BASILISK:
         case USB_DEVICE_ID_RAZER_DEATHADDER_V2:
@@ -4169,6 +4559,11 @@ static void razer_mouse_disconnect(struct hid_device *hdev)
             device_remove_file(&hdev->dev, &dev_attr_matrix_custom_frame);
             break;
 
+        case USB_DEVICE_ID_RAZER_BASILISK_V2:
+            device_remove_file(&hdev->dev, &dev_attr_tilt_hwheel);
+            device_remove_file(&hdev->dev, &dev_attr_tilt_repeat_delay);
+            device_remove_file(&hdev->dev, &dev_attr_tilt_repeat);
+        /* Fall through */
         case USB_DEVICE_ID_RAZER_DEATHADDER_ELITE:
         case USB_DEVICE_ID_RAZER_BASILISK:
         case USB_DEVICE_ID_RAZER_DEATHADDER_V2:
@@ -4559,6 +4954,8 @@ static void razer_mouse_disconnect(struct hid_device *hdev)
 
 
     hid_hw_stop(hdev);
+    hrtimer_cancel(&dev->repeat_timer);
+
     kfree(dev);
     dev_info(&intf->dev, "Razer Device disconnected\n");
 }
@@ -4614,6 +5011,7 @@ static const struct hid_device_id razer_devices[] = {
     { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_VIPER_ULTIMATE_WIRED) },
     { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_VIPER_ULTIMATE_WIRELESS) },
     { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_BASILISK) },
+    { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_BASILISK_V2) },
     { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_DEATHADDER_V2) },
     { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_DEATHADDER_V2_PRO_WIRED) },
     { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,USB_DEVICE_ID_RAZER_DEATHADDER_V2_PRO_WIRELESS) },
@@ -4638,6 +5036,8 @@ static struct hid_driver razer_mouse_driver = {
     .remove    = razer_mouse_disconnect,
 
     .raw_event = razer_raw_event,
+    .input_mapping = razer_input_mapping,
+    .input_configured = razer_input_configured,
 };
 
 module_hid_driver(razer_mouse_driver);
diff --git a/driver/razermouse_driver.h b/driver/razermouse_driver.h
index aab8656eb75dc9deeecdac2b90165a756cac83ac..adf7a6928958ab3321107628b151f861c88c3d56 100644
--- a/driver/razermouse_driver.h
+++ b/driver/razermouse_driver.h
@@ -63,6 +63,7 @@
 #define USB_DEVICE_ID_RAZER_DEATHADDER_V2_PRO_WIRELESS 0x007D
 #define USB_DEVICE_ID_RAZER_BASILISK_X_HYPERSPEED 0x0083
 #define USB_DEVICE_ID_RAZER_DEATHADDER_V2 0x0084
+#define USB_DEVICE_ID_RAZER_BASILISK_V2 0x0085
 #define USB_DEVICE_ID_RAZER_VIPER_MINI 0x008A
 #define USB_DEVICE_ID_RAZER_DEATHADDER_V2_MINI 0x008C
 #define USB_DEVICE_ID_RAZER_NAGA_LEFT_HANDED_2020 0x008D
@@ -91,6 +92,16 @@
 struct razer_mouse_device {
     struct usb_device *usb_dev;
     struct mutex lock;
+
+    struct input_dev *input;
+    struct hrtimer repeat_timer;
+    unsigned int tilt_hwheel;
+    unsigned int tilt_repeat_delay;
+    unsigned int tilt_repeat;
+    __s32 hwheel_value;
+    u8 button_byte; // Previous value of mouse button byte in HID record
+    u8 rep4[16]; // Previous value of report 4 on the keyboard intf
+
     unsigned char usb_interface_protocol;
     unsigned char usb_interface_subclass;
 
diff --git a/install_files/appstream/io.github.openrazer.openrazer.metainfo.xml b/install_files/appstream/io.github.openrazer.openrazer.metainfo.xml
index 46af1acb51868f05d627fc697f04f2aa2a277a09..a9b5f585713b8272498138d23b7cda22a5444e25 100644
--- a/install_files/appstream/io.github.openrazer.openrazer.metainfo.xml
+++ b/install_files/appstream/io.github.openrazer.openrazer.metainfo.xml
@@ -67,6 +67,7 @@
     <modalias>usb:v1532p007Dd*</modalias>
     <modalias>usb:v1532p0083d*</modalias>
     <modalias>usb:v1532p0084d*</modalias>
+    <modalias>usb:v1532p0085d*</modalias>
     <modalias>usb:v1532p008Ad*</modalias>
     <modalias>usb:v1532p008Cd*</modalias>
     <modalias>usb:v1532p008Dd*</modalias>
diff --git a/install_files/udev/99-razer.rules b/install_files/udev/99-razer.rules
index d5b9f646068640fcaa2af1052b47c100c12d972a..4155ce42e2104c4f595b6dfb9e987870b398aa06 100644
--- a/install_files/udev/99-razer.rules
+++ b/install_files/udev/99-razer.rules
@@ -5,7 +5,7 @@ GOTO="razer_end"
 LABEL="razer_vendor"
 
 # Mice
-ATTRS{idProduct}=="0013|0016|0020|0024|0025|002e|002f|0032|0034|0036|0037|0038|0039|0040|0041|0042|0043|0044|0045|0046|0048|004c|004f|0050|0053|0054|0059|005a|005b|005c|005e|0060|0062|0064|0067|006a|006b|006c|006e|006f|0070|0071|0072|0073|0078|007a|007b|007c|007d|0083|0084|008a|008c|008d", \
+ATTRS{idProduct}=="0013|0016|0020|0024|0025|002e|002f|0032|0034|0036|0037|0038|0039|0040|0041|0042|0043|0044|0045|0046|0048|004c|004f|0050|0053|0054|0059|005a|005b|005c|005e|0060|0062|0064|0067|006a|006b|006c|006e|006f|0070|0071|0072|0073|0078|007a|007b|007c|007d|0083|0084|0085|008a|008c|008d", \
     ATTRS{idVendor}=="1532", \
     ENV{ID_RAZER_CHROMA}="1", ENV{RAZER_DRIVER}="razermouse"
 
diff --git a/pylib/openrazer/_fake_driver/razerbasiliskv2.cfg b/pylib/openrazer/_fake_driver/razerbasiliskv2.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..925f02243722d81815cf290cc7a42cdaf077e5e2
--- /dev/null
+++ b/pylib/openrazer/_fake_driver/razerbasiliskv2.cfg
@@ -0,0 +1,26 @@
+[device]
+dir_name = 0003:1532:0085.0001
+name = Razer Basilisk V2
+files = r,device_serial,XX0000000085
+        r,device_type,%(name)s
+        rw,dpi,800:800
+        r,firmware_version,v1.0
+        rw,logo_led_brightness,0
+        w,logo_matrix_effect_breath
+        w,logo_matrix_effect_none
+        w,logo_matrix_effect_reactive
+        w,logo_matrix_effect_spectrum
+        w,logo_matrix_effect_static
+        w,matrix_custom_frame
+        w,matrix_effect_custom
+        rw,poll_rate,500
+        rw,scroll_led_brightness,0
+        w,scroll_matrix_effect_breath
+        w,scroll_matrix_effect_none
+        w,scroll_matrix_effect_reactive
+        w,scroll_matrix_effect_spectrum
+        w,scroll_matrix_effect_static
+        rw,tilt_hwheel,0
+        rw,tilt_repeat,0
+        rw,tilt_repeat_delay,0
+        r,version,1.0.0
diff --git a/scripts/generate_fake_driver.sh b/scripts/generate_fake_driver.sh
index dd5e8b313e40c8d1a652a6bd9e14dd031eb1587b..e50a5417f464af207de30d414168cc86c111ec7e 100755
--- a/scripts/generate_fake_driver.sh
+++ b/scripts/generate_fake_driver.sh
@@ -71,6 +71,9 @@ declare -A files_metadata=(
     ["scroll_matrix_effect_spectrum"]="w;"
     ["scroll_matrix_effect_static"]="w;"
     ["scroll_matrix_effect_wave"]="w;"
+    ["tilt_hwheel"]="rw;0"
+    ["tilt_repeat"]="rw;0"
+    ["tilt_repeat_delay"]="rw;0"
     ["version"]="r;1.0.0"
 )