Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 549b4930 authored by Mario Limonciello's avatar Mario Limonciello Committed by Darren Hart (VMware)
Browse files

platform/x86: dell-smbios: Introduce dispatcher for SMM calls



This splits up the dell-smbios driver into two drivers:
* dell-smbios
* dell-smbios-smm

dell-smbios can operate with multiple different dispatcher drivers to
perform SMBIOS operations.

Also modify the interface that dell-laptop and dell-wmi use align to this
model more closely.  Rather than a single global buffer being allocated
for all drivers, each driver will allocate and be responsible for it's own
buffer. The pointer will be passed to the calling function and each
dispatcher driver will then internally copy it to the proper location to
perform it's call.

Add defines for calls used by these methods in the dell-smbios.h header
for tracking purposes.

Signed-off-by: default avatarMario Limonciello <mario.limonciello@dell.com>
Reviewed-by: default avatarEdward O'Callaghan <quasisec@google.com>
Signed-off-by: default avatarDarren Hart (VMware) <dvhart@infradead.org>
parent 33b9ca1e
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -3985,6 +3985,12 @@ L: platform-driver-x86@vger.kernel.org
S:	Maintained
S:	Maintained
F:	drivers/platform/x86/dell-smbios.*
F:	drivers/platform/x86/dell-smbios.*


DELL SMBIOS SMM DRIVER
M:	Mario Limonciello <mario.limonciello@dell.com>
L:	platform-driver-x86@vger.kernel.org
S:	Maintained
F:	drivers/platform/x86/dell-smbios-smm.c

DELL LAPTOP DRIVER
DELL LAPTOP DRIVER
M:	Matthew Garrett <mjg59@srcf.ucam.org>
M:	Matthew Garrett <mjg59@srcf.ucam.org>
M:	Pali Rohár <pali.rohar@gmail.com>
M:	Pali Rohár <pali.rohar@gmail.com>
+11 −4
Original line number Original line Diff line number Diff line
@@ -93,12 +93,19 @@ config ASUS_LAPTOP


config DELL_SMBIOS
config DELL_SMBIOS
	tristate
	tristate
	select DCDBAS

config DELL_SMBIOS_SMM
	tristate "Dell SMBIOS calling interface (SMM implementation)"
	depends on DCDBAS
	default DCDBAS
	select DELL_SMBIOS
	---help---
	---help---
	This module provides common functions for kernel modules using
	This provides an implementation for the Dell SMBIOS calling interface
	Dell SMBIOS.
	communicated over SMI/SMM.


	If you have a Dell laptop, say Y or M here.
	If you have a Dell computer from <=2017 you should say Y or M here.
	If you aren't sure and this module doesn't work for your computer
	it just won't load.


config DELL_LAPTOP
config DELL_LAPTOP
	tristate "Dell Laptop Extras"
	tristate "Dell Laptop Extras"
+1 −0
Original line number Original line Diff line number Diff line
@@ -12,6 +12,7 @@ obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
obj-$(CONFIG_ACPI_CMPC)		+= classmate-laptop.o
obj-$(CONFIG_ACPI_CMPC)		+= classmate-laptop.o
obj-$(CONFIG_COMPAL_LAPTOP)	+= compal-laptop.o
obj-$(CONFIG_COMPAL_LAPTOP)	+= compal-laptop.o
obj-$(CONFIG_DELL_SMBIOS)	+= dell-smbios.o
obj-$(CONFIG_DELL_SMBIOS)	+= dell-smbios.o
obj-$(CONFIG_DELL_SMBIOS_SMM)	+= dell-smbios-smm.o
obj-$(CONFIG_DELL_LAPTOP)	+= dell-laptop.o
obj-$(CONFIG_DELL_LAPTOP)	+= dell-laptop.o
obj-$(CONFIG_DELL_WMI)		+= dell-wmi.o
obj-$(CONFIG_DELL_WMI)		+= dell-wmi.o
obj-$(CONFIG_DELL_WMI_DESCRIPTOR)	+= dell-wmi-descriptor.o
obj-$(CONFIG_DELL_WMI_DESCRIPTOR)	+= dell-wmi-descriptor.o
+105 −178
Original line number Original line Diff line number Diff line
@@ -35,18 +35,6 @@
#include "dell-rbtn.h"
#include "dell-rbtn.h"
#include "dell-smbios.h"
#include "dell-smbios.h"


