From: "Kurt Borja" <kuurtb@gmail.com>
To: "Armin Wolf" <W_Armin@gmx.de>,
"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
Cc: "Hans de Goede" <hdegoede@redhat.com>,
<platform-driver-x86@vger.kernel.org>,
<Dell.Client.Kernel@dell.com>, <linux-kernel@vger.kernel.org>,
"Guenter Roeck" <linux@roeck-us.net>,
"Jean Delvare" <jdelvare@suse.com>, <linux-hwmon@vger.kernel.org>
Subject: Re: [PATCH v4 07/12] platform/x86: alienware-wmi-wmax: Add HWMON support
Date: Wed, 12 Mar 2025 00:06:01 -0500 [thread overview]
Message-ID: <D8E0YIGL8OB5.1LCMBZ0T595Q1@gmail.com> (raw)
In-Reply-To: <f48154cb-c282-4783-94b9-b5378b146f4a@gmx.de>
On Tue Mar 11, 2025 at 1:55 PM -05, Armin Wolf wrote:
> Am 08.03.25 um 21:23 schrieb Kurt Borja:
>
>> All models with the "AWCC" WMAX device support monitoring fan speed and
>> temperature sensors. Expose this feature through the HWMON interface.
>>
>> Cc: Guenter Roeck <linux@roeck-us.net>
>> Cc: Jean Delvare <jdelvare@suse.com>
>> Cc: linux-hwmon@vger.kernel.org
>> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>> ---
>> drivers/platform/x86/dell/Kconfig | 1 +
>> drivers/platform/x86/dell/alienware-wmi-wmax.c | 396 +++++++++++++++++++++++++
>> 2 files changed, 397 insertions(+)
>>
>> diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
>> index f8a0dffcaab7c3b423472c5b9093011334a698c8..85a57c01aaada5d899cd8252e77ed6043da5cbdf 100644
>> --- a/drivers/platform/x86/dell/Kconfig
>> +++ b/drivers/platform/x86/dell/Kconfig
>> @@ -43,6 +43,7 @@ config ALIENWARE_WMI_WMAX
>> bool "Alienware WMAX WMI device driver"
>> default y
>> depends on ALIENWARE_WMI
>> + depends on HWMON
>> select ACPI_PLATFORM_PROFILE
>> help
>> Alienware WMI driver with AlienFX LED, HDMI, amplifier, deep sleep and
>> diff --git a/drivers/platform/x86/dell/alienware-wmi-wmax.c b/drivers/platform/x86/dell/alienware-wmi-wmax.c
>> index 3b37e4456482bc284b8e867c1c5b6255fc6c8ef2..f1f20db206d47f644e8cd58a6d169c850606e75b 100644
>> --- a/drivers/platform/x86/dell/alienware-wmi-wmax.c
>> +++ b/drivers/platform/x86/dell/alienware-wmi-wmax.c
>> @@ -9,10 +9,13 @@
>> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>>
>> #include <linux/bitfield.h>
>> +#include <linux/bitmap.h>
>> #include <linux/bits.h>
>> #include <linux/dmi.h>
>> +#include <linux/hwmon.h>
>> #include <linux/moduleparam.h>
>> #include <linux/platform_profile.h>
>> +#include <linux/units.h>
>> #include <linux/wmi.h>
>> #include "alienware-wmi.h"
>>
>> @@ -25,6 +28,7 @@
>> #define WMAX_METHOD_BRIGHTNESS 0x3
>> #define WMAX_METHOD_ZONE_CONTROL 0x4
>>
>> +#define AWCC_METHOD_GET_FAN_SENSORS 0x13
>> #define AWCC_METHOD_THERMAL_INFORMATION 0x14
>> #define AWCC_METHOD_THERMAL_CONTROL 0x15
>> #define AWCC_METHOD_GAME_SHIFT_STATUS 0x25
>> @@ -38,6 +42,12 @@
>>
>> /* Arbitrary limit based on supported models */
>> #define AWCC_MAX_RES_COUNT 16
>> +#define AWCC_ID_BITMAP_SIZE (U8_MAX + 1)
>> +#define AWCC_ID_BITMAP_LONGS BITS_TO_LONGS(AWCC_ID_BITMAP_SIZE)
>> +
>> +static bool force_hwmon;
>> +module_param_unsafe(force_hwmon, bool, 0);
>> +MODULE_PARM_DESC(force_hwmon, "Force probing for HWMON support without checking if the WMI backend is available");
>>
>> static bool force_platform_profile;
>> module_param_unsafe(force_platform_profile, bool, 0);
>> @@ -48,16 +58,19 @@ module_param_unsafe(force_gmode, bool, 0);
>> MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected");
>>
>> struct awcc_quirks {
>> + bool hwmon;
>> bool pprof;
>> bool gmode;
>> };
>>
>> static struct awcc_quirks g_series_quirks = {
>> + .hwmon = true,
>> .pprof = true,
>> .gmode = true,
>> };
>>
>> static struct awcc_quirks generic_quirks = {
>> + .hwmon = true,
>> .pprof = true,
>> .gmode = false,
>> };
>> @@ -155,9 +168,18 @@ static const struct dmi_system_id awcc_dmi_table[] __initconst = {
>> },
>> };
>>
>> +enum AWCC_GET_FAN_SENSORS_OPERATIONS {
>> + AWCC_OP_GET_TOTAL_FAN_TEMPS = 0x01,
>> + AWCC_OP_GET_FAN_TEMP_ID = 0x02,
>> +};
>> +
>> enum AWCC_THERMAL_INFORMATION_OPERATIONS {
>> AWCC_OP_GET_SYSTEM_DESCRIPTION = 0x02,
>> AWCC_OP_GET_RESOURCE_ID = 0x03,
>> + AWCC_OP_GET_TEMPERATURE = 0x04,
>> + AWCC_OP_GET_FAN_RPM = 0x05,
>> + AWCC_OP_GET_FAN_MIN_RPM = 0x08,
>> + AWCC_OP_GET_FAN_MAX_RPM = 0x09,
>> AWCC_OP_GET_CURRENT_PROFILE = 0x0B,
>> };
>>
>> @@ -180,6 +202,11 @@ enum AWCC_SPECIAL_THERMAL_CODES {
>> AWCC_SPECIAL_PROFILE_GMODE = 0xAB,
>> };
>>
>> +enum AWCC_TEMP_SENSOR_TYPES {
>> + AWCC_TEMP_SENSOR_CPU = 0x01,
>> + AWCC_TEMP_SENSOR_GPU = 0x06,
>> +};
>> +
>> enum awcc_thermal_profile {
>> AWCC_PROFILE_USTT_BALANCED,
>> AWCC_PROFILE_USTT_BALANCED_PERFORMANCE,
>> @@ -216,6 +243,14 @@ struct wmax_u32_args {
>> u8 arg3;
>> };
>>
>> +struct awcc_fan_data {
>> + unsigned long auto_channels_temp;
>> + const char *label;
>> + u32 min_rpm;
>> + u32 max_rpm;
>> + u8 id;
>> +};
>> +
>> struct awcc_priv {
>> struct wmi_device *wdev;
>> union {
>> @@ -231,6 +266,10 @@ struct awcc_priv {
>>
>> struct device *ppdev;
>> u8 supported_profiles[PLATFORM_PROFILE_LAST];
>> +
>> + struct device *hwdev;
>> + struct awcc_fan_data **fan_data;
>> + unsigned long temp_sensors[AWCC_ID_BITMAP_LONGS];
>> };
>>
>> static const enum platform_profile_option awcc_mode_to_platform_profile[AWCC_PROFILE_LAST] = {
>> @@ -495,6 +534,19 @@ static int __awcc_wmi_command(struct wmi_device *wdev, u32 method_id,
>> return 0;
>> }
>>
>> +static inline int awcc_get_fan_sensors(struct wmi_device *wdev, u8 operation,
>> + u8 fan_id, u8 index, u32 *out)
>> +{
>> + struct wmax_u32_args args = {
>> + .operation = operation,
>> + .arg1 = fan_id,
>> + .arg2 = index,
>> + .arg3 = 0,
>> + };
>> +
>> + return __awcc_wmi_command(wdev, AWCC_METHOD_GET_FAN_SENSORS, &args, out);
>> +}
>> +
>> static inline int awcc_thermal_information(struct wmi_device *wdev, u8 operation,
>> u8 arg, u32 *out)
>> {
>> @@ -552,6 +604,30 @@ static inline int awcc_op_get_resource_id(struct wmi_device *wdev, u8 index, u32
>> return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
>> }
>>
>> +static inline int awcc_op_get_fan_rpm(struct wmi_device *wdev, u8 fan_id, u32 *out)
>> +{
>> + struct wmax_u32_args args = {
>> + .operation = AWCC_OP_GET_FAN_RPM,
>> + .arg1 = fan_id,
>> + .arg2 = 0,
>> + .arg3 = 0,
>> + };
>> +
>> + return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
>> +}
>> +
>> +static inline int awcc_op_get_temperature(struct wmi_device *wdev, u8 temp_id, u32 *out)
>> +{
>> + struct wmax_u32_args args = {
>> + .operation = AWCC_OP_GET_TEMPERATURE,
>> + .arg1 = temp_id,
>> + .arg2 = 0,
>> + .arg3 = 0,
>> + };
>> +
>> + return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
>> +}
>> +
>> static inline int awcc_op_get_current_profile(struct wmi_device *wdev, u32 *out)
>> {
>> struct wmax_u32_args args = {
>> @@ -577,6 +653,313 @@ static inline int awcc_op_activate_profile(struct wmi_device *wdev, u8 profile)
>> return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_CONTROL, &args, &out);
>> }
>>
>> +/*
>> + * HWMON
>> + * - Provides temperature and fan speed monitoring as well as manual fan
>> + * control
>> + */
>> +static umode_t awcc_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
>> + u32 attr, int channel)
>> +{
>> + const struct awcc_priv *priv = drvdata;
>> + unsigned int temp_count;
>> +
>> + switch (type) {
>> + case hwmon_temp:
>> + temp_count = bitmap_weight(priv->temp_sensors, AWCC_ID_BITMAP_SIZE);
>> +
>> + return channel < temp_count ? 0444 : 0;
>> + case hwmon_fan:
>> + return channel < priv->fan_count ? 0444 : 0;
>> + case hwmon_pwm:
>> + return channel < priv->fan_count ? 0444 : 0;
>> + default:
>> + return 0;
>> + }
>> +}
>> +
>> +static int awcc_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
>> + u32 attr, int channel, long *val)
>> +{
>> + struct awcc_priv *priv = dev_get_drvdata(dev);
>> + const struct awcc_fan_data *fan;
>> + u32 state;
>> + int ret;
>> + u8 temp;
>> +
>> + switch (type) {
>> + case hwmon_temp:
>> + temp = find_nth_bit(priv->temp_sensors, AWCC_ID_BITMAP_SIZE, channel);
>> +
>> + switch (attr) {
>> + case hwmon_temp_input:
>> + ret = awcc_op_get_temperature(priv->wdev, temp, &state);
>> + if (ret)
>> + return ret;
>> +
>> + *val = state * MILLIDEGREE_PER_DEGREE;
>> + break;
>> + default:
>> + return -EOPNOTSUPP;
>> + }
>> +
>> + break;
>> + case hwmon_fan:
>> + fan = priv->fan_data[channel];
>> +
>> + switch (attr) {
>> + case hwmon_fan_input:
>> + ret = awcc_op_get_fan_rpm(priv->wdev, fan->id, &state);
>> + if (ret)
>> + return ret;
>> +
>> + *val = state;
>> + break;
>> + case hwmon_fan_min:
>> + *val = fan->min_rpm;
>> + break;
>> + case hwmon_fan_max:
>> + *val = fan->max_rpm;
>> + break;
>> + default:
>> + return -EOPNOTSUPP;
>> + }
>> +
>> + break;
>> + case hwmon_pwm:
>> + fan = priv->fan_data[channel];
>> +
>> + switch (attr) {
>> + case hwmon_pwm_auto_channels_temp:
>> + *val = fan->auto_channels_temp;
>> + break;
>> + default:
>> + return -EOPNOTSUPP;
>> + }
>> +
>> + break;
>> + default:
>> + return -EOPNOTSUPP;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int awcc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
>> + u32 attr, int channel, const char **str)
>> +{
>> + struct awcc_priv *priv = dev_get_drvdata(dev);
>> + u8 temp;
>> +
>> + switch (type) {
>> + case hwmon_temp:
>> + temp = find_nth_bit(priv->temp_sensors, AWCC_ID_BITMAP_SIZE, channel);
>> +
>> + switch (temp) {
>> + case AWCC_TEMP_SENSOR_CPU:
>> + *str = "CPU";
>> + break;
>> + case AWCC_TEMP_SENSOR_GPU:
>> + *str = "GPU";
>> + break;
>> + default:
>> + *str = "Unknown";
>> + break;
>> + }
>> +
>> + break;
>> + case hwmon_fan:
>> + *str = priv->fan_data[channel]->label;
>> + break;
>> + default:
>> + return -EOPNOTSUPP;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static const struct hwmon_ops awcc_hwmon_ops = {
>> + .is_visible = awcc_hwmon_is_visible,
>> + .read = awcc_hwmon_read,
>> + .read_string = awcc_hwmon_read_string,
>> +};
>> +
>> +static const struct hwmon_channel_info * const awcc_hwmon_info[] = {
>> + HWMON_CHANNEL_INFO(temp,
>> + HWMON_T_LABEL | HWMON_T_INPUT,
>> + HWMON_T_LABEL | HWMON_T_INPUT,
>> + HWMON_T_LABEL | HWMON_T_INPUT,
>> + HWMON_T_LABEL | HWMON_T_INPUT,
>> + HWMON_T_LABEL | HWMON_T_INPUT,
>> + HWMON_T_LABEL | HWMON_T_INPUT
>> + ),
>> + HWMON_CHANNEL_INFO(fan,
>> + HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
>> + HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
>> + HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
>> + HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
>> + HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
>> + HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX
>> + ),
>> + HWMON_CHANNEL_INFO(pwm,
>> + HWMON_PWM_AUTO_CHANNELS_TEMP,
>> + HWMON_PWM_AUTO_CHANNELS_TEMP,
>> + HWMON_PWM_AUTO_CHANNELS_TEMP,
>> + HWMON_PWM_AUTO_CHANNELS_TEMP,
>> + HWMON_PWM_AUTO_CHANNELS_TEMP,
>> + HWMON_PWM_AUTO_CHANNELS_TEMP
>> + ),
>> + NULL
>> +};
>> +
>> +static const struct hwmon_chip_info awcc_hwmon_chip_info = {
>> + .ops = &awcc_hwmon_ops,
>> + .info = awcc_hwmon_info,
>> +};
>> +
>> +static int awcc_hwmon_temps_init(struct wmi_device *wdev)
>> +{
>> + struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
>> + unsigned int i;
>> + int ret;
>> + u32 id;
>> +
>> + for (i = 0; i < priv->temp_count; i++) {
>> + /*
>> + * Temperature sensors IDs are listed after the fan IDs at
>> + * offset `fan_count`
>> + */
>> + ret = awcc_op_get_resource_id(wdev, i + priv->fan_count, &id);
>> + if (ret)
>> + return ret;
>> +
>> + __set_bit(FIELD_GET(AWCC_RESOURCE_ID_MASK, id), priv->temp_sensors);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static char *awcc_alloc_fan_label(struct device *dev, u32 temp_count, u8 temp_id)
>> +{
>> + char *label;
>> +
>> + switch (temp_count) {
>> + case 0:
>> + label = devm_kstrdup(dev, "Independent Fan", GFP_KERNEL);
>> + break;
>> + case 1:
>> + switch (temp_id) {
>> + case AWCC_TEMP_SENSOR_CPU:
>> + label = devm_kstrdup(dev, "Processor Fan", GFP_KERNEL);
>> + break;
>> + case AWCC_TEMP_SENSOR_GPU:
>> + label = devm_kstrdup(dev, "Video Fan", GFP_KERNEL);
>> + break;
>> + default:
>> + label = devm_kstrdup(dev, "Unknown Fan", GFP_KERNEL);
>> + break;
>> + }
>> +
>> + break;
>> + default:
>> + label = devm_kstrdup(dev, "Shared Fan", GFP_KERNEL);
>
> All of those strings are static, please drop the usage of devm_kstrdup() here.
Right! They are string literals.
>
> With those issues being addressed:
> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>
>> + break;
>> + }
>> +
>> + return label;
>> +}
>> +
>> +static int awcc_hwmon_fans_init(struct wmi_device *wdev)
>> +{
>> + struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
>> + u32 id, min_rpm, max_rpm, temp_count, temp_id;
>> + unsigned long fan_temps[AWCC_ID_BITMAP_LONGS];
>> + unsigned long gather[AWCC_ID_BITMAP_LONGS];
>> + struct awcc_fan_data *fan_data;
>> + unsigned int i, j;
>> + char *label;
>> + int ret;
>> +
>> + for (i = 0; i < priv->fan_count; i++) {
>> + fan_data = devm_kzalloc(&wdev->dev, sizeof(*fan_data), GFP_KERNEL);
>> + if (!fan_data)
>> + return -ENOMEM;
>> +
>> + /*
>> + * Fan IDs are listed first at offset 0
>> + */
>> + ret = awcc_op_get_resource_id(wdev, i, &id);
>> + if (ret)
>> + return ret;
>> + id = FIELD_GET(AWCC_RESOURCE_ID_MASK, id);
>> +
>> + ret = awcc_thermal_information(wdev, AWCC_OP_GET_FAN_MIN_RPM, id,
>> + &min_rpm);
>> + if (ret)
>> + return ret;
>> +
>> + ret = awcc_thermal_information(wdev, AWCC_OP_GET_FAN_MAX_RPM, id,
>> + &max_rpm);
>> + if (ret)
>> + return ret;
>> +
>> + ret = awcc_get_fan_sensors(wdev, AWCC_OP_GET_TOTAL_FAN_TEMPS, id,
>> + 0, &temp_count);
>> + if (ret)
>> + return ret;
>> +
>> + for (j = 0; j < temp_count; j++) {
>> + ret = awcc_get_fan_sensors(wdev, AWCC_OP_GET_FAN_TEMP_ID,
>> + id, j, &temp_id);
>> + if (ret)
>> + break;
>> +
>> + temp_id = FIELD_GET(AWCC_RESOURCE_ID_MASK, temp_id);
>> + __set_bit(temp_id, fan_temps);
>> + }
>> +
>> + label = awcc_alloc_fan_label(&wdev->dev, temp_count, temp_id);
>> + if (!label)
>> + return -ENOMEM;
>> +
>> + fan_data->id = id;
>> + fan_data->min_rpm = min_rpm;
>> + fan_data->max_rpm = max_rpm;
>> + fan_data->label = label;
>> + bitmap_gather(gather, fan_temps, priv->temp_sensors, AWCC_ID_BITMAP_SIZE);
>> + bitmap_copy(&fan_data->auto_channels_temp, gather, priv->temp_count);
>
> Please check that the bitmap can fit into the long variable before using bitmap_copy().
priv->temp_count is bounded by AWCC_MAX_RES_COUNT which is 16.
I don't think it will ever be bigger than 64, but I will replace it with
BITS_PER_LONG just to be extra careful.
Thank you very much for reviewing the whole series!
--
~ Kurt
next prev parent reply other threads:[~2025-03-12 5:06 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-08 20:23 [PATCH v4 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements Kurt Borja
2025-03-08 20:23 ` [PATCH v4 01/12] platform/x86: alienware-wmi-wmax: Rename thermal related symbols Kurt Borja
2025-03-08 20:23 ` [PATCH v4 02/12] platform/x86: alienware-wmi-wmax: Refactor is_awcc_thermal_mode() Kurt Borja
2025-03-08 20:23 ` [PATCH v4 03/12] platform/x86: alienware-wmi-wmax: Improve internal AWCC API Kurt Borja
2025-03-08 20:23 ` [PATCH v4 04/12] platform/x86: alienware-wmi-wmax: Modify supported_thermal_profiles[] Kurt Borja
2025-03-08 20:23 ` [PATCH v4 05/12] platform/x86: alienware-wmi-wmax: Improve platform profile probe Kurt Borja
2025-03-08 20:23 ` [PATCH v4 06/12] platform/x86: alienware-wmi-wmax: Add support for the "custom" thermal profile Kurt Borja
2025-03-08 20:23 ` [PATCH v4 07/12] platform/x86: alienware-wmi-wmax: Add HWMON support Kurt Borja
2025-03-11 18:55 ` Armin Wolf
2025-03-12 5:06 ` Kurt Borja [this message]
2025-03-08 20:23 ` [PATCH v4 08/12] platform/x86: alienware-wmi-wmax: Add support for manual fan control Kurt Borja
2025-03-11 18:59 ` Armin Wolf
2025-03-08 20:23 ` [PATCH v4 09/12] platform/x86: alienware-wmi-wmax: Add a DebugFS interface Kurt Borja
2025-03-08 20:23 ` [PATCH v4 10/12] Documentation: wmi: Improve and update alienware-wmi documentation Kurt Borja
2025-03-08 20:23 ` [PATCH v4 11/12] Documentation: admin-guide: laptops: Add documentation for alienware-wmi Kurt Borja
2025-03-11 19:27 ` Armin Wolf
2025-03-08 20:23 ` [PATCH v4 12/12] Documentation: ABI: Add sysfs platform and debugfs ABI " Kurt Borja
2025-03-11 19:29 ` Armin Wolf
2025-03-08 21:23 ` [PATCH v4 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements Guenter Roeck
2025-03-09 16:05 ` Kurt Borja
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=D8E0YIGL8OB5.1LCMBZ0T595Q1@gmail.com \
--to=kuurtb@gmail.com \
--cc=Dell.Client.Kernel@dell.com \
--cc=W_Armin@gmx.de \
--cc=hdegoede@redhat.com \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=jdelvare@suse.com \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=platform-driver-x86@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.