new file mode 100644
@@ -0,0 +1,239 @@
+From 84c7d189d0f4227f7b249a73c2c1bd26ed4f46a6 Mon Sep 17 00:00:00 2001
+From: jaap aarts <jaap.aarts1@gmail.com>
+Date: Tue, 30 Jun 2020 13:46:14 +0200
+Subject: [PATCH 1/5] skeleton for asetek gen 6 driver
+
+---
+ drivers/hwmon/Kconfig | 6 ++
+ drivers/hwmon/Makefile | 1 +
+ drivers/hwmon/asetek_gen6.c | 186 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 193 insertions(+)
+ create mode 100644 drivers/hwmon/asetek_gen6.c
+
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index 288ae9f63588..521a9e0c88ca 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -378,6 +378,12 @@ config SENSORS_ARM_SCPI
+ and power sensors available on ARM Ltd's SCP based platforms. The
+ actual number and type of sensors exported depend on the platform.
+
++config SENSORS_ASETEK_GEN6
++ tristate "Asetek generation 6 driver"
++ help
++ If you say yes here you get support for asetek generation 6 boards
++ found on most AIO liquid coolers with an asetek pump.
++
+ config SENSORS_ASB100
+ tristate "Asus ASB100 Bach"
+ depends on X86 && I2C
+diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
+index 3e32c21f5efe..9683d2aae2b2 100644
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -20,6 +20,7 @@ obj-$(CONFIG_SENSORS_W83793) += w83793.o
+ obj-$(CONFIG_SENSORS_W83795) += w83795.o
+ obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
+ obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
++obj-$(CONFIG_SENSORS_ASETEK_GEN6) += asetek_gen6.o
+
+ obj-$(CONFIG_SENSORS_AB8500) += abx500.o ab8500.o
+ obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
+diff --git a/drivers/hwmon/asetek_gen6.c b/drivers/hwmon/asetek_gen6.c
+new file mode 100644
+index 000000000000..4d530a4cb71d
+--- /dev/null
++++ b/drivers/hwmon/asetek_gen6.c
+@@ -0,0 +4,189 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * A hwmon driver for most asetek gen 6 all-in-one liquid coolers.
++ * Copyright (c) Jaap Aarts 2020
++ *
++ * Protocol reverse engineered by audiohacked
++ * https://github.com/audiohacked/OpenCorsairLink
++ */
++
++/*
++ * Supports following chips:
++ * driver h100i platinum
++ *
++ * Other products should work with this driver but no testing has been done.
++ *
++ * Note: platinum is the codename name for pro within driver
++ *
++ * Note: fan curve control has not been implemented
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/usb.h>
++#include <linux/slab.h>
++#include <linux/mutex.h>
++#include <linux/errno.h>
++#include <linux/hwmon.h>
++#include <linux/hwmon-sysfs.h>
++
++struct driver {
++ struct usb_device *udev;
++
++ char *bulk_out_buffer;
++ char *bulk_in_buffer;
++ size_t bulk_out_size;
++ size_t bulk_in_size;
++ char bulk_in_endpointAddr;
++ char bulk_out_endpointAddr;
++
++ struct usb_interface *interface; /* the interface for this device */
++ struct mutex io_mutex; /* synchronize I/O with disconnect */
++ struct semaphore
++ limit_sem; /* limiting the number of writes in progress */
++
++ struct kref kref;
++};
++
++/* devices that work with this driver */
++static const struct usb_device_id astk_table[] = {
++ { USB_DEVICE(0x1b1c, 0x0c15) },
++ {} /* Terminating entry */
++};
++
++MODULE_DEVICE_TABLE(usb, astk_table);
++
++int init_device(struct usb_device *udev)
++{
++ int retval;
++
++ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x40,
++ 0xffff, 0x0000, 0, 0, 0);
++ //this always returns error
++ if (retval != 0)
++ ;
++
++ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x02, 0x40,
++ 0x0002, 0x0000, 0, 0, 0);
++ if (retval != 0)
++ return retval;
++
++ return 0;
++}
++int deinit_device(struct usb_device *udev)
++{
++ int retval;
++
++ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x02, 0x40,
++ 0x0004, 0x0000, 0, 0, 0);
++ if (retval != 0)
++ return retval;
++
++ return 0;
++}
++
++static void astk_delete(struct kref *kref)
++{
++ struct driver *dev = container_of(kref, struct driver, kref);
++
++ usb_put_intf(dev->interface);
++ usb_put_dev(dev->udev);
++ kfree(dev->bulk_in_buffer);
++ kfree(dev->bulk_out_buffer);
++ kfree(dev);
++}
++
++static int astk_probe(struct usb_interface *interface,
++ const struct usb_device_id *id)
++{
++ struct driver *dev;
++ int retval = 0;
++ struct usb_endpoint_descriptor *bulk_in, *bulk_out;
++
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++ if (!dev) {
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ retval = usb_find_common_endpoints(interface->cur_altsetting, &bulk_in,
++ &bulk_out, NULL, NULL);
++ if (retval != 0)
++ goto exit;
++
++ dev->udev = usb_get_dev(interface_to_usbdev(interface));
++ dev->interface = usb_get_intf(interface);
++
++ /* set up the endpoint information */
++ /* use only the first bulk-in and bulk-out endpoints */
++ dev->bulk_in_size = usb_endpoint_maxp(bulk_in);
++ dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);
++ dev->bulk_in_endpointAddr = bulk_in->bEndpointAddress;
++ dev->bulk_out_size = usb_endpoint_maxp(bulk_out);
++ dev->bulk_out_buffer = kmalloc(dev->bulk_out_size, GFP_KERNEL);
++ dev->bulk_out_endpointAddr = bulk_out->bEndpointAddress;
++
++ //hwmon_init(dev);
++ retval = init_device(dev->udev);
++ if (retval) {
++ dev_err(&interface->dev, "Failled initialising this device.\n");
++ goto exit;
++ }
++
++ usb_set_intfdata(interface, dev);
++ kref_init(&dev->kref);
++ mutex_init(&dev->io_mutex);
++ sema_init(&dev->limit_sem, 8);
++exit:
++ return retval;
++}
++
++static void astk_disconnect(struct usb_interface *interface)
++{
++ struct driver *dev = usb_get_intfdata(interface);
++ //hwmon_deinit(dev);
++ usb_set_intfdata(interface, NULL);
++ kref_put(&dev->kref, astk_delete);
++ deinit_device(dev->udev);
++ /* let the user know what node this device is now attached to */
++}
++static int astk_resume(struct usb_interface *intf)
++{
++ return 0;
++}
++
++static int astk_suspend(struct usb_interface *intf, pm_message_t message)
++{
++ return 0;
++}
++
++static struct usb_driver astk_driver = {
++ .name = "Asetek gen6 driver",
++ .id_table = astk_table,
++ .probe = astk_probe,
++ .disconnect = astk_disconnect,
++ .resume = astk_resume,
++ .suspend = astk_suspend,
++ .supports_autosuspend = 1,
++};
++
++static int __init astk_init(void)
++{
++ int ret = -1;
++
++ ret = usb_register(&astk_driver);
++
++ return ret;
++}
++
++static void __exit astk_exit(void)
++{
++ usb_deregister(&astk_driver);
++}
++
++module_init(astk_init);
++module_exit(astk_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Jaap Aarts <jaap.aarts1@gmail.com>");
++MODULE_DESCRIPTION("Asetek gen6 driver");
+--
+2.27.0
+
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-or-later
-/*
+/*
* A hwmon driver for most asetek gen 6 all-in-one liquid coolers.
* Copyright (c) Jaap Aarts 2020
*
@@ -15,7 +15,7 @@
*
* Note: platinum is the codename name for pro within driver, so h100i platinum = h1ooi pro
*
- * Note: fan curve controll has not been implemented
+ * Note: fan curve control has not been implemented
*/
#include <linux/module.h>
@@ -158,6 +158,7 @@ struct curve_point extreme_curve[] = {
};
#define default_curve quiet_curve
+
static const char SUCCESS[2] = { 0x12, 0x34 };
#define SUCCES_LENGTH 3
@@ -211,6 +212,7 @@ int set_fan_rpm_curve(struct driver *cdev, struct hwmon_fan_data *fan_data,
recv_buf[0], recv_buf[1], recv_buf[2], recv_buf[3]);
return 0;
}
+
int set_fan_target_rpm(struct driver *cdev, struct hwmon_fan_data *fan_data,
long val)
{
@@ -244,10 +246,12 @@ int set_fan_target_rpm(struct driver *cdev, struct hwmon_fan_data *fan_data,
recv_buf[0], recv_buf[1], recv_buf[2], recv_buf[3]);
return 0;
}
+
void get_fan_target_rpm(struct hwmon_fan_data *fan_data, long *val)
{
*val = fan_data->fan_target;
}
+
int get_fan_current_rpm(struct driver *cdev, struct hwmon_fan_data *fan_data,
long *val)
{
@@ -272,7 +276,7 @@ int get_fan_current_rpm(struct driver *cdev, struct hwmon_fan_data *fan_data,
if (!check_succes(0x41, recv_buf) ||
recv_buf[3] != fan_data->fan_channel)
- printk(KERN_INFO "[*] Failled retreiving fan rmp %d,%d,%d/%d\n",
+ printk(KERN_INFO "[*] Failled retrieving fan rmp %d,%d,%d/%d\n",
recv_buf[0], recv_buf[1], recv_buf[2], recv_buf[3]);
*val = (((uint8_t)recv_buf[4]) << 8) + (uint8_t)recv_buf[5];
@@ -470,6 +474,7 @@ static int write_func(struct device *dev, enum hwmon_sensor_types type,
exit:
return retval;
}
+
int read_func(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
{
@@ -540,8 +545,9 @@ int read_func(struct device *dev, enum hwmon_sensor_types type, u32 attr,
exit:
return retval;
}
-#define fan_config HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_MIN
-#define pwm_config HWMON_PWM_INPUT | HWMON_PWM_ENABLE
+
+#define fan_config (HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_MIN)
+#define pwm_config (HWMON_PWM_INPUT | HWMON_PWM_ENABLE)
static const struct hwmon_ops asetek_6_ops = {
.is_visible = is_visible_func,
@@ -580,8 +586,8 @@ bool does_fan_exist(struct driver *cdev, int channel)
int get_fan_count(struct driver *dev)
{
int fan;
- for (fan = 0; does_fan_exist(dev, fan); fan += 1) {
- }
+ for (fan = 0; does_fan_exist(dev, fan); fan += 1)
+ ;
return fan;
}
@@ -594,10 +600,13 @@ void hwmon_init(struct driver *dev)
&dev->udev->dev, sizeof(struct hwmon_data), GFP_KERNEL);
struct hwmon_chip_info *hwmon_info = devm_kzalloc(
&dev->udev->dev, sizeof(struct hwmon_chip_info), GFP_KERNEL);
+ //Allocate the info table
struct hwmon_channel_info **aio_info =
devm_kzalloc(&dev->udev->dev,
sizeof(struct hwmon_channel_info *) * 2,
- GFP_KERNEL); //2==amount of channel infos.
+ GFP_KERNEL); //2 for each channel info.
+
+ //Allocate the fan and PWM configuration
u32 *fans_config = devm_kzalloc(&dev->udev->dev,
sizeof(u32) * (data->channel_count + 1),
GFP_KERNEL);
@@ -610,6 +619,7 @@ void hwmon_init(struct driver *dev)
devm_kzalloc(&dev->udev->dev,
sizeof(char *) * data->channel_count, GFP_KERNEL);
+ //For each fan create a data channel a fan config entry and a pwm config entry
for (fan_id = 0; fan_id <= data->channel_count; fan_id++) {
fan = devm_kzalloc(&dev->udev->dev,
sizeof(struct hwmon_fan_data), GFP_KERNEL);
@@ -648,8 +658,8 @@ void hwmon_deinit(struct driver *dev)
}
/*
- Devices that work with this driver.
- More devices should work, however none have been tested.
+ * Devices that work with this driver.
+ * More devices should work, however none have been tested.
*/
static const struct usb_device_id astk_table[] = {
{ USB_DEVICE(0x1b1c, 0x0c15) },
@@ -674,6 +684,7 @@ int init_device(struct usb_device *udev)
return 0;
}
+
int deinit_device(struct usb_device *udev)
{
int retval;
Signed-off-by: Jaap Aarts <jaap.aarts1@example.com> --- ...driver.patch.EXPERIMENTAL-checkpatch-fixes | 239 ++++++++++++++++++ drivers/hwmon/asetek_gen6.c | 31 ++- 2 files changed, 260 insertions(+), 10 deletions(-) create mode 100644 0001-skeleton-for-asetek-gen-6-driver.patch.EXPERIMENTAL-checkpatch-fixes