lkml.org 
[lkml]   [2022]   [Feb]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: [PATCH] hwmon: add driver for Aquacomputer Farbwerk 360
Hi,

On 2/21/22 02:21, Aleksa Savic wrote:
> This driver exposes hardware temperature sensors of the Aquacomputer
> Farbwerk 360 RGB controller, which communicates through a proprietary
> USB HID protocol.
>
> Four temperature sensors are available. If a sensor is not connected,
> it will report zeroes. Additionally, serial number and firmware version
> are exposed through debugfs.
>
> This driver has been tested on x86_64.
>
> Signed-off-by: Aleksa Savic <savicaleksa83@gmail.com>

Unless I amm missing something, this driver is quite similar to the
d5next driver (drivers/hwmon/aquacomputer_d5next.c), except that there
are 4 instead of 1 temperature sensor, the sensor data is in a different place,
and there is _only_ temperature data. It should be quite straightforward
to merge the two drivers into one, so please do that.

Additional comment inline.

Thanks,
Guenter

> ---
> .../hwmon/aquacomputer_farbwerk360.rst | 40 +++
> Documentation/hwmon/index.rst | 1 +
> MAINTAINERS | 7 +
> drivers/hwmon/Kconfig | 10 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/aquacomputer_farbwerk360.c | 261 ++++++++++++++++++
> 6 files changed, 320 insertions(+)
> create mode 100644 Documentation/hwmon/aquacomputer_farbwerk360.rst
> create mode 100644 drivers/hwmon/aquacomputer_farbwerk360.c
>
> diff --git a/Documentation/hwmon/aquacomputer_farbwerk360.rst b/Documentation/hwmon/aquacomputer_farbwerk360.rst
> new file mode 100644
> index 000000000000..cebaffccd818
> --- /dev/null
> +++ b/Documentation/hwmon/aquacomputer_farbwerk360.rst
> @@ -0,0 +1,40 @@
> +.. SPDX-License-Identifier: GPL-2.0-or-later
> +
> +Kernel driver aquacomputer_farbwerk360
> +======================================
> +
> +Supported devices:
> +
> +* Aquacomputer Farbwerk 360 RGB controller
> +
> +Author: Aleksa Savic
> +
> +Description
> +-----------
> +
> +This driver exposes hardware temperature sensors of the Aquacomputer Farbwerk 360
> +RGB controller, which communicates through a proprietary USB HID protocol.
> +
> +Four temperature sensors are available. If a sensor is not connected, it will report
> +zeroes. Additionally, serial number and firmware version are exposed through debugfs.
> +
> +Usage notes
> +-----------
> +
> +Farbwerk 360 communicates via HID reports. The driver is loaded automatically by
> +the kernel and supports hotswapping.
> +
> +Sysfs entries
> +-------------
> +
> +=============== ==============================================
> +temp[1-4]_input Measured temperature (in millidegrees Celsius)
> +=============== ==============================================
> +
> +Debugfs entries
> +---------------
> +
> +================ ===============================================
> +serial_number Serial number of the pump
> +firmware_version Version of installed firmware
> +================ ===============================================
> diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
> index df20022c741f..42b7369340ba 100644
> --- a/Documentation/hwmon/index.rst
> +++ b/Documentation/hwmon/index.rst
> @@ -40,6 +40,7 @@ Hardware Monitoring Kernel Drivers
> aht10
> amc6821
> aquacomputer_d5next
> + aquacomputer_farbwerk360
> asb100
> asc7621
> aspeed-pwm-tacho
> diff --git a/MAINTAINERS b/MAINTAINERS
> index bd86ed9fbc79..fb8b8d7aebbc 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1386,6 +1386,13 @@ S: Maintained
> F: Documentation/hwmon/aquacomputer_d5next.rst
> F: drivers/hwmon/aquacomputer_d5next.c
>
> +AQUACOMPUTER FARBWERK 360 RGB CONTROLLER SENSOR DRIVER
> +M: Aleksa Savic <savicaleksa83@gmail.com>
> +L: linux-hwmon@vger.kernel.org
> +S: Maintained
> +F: Documentation/hwmon/aquacomputer_farbwerk360.rst
> +F: drivers/hwmon/aquacomputer_farbwerk360.c
> +
> AQUANTIA ETHERNET DRIVER (atlantic)
> M: Igor Russkikh <irusskikh@marvell.com>
> L: netdev@vger.kernel.org
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 8df25f1079ba..e1ca5e1e6ab0 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -264,6 +264,16 @@ config SENSORS_AQUACOMPUTER_D5NEXT
> This driver can also be built as a module. If so, the module
> will be called aquacomputer_d5next.
>
> +config SENSORS_AQUACOMPUTER_FARBWERK360
> + tristate "Aquacomputer Farbwerk 360 RGB controller"
> + depends on USB_HID
> + help
> + If you say yes here you get support for temperature sensors provided by
> + the Aquacomputer Farbwerk 360 RGB controller.
> +
> + This driver can also be built as a module. If so, the module
> + will be called aquacomputer_farbwerk360.
> +
> config SENSORS_AS370
> tristate "Synaptics AS370 SoC hardware monitoring driver"
> help
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 185f946d698b..1c1556a53f6d 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
> obj-$(CONFIG_SENSORS_AHT10) += aht10.o
> obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
> obj-$(CONFIG_SENSORS_AQUACOMPUTER_D5NEXT) += aquacomputer_d5next.o
> +obj-$(CONFIG_SENSORS_AQUACOMPUTER_FARBWERK360) += aquacomputer_farbwerk360.o
> obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o
> obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
> obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o
> diff --git a/drivers/hwmon/aquacomputer_farbwerk360.c b/drivers/hwmon/aquacomputer_farbwerk360.c
> new file mode 100644
> index 000000000000..14b760a2c8a8
> --- /dev/null
> +++ b/drivers/hwmon/aquacomputer_farbwerk360.c
> @@ -0,0 +1,261 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * hwmon driver for Aquacomputer Farbwerk 360 (RGB controller)
> + *
> + * The Farbwerk 360 sends HID reports (with ID 0x01) every second to report sensor values
> + * of up to four connected temperature sensors.
> + *
> + * Copyright 2022 Aleksa Savic <savicaleksa83@gmail.com>
> + */
> +
> +#include <asm/unaligned.h>