#define BRIGHTNESS_TOKEN 0x7d
#define KBD_LED_OFF_TOKEN 0x01E1
#define KBD_LED_ON_TOKEN 0x01E2
#define KBD_LED_AUTO_TOKEN 0x01E3
#define KBD_LED_AUTO_25_TOKEN 0x02EA
#define KBD_LED_AUTO_50_TOKEN 0x02EB
#define KBD_LED_AUTO_75_TOKEN 0x02EC
#define KBD_LED_AUTO_100_TOKEN 0x02F6
#define GLOBAL_MIC_MUTE_ENABLE 0x0364
#define GLOBAL_MIC_MUTE_DISABLE 0x0365
#define KBD_LED_AC_TOKEN 0x0451

struct quirk_entry {
struct quirk_entry {
	u8 touchpad_led;
	u8 touchpad_led;


@@ -85,6 +73,7 @@ static struct platform_driver platform_driver = {
	}
	}
};
};


static struct calling_interface_buffer *buffer;
static struct platform_device *platform_device;
static struct platform_device *platform_device;
static struct backlight_device *dell_backlight_device;
static struct backlight_device *dell_backlight_device;
static struct rfkill *wifi_rfkill;
static struct rfkill *wifi_rfkill;
@@ -283,6 +272,27 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
	{ }
	{ }
};
};


void dell_set_arguments(u32 arg0, u32 arg1, u32 arg2, u32 arg3)
{
	memset(buffer, 0, sizeof(struct calling_interface_buffer));
	buffer->input[0] = arg0;
	buffer->input[1] = arg1;
	buffer->input[2] = arg2;
	buffer->input[3] = arg3;
}

int dell_send_request(u16 class, u16 select)
{
	int ret;

	buffer->cmd_class = class;
	buffer->cmd_select = select;
	ret = dell_smbios_call(buffer);
	if (ret != 0)
		return ret;
	return dell_smbios_error(buffer->output[0]);
}

