linux/drivers/hwmon/corsair-psu.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * corsair-psu.c - Linux driver for Corsair power supplies with HID sensors interface
 * Copyright (C) 2020 Wilken Gottwalt <[email protected]>
 */

#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/errno.h>
#include <linux/hid.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/types.h>

/*
 * Corsair protocol for PSUs
 *
 * message size = 64 bytes (request and response, little endian)
 * request:
 *	[length][command][param0][param1][paramX]...
 * reply:
 *	[echo of length][echo of command][data0][data1][dataX]...
 *
 *	- commands are byte sized opcodes
 *	- length is the sum of all bytes of the commands/params
 *	- the micro-controller of most of these PSUs support concatenation in the request and reply,
 *	  but it is better to not rely on this (it is also hard to parse)
 *	- the driver uses raw events to be accessible from userspace (though this is not really
 *	  supported, it is just there for convenience, may be removed in the future)
 *	- a reply always starts with the length and command in the same order the request used it
 *	- length of the reply data is specific to the command used
 *	- some of the commands work on a rail and can be switched to a specific rail (0 = 12v,
 *	  1 = 5v, 2 = 3.3v)
 *	- the format of the init command 0xFE is swapped length/command bytes
 *	- parameter bytes amount and values are specific to the command (rail setting is the only
 *	  one for now that uses non-zero values)
 *	- the driver supports debugfs for values not fitting into the hwmon class
 *	- not every device class (HXi or RMi) supports all commands
 *	- if configured wrong the PSU resets or shuts down, often before actually hitting the
 *	  reported critical temperature
 *	- new models like HX1500i Series 2023 have changes in the reported vendor and product
 *	  strings, both are slightly longer now, report vendor and product in one string and are
 *	  the same now
 */

#define DRIVER_NAME

#define REPLY_SIZE
#define CMD_BUFFER_SIZE
#define CMD_TIMEOUT_MS
#define SECONDS_PER_HOUR
#define SECONDS_PER_DAY
#define RAIL_COUNT
#define TEMP_COUNT
#define OCP_MULTI_RAIL

#define PSU_CMD_SELECT_RAIL
#define PSU_CMD_FAN_PWM
#define PSU_CMD_RAIL_VOLTS_HCRIT
#define PSU_CMD_RAIL_VOLTS_LCRIT
#define PSU_CMD_RAIL_AMPS_HCRIT
#define PSU_CMD_TEMP_HCRIT
#define PSU_CMD_IN_VOLTS
#define PSU_CMD_IN_AMPS
#define PSU_CMD_RAIL_VOLTS
#define PSU_CMD_RAIL_AMPS
#define PSU_CMD_TEMP0
#define PSU_CMD_TEMP1
#define PSU_CMD_FAN
#define PSU_CMD_RAIL_WATTS
#define PSU_CMD_VEND_STR
#define PSU_CMD_PROD_STR
#define PSU_CMD_TOTAL_UPTIME
#define PSU_CMD_UPTIME
#define PSU_CMD_OCPMODE
#define PSU_CMD_TOTAL_WATTS
#define PSU_CMD_FAN_PWM_ENABLE
#define PSU_CMD_INIT

#define L_IN_VOLTS
#define L_OUT_VOLTS_12V
#define L_OUT_VOLTS_5V
#define L_OUT_VOLTS_3_3V
#define L_IN_AMPS
#define L_AMPS_12V
#define L_AMPS_5V
#define L_AMPS_3_3V
#define L_FAN
#define L_TEMP0
#define L_TEMP1
#define L_WATTS
#define L_WATTS_12V
#define L_WATTS_5V
#define L_WATTS_3_3V

static const char *const label_watts[] =;

static const char *const label_volts[] =;

static const char *const label_amps[] =;

struct corsairpsu_data {};

/* some values are SMBus LINEAR11 data which need a conversion */
static int corsairpsu_linear11_to_int(const u16 val, const int scale)
{}

/* the micro-controller uses percentage values to control pwm */
static int corsairpsu_dutycycle_to_pwm(const long dutycycle)
{}

static int corsairpsu_usb_cmd(struct corsairpsu_data *priv, u8 p0, u8 p1, u8 p2, void *data)
{}