asm includes should be last (ah, my bad, this is from the d5 driver.
but still, just for the future).

> +#include <linux/debugfs.h>
> +#include <linux/hid.h>
> +#include <linux/hwmon.h>
> +#include <linux/jiffies.h>
> +#include <linux/module.h>
> +#include <linux/seq_file.h>
> +
> +#define DRIVER_NAME "aquacomputer_farbwerk360"
> +
> +#define STATUS_REPORT_ID 0x01
> +#define STATUS_UPDATE_INTERVAL (2 * HZ) /* In seconds */
> +
> +/* Register offsets */
> +#define SERIAL_FIRST_PART 0x03
> +#define SERIAL_SECOND_PART 0x05
> +#define FIRMWARE_VERSION 0x0D
> +
> +#define NUM_SENSORS 4
> +#define SENSOR_START 0x32
> +#define SENSOR_SIZE 0x02
> +#define SENSOR_DISCONNECTED 0x7FFF
> +
> +static const char *const label_temps[] = { "Sensor 1", "Sensor 2", "Sensor 3", "Sensor 4" };
> +
> +struct farbwerk360_data {
> + struct hid_device *hdev;
> + struct device *hwmon_dev;
> + struct dentry *debugfs;
> + s32 temp_input[4];
> + u32 serial_number[2];
> + u16 firmware_version;
> + unsigned long updated;
> +};
> +
> +static umode_t farbwerk360_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
> + int channel)
> +{
> + return 0444;
> +}
> +
> +static int farbwerk360_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
> + long *val)
> +{
> + struct farbwerk360_data *priv = dev_get_drvdata(dev);
> +
> + if (time_after(jiffies, priv->updated + STATUS_UPDATE_INTERVAL))
> + return -ENODATA;
> +
> + switch (type) {
> + case hwmon_temp:
> + *val = priv->temp_input[channel];
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static int farbwerk360_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> + int channel, const char **str)
> +{
> + switch (type) {
> + case hwmon_temp:
> + *str = label_temps[channel];
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static const struct hwmon_ops farbwerk360_hwmon_ops = {
> + .is_visible = farbwerk360_is_visible,
> + .read = farbwerk360_read,
> + .read_string = farbwerk360_read_string,
> +};
> +
> +static const struct hwmon_channel_info *farbwerk360_info[] = {
> + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
> + HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL),
> + NULL
> +};
> +
> +static const struct hwmon_chip_info farbwerk360_chip_info = {
> + .ops = &farbwerk360_hwmon_ops,
> + .info = farbwerk360_info,
> +};
> +
> +static int farbwerk360_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
> + int size)
> +{
> + int i, sensor_value;
> + struct farbwerk360_data *priv;
> +
> + if (report->id != STATUS_REPORT_ID)
> + return 0;
> +
> + priv = hid_get_drvdata(hdev);
> +
> + /* Info provided with every report */
> + priv->serial_number[0] = get_unaligned_be16(data + SERIAL_FIRST_PART);
> + priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART);
> +
> + priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION);
> +
> + /* Temperature sensor readings */
> + for (i = 0; i < NUM_SENSORS; i++) {
> + sensor_value = get_unaligned_be16(data + SENSOR_START + i * SENSOR_SIZE);
> + if (sensor_value == SENSOR_DISCONNECTED)
> + sensor_value = 0;

If it is known that a sensor is disconnected, it would make
more sense to report -ENODATA when it is read instead of a
temperature of 0 degrees.

> +
> + priv->temp_input[i] = sensor_value * 10;
> + }
> +
> + priv->updated = jiffies;
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +
> +static int serial_number_show(struct seq_file *seqf, void *unused)
> +{
> + struct farbwerk360_data *priv = seqf->private;
> +
> + seq_printf(seqf, "%05u-%05u\n", priv->serial_number[0], priv->serial_number[1]);
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(serial_number);
> +
> +static int firmware_version_show(struct seq_file *seqf, void *unused)
> +{
> + struct farbwerk360_data *priv = seqf->private;
> +
> + seq_printf(seqf, "%u\n", priv->firmware_version);
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(firmware_version);
> +
> +static void farbwerk360_debugfs_init(struct farbwerk360_data *priv)
> +{
> + char name[32];
> +
> + scnprintf(name, sizeof(name), "%s-%s", DRIVER_NAME, dev_name(&priv->hdev->dev));
> +
> + priv->debugfs = debugfs_create_dir(name, NULL);
> + debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops);
> + debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops);
> +}
> +
> +#else
> +
> +static void farbwerk360_debugfs_init(struct farbwerk360_data *priv)
> +{
> +}
> +
> +#endif
> +
> +static int farbwerk360_probe(struct hid_device *hdev, const struct hid_device_id *id)
> +{
> + int ret;
> + struct farbwerk360_data *priv;
> +
> + priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->hdev = hdev;
> + hid_set_drvdata(hdev, priv);
> +
> + priv->updated = jiffies - STATUS_UPDATE_INTERVAL;
> +
> + ret = hid_parse(hdev);
> + if (ret)
> + return ret;
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> + if (ret)
> + return ret;
> +
> + ret = hid_hw_open(hdev);
> + if (ret)
> + goto fail_and_stop;
> +
> + priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "farbwerk360", priv,
> + &farbwerk360_chip_info, NULL);
> +
> + if (IS_ERR(priv->hwmon_dev)) {
> + ret = PTR_ERR(priv->hwmon_dev);
> + goto fail_and_close;
> + }
> +
> + farbwerk360_debugfs_init(priv);
> +
> + return 0;
> +
> +fail_and_close:
> + hid_hw_close(hdev);
> +fail_and_stop:
> + hid_hw_stop(hdev);
> + return ret;
> +}
> +
> +static void farbwerk360_remove(struct hid_device *hdev)
> +{
> + struct farbwerk360_data *priv = hid_get_drvdata(hdev);
> +
> + debugfs_remove_recursive(priv->debugfs);
> + hwmon_device_unregister(priv->hwmon_dev);
> +
> + hid_hw_close(hdev);
> + hid_hw_stop(hdev);
> +}
> +
> +static const struct hid_device_id farbwerk360_table[] = {
> + { HID_USB_DEVICE(0x0c70, 0xf010) }, /* Aquacomputer Farbwerk 360 */
> + {},
> +};
> +
> +MODULE_DEVICE_TABLE(hid, farbwerk360_table);
> +
> +static struct hid_driver farbwerk360_driver = {
> + .name = DRIVER_NAME,
> + .id_table = farbwerk360_table,
> + .probe = farbwerk360_probe,
> + .remove = farbwerk360_remove,
> + .raw_event = farbwerk360_raw_event,
> +};
> +
> +static int __init farbwerk360_init(void)
> +{
> + return hid_register_driver(&farbwerk360_driver);
> +}
> +
> +static void __exit farbwerk360_exit(void)
> +{
> + hid_unregister_driver(&farbwerk360_driver);
> +}
> +
> +/* Request to initialize after the HID bus to ensure it's not being loaded before */
> +late_initcall(farbwerk360_init);
> +module_exit(farbwerk360_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
> +MODULE_DESCRIPTION("Hwmon driver for Aquacomputer Farbwerk 360");

\
 
 \ /
  Last update: 2022-02-24 20:59    [W:0.095 / U:0.472 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site