/*
/*
 * Derived from information in smbios-wireless-ctl:
 * Derived from information in smbios-wireless-ctl:
 *
 *
@@ -405,7 +415,6 @@ static const struct dmi_system_id dell_quirks[] __initconst = {


static int dell_rfkill_set(void *data, bool blocked)
static int dell_rfkill_set(void *data, bool blocked)
{
{
	struct calling_interface_buffer *buffer;
	int disable = blocked ? 1 : 0;
	int disable = blocked ? 1 : 0;
	unsigned long radio = (unsigned long)data;
	unsigned long radio = (unsigned long)data;
	int hwswitch_bit = (unsigned long)data - 1;
	int hwswitch_bit = (unsigned long)data - 1;
@@ -413,20 +422,16 @@ static int dell_rfkill_set(void *data, bool blocked)
	int status;
	int status;
	int ret;
	int ret;


	buffer = dell_smbios_get_buffer();
	dell_set_arguments(0, 0, 0, 0);

	ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
	dell_smbios_send_request(17, 11);
	if (ret)
	ret = buffer->output[0];
		return ret;
	status = buffer->output[1];
	status = buffer->output[1];


	if (ret != 0)
	dell_set_arguments(0x2, 0, 0, 0);
		goto out;
	ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);

	if (ret)
	dell_smbios_clear_buffer();
		return ret;

	buffer->input[0] = 0x2;
	dell_smbios_send_request(17, 11);
	ret = buffer->output[0];
	hwswitch = buffer->output[1];
	hwswitch = buffer->output[1];


	/* If the hardware switch controls this radio, and the hardware
	/* If the hardware switch controls this radio, and the hardware
@@ -435,28 +440,19 @@ static int dell_rfkill_set(void *data, bool blocked)
	    (status & BIT(0)) && !(status & BIT(16)))
	    (status & BIT(0)) && !(status & BIT(16)))
		disable = 1;
		disable = 1;


	dell_smbios_clear_buffer();
	dell_set_arguments(1 | (radio<<8) | (disable << 16), 0, 0, 0);

	ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
	buffer->input[0] = (1 | (radio<<8) | (disable << 16));
	return ret;
	dell_smbios_send_request(17, 11);
	ret = buffer->output[0];

 out:
	dell_smbios_release_buffer();
	return dell_smbios_error(ret);
}
}


/* Must be called with the buffer held */
static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
					int status,
					int status)
					struct calling_interface_buffer *buffer)
{
{
	if (status & BIT(0)) {
	if (status & BIT(0)) {
		/* Has hw-switch, sync sw_state to BIOS */
		/* Has hw-switch, sync sw_state to BIOS */
		int block = rfkill_blocked(rfkill);
		int block = rfkill_blocked(rfkill);
		dell_smbios_clear_buffer();
		dell_set_arguments(1 | (radio << 8) | (block << 16), 0, 0, 0);
		buffer->input[0] = (1 | (radio << 8) | (block << 16));
		dell_send_request(CLASS_INFO, SELECT_RFKILL);
		dell_smbios_send_request(17, 11);
	} else {
	} else {
		/* No hw-switch, sync BIOS state to sw_state */
		/* No hw-switch, sync BIOS state to sw_state */
		rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
		rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
@@ -472,32 +468,23 @@ static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,


static void dell_rfkill_query(struct rfkill *rfkill, void *data)
static void dell_rfkill_query(struct rfkill *rfkill, void *data)
{
{
	struct calling_interface_buffer *buffer;
	int radio = ((unsigned long)data & 0xF);
	int radio = ((unsigned long)data & 0xF);
	int hwswitch;
	int hwswitch;
	int status;
	int status;
	int ret;
	int ret;


	buffer = dell_smbios_get_buffer();
	dell_set_arguments(0, 0, 0, 0);

	ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
	dell_smbios_send_request(17, 11);
	ret = buffer->output[0];
	status = buffer->output[1];
	status = buffer->output[1];


	if (ret != 0 || !(status & BIT(0))) {
	if (ret != 0 || !(status & BIT(0))) {
		dell_smbios_release_buffer();
		return;
		return;
	}
	}


	dell_smbios_clear_buffer();
	dell_set_arguments(0, 0x2, 0, 0);

	ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
	buffer->input[0] = 0x2;
	dell_smbios_send_request(17, 11);
	ret = buffer->output[0];
	hwswitch = buffer->output[1];
	hwswitch = buffer->output[1];


	dell_smbios_release_buffer();

	if (ret != 0)
	if (ret != 0)
		return;
		return;


@@ -513,27 +500,23 @@ static struct dentry *dell_laptop_dir;


static int dell_debugfs_show(struct seq_file *s, void *data)
static int dell_debugfs_show(struct seq_file *s, void *data)
{
{
	struct calling_interface_buffer *buffer;
	int hwswitch_state;
	int hwswitch_state;
	int hwswitch_ret;
	int hwswitch_ret;
	int status;
	int status;
	int ret;
	int ret;


	buffer = dell_smbios_get_buffer();
	dell_set_arguments(0, 0, 0, 0);

	ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
	dell_smbios_send_request(17, 11);
	if (ret)
	ret = buffer->output[0];
		return ret;
	status = buffer->output[1];
	status = buffer->output[1];


	dell_smbios_clear_buffer();
	dell_set_arguments(0, 0x2, 0, 0);

	hwswitch_ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
	buffer->input[0] = 0x2;
	if (hwswitch_ret)
	dell_smbios_send_request(17, 11);
		return hwswitch_ret;
	hwswitch_ret = buffer->output[0];
	hwswitch_state = buffer->output[1];
	hwswitch_state = buffer->output[1];


	dell_smbios_release_buffer();

	seq_printf(s, "return:\t%d\n", ret);
	seq_printf(s, "return:\t%d\n", ret);
	seq_printf(s, "status:\t0x%X\n", status);
	seq_printf(s, "status:\t0x%X\n", status);
	seq_printf(s, "Bit 0 : Hardware switch supported:   %lu\n",
	seq_printf(s, "Bit 0 : Hardware switch supported:   %lu\n",
@@ -613,46 +596,36 @@ static const struct file_operations dell_debugfs_fops = {


static void dell_update_rfkill(struct work_struct *ignored)
static void dell_update_rfkill(struct work_struct *ignored)
{
{
	struct calling_interface_buffer *buffer;
	int hwswitch = 0;
	int hwswitch = 0;
	int status;
	int status;
	int ret;
	int ret;


	buffer = dell_smbios_get_buffer();
	dell_set_arguments(0, 0, 0, 0);

	ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
	dell_smbios_send_request(17, 11);
	ret = buffer->output[0];
	status = buffer->output[1];
	status = buffer->output[1];


	if (ret != 0)
	if (ret != 0)
		goto out;
		return;

	dell_smbios_clear_buffer();


	buffer->input[0] = 0x2;
	dell_set_arguments(0, 0x2, 0, 0);
	dell_smbios_send_request(17, 11);
	ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
	ret = buffer->output[0];


	if (ret == 0 && (status & BIT(0)))
	if (ret == 0 && (status & BIT(0)))
		hwswitch = buffer->output[1];
		hwswitch = buffer->output[1];


	if (wifi_rfkill) {
	if (wifi_rfkill) {
		dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch);
		dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch);
		dell_rfkill_update_sw_state(wifi_rfkill, 1, status, buffer);
		dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
	}
	}
	if (bluetooth_rfkill) {
	if (bluetooth_rfkill) {
		dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status,
		dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status,
					    hwswitch);
					    hwswitch);
		dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status,
		dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
					    buffer);
	}
	}
	if (wwan_rfkill) {
	if (wwan_rfkill) {
		dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch);
		dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch);
		dell_rfkill_update_sw_state(wwan_rfkill, 3, status, buffer);
		dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
	}
	}

 out:
	dell_smbios_release_buffer();
}
}
static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);


@@ -696,7 +669,6 @@ static struct notifier_block dell_laptop_rbtn_notifier = {


static int __init dell_setup_rfkill(void)
static int __init dell_setup_rfkill(void)
{
{
	struct calling_interface_buffer *buffer;
	int status, ret, whitelisted;
	int status, ret, whitelisted;
	const char *product;
	const char *product;


@@ -712,11 +684,9 @@ static int __init dell_setup_rfkill(void)
	if (!force_rfkill && !whitelisted)
	if (!force_rfkill && !whitelisted)
		return 0;
		return 0;


	buffer = dell_smbios_get_buffer();
	dell_set_arguments(0, 0, 0, 0);
	dell_smbios_send_request(17, 11);
	ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
	ret = buffer->output[0];
	status = buffer->output[1];
	status = buffer->output[1];
	dell_smbios_release_buffer();


	/* dell wireless info smbios call is not supported */
	/* dell wireless info smbios call is not supported */
	if (ret != 0)
	if (ret != 0)
@@ -869,7 +839,6 @@ static void dell_cleanup_rfkill(void)


static int dell_send_intensity(struct backlight_device *bd)
static int dell_send_intensity(struct backlight_device *bd)
{
{
	struct calling_interface_buffer *buffer;
	struct calling_interface_token *token;
	struct calling_interface_token *token;
	int ret;
	int ret;


@@ -877,24 +846,17 @@ static int dell_send_intensity(struct backlight_device *bd)
	if (!token)
	if (!token)
		return -ENODEV;
		return -ENODEV;


	buffer = dell_smbios_get_buffer();
	dell_set_arguments(token->location, bd->props.brightness, 0, 0);
	buffer->input[0] = token->location;
	buffer->input[1] = bd->props.brightness;

	if (power_supply_is_system_supplied() > 0)
	if (power_supply_is_system_supplied() > 0)
		dell_smbios_send_request(1, 2);
		ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_AC);
	else
	else
		dell_smbios_send_request(1, 1);
		ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT);

	ret = dell_smbios_error(buffer->output[0]);


	dell_smbios_release_buffer();
	return ret;
	return ret;
}
}


static int dell_get_intensity(struct backlight_device *bd)
static int dell_get_intensity(struct backlight_device *bd)
{
{
	struct calling_interface_buffer *buffer;
	struct calling_interface_token *token;
	struct calling_interface_token *token;
	int ret;
	int ret;


@@ -902,20 +864,14 @@ static int dell_get_intensity(struct backlight_device *bd)
	if (!token)
	if (!token)
		return -ENODEV;
		return -ENODEV;


	buffer = dell_smbios_get_buffer();
	dell_set_arguments(token->location, 0, 0, 0);
	buffer->input[0] = token->location;

	if (power_supply_is_system_supplied() > 0)
	if (power_supply_is_system_supplied() > 0)
		dell_smbios_send_request(0, 2);
		ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC);
	else
	else
		dell_smbios_send_request(0, 1);
		ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_BAT);


	if (buffer->output[0])
	if (ret == 0)
		ret = dell_smbios_error(buffer->output[0]);
	else
		ret = buffer->output[1];
		ret = buffer->output[1];

	dell_smbios_release_buffer();
	return ret;
	return ret;
}
}


@@ -1179,20 +1135,13 @@ static DEFINE_MUTEX(kbd_led_mutex);


static int kbd_get_info(struct kbd_info *info)
static int kbd_get_info(struct kbd_info *info)
{
{
	struct calling_interface_buffer *buffer;
	u8 units;
	u8 units;
	int ret;
	int ret;


	buffer = dell_smbios_get_buffer();
	dell_set_arguments(0, 0, 0, 0);

	ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
	buffer->input[0] = 0x0;
	if (ret)
	dell_smbios_send_request(4, 11);
		return ret;
	ret = buffer->output[0];

	if (ret) {
		ret = dell_smbios_error(ret);
		goto out;
	}


	info->modes = buffer->output[1] & 0xFFFF;
	info->modes = buffer->output[1] & 0xFFFF;
	info->type = (buffer->output[1] >> 24) & 0xFF;
	info->type = (buffer->output[1] >> 24) & 0xFF;
@@ -1209,8 +1158,6 @@ static int kbd_get_info(struct kbd_info *info)
	if (units & BIT(3))
	if (units & BIT(3))
		info->days = (buffer->output[3] >> 24) & 0xFF;
		info->days = (buffer->output[3] >> 24) & 0xFF;


 out:
	dell_smbios_release_buffer();
	return ret;
	return ret;
}
}


@@ -1269,19 +1216,12 @@ static int kbd_set_level(struct kbd_state *state, u8 level)


static int kbd_get_state(struct kbd_state *state)
static int kbd_get_state(struct kbd_state *state)
{
{
	struct calling_interface_buffer *buffer;
	int ret;
	int ret;


	buffer = dell_smbios_get_buffer();
	dell_set_arguments(0x1, 0, 0, 0);

	ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
	buffer->input[0] = 0x1;
	if (ret)
	dell_smbios_send_request(4, 11);
		return ret;
	ret = buffer->output[0];

	if (ret) {
		ret = dell_smbios_error(ret);
		goto out;
	}


	state->mode_bit = ffs(buffer->output[1] & 0xFFFF);
	state->mode_bit = ffs(buffer->output[1] & 0xFFFF);
	if (state->mode_bit != 0)
	if (state->mode_bit != 0)
@@ -1296,31 +1236,27 @@ static int kbd_get_state(struct kbd_state *state)
	state->timeout_value_ac = (buffer->output[2] >> 24) & 0x3F;
	state->timeout_value_ac = (buffer->output[2] >> 24) & 0x3F;
	state->timeout_unit_ac = (buffer->output[2] >> 30) & 0x3;
	state->timeout_unit_ac = (buffer->output[2] >> 30) & 0x3;


 out:
	dell_smbios_release_buffer();
	return ret;
	return ret;
}
}


static int kbd_set_state(struct kbd_state *state)
static int kbd_set_state(struct kbd_state *state)
{
{
	struct calling_interface_buffer *buffer;
	int ret;
	int ret;
	u32 input1;
	u32 input2;

	input1 = BIT(state->mode_bit) & 0xFFFF;
	input1 |= (state->triggers & 0xFF) << 16;
	input1 |= (state->timeout_value & 0x3F) << 24;
	input1 |= (state->timeout_unit & 0x3) << 30;
	input2 = state->als_setting & 0xFF;
	input2 |= (state->level & 0xFF) << 16;
	input2 |= (state->timeout_value_ac & 0x3F) << 24;
	input2 |= (state->timeout_unit_ac & 0x3) << 30;
	dell_set_arguments(0x2, input1, input2, 0);
	ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);


	buffer = dell_smbios_get_buffer();
	return ret;
	buffer->input[0] = 0x2;
	buffer->input[1] = BIT(state->mode_bit) & 0xFFFF;
	buffer->input[1] |= (state->triggers & 0xFF) << 16;
	buffer->input[1] |= (state->timeout_value & 0x3F) << 24;
	buffer->input[1] |= (state->timeout_unit & 0x3) << 30;
	buffer->input[2] = state->als_setting & 0xFF;
	buffer->input[2] |= (state->level & 0xFF) << 16;
	buffer->input[2] |= (state->timeout_value_ac & 0x3F) << 24;
	buffer->input[2] |= (state->timeout_unit_ac & 0x3) << 30;
	dell_smbios_send_request(4, 11);
	ret = buffer->output[0];
	dell_smbios_release_buffer();

	return dell_smbios_error(ret);
}
}


static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
@@ -1345,7 +1281,6 @@ static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)


