lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <47250D6A.9070000@antonello.org>
Date:	Sun, 28 Oct 2007 23:30:02 +0100
From:	jack@...onello.org
To:	jack@...onello.org
CC:	linux-kernel@...r.kernel.org
Subject: Re: [PATCH] Dell laptop backlight driver

Fixed style.

jacopo


--- linux-2.6.23.1/Documentation/dell-laptop.txt	1970-01-01 01:00:00.000000000 +0100
+++ b/Documentation/dell-laptop.txt	2007-10-28 23:25:26.000000000 +0100
@@ -0,0 +1,107 @@
+This driver is EXPERIMENTAL, use it at YOUR OWN RISK.
+
+BEFORE TRYING THIS DRIVER OUT:
+You MUST FILL IN your laptop parameters in the source code. This
+driver is not capable of probing these parameters on its own.
+
+The parameters involved are:
+- IO Port address
+- IO command code
+- Location
+
+These parameters depend (hopefully) on the model of your laptop.
+Such information is found in a private Dell DMI table. You may
+use dmidecode or libsmbios' dumpCmos
+(http://linux.dell.com/libsmbios/download/libsmbios/). The
+latter is best since it DECODES COMPLETELY Dell's tokens.
+
+******************************************************* WITH LIBSMBIOS
+root@...e:~# dumpCmos | grep 0x007d
+and you will get a line like the following...
+DMI type 0xda  Handle 0xda00  CmdIO Port 0x00b2  CmdIO Code 0x0d \
+                         Type 0x007d  Location 0x0000 value 0000
+
+open dell-laptop.c and write the parameters accordingly under
+the probe_smi_params() function
+
+static int probe_smi_params(void)
+{
+  /*
+   * these should be probed by parsing Dell's CMOS table (type 0xda)
+   */
+  smi_params.cmdio_port = 0x00b2;     /* dumpCmos CmdIO Port  */
+  smi_params.cmdio_code = 0x0d;       /* dumpCmos CmdIO Code  */
+  smi_params.backlight_location = 0;  /* dumpCmos Location        */
+  smi_params.backlight_value = 0;     /* dumpCmos Value (unused)  */
+
+  return 0;
+}
+**********************************************************************
+
+******************************************************* WITH DMIDECODE
+(this is HAND parsing!!)
+dmidecode & look for table type 218
+
+Handle 0xDA00, DMI type 218, 125 bytes
+OEM-specific Type
+        Header and Data:
+                DA 7D 00 DA B2 00 0D 5F 0F 37 40 7D 00 00 00 00
+                00 7E 00 02 00 00 00 40 00 04 00 01 00 41 00 04
+                00 00 00 90 00 05 00 00 00 91 00 05 00 01 00 92
+                00 05 00 02 00 45 01 45 01 01 00 44 01 44 01 00
+                00 00 80 00 80 01 00 00 A0 00 A0 01 00 05 80 05
+                80 01 00 76 01 76 01 01 00 75 01 75 01 01 00 01
+                F0 01 F0 00 00 02 F0 02 F0 00 00 03 F0 03 F0 00
+                00 04 F0 04 F0 00 00 FF FF 00 00 00 00
+
+now you have:
+1 byte  DA               (table type)
+1 byte  7D               (table size)
+2 bytes 00 DA            (handle)
+2 bytes B2 00            (cmdio port address (swapped) )
+1 byte  0D               (cmdio code)
+4 bytes 5F 0F 37 40      (supported commands ?)
+
+after that you have triplets terminated by FF FF 00 00 00 00
+in the form (type, location, value)
+
+type | loc | val
+7D 00|00 00|00 00
+7E 00|02 00|00 00
+40 00|04 00|01 00
+41 00|04 00|00 00
+90 00|05 00|00 00
+91 00|05 00|01 00
+92 00|05 00|02 00
+45 01|45 01|01 00
+44 01|44 01|00 00
+00 80|00 80|01 00
+00 A0|00 A0|01 00
+05 80|05 80|01 00
+76 01|76 01|01 00
+75 01|75 01|01 00
+01 F0|01 F0|00 00
+02 F0|02 F0|00 00
+03 F0|03 F0|00 00
+04 F0|04 F0|00 00
+end
+FF FF|00 00|00 00
+
+you can read the Location field in type 0x007d (swapped 7D 00)
+
+then modify dell-laptop.c accordingly (remember to swap the lsb
+with the msb in the port address also!)
+
+static int probe_smi_params(void)
+{
+  /*
+   * these should be probed by parsing Dell's CMOS table (type 0xda)
+   */
+  smi_params.cmdio_port = 0x00b2;     /* dumpCmos CmdIO Port  */
+  smi_params.cmdio_code = 0x0d;       /* dumpCmos CmdIO Code  */
+  smi_params.backlight_location = 0;  /* dumpCmos Location        */
+  smi_params.backlight_value = 0;     /* dumpCmos Value (unused)  */
+
+  return 0;
+}
+**********************************************************************
--- linux-2.6.23.1/drivers/misc/Makefile	2007-10-12 18:43:44.000000000 +0200
+++ b/drivers/misc/Makefile	2007-10-28 16:42:12.000000000 +0100
@@ -15,3 +15,4 @@ obj-$(CONFIG_SGI_IOC4)		+= ioc4.o
 obj-$(CONFIG_SONY_LAPTOP)	+= sony-laptop.o
 obj-$(CONFIG_THINKPAD_ACPI)	+= thinkpad_acpi.o
 obj-$(CONFIG_EEPROM_93CX6)	+= eeprom_93cx6.o
+obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
--- linux-2.6.23.1/drivers/misc/dell-laptop.h	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/misc/dell-laptop.h	2007-10-28 21:57:21.000000000 +0100
@@ -0,0 +1,117 @@
+/*
+ *  dell-laptop.h - Dell laptop extras
+ *
+ *
+ *  Copyright (C) 2007 Jacopo Antonello <jack@...onello.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _DELL_LAPTOP_H_
+#define _DELL_LAPTOP_H_
+
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#define SMI_CMD_MAGIC  0x534D4931 /* "SMI1" */
+#define SMI_CMD_ECX    0x42534931 /* "BSI1" */
+
+#define SMI_CMD_SIZE   sizeof(struct smi_cmd)
+#define SMI_BUF_SIZE   sizeof(struct smi_cmd_buffer)
+#define SMI_IF_SIZE    (SMI_CMD_SIZE + SMI_BUF_SIZE)
+
+#define SMI_CLASS_READ_SETTING     0
+#define SMI_CLASS_WRITE_SETTING    1
+
+#define SMI_SELECT_BATTERY         1
+#define SMI_SELECT_AC              2
+
+#define BRIGHTNESS_BATTERY  (SMI_SELECT_BATTERY - 1)
+#define BRIGHTNESS_AC       (SMI_SELECT_AC - 1)
+
+#define DELL_TABLE_DA              0xda
+#define DELL_CALLIF_ID_BACKLIGHT   0x007d
+
+#define DRIVER_NAME         "dell-laptop"
+#define DRIVER_DESCRIPTION  "Dell laptop extras"
+#define DRV_PFX             "dell-laptop: "
+
+struct smi_cmd {
+  __u32 magic;
+  __u32 ebx;
+  __u32 ecx;
+  __u16 command_address;
+  __u8 command_code;
+  __u8 reserved;
+  /* __u8 command_buffer[1]; */
+} __attribute__ ((packed));
+
+struct smi_cmd_buffer
+{
+  __u16      smi_class;
+  __u16      smi_select;
+
+  __u32 cbARG1;
+  __u32 cbARG2;
+  __u32 cbARG3;
+  __u32 cbARG4;
+
+  __s32 cbRES1;
+  __s32 cbRES2;
+  __s32 cbRES3;
+  __s32 cbRES4;
+} __attribute__ ((packed));
+
+struct smi_params_t {
+	/* serialize smi calls */
+  struct mutex lock;
+
+  u16 cmdio_port;
+  u8 cmdio_code;
+  u32 supported_cmds;
+
+  u16 backlight_location;
+  u16 backlight_value;
+};
+
+enum backlight_drive_mode {
+  BACKLIGHT_DRIVE_BOTH,
+  BACKLIGHT_DRIVE_CURRENT,
+  BACKLIGHT_DRIVE_BATTERY,
+  BACKLIGHT_DRIVE_AC
+};
+
+static struct platform_driver dell_driver = {
+  .driver = {
+    .name = DRIVER_NAME,
+    .owner = THIS_MODULE,
+  }
+};
+
+static struct backlight_device *dell_backlight_dev;
+static struct platform_device *dell_platform_dev;
+
+static u8 *smi_if;
+static dma_addr_t smi_handle;
+
+static struct smi_params_t smi_params;
+
+static enum backlight_drive_mode backlight_drive_mode;
+static u32 brightness_vals[2];
+static u32 brightness_min_value;
+
+#endif
--- linux-2.6.23.1/drivers/misc/dell-laptop.c	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/misc/dell-laptop.c	2007-10-28 22:17:09.000000000 +0100
@@ -0,0 +1,683 @@
+/*
+ *  dell-laptop.c - Dell laptop extras
+ *
+ *  This driver implements only backlight control for now.
+ *  This driver assumes that you are not using bios password
+ *  protection and does not work if it is enabled.
+ *
+ *  This driver is EXPERIMENTAL and it is not yet complete. In
+ *  particular it lacks proper probing for the hardware
+ *  it is meant to control.
+ *
+ *  WARNING: you MUST fill in your laptop CMOS parameters, by
+ *           modifying the default values found in probe_smi_params().
+ *           Failing to do so could cause unpredictable effects on your
+ *           laptop. In order to do this you may use the dumpCmos utility
+ *           found in libsmbios
+ *           (http://linux.dell.com/libsmbios/download/libsmbios/)
+ *
+ *  Parts of this driver were inspired by dcdbas.c and libsmbios
+ *  (http://linux.dell.com/libsmbios/download/libsmbios/)
+ *
+ *  Copyright (C) 2007 Jacopo Antonello <jack@...onello.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#define DRIVER_VERSION      "0.1"
+
+/*
+ *  Changelog:
+ *  2007-12-22	0.01	initial release
+ *      support for backlight on Dell M70 laptops
+ *      TODO fix framebuffer support
+ *      TODO add cmos token lookup
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/backlight.h>
+
+#include "dell-laptop.h"
+
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION " (version " DRIVER_VERSION ")");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_AUTHOR("jacopo antonello <jack at antonello.org>");
+MODULE_LICENSE("GPL");
+
+/* --------------------------------------------------------------------------
+ *                                  smi
+ * -------------------------------------------------------------------------- */
+
+static int probe_smi_params(void)
+{
+	/*
+	 * these should be probed by parsing Dell's CMOS table (dmi type 0xda)
+	 */
+	smi_params.cmdio_port = 0x00b2;     /* libsmbios' dumpCmos CmdIO Port */
+	smi_params.cmdio_code = 0x0d;       /* libsmbios' dumpCmos CmdIO Code */
+	smi_params.backlight_location = 0;  /* set to dumpCmos Location       */
+	smi_params.backlight_value = 0;     /* set to dumpCmos Value (unused) */
+
+	return 0;
+}
+
+static int trigger_smi(u16 ioport, u8 iocode, struct smi_cmd_buffer *parms)
+{
+	struct smi_cmd *cmd = (struct smi_cmd *)smi_if;
+	struct smi_cmd_buffer *buf = (struct smi_cmd_buffer *)
+		(smi_if + SMI_CMD_SIZE);
+	cpumask_t old_mask;
+	int ret = 0;
+
+	mutex_lock(&smi_params.lock);
+
+	cmd->magic = SMI_CMD_MAGIC;
+	cmd->ebx = (u32)virt_to_phys(buf);
+	cmd->ecx = SMI_CMD_ECX;
+	cmd->command_address = ioport;  /* libsmbios' dumpCmos CmdIO Port */
+	cmd->command_code = iocode;     /* libsmbios' dumpCmos CmdIO Code */
+	cmd->reserved = 0;
+
+	buf->smi_class  = parms->smi_class;    /* 0 read, 1 write           */
+	buf->smi_select = parms->smi_select;   /* 1 battery mode, 2 ac mode */
+
+	buf->cbARG1 = parms->cbARG1;  /* set to dumpCmos Location */
+	buf->cbARG2 = parms->cbARG2;
+	buf->cbARG3 = parms->cbARG3;
+	buf->cbARG4 = parms->cbARG4;
+
+	buf->cbRES1 = parms->cbRES1;  /* set to -1 to tell errors */
+	buf->cbRES2 = parms->cbRES2;
+	buf->cbRES3 = parms->cbRES3;
+	buf->cbRES4 = parms->cbRES4;
+
+	/* dcdbas.c */
+	old_mask = current->cpus_allowed;
+	set_cpus_allowed(current, cpumask_of_cpu(0));
+	if (smp_processor_id() != 0) {
+		dev_dbg(&dell_backlight_dev->dev, "%s: failed to get CPU 0\n",
+			__FUNCTION__);
+		ret = -EBUSY;
+		goto out;
+	}
+
+	asm volatile (
+		"outb %b0,%w1"
+		: /* no output args */
+		: "a" (cmd->command_code),
+			"d" (cmd->command_address),
+			"b" (cmd->ebx),
+			"c" (cmd->ecx)
+		: "memory");
+
+	switch (buf->cbRES1) {
+	case -6: /* output buffer not large enough */
+	case -5: /* output buffer format error */
+	case -3: /* unhandled smi call */
+	case -2: /* unsupported smi call */
+	case -1: /* bios returned error for smi call */
+		ret = -EBUSY;
+		printk(KERN_WARNING DRV_PFX "SMI failed\n");
+		goto out;
+	}
+
+	/* copy back results */
+	parms->smi_class = buf->smi_class;
+	parms->smi_select = buf->smi_select;
+
+	parms->cbARG1 = buf->cbARG1;
+	parms->cbARG2 = buf->cbARG2;
+	parms->cbARG3 = buf->cbARG3;
+	parms->cbARG4 = buf->cbARG4;
+
+	parms->cbRES1 = buf->cbRES1;
+	parms->cbRES2 = buf->cbRES2;
+	parms->cbRES3 = buf->cbRES3;
+	parms->cbRES4 = buf->cbRES4;
+
+out:
+	set_cpus_allowed(current, old_mask);
+
+	mutex_unlock(&smi_params.lock);
+
+	return ret;
+}
+
+/* --------------------------------------------------------------------------
+ *                                  brightness
+ * -------------------------------------------------------------------------- */
+
+/* use read_brightness(SMI_SELECT_*) */
+static void read_brightness(int select)
+{
+	struct smi_cmd_buffer buf = {
+		.smi_class = SMI_CLASS_READ_SETTING,
+		.smi_select = select,
+
+		.cbARG1 = smi_params.backlight_location,
+		.cbARG2 = 0,
+		.cbARG3 = 0,
+		.cbARG4 = 0,
+
+		.cbRES1 = -1,
+		.cbRES2 = 0,
+		.cbRES3 = 0,
+		.cbRES4 = 0
+	};
+
+	if (trigger_smi(smi_params.cmdio_port, smi_params.cmdio_code, &buf)) {
+		printk(KERN_ERR DRV_PFX "unable to read brightness levels\n");
+		return;
+	}
+
+	brightness_vals[select - 1] = buf.cbRES2;
+	printk(KERN_DEBUG DRV_PFX "  brightness_vals[ %d ]: %d\n", select - 1,
+			brightness_vals[select - 1]);
+	brightness_min_value = buf.cbRES3;
+	printk(KERN_DEBUG DRV_PFX "  brightness_min_value: %d\n",
+			brightness_min_value);
+	dell_backlight_dev->props.max_brightness = buf.cbRES4;
+	printk(KERN_DEBUG DRV_PFX "  props.max_brightness: %d\n",
+			dell_backlight_dev->props.max_brightness);
+}
+
+/* use write_brightness(SMI_SELECT_*) */
+static void write_brightness(int select)
+{
+	struct smi_cmd_buffer buf = {
+		.smi_class = SMI_CLASS_WRITE_SETTING,
+		.smi_select = select,
+
+		.cbARG1 = smi_params.backlight_location,
+		.cbARG2 = brightness_vals[select - 1],
+		.cbARG3 = 0,
+		.cbARG4 = 0,
+
+		.cbRES1 = -1,
+		.cbRES2 = 0,
+		.cbRES3 = 0,
+		.cbRES4 = 0
+	};
+
+	if (trigger_smi(smi_params.cmdio_port, smi_params.cmdio_code, &buf)) {
+		printk(KERN_ERR DRV_PFX "unable to write brightness\n");
+		return;
+	}
+
+	if (buf.cbRES2 != brightness_vals[select - 1])
+		printk(KERN_ERR DRV_PFX "brightness value unchanged\n");
+
+	brightness_vals[select - 1] = buf.cbRES2;
+	printk(KERN_DEBUG DRV_PFX "  brightness_vals[ %d ]: %d\n", select - 1,
+			brightness_vals[select - 1]);
+}
+
+static int dell_backlight_update_status(struct backlight_device *dev)
+{
+	dev->props.brightness = dev->props.brightness < brightness_min_value ?
+		brightness_min_value :
+		dev->props.brightness > dev->props.max_brightness ?
+		dev->props.max_brightness : dev->props.brightness;
+
+	switch (backlight_drive_mode) {
+	case BACKLIGHT_DRIVE_BOTH:
+		brightness_vals[BRIGHTNESS_BATTERY] = dev->props.brightness;
+		brightness_vals[BRIGHTNESS_AC] = dev->props.brightness;
+		write_brightness(SMI_SELECT_BATTERY);
+		write_brightness(SMI_SELECT_AC);
+		break;
+	case BACKLIGHT_DRIVE_AC:
+		brightness_vals[BRIGHTNESS_AC] = dev->props.brightness;
+		write_brightness(SMI_SELECT_AC);
+		break;
+	case BACKLIGHT_DRIVE_BATTERY:
+		brightness_vals[BRIGHTNESS_BATTERY] = dev->props.brightness;
+		write_brightness(SMI_SELECT_BATTERY);
+		break;
+	case BACKLIGHT_DRIVE_CURRENT:
+		printk(KERN_ERR DRV_PFX
+				"BACKLIGHT_DRIVER_CURRENT unimplemented\n");
+	default:
+		printk(KERN_ERR DRV_PFX "unknown backlight_drive_mode\n");
+	}
+
+	return dev->props.brightness;
+}
+
+static void update_from_brightness_vals(void)
+{
+	switch (backlight_drive_mode) {
+	case BACKLIGHT_DRIVE_BOTH:
+		dell_backlight_dev->props.brightness =
+			brightness_vals[BRIGHTNESS_BATTERY];
+		if (brightness_vals[BRIGHTNESS_AC] !=
+				brightness_vals[BRIGHTNESS_BATTERY]) {
+			brightness_vals[BRIGHTNESS_AC] =
+				brightness_vals[BRIGHTNESS_BATTERY];
+			/* force ac to the same value */
+			write_brightness(SMI_SELECT_AC);
+		}
+		break;
+	case BACKLIGHT_DRIVE_AC:
+		dell_backlight_dev->props.brightness =
+			brightness_vals[BRIGHTNESS_AC];
+		break;
+	case BACKLIGHT_DRIVE_BATTERY:
+		dell_backlight_dev->props.brightness =
+			brightness_vals[BRIGHTNESS_BATTERY];
+		break;
+	case BACKLIGHT_DRIVE_CURRENT:
+		printk(KERN_ERR DRV_PFX
+				"BACKLIGHT_DRIVER_CURRENT unimplemented\n");
+	default:
+		printk(KERN_ERR DRV_PFX "unknown backlight_drive_mode\n");
+	}
+}
+
+static int dell_backlight_get_brightness(struct backlight_device *dev)
+{
+	/* is locking needed? */
+	mutex_lock(&dell_backlight_dev->update_lock);
+
+	read_brightness(SMI_SELECT_BATTERY);
+	read_brightness(SMI_SELECT_AC);
+
+	update_from_brightness_vals();
+
+	mutex_unlock(&dell_backlight_dev->update_lock);
+
+	return dev->props.brightness;
+}
+
+static struct backlight_ops dell_backlight_ops = {
+	.update_status = dell_backlight_update_status,
+	.get_brightness = dell_backlight_get_brightness
+};
+
+static int dell_backlight_init(struct device *dev)
+{
+	dell_backlight_dev = backlight_device_register(
+			"dell-laptop",
+			NULL,
+			NULL,
+			&dell_backlight_ops);
+
+	if (IS_ERR(dell_backlight_dev)) {
+		dell_backlight_dev = NULL;
+		return PTR_ERR(dell_backlight_dev);
+	}
+
+	backlight_drive_mode = BACKLIGHT_DRIVE_AC;
+
+	dell_backlight_get_brightness(dell_backlight_dev);
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------------
+ *                                  sysfs
+ * -------------------------------------------------------------------------- */
+
+/* show */
+static ssize_t show_bl_drive_mode(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	char *mode = "unknown";
+
+	switch (backlight_drive_mode) {
+	case BACKLIGHT_DRIVE_BOTH:
+		mode = "BACKLIGHT_DRIVE_BOTH";
+		break;
+	case BACKLIGHT_DRIVE_CURRENT:
+		mode = "BACKLIGHT_DRIVE_CURRENT";
+		break;
+	case BACKLIGHT_DRIVE_BATTERY:
+		mode = "BACKLIGHT_DRIVE_BATTERY";
+		break;
+	case BACKLIGHT_DRIVE_AC:
+		mode = "BACKLIGHT_DRIVE_AC";
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", mode);
+}
+
+static ssize_t show_bl_drive_mode_list(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int len = 0;
+
+	len = snprintf(buf, PAGE_SIZE,
+			"%d:BACKLIGHT_DRIVE_BOTH\n",
+			BACKLIGHT_DRIVE_BOTH);
+	len += snprintf(buf + len, PAGE_SIZE - len,
+			"%d:BACKLIGHT_DRIVE_CURRENT\n",
+			BACKLIGHT_DRIVE_CURRENT);
+	len += snprintf(buf + len, PAGE_SIZE - len,
+			"%d:BACKLIGHT_DRIVE_BATTERY\n",
+			BACKLIGHT_DRIVE_BATTERY);
+	len += snprintf(buf + len, PAGE_SIZE - len,
+			"%d:BACKLIGHT_DRIVE_AC\n",
+			BACKLIGHT_DRIVE_AC);
+
+	return len;
+}
+
+static ssize_t show_cmdio_port(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%04x\n", smi_params.cmdio_port);
+}
+
+static ssize_t show_cmdio_code(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%02x\n", smi_params.cmdio_code);
+}
+
+static ssize_t show_bl_location(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%04x\n",
+			smi_params.backlight_location);
+}
+
+static ssize_t show_bl_battery_val(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			brightness_vals[BRIGHTNESS_BATTERY]);
+}
+
+static ssize_t show_bl_ac_val(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			brightness_vals[BRIGHTNESS_AC]);
+}
+
+static ssize_t show_bl_min_val(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			brightness_min_value);
+}
+
+static ssize_t show_bl_max_val(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			dell_backlight_dev->props.max_brightness);
+}
+
+/* store */
+static ssize_t store_bl_drive_mode(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	char *endp;
+	int mode = simple_strtoul(buf, &endp, 0);
+
+	switch (mode) {
+	case BACKLIGHT_DRIVE_BOTH:
+	case BACKLIGHT_DRIVE_BATTERY:
+	case BACKLIGHT_DRIVE_AC:
+		break;
+	case BACKLIGHT_DRIVE_CURRENT:
+	default:
+		return -ENXIO;
+	}
+
+	mutex_lock(&dell_backlight_dev->update_lock);
+	backlight_drive_mode = mode;
+	mutex_unlock(&dell_backlight_dev->update_lock);
+
+	return count;
+}
+
+static ssize_t store_cmdio_port(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	char *endp;
+	int port = simple_strtoul(buf, &endp, 0);
+
+	if (port <= 0 || port >= 0xffff)
+		return -ENXIO;
+
+	mutex_lock(&smi_params.lock);
+	smi_params.cmdio_port = port;
+	mutex_unlock(&smi_params.lock);
+
+	return count;
+}
+
+static ssize_t store_cmdio_code(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	char *endp;
+	int code = simple_strtoul(buf, &endp, 0);
+
+	if (code <= 0 || code >= 0xff)
+		return -ENXIO;
+
+	mutex_lock(&smi_params.lock);
+	smi_params.cmdio_code = code;
+	mutex_unlock(&smi_params.lock);
+
+	return count;
+}
+
+static ssize_t store_bl_location(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	char *endp;
+	int location = simple_strtoul(buf, &endp, 0);
+
+	if (location <= 0 || location >= 0xffff)
+		return -ENXIO;
+
+	mutex_lock(&dell_backlight_dev->update_lock);
+	smi_params.backlight_location = location;
+	mutex_unlock(&dell_backlight_dev->update_lock);
+
+	return count;
+}
+
+static ssize_t store_bl_battery_val(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	char *endp;
+	int level = simple_strtoul(buf, &endp, 0);
+
+	if (level < brightness_min_value ||
+			level > dell_backlight_dev->props.max_brightness)
+		return -ENXIO;
+
+	mutex_lock(&dell_backlight_dev->update_lock);
+	brightness_vals[BRIGHTNESS_BATTERY] = level;
+	write_brightness(SMI_SELECT_BATTERY);
+	update_from_brightness_vals();
+	mutex_unlock(&dell_backlight_dev->update_lock);
+
+	return count;
+}
+
+static ssize_t store_bl_ac_val(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	char *endp;
+	int level = simple_strtoul(buf, &endp, 0);
+
+	if (level < brightness_min_value ||
+			level > dell_backlight_dev->props.max_brightness)
+		return -ENXIO;
+
+	mutex_lock(&dell_backlight_dev->update_lock);
+	brightness_vals[BRIGHTNESS_AC] = level;
+	write_brightness(SMI_SELECT_AC);
+	if (backlight_drive_mode == BACKLIGHT_DRIVE_BOTH) {
+		/* update_from_brightness_vals forces level to battery mode */
+		brightness_vals[BRIGHTNESS_BATTERY] = level;
+	}
+	update_from_brightness_vals();
+	mutex_unlock(&dell_backlight_dev->update_lock);
+
+	return count;
+}
+
+/* static */
+#define DELL_DEVICE_ATTR(_name, _mode, _show, _store) \
+	struct device_attribute dev_attr_##_name = { \
+		.attr = { \
+			.name = __stringify(_name), \
+			.mode = _mode \
+		}, \
+		.show = _show, \
+		.store = _store \
+	}
+
+static DELL_DEVICE_ATTR(backlight_drive_mode, 0666,
+		show_bl_drive_mode, store_bl_drive_mode);
+static DELL_DEVICE_ATTR(backlight_drive_mode_list, 0444,
+		show_bl_drive_mode_list, NULL);
+static DELL_DEVICE_ATTR(cmdio_port, 0600,
+		show_cmdio_port, store_cmdio_port);
+static DELL_DEVICE_ATTR(cmdio_code, 0600,
+		show_cmdio_code, store_cmdio_code);
+static DELL_DEVICE_ATTR(backlight_location, 0600,
+		show_bl_location, store_bl_location);
+static DELL_DEVICE_ATTR(backlight_battery_val, 0666,
+		show_bl_battery_val, store_bl_battery_val);
+static DELL_DEVICE_ATTR(backlight_ac_val, 0666,
+		show_bl_ac_val, store_bl_ac_val);
+static DELL_DEVICE_ATTR(backlight_min_val, 0444,
+		show_bl_min_val, NULL);
+static DELL_DEVICE_ATTR(backlight_max_val, 0444,
+		show_bl_max_val, NULL);
+
+static struct attribute *dell_laptop_attrs[] = {
+	&dev_attr_backlight_drive_mode.attr,
+	&dev_attr_backlight_drive_mode_list.attr,
+	&dev_attr_cmdio_port.attr,
+	&dev_attr_cmdio_code.attr,
+	&dev_attr_backlight_location.attr,
+	&dev_attr_backlight_battery_val.attr,
+	&dev_attr_backlight_ac_val.attr,
+	&dev_attr_backlight_min_val.attr,
+	&dev_attr_backlight_max_val.attr,
+	NULL
+};
+
+static struct attribute_group dell_laptop_attr_group = {
+	.attrs = dell_laptop_attrs
+};
+
+/* --------------------------------------------------------------------------
+ *	                                init / cleanup
+ * -------------------------------------------------------------------------- */
+
+static int __init dell_laptop_init(void)
+{
+	int error;
+
+	error = platform_driver_register(&dell_driver);
+	if (error)
+		return error;
+
+	dell_platform_dev = platform_device_alloc(DRIVER_NAME, -1);
+	if (!dell_platform_dev) {
+		error = -ENOMEM;
+		goto unregister_platform;
+	}
+
+	error = platform_device_add(dell_platform_dev);
+	if (error)
+		goto put_platform;
+
+	/* 32-bit address space	*/
+	dell_platform_dev->dev.coherent_dma_mask = DMA_32BIT_MASK;
+	dell_platform_dev->dev.dma_mask =
+		&dell_backlight_dev->dev.coherent_dma_mask;
+
+	/* init smi mutex */
+	mutex_init(&smi_params.lock);
+
+	probe_smi_params();
+
+	/* allocate smi buffer */
+	smi_if = dma_alloc_coherent(&dell_platform_dev->dev,
+			SMI_IF_SIZE, &smi_handle, GFP_KERNEL);
+	if (!smi_if) {
+		error = -ENOMEM;
+		goto put_platform;
+	}
+
+	error = dell_backlight_init(&dell_platform_dev->dev);
+	if (error)
+		goto fail_backlight;
+
+	error = sysfs_create_group(&dell_platform_dev->dev.kobj,
+			&dell_laptop_attr_group);
+	if (error) {
+		error = -ENODEV;
+		goto fail_sysfs;
+
+	}
+
+	printk(KERN_INFO DRV_PFX "%s (ver: %s) loaded\n",
+			DRIVER_NAME, DRIVER_VERSION);
+
+	return 0;
+
+
+fail_sysfs:
+	/* */
+fail_backlight:
+	/* */
+put_platform:
+	platform_device_put(dell_platform_dev);
+unregister_platform:
+	platform_driver_unregister(&dell_driver);
+
+	return error;
+}
+
+static void __exit dell_laptop_exit(void)
+{
+	backlight_device_unregister(dell_backlight_dev);
+
+	dma_free_coherent(&dell_platform_dev->dev,
+			SMI_IF_SIZE, smi_if, smi_handle);
+	smi_if = 0;
+	smi_handle = 0;
+
+	sysfs_remove_group(&dell_platform_dev->dev.kobj,
+			&dell_laptop_attr_group);
+
+	platform_device_unregister(dell_platform_dev);
+	platform_driver_unregister(&dell_driver);
+
+	printk(KERN_INFO DRV_PFX "%s (ver: %s) unloaded\n",
+			DRIVER_NAME, DRIVER_VERSION);
+}
+
+module_init(dell_laptop_init);
+module_exit(dell_laptop_exit);
+
--- linux-2.6.23.1/drivers/misc/Kconfig	2007-10-12 18:43:44.000000000 +0200
+++ b/drivers/misc/Kconfig	2007-10-28 16:42:12.000000000 +0100
@@ -130,6 +130,21 @@ config MSI_LAPTOP
 
 	  If you have an MSI S270 laptop, say Y or M here.
 
+config DELL_LAPTOP
+        tristate "Dell Laptop Extras (EXPERIMENTAL)"
+        depends on X86
+	depends on EXPERIMENTAL
+	depends on BACKLIGHT_CLASS_DEVICE
+        ---help---
+    This driver implements LCD backlight control for Dell laptops. BEFORE
+    using this driver, please fill in your laptop model parameters as described
+    in dell-laptop.txt. Using this driver without doing this operation
+    may have UNPREDICTABLE effects on your laptop. You have been warned!
+
+	  Just read <file:Documentation/dell-laptop.txt> first.
+
+	  If you have a Dell laptop, say Y or M here.
+
 config SONY_LAPTOP
 	tristate "Sony Laptop Extras"
 	depends on X86 && ACPI


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