static int corsairpsu_init(struct corsairpsu_data *priv)
{}

static int corsairpsu_fwinfo(struct corsairpsu_data *priv)
{}

static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, void *data)
{}

static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, long *val)
{}

static void corsairpsu_get_criticals(struct corsairpsu_data *priv)
{}

static void corsairpsu_check_cmd_support(struct corsairpsu_data *priv)
{}

static umode_t corsairpsu_hwmon_temp_is_visible(const struct corsairpsu_data *priv, u32 attr,
						int channel)
{}

static umode_t corsairpsu_hwmon_fan_is_visible(const struct corsairpsu_data *priv, u32 attr,
					       int channel)
{}

static umode_t corsairpsu_hwmon_pwm_is_visible(const struct corsairpsu_data *priv, u32 attr,
					       int channel)
{}

static umode_t corsairpsu_hwmon_power_is_visible(const struct corsairpsu_data *priv, u32 attr,
						 int channel)
{}

static umode_t corsairpsu_hwmon_in_is_visible(const struct corsairpsu_data *priv, u32 attr,
					      int channel)
{}

static umode_t corsairpsu_hwmon_curr_is_visible(const struct corsairpsu_data *priv, u32 attr,
						int channel)
{}

static umode_t corsairpsu_hwmon_ops_is_visible(const void *data, enum hwmon_sensor_types type,
					       u32 attr, int channel)
{}

static int corsairpsu_hwmon_temp_read(struct corsairpsu_data *priv, u32 attr, int channel,
				      long *val)
{}

static int corsairpsu_hwmon_pwm_read(struct corsairpsu_data *priv, u32 attr, int channel, long *val)
{}

static int corsairpsu_hwmon_power_read(struct corsairpsu_data *priv, u32 attr, int channel,
				       long *val)
{}

static int corsairpsu_hwmon_in_read(struct corsairpsu_data *priv, u32 attr, int channel, long *val)
{}

static int corsairpsu_hwmon_curr_read(struct corsairpsu_data *priv, u32 attr, int channel,
				      long *val)
{}

static int corsairpsu_hwmon_ops_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
				     int channel, long *val)
{}

static int corsairpsu_hwmon_ops_read_string(struct device *dev, enum hwmon_sensor_types type,
					    u32 attr, int channel, const char **str)
{}

static const struct hwmon_ops corsairpsu_hwmon_ops =;

static const struct hwmon_channel_info *const corsairpsu_info[] =;

static const struct hwmon_chip_info corsairpsu_chip_info =;

#ifdef CONFIG_DEBUG_FS

static void print_uptime(struct seq_file *seqf, u8 cmd)
{}

static int uptime_show(struct seq_file *seqf, void *unused)
{}
DEFINE_SHOW_ATTRIBUTE();

static int uptime_total_show(struct seq_file *seqf, void *unused)
{}
DEFINE_SHOW_ATTRIBUTE();

static int vendor_show(struct seq_file *seqf, void *unused)
{}
DEFINE_SHOW_ATTRIBUTE();

static int product_show(struct seq_file *seqf, void *unused)
{}
DEFINE_SHOW_ATTRIBUTE();

static int ocpmode_show(struct seq_file *seqf, void *unused)
{}
DEFINE_SHOW_ATTRIBUTE();

static void corsairpsu_debugfs_init(struct corsairpsu_data *priv)
{}

#else

static void corsairpsu_debugfs_init(struct corsairpsu_data *priv)
{
}

#endif

static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id *id)
{}

static void corsairpsu_remove(struct hid_device *hdev)
{}

static int corsairpsu_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
				int size)
{}

#ifdef CONFIG_PM
static int corsairpsu_resume(struct hid_device *hdev)
{}
#endif

static const struct hid_device_id corsairpsu_idtable[] =;
MODULE_DEVICE_TABLE(hid, corsairpsu_idtable);

static struct hid_driver corsairpsu_driver =;

static int __init corsair_init(void)
{}

static void __exit corsair_exit(void)
{}

/*
 * With module_init() the driver would load before the HID bus when
 * built-in, so use late_initcall() instead.
 */
late_initcall(corsair_init);
module_exit(corsair_exit);

MODULE_LICENSE();
MODULE_AUTHOR();
MODULE_DESCRIPTION();