static int kbd_set_token_bit(u8 bit)
static int kbd_set_token_bit(u8 bit)
{
{
	struct calling_interface_buffer *buffer;
	struct calling_interface_token *token;
	struct calling_interface_token *token;
	int ret;
	int ret;


@@ -1356,19 +1291,14 @@ static int kbd_set_token_bit(u8 bit)
	if (!token)
	if (!token)
		return -EINVAL;
		return -EINVAL;


	buffer = dell_smbios_get_buffer();
	dell_set_arguments(token->location, token->value, 0, 0);
	buffer->input[0] = token->location;
	ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
	buffer->input[1] = token->value;
	dell_smbios_send_request(1, 0);
	ret = buffer->output[0];
	dell_smbios_release_buffer();


	return dell_smbios_error(ret);
	return ret;
}
}


static int kbd_get_token_bit(u8 bit)
static int kbd_get_token_bit(u8 bit)
{
{
	struct calling_interface_buffer *buffer;
	struct calling_interface_token *token;
	struct calling_interface_token *token;
	int ret;
	int ret;
	int val;
	int val;
@@ -1380,15 +1310,12 @@ static int kbd_get_token_bit(u8 bit)
	if (!token)
	if (!token)
		return -EINVAL;
		return -EINVAL;


	buffer = dell_smbios_get_buffer();
	dell_set_arguments(token->location, 0, 0, 0);
	buffer->input[0] = token->location;
	ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_STD);
	dell_smbios_send_request(0, 0);
	ret = buffer->output[0];
	val = buffer->output[1];
	val = buffer->output[1];
	dell_smbios_release_buffer();


	if (ret)
	if (ret)
		return dell_smbios_error(ret);
		return ret;


	return (val == token->value);
	return (val == token->value);
}
}
@@ -2102,7 +2029,6 @@ static struct notifier_block dell_laptop_notifier = {


int dell_micmute_led_set(int state)
int dell_micmute_led_set(int state)
{
{
	struct calling_interface_buffer *buffer;
	struct calling_interface_token *token;
	struct calling_interface_token *token;


	if (state == 0)
	if (state == 0)
@@ -2115,11 +2041,8 @@ int dell_micmute_led_set(int state)
	if (!token)
	if (!token)
		return -ENODEV;
		return -ENODEV;


	buffer = dell_smbios_get_buffer();
	dell_set_arguments(token->location, token->value, 0, 0);
	buffer->input[0] = token->location;
	dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
	buffer->input[1] = token->value;
	dell_smbios_send_request(1, 0);
	dell_smbios_release_buffer();


	return state;
	return state;
}
}
@@ -2127,7 +2050,6 @@ EXPORT_SYMBOL_GPL(dell_micmute_led_set);


static int __init dell_init(void)
static int __init dell_init(void)
{
{
	struct calling_interface_buffer *buffer;
	struct calling_interface_token *token;
	struct calling_interface_token *token;
	int max_intensity = 0;
	int max_intensity = 0;
	int ret;
	int ret;
@@ -2158,6 +2080,10 @@ static int __init dell_init(void)
		goto fail_rfkill;
		goto fail_rfkill;
	}
	}


	buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL);
	if (!buffer)
		goto fail_buffer;

	if (quirks && quirks->touchpad_led)
	if (quirks && quirks->touchpad_led)
		touchpad_led_init(&platform_device->dev);
		touchpad_led_init(&platform_device->dev);


