Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * corsair-psu.c - Linux driver for Corsair power supplies with HID sensors interface
0004  * Copyright (C) 2020 Wilken Gottwalt <wilken.gottwalt@posteo.net>
0005  */
0006 
0007 #include <linux/completion.h>
0008 #include <linux/debugfs.h>
0009 #include <linux/errno.h>
0010 #include <linux/hid.h>
0011 #include <linux/hwmon.h>
0012 #include <linux/hwmon-sysfs.h>
0013 #include <linux/jiffies.h>
0014 #include <linux/kernel.h>
0015 #include <linux/module.h>
0016 #include <linux/mutex.h>
0017 #include <linux/slab.h>
0018 #include <linux/types.h>
0019 
0020 /*
0021  * Corsair protocol for PSUs
0022  *
0023  * message size = 64 bytes (request and response, little endian)
0024  * request:
0025  *  [length][command][param0][param1][paramX]...
0026  * reply:
0027  *  [echo of length][echo of command][data0][data1][dataX]...
0028  *
0029  *  - commands are byte sized opcodes
0030  *  - length is the sum of all bytes of the commands/params
0031  *  - the micro-controller of most of these PSUs support concatenation in the request and reply,
0032  *    but it is better to not rely on this (it is also hard to parse)
0033  *  - the driver uses raw events to be accessible from userspace (though this is not really
0034  *    supported, it is just there for convenience, may be removed in the future)
0035  *  - a reply always start with the length and command in the same order the request used it
0036  *  - length of the reply data is specific to the command used
0037  *  - some of the commands work on a rail and can be switched to a specific rail (0 = 12v,
0038  *    1 = 5v, 2 = 3.3v)
0039  *  - the format of the init command 0xFE is swapped length/command bytes
0040  *  - parameter bytes amount and values are specific to the command (rail setting is the only
0041  *    for now that uses non-zero values)
0042  *  - there are much more commands, especially for configuring the device, but they are not
0043  *    supported because a wrong command/length can lockup the micro-controller
0044  *  - the driver supports debugfs for values not fitting into the hwmon class
0045  *  - not every device class (HXi, RMi or AXi) supports all commands
0046  *  - it is a pure sensors reading driver (will not support configuring)
0047  */
0048 
0049 #define DRIVER_NAME     "corsair-psu"
0050 
0051 #define REPLY_SIZE      16 /* max length of a reply to a single command */
0052 #define CMD_BUFFER_SIZE     64
0053 #define CMD_TIMEOUT_MS      250
0054 #define SECONDS_PER_HOUR    (60 * 60)
0055 #define SECONDS_PER_DAY     (SECONDS_PER_HOUR * 24)
0056 #define RAIL_COUNT      3 /* 3v3 + 5v + 12v */
0057 #define TEMP_COUNT      2
0058 
0059 #define PSU_CMD_SELECT_RAIL 0x00 /* expects length 2 */
0060 #define PSU_CMD_RAIL_VOLTS_HCRIT 0x40 /* the rest of the commands expect length 3 */
0061 #define PSU_CMD_RAIL_VOLTS_LCRIT 0x44
0062 #define PSU_CMD_RAIL_AMPS_HCRIT 0x46
0063 #define PSU_CMD_TEMP_HCRIT  0x4F
0064 #define PSU_CMD_IN_VOLTS    0x88
0065 #define PSU_CMD_IN_AMPS     0x89
0066 #define PSU_CMD_RAIL_VOLTS  0x8B
0067 #define PSU_CMD_RAIL_AMPS   0x8C
0068 #define PSU_CMD_TEMP0       0x8D
0069 #define PSU_CMD_TEMP1       0x8E
0070 #define PSU_CMD_FAN     0x90
0071 #define PSU_CMD_RAIL_WATTS  0x96
0072 #define PSU_CMD_VEND_STR    0x99
0073 #define PSU_CMD_PROD_STR    0x9A
0074 #define PSU_CMD_TOTAL_WATTS 0xEE
0075 #define PSU_CMD_TOTAL_UPTIME    0xD1
0076 #define PSU_CMD_UPTIME      0xD2
0077 #define PSU_CMD_INIT        0xFE
0078 
0079 #define L_IN_VOLTS      "v_in"
0080 #define L_OUT_VOLTS_12V     "v_out +12v"
0081 #define L_OUT_VOLTS_5V      "v_out +5v"
0082 #define L_OUT_VOLTS_3_3V    "v_out +3.3v"
0083 #define L_IN_AMPS       "curr in"
0084 #define L_AMPS_12V      "curr +12v"
0085 #define L_AMPS_5V       "curr +5v"
0086 #define L_AMPS_3_3V     "curr +3.3v"
0087 #define L_FAN           "psu fan"
0088 #define L_TEMP0         "vrm temp"
0089 #define L_TEMP1         "case temp"
0090 #define L_WATTS         "power total"
0091 #define L_WATTS_12V     "power +12v"
0092 #define L_WATTS_5V      "power +5v"
0093 #define L_WATTS_3_3V        "power +3.3v"
0094 
0095 static const char *const label_watts[] = {
0096     L_WATTS,
0097     L_WATTS_12V,
0098     L_WATTS_5V,
0099     L_WATTS_3_3V
0100 };
0101 
0102 static const char *const label_volts[] = {
0103     L_IN_VOLTS,
0104     L_OUT_VOLTS_12V,
0105     L_OUT_VOLTS_5V,
0106     L_OUT_VOLTS_3_3V
0107 };
0108 
0109 static const char *const label_amps[] = {
0110     L_IN_AMPS,
0111     L_AMPS_12V,
0112     L_AMPS_5V,
0113     L_AMPS_3_3V
0114 };
0115 
0116 struct corsairpsu_data {
0117     struct hid_device *hdev;
0118     struct device *hwmon_dev;
0119     struct dentry *debugfs;
0120     struct completion wait_completion;
0121     struct mutex lock; /* for locking access to cmd_buffer */
0122     u8 *cmd_buffer;
0123     char vendor[REPLY_SIZE];
0124     char product[REPLY_SIZE];
0125     long temp_crit[TEMP_COUNT];
0126     long in_crit[RAIL_COUNT];
0127     long in_lcrit[RAIL_COUNT];
0128     long curr_crit[RAIL_COUNT];
0129     u8 temp_crit_support;
0130     u8 in_crit_support;
0131     u8 in_lcrit_support;
0132     u8 curr_crit_support;
0133     bool in_curr_cmd_support; /* not all commands are supported on every PSU */
0134 };
0135 
0136 /* some values are SMBus LINEAR11 data which need a conversion */
0137 static int corsairpsu_linear11_to_int(const u16 val, const int scale)
0138 {
0139     const int exp = ((s16)val) >> 11;
0140     const int mant = (((s16)(val & 0x7ff)) << 5) >> 5;
0141     const int result = mant * scale;
0142 
0143     return (exp >= 0) ? (result << exp) : (result >> -exp);
0144 }
0145 
0146 static int corsairpsu_usb_cmd(struct corsairpsu_data *priv, u8 p0, u8 p1, u8 p2, void *data)
0147 {
0148     unsigned long time;
0149     int ret;
0150 
0151     memset(priv->cmd_buffer, 0, CMD_BUFFER_SIZE);
0152     priv->cmd_buffer[0] = p0;
0153     priv->cmd_buffer[1] = p1;
0154     priv->cmd_buffer[2] = p2;
0155 
0156     reinit_completion(&priv->wait_completion);
0157 
0158     ret = hid_hw_output_report(priv->hdev, priv->cmd_buffer, CMD_BUFFER_SIZE);
0159     if (ret < 0)
0160         return ret;
0161 
0162     time = wait_for_completion_timeout(&priv->wait_completion,
0163                        msecs_to_jiffies(CMD_TIMEOUT_MS));
0164     if (!time)
0165         return -ETIMEDOUT;
0166 
0167     /*
0168      * at the start of the reply is an echo of the send command/length in the same order it
0169      * was send, not every command is supported on every device class, if a command is not
0170      * supported, the length value in the reply is okay, but the command value is set to 0
0171      */
0172     if (p0 != priv->cmd_buffer[0] || p1 != priv->cmd_buffer[1])
0173         return -EOPNOTSUPP;
0174 
0175     if (data)
0176         memcpy(data, priv->cmd_buffer + 2, REPLY_SIZE);
0177 
0178     return 0;
0179 }
0180 
0181 static int corsairpsu_init(struct corsairpsu_data *priv)
0182 {
0183     /*
0184      * PSU_CMD_INIT uses swapped length/command and expects 2 parameter bytes, this command
0185      * actually generates a reply, but we don't need it
0186      */
0187     return corsairpsu_usb_cmd(priv, PSU_CMD_INIT, 3, 0, NULL);
0188 }
0189 
0190 static int corsairpsu_fwinfo(struct corsairpsu_data *priv)
0191 {
0192     int ret;
0193 
0194     ret = corsairpsu_usb_cmd(priv, 3, PSU_CMD_VEND_STR, 0, priv->vendor);
0195     if (ret < 0)
0196         return ret;
0197 
0198     ret = corsairpsu_usb_cmd(priv, 3, PSU_CMD_PROD_STR, 0, priv->product);
0199     if (ret < 0)
0200         return ret;
0201 
0202     return 0;
0203 }
0204 
0205 static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, void *data)
0206 {
0207     int ret;
0208 
0209     mutex_lock(&priv->lock);
0210     switch (cmd) {
0211     case PSU_CMD_RAIL_VOLTS_HCRIT:
0212     case PSU_CMD_RAIL_VOLTS_LCRIT:
0213     case PSU_CMD_RAIL_AMPS_HCRIT:
0214     case PSU_CMD_RAIL_VOLTS:
0215     case PSU_CMD_RAIL_AMPS:
0216     case PSU_CMD_RAIL_WATTS:
0217         ret = corsairpsu_usb_cmd(priv, 2, PSU_CMD_SELECT_RAIL, rail, NULL);
0218         if (ret < 0)
0219             goto cmd_fail;
0220         break;
0221     default:
0222         break;
0223     }
0224 
0225     ret = corsairpsu_usb_cmd(priv, 3, cmd, 0, data);
0226 
0227 cmd_fail:
0228     mutex_unlock(&priv->lock);
0229     return ret;
0230 }
0231 
0232 static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, long *val)
0233 {
0234     u8 data[REPLY_SIZE];
0235     long tmp;
0236     int ret;
0237 
0238     ret = corsairpsu_request(priv, cmd, rail, data);
0239     if (ret < 0)
0240         return ret;
0241 
0242     /*
0243      * the biggest value here comes from the uptime command and to exceed MAXINT total uptime
0244      * needs to be about 68 years, the rest are u16 values and the biggest value coming out of
0245      * the LINEAR11 conversion are the watts values which are about 1200 for the strongest psu
0246      * supported (HX1200i)
0247      */
0248     tmp = ((long)data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0];
0249     switch (cmd) {
0250     case PSU_CMD_RAIL_VOLTS_HCRIT:
0251     case PSU_CMD_RAIL_VOLTS_LCRIT:
0252     case PSU_CMD_RAIL_AMPS_HCRIT:
0253     case PSU_CMD_TEMP_HCRIT:
0254     case PSU_CMD_IN_VOLTS:
0255     case PSU_CMD_IN_AMPS:
0256     case PSU_CMD_RAIL_VOLTS:
0257     case PSU_CMD_RAIL_AMPS:
0258     case PSU_CMD_TEMP0:
0259     case PSU_CMD_TEMP1:
0260         *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000);
0261         break;
0262     case PSU_CMD_FAN:
0263         *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1);
0264         break;
0265     case PSU_CMD_RAIL_WATTS:
0266     case PSU_CMD_TOTAL_WATTS:
0267         *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000000);
0268         break;
0269     case PSU_CMD_TOTAL_UPTIME:
0270     case PSU_CMD_UPTIME:
0271         *val = tmp;
0272         break;
0273     default:
0274         ret = -EOPNOTSUPP;
0275         break;
0276     }
0277 
0278     return ret;
0279 }
0280 
0281 static void corsairpsu_get_criticals(struct corsairpsu_data *priv)
0282 {
0283     long tmp;
0284     int rail;
0285 
0286     for (rail = 0; rail < TEMP_COUNT; ++rail) {
0287         if (!corsairpsu_get_value(priv, PSU_CMD_TEMP_HCRIT, rail, &tmp)) {
0288             priv->temp_crit_support |= BIT(rail);
0289             priv->temp_crit[rail] = tmp;
0290         }
0291     }
0292 
0293     for (rail = 0; rail < RAIL_COUNT; ++rail) {
0294         if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_HCRIT, rail, &tmp)) {
0295             priv->in_crit_support |= BIT(rail);
0296             priv->in_crit[rail] = tmp;
0297         }
0298 
0299         if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_LCRIT, rail, &tmp)) {
0300             priv->in_lcrit_support |= BIT(rail);
0301             priv->in_lcrit[rail] = tmp;
0302         }
0303 
0304         if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS_HCRIT, rail, &tmp)) {
0305             priv->curr_crit_support |= BIT(rail);
0306             priv->curr_crit[rail] = tmp;
0307         }
0308     }
0309 }
0310 
0311 static void corsairpsu_check_cmd_support(struct corsairpsu_data *priv)
0312 {
0313     long tmp;
0314 
0315     priv->in_curr_cmd_support = !corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, &tmp);
0316 }
0317 
0318 static umode_t corsairpsu_hwmon_temp_is_visible(const struct corsairpsu_data *priv, u32 attr,
0319                         int channel)
0320 {
0321     umode_t res = 0444;
0322 
0323     switch (attr) {
0324     case hwmon_temp_input:
0325     case hwmon_temp_label:
0326     case hwmon_temp_crit:
0327         if (channel > 0 && !(priv->temp_crit_support & BIT(channel - 1)))
0328             res = 0;
0329         break;
0330     default:
0331         break;
0332     }
0333 
0334     return res;
0335 }
0336 
0337 static umode_t corsairpsu_hwmon_fan_is_visible(const struct corsairpsu_data *priv, u32 attr,
0338                            int channel)
0339 {
0340     switch (attr) {
0341     case hwmon_fan_input:
0342     case hwmon_fan_label:
0343         return 0444;
0344     default:
0345         return 0;
0346     }
0347 }
0348 
0349 static umode_t corsairpsu_hwmon_power_is_visible(const struct corsairpsu_data *priv, u32 attr,
0350                          int channel)
0351 {
0352     switch (attr) {
0353     case hwmon_power_input:
0354     case hwmon_power_label:
0355         return 0444;
0356     default:
0357         return 0;
0358     }
0359 }
0360 
0361 static umode_t corsairpsu_hwmon_in_is_visible(const struct corsairpsu_data *priv, u32 attr,
0362                           int channel)
0363 {
0364     umode_t res = 0444;
0365 
0366     switch (attr) {
0367     case hwmon_in_input:
0368     case hwmon_in_label:
0369     case hwmon_in_crit:
0370         if (channel > 0 && !(priv->in_crit_support & BIT(channel - 1)))
0371             res = 0;
0372         break;
0373     case hwmon_in_lcrit:
0374         if (channel > 0 && !(priv->in_lcrit_support & BIT(channel - 1)))
0375             res = 0;
0376         break;
0377     default:
0378         break;
0379     }
0380 
0381     return res;
0382 }
0383 
0384 static umode_t corsairpsu_hwmon_curr_is_visible(const struct corsairpsu_data *priv, u32 attr,
0385                         int channel)
0386 {
0387     umode_t res = 0444;
0388 
0389     switch (attr) {
0390     case hwmon_curr_input:
0391         if (channel == 0 && !priv->in_curr_cmd_support)
0392             res = 0;
0393         break;
0394     case hwmon_curr_label:
0395     case hwmon_curr_crit:
0396         if (channel > 0 && !(priv->curr_crit_support & BIT(channel - 1)))
0397             res = 0;
0398         break;
0399     default:
0400         break;
0401     }
0402 
0403     return res;
0404 }
0405 
0406 static umode_t corsairpsu_hwmon_ops_is_visible(const void *data, enum hwmon_sensor_types type,
0407                            u32 attr, int channel)
0408 {
0409     const struct corsairpsu_data *priv = data;
0410 
0411     switch (type) {
0412     case hwmon_temp:
0413         return corsairpsu_hwmon_temp_is_visible(priv, attr, channel);
0414     case hwmon_fan:
0415         return corsairpsu_hwmon_fan_is_visible(priv, attr, channel);
0416     case hwmon_power:
0417         return corsairpsu_hwmon_power_is_visible(priv, attr, channel);
0418     case hwmon_in:
0419         return corsairpsu_hwmon_in_is_visible(priv, attr, channel);
0420     case hwmon_curr:
0421         return corsairpsu_hwmon_curr_is_visible(priv, attr, channel);
0422     default:
0423         return 0;
0424     }
0425 }
0426 
0427 static int corsairpsu_hwmon_temp_read(struct corsairpsu_data *priv, u32 attr, int channel,
0428                       long *val)
0429 {
0430     int err = -EOPNOTSUPP;
0431 
0432     switch (attr) {
0433     case hwmon_temp_input:
0434         return corsairpsu_get_value(priv, channel ? PSU_CMD_TEMP1 : PSU_CMD_TEMP0,
0435                         channel, val);
0436     case hwmon_temp_crit:
0437         *val = priv->temp_crit[channel];
0438         err = 0;
0439         break;
0440     default:
0441         break;
0442     }
0443 
0444     return err;
0445 }
0446 
0447 static int corsairpsu_hwmon_power_read(struct corsairpsu_data *priv, u32 attr, int channel,
0448                        long *val)
0449 {
0450     if (attr == hwmon_power_input) {
0451         switch (channel) {
0452         case 0:
0453             return corsairpsu_get_value(priv, PSU_CMD_TOTAL_WATTS, 0, val);
0454         case 1 ... 3:
0455             return corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val);
0456         default:
0457             break;
0458         }
0459     }
0460 
0461     return -EOPNOTSUPP;
0462 }
0463 
0464 static int corsairpsu_hwmon_in_read(struct corsairpsu_data *priv, u32 attr, int channel, long *val)
0465 {
0466     int err = -EOPNOTSUPP;
0467 
0468     switch (attr) {
0469     case hwmon_in_input:
0470         switch (channel) {
0471         case 0:
0472             return corsairpsu_get_value(priv, PSU_CMD_IN_VOLTS, 0, val);
0473         case 1 ... 3:
0474             return corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS, channel - 1, val);
0475         default:
0476             break;
0477         }
0478         break;
0479     case hwmon_in_crit:
0480         *val = priv->in_crit[channel - 1];
0481         err = 0;
0482         break;
0483     case hwmon_in_lcrit:
0484         *val = priv->in_lcrit[channel - 1];
0485         err = 0;
0486         break;
0487     }
0488 
0489     return err;
0490 }
0491 
0492 static int corsairpsu_hwmon_curr_read(struct corsairpsu_data *priv, u32 attr, int channel,
0493                       long *val)
0494 {
0495     int err = -EOPNOTSUPP;
0496 
0497     switch (attr) {
0498     case hwmon_curr_input:
0499         switch (channel) {
0500         case 0:
0501             return corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, val);
0502         case 1 ... 3:
0503             return corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val);
0504         default:
0505             break;
0506         }
0507         break;
0508     case hwmon_curr_crit:
0509         *val = priv->curr_crit[channel - 1];
0510         err = 0;
0511         break;
0512     default:
0513         break;
0514     }
0515 
0516     return err;
0517 }
0518 
0519 static int corsairpsu_hwmon_ops_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
0520                      int channel, long *val)
0521 {
0522     struct corsairpsu_data *priv = dev_get_drvdata(dev);
0523 
0524     switch (type) {
0525     case hwmon_temp:
0526         return corsairpsu_hwmon_temp_read(priv, attr, channel, val);
0527     case hwmon_fan:
0528         if (attr == hwmon_fan_input)
0529             return corsairpsu_get_value(priv, PSU_CMD_FAN, 0, val);
0530         return -EOPNOTSUPP;
0531     case hwmon_power:
0532         return corsairpsu_hwmon_power_read(priv, attr, channel, val);
0533     case hwmon_in:
0534         return corsairpsu_hwmon_in_read(priv, attr, channel, val);
0535     case hwmon_curr:
0536         return corsairpsu_hwmon_curr_read(priv, attr, channel, val);
0537     default:
0538         return -EOPNOTSUPP;
0539     }
0540 }
0541 
0542 static int corsairpsu_hwmon_ops_read_string(struct device *dev, enum hwmon_sensor_types type,
0543                         u32 attr, int channel, const char **str)
0544 {
0545     if (type == hwmon_temp && attr == hwmon_temp_label) {
0546         *str = channel ? L_TEMP1 : L_TEMP0;
0547         return 0;
0548     } else if (type == hwmon_fan && attr == hwmon_fan_label) {
0549         *str = L_FAN;
0550         return 0;
0551     } else if (type == hwmon_power && attr == hwmon_power_label && channel < 4) {
0552         *str = label_watts[channel];
0553         return 0;
0554     } else if (type == hwmon_in && attr == hwmon_in_label && channel < 4) {
0555         *str = label_volts[channel];
0556         return 0;
0557     } else if (type == hwmon_curr && attr == hwmon_curr_label && channel < 4) {
0558         *str = label_amps[channel];
0559         return 0;
0560     }
0561 
0562     return -EOPNOTSUPP;
0563 }
0564 
0565 static const struct hwmon_ops corsairpsu_hwmon_ops = {
0566     .is_visible = corsairpsu_hwmon_ops_is_visible,
0567     .read       = corsairpsu_hwmon_ops_read,
0568     .read_string    = corsairpsu_hwmon_ops_read_string,
0569 };
0570 
0571 static const struct hwmon_channel_info *corsairpsu_info[] = {
0572     HWMON_CHANNEL_INFO(chip,
0573                HWMON_C_REGISTER_TZ),
0574     HWMON_CHANNEL_INFO(temp,
0575                HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
0576                HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT),
0577     HWMON_CHANNEL_INFO(fan,
0578                HWMON_F_INPUT | HWMON_F_LABEL),
0579     HWMON_CHANNEL_INFO(power,
0580                HWMON_P_INPUT | HWMON_P_LABEL,
0581                HWMON_P_INPUT | HWMON_P_LABEL,
0582                HWMON_P_INPUT | HWMON_P_LABEL,
0583                HWMON_P_INPUT | HWMON_P_LABEL),
0584     HWMON_CHANNEL_INFO(in,
0585                HWMON_I_INPUT | HWMON_I_LABEL,
0586                HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT,
0587                HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT,
0588                HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT),
0589     HWMON_CHANNEL_INFO(curr,
0590                HWMON_C_INPUT | HWMON_C_LABEL,
0591                HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT,
0592                HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT,
0593                HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT),
0594     NULL
0595 };
0596 
0597 static const struct hwmon_chip_info corsairpsu_chip_info = {
0598     .ops    = &corsairpsu_hwmon_ops,
0599     .info   = corsairpsu_info,
0600 };
0601 
0602 #ifdef CONFIG_DEBUG_FS
0603 
0604 static void print_uptime(struct seq_file *seqf, u8 cmd)
0605 {
0606     struct corsairpsu_data *priv = seqf->private;
0607     long val;
0608     int ret;
0609 
0610     ret = corsairpsu_get_value(priv, cmd, 0, &val);
0611     if (ret < 0) {
0612         seq_puts(seqf, "N/A\n");
0613         return;
0614     }
0615 
0616     if (val > SECONDS_PER_DAY) {
0617         seq_printf(seqf, "%ld day(s), %02ld:%02ld:%02ld\n", val / SECONDS_PER_DAY,
0618                val % SECONDS_PER_DAY / SECONDS_PER_HOUR, val % SECONDS_PER_HOUR / 60,
0619                val % 60);
0620         return;
0621     }
0622 
0623     seq_printf(seqf, "%02ld:%02ld:%02ld\n", val % SECONDS_PER_DAY / SECONDS_PER_HOUR,
0624            val % SECONDS_PER_HOUR / 60, val % 60);
0625 }
0626 
0627 static int uptime_show(struct seq_file *seqf, void *unused)
0628 {
0629     print_uptime(seqf, PSU_CMD_UPTIME);
0630 
0631     return 0;
0632 }
0633 DEFINE_SHOW_ATTRIBUTE(uptime);
0634 
0635 static int uptime_total_show(struct seq_file *seqf, void *unused)
0636 {
0637     print_uptime(seqf, PSU_CMD_TOTAL_UPTIME);
0638 
0639     return 0;
0640 }
0641 DEFINE_SHOW_ATTRIBUTE(uptime_total);
0642 
0643 static int vendor_show(struct seq_file *seqf, void *unused)
0644 {
0645     struct corsairpsu_data *priv = seqf->private;
0646 
0647     seq_printf(seqf, "%s\n", priv->vendor);
0648 
0649     return 0;
0650 }
0651 DEFINE_SHOW_ATTRIBUTE(vendor);
0652 
0653 static int product_show(struct seq_file *seqf, void *unused)
0654 {
0655     struct corsairpsu_data *priv = seqf->private;
0656 
0657     seq_printf(seqf, "%s\n", priv->product);
0658 
0659     return 0;
0660 }
0661 DEFINE_SHOW_ATTRIBUTE(product);
0662 
0663 static void corsairpsu_debugfs_init(struct corsairpsu_data *priv)
0664 {
0665     char name[32];
0666 
0667     scnprintf(name, sizeof(name), "%s-%s", DRIVER_NAME, dev_name(&priv->hdev->dev));
0668 
0669     priv->debugfs = debugfs_create_dir(name, NULL);
0670     debugfs_create_file("uptime", 0444, priv->debugfs, priv, &uptime_fops);
0671     debugfs_create_file("uptime_total", 0444, priv->debugfs, priv, &uptime_total_fops);
0672     debugfs_create_file("vendor", 0444, priv->debugfs, priv, &vendor_fops);
0673     debugfs_create_file("product", 0444, priv->debugfs, priv, &product_fops);
0674 }
0675 
0676 #else
0677 
0678 static void corsairpsu_debugfs_init(struct corsairpsu_data *priv)
0679 {
0680 }
0681 
0682 #endif
0683 
0684 static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id *id)
0685 {
0686     struct corsairpsu_data *priv;
0687     int ret;
0688 
0689     priv = devm_kzalloc(&hdev->dev, sizeof(struct corsairpsu_data), GFP_KERNEL);
0690     if (!priv)
0691         return -ENOMEM;
0692 
0693     priv->cmd_buffer = devm_kmalloc(&hdev->dev, CMD_BUFFER_SIZE, GFP_KERNEL);
0694     if (!priv->cmd_buffer)
0695         return -ENOMEM;
0696 
0697     ret = hid_parse(hdev);
0698     if (ret)
0699         return ret;
0700 
0701     ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
0702     if (ret)
0703         return ret;
0704 
0705     ret = hid_hw_open(hdev);
0706     if (ret)
0707         goto fail_and_stop;
0708 
0709     priv->hdev = hdev;
0710     hid_set_drvdata(hdev, priv);
0711     mutex_init(&priv->lock);
0712     init_completion(&priv->wait_completion);
0713 
0714     hid_device_io_start(hdev);
0715 
0716     ret = corsairpsu_init(priv);
0717     if (ret < 0) {
0718         dev_err(&hdev->dev, "unable to initialize device (%d)\n", ret);
0719         goto fail_and_stop;
0720     }
0721 
0722     ret = corsairpsu_fwinfo(priv);
0723     if (ret < 0) {
0724         dev_err(&hdev->dev, "unable to query firmware (%d)\n", ret);
0725         goto fail_and_stop;
0726     }
0727 
0728     corsairpsu_get_criticals(priv);
0729     corsairpsu_check_cmd_support(priv);
0730 
0731     priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsairpsu", priv,
0732                               &corsairpsu_chip_info, NULL);
0733 
0734     if (IS_ERR(priv->hwmon_dev)) {
0735         ret = PTR_ERR(priv->hwmon_dev);
0736         goto fail_and_close;
0737     }
0738 
0739     corsairpsu_debugfs_init(priv);
0740 
0741     return 0;
0742 
0743 fail_and_close:
0744     hid_hw_close(hdev);
0745 fail_and_stop:
0746     hid_hw_stop(hdev);
0747     return ret;
0748 }
0749 
0750 static void corsairpsu_remove(struct hid_device *hdev)
0751 {
0752     struct corsairpsu_data *priv = hid_get_drvdata(hdev);
0753 
0754     debugfs_remove_recursive(priv->debugfs);
0755     hwmon_device_unregister(priv->hwmon_dev);
0756     hid_hw_close(hdev);
0757     hid_hw_stop(hdev);
0758 }
0759 
0760 static int corsairpsu_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
0761                 int size)
0762 {
0763     struct corsairpsu_data *priv = hid_get_drvdata(hdev);
0764 
0765     if (completion_done(&priv->wait_completion))
0766         return 0;
0767 
0768     memcpy(priv->cmd_buffer, data, min(CMD_BUFFER_SIZE, size));
0769     complete(&priv->wait_completion);
0770 
0771     return 0;
0772 }
0773 
0774 #ifdef CONFIG_PM
0775 static int corsairpsu_resume(struct hid_device *hdev)
0776 {
0777     struct corsairpsu_data *priv = hid_get_drvdata(hdev);
0778 
0779     /* some PSUs turn off the microcontroller during standby, so a reinit is required */
0780     return corsairpsu_init(priv);
0781 }
0782 #endif
0783 
0784 static const struct hid_device_id corsairpsu_idtable[] = {
0785     { HID_USB_DEVICE(0x1b1c, 0x1c03) }, /* Corsair HX550i */
0786     { HID_USB_DEVICE(0x1b1c, 0x1c04) }, /* Corsair HX650i */
0787     { HID_USB_DEVICE(0x1b1c, 0x1c05) }, /* Corsair HX750i */
0788     { HID_USB_DEVICE(0x1b1c, 0x1c06) }, /* Corsair HX850i */
0789     { HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i */
0790     { HID_USB_DEVICE(0x1b1c, 0x1c08) }, /* Corsair HX1200i */
0791     { HID_USB_DEVICE(0x1b1c, 0x1c09) }, /* Corsair RM550i */
0792     { HID_USB_DEVICE(0x1b1c, 0x1c0a) }, /* Corsair RM650i */
0793     { HID_USB_DEVICE(0x1b1c, 0x1c0b) }, /* Corsair RM750i */
0794     { HID_USB_DEVICE(0x1b1c, 0x1c0c) }, /* Corsair RM850i */
0795     { HID_USB_DEVICE(0x1b1c, 0x1c0d) }, /* Corsair RM1000i */
0796     { },
0797 };
0798 MODULE_DEVICE_TABLE(hid, corsairpsu_idtable);
0799 
0800 static struct hid_driver corsairpsu_driver = {
0801     .name       = DRIVER_NAME,
0802     .id_table   = corsairpsu_idtable,
0803     .probe      = corsairpsu_probe,
0804     .remove     = corsairpsu_remove,
0805     .raw_event  = corsairpsu_raw_event,
0806 #ifdef CONFIG_PM
0807     .resume     = corsairpsu_resume,
0808     .reset_resume   = corsairpsu_resume,
0809 #endif
0810 };
0811 module_hid_driver(corsairpsu_driver);
0812 
0813 MODULE_LICENSE("GPL");
0814 MODULE_AUTHOR("Wilken Gottwalt <wilken.gottwalt@posteo.net>");
0815 MODULE_DESCRIPTION("Linux driver for Corsair power supplies with HID sensors interface");