@@ -2175,12 +2101,10 @@ static int __init dell_init(void)


	token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
	token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
	if (token) {
	if (token) {
		buffer = dell_smbios_get_buffer();
		dell_set_arguments(token->location, 0, 0, 0);
		buffer->input[0] = token->location;
		ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC);
		dell_smbios_send_request(0, 2);
		if (ret)
		if (buffer->output[0] == 0)
			max_intensity = buffer->output[3];
			max_intensity = buffer->output[3];
		dell_smbios_release_buffer();
	}
	}


	if (max_intensity) {
	if (max_intensity) {
@@ -2214,6 +2138,8 @@ static int __init dell_init(void)
fail_get_brightness:
fail_get_brightness:
	backlight_device_unregister(dell_backlight_device);
	backlight_device_unregister(dell_backlight_device);
fail_backlight:
fail_backlight:
	kfree(buffer);
fail_buffer:
	dell_cleanup_rfkill();
	dell_cleanup_rfkill();
fail_rfkill:
fail_rfkill:
	platform_device_del(platform_device);
	platform_device_del(platform_device);
@@ -2233,6 +2159,7 @@ static void __exit dell_exit(void)
		touchpad_led_exit();
		touchpad_led_exit();
	kbd_led_exit();
	kbd_led_exit();
	backlight_device_unregister(dell_backlight_device);
	backlight_device_unregister(dell_backlight_device);
	kfree(buffer);
	dell_cleanup_rfkill();
	dell_cleanup_rfkill();
	if (platform_device) {
	if (platform_device) {
		platform_device_unregister(platform_device);
		platform_device_unregister(platform_device);
+163 −0
Original line number Original line Diff line number Diff line
/*
 *  SMI methods for use with dell-smbios
 *
 *  Copyright (c) Red Hat <mjg@redhat.com>
 *  Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
 *  Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
 *  Copyright (c) 2017 Dell Inc.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/dmi.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include "../../firmware/dcdbas.h"
#include "dell-smbios.h"

static int da_command_address;
static int da_command_code;
static struct calling_interface_buffer *buffer;
struct platform_device *platform_device;
static DEFINE_MUTEX(smm_mutex);

static const struct dmi_system_id dell_device_table[] __initconst = {
	{
		.ident = "Dell laptop",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
			DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
		},
	},
	{
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
			DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
		},
	},
	{
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
			DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/
		},
	},
	{
		.ident = "Dell Computer Corporation",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
			DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
		},
	},
	{ }
};
MODULE_DEVICE_TABLE(dmi, dell_device_table);

static void __init parse_da_table(const struct dmi_header *dm)
{
	struct calling_interface_structure *table =
		container_of(dm, struct calling_interface_structure, header);

	/* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
	 * 6 bytes of entry
	 */
	if (dm->length < 17)
		return;

	da_command_address = table->cmdIOAddress;
	da_command_code = table->cmdIOCode;
}

static void __init find_cmd_address(const struct dmi_header *dm, void *dummy)
{
	switch (dm->type) {
	case 0xda: /* Calling interface */
		parse_da_table(dm);
		break;
	}
}

int dell_smbios_smm_call(struct calling_interface_buffer *input)
{
	struct smi_cmd command;
	size_t size;

	size = sizeof(struct calling_interface_buffer);
	command.magic = SMI_CMD_MAGIC;
	command.command_address = da_command_address;
	command.command_code = da_command_code;
	command.ebx = virt_to_phys(buffer);
	command.ecx = 0x42534931;

	mutex_lock(&smm_mutex);
	memcpy(buffer, input, size);
	dcdbas_smi_request(&command);
	memcpy(input, buffer, size);
	mutex_unlock(&smm_mutex);
	return 0;
}

static int __init dell_smbios_smm_init(void)
{
	int ret;
	/*
	 * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
	 * is passed to SMI handler.
	 */
	buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
	if (!buffer)
		return -ENOMEM;

	dmi_walk(find_cmd_address, NULL);

	platform_device = platform_device_alloc("dell-smbios", 1);
	if (!platform_device) {
		ret = -ENOMEM;
		goto fail_platform_device_alloc;
	}

	ret = platform_device_add(platform_device);
	if (ret)
		goto fail_platform_device_add;

	ret = dell_smbios_register_device(&platform_device->dev,
					  &dell_smbios_smm_call);
	if (ret)
		goto fail_register;

	return 0;

fail_register:
	platform_device_del(platform_device);

fail_platform_device_add:
	platform_device_put(platform_device);

fail_platform_device_alloc:
	free_page((unsigned long)buffer);
	return ret;
}

static void __exit dell_smbios_smm_exit(void)
{
	if (platform_device) {
		dell_smbios_unregister_device(&platform_device->dev);
		platform_device_unregister(platform_device);
		free_page((unsigned long)buffer);
	}
}

subsys_initcall(dell_smbios_smm_init);
module_exit(dell_smbios_smm_exit);

MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
MODULE_DESCRIPTION("Dell SMBIOS communications over SMI");
MODULE_LICENSE("GPL");
Loading