Call for testers acpi_dell

Daniel Walter d.walter at 0x90.at
Fri Feb 12 18:05:11 UTC 2010


Hi all,

I've written a small kernel driver to support Dell Mini 1011 (aka Dell Mini 10v) 
Brightness Controll Buttons. Since I do not have any other Dell Notebooks, I'd like you to test this module on other Dell Notebooks as well.

This driver is in an early stage, but future releases will also support the rfkill switch and CRT switch.

Currently following features are implemented:
* Increase / Decrease Brightness
* Change brightness if AC is connected / disconnected

TODO:
* Add CRT Support
* Add Sysctl to define brightness levels for eco and ac powerprofile
* Add Support for RF-Kill Switch

Any suggestions are welcome

regards

Daniel
-------------- next part --------------
KMOD=	acpi_dell
SRCS=	acpi_dell.c opt_acpi.h acpi_if.h bus_if.h device_if.h

.include <bsd.kmod.mk>
-------------- next part --------------
/*-
 * Copyright (c) 2010 WALTER Daniel <sahne at 0x90.at>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <sys/cdefs.h>

#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/power.h>

#include <contrib/dev/acpica/include/acpi.h>

#include <dev/acpica/acpivar.h>

#define _COMPONENT	ACPI_OEM
ACPI_MODULE_NAME("Dell")

/* Commands */
#define ACPI_DELL_BRIGHTNESS_GET	"_BQC"
#define ACPI_DELL_BRIGHTNESS_SET	"_BCM"
#define	ACPI_DELL_BRIGHTNESS_SUPPORTED	"_BCL"

/* Notifycations */
#define	ACPI_DELL_BRIGHTNESS_INCREASE	0x86
#define	ACPI_DELL_BRIGHTNESS_DECREASE	0x87
#define ACPI_DELL_SWITCH_OUTPUT		0	

/* DEBUG NOTIFICATIONS */
#define	ACPI_DELL_DEBUG			1

struct acpi_dell_softc {
	device_t		dev;
	ACPI_HANDLE		handle;
	eventhandler_tag	power_profile;
	int			brightness;
	int			brightness_index;
	int			eco_brightness;
	int			ac_brightness;	
	int			num_levels;
	int			*brightness_levels;
};

static int	acpi_dell_probe(device_t dev);
static int	acpi_dell_attach(device_t dev);
static int	acpi_dell_detach(device_t dev);
static void	acpi_dell_notify(ACPI_HANDLE h, UINT32 notify, void *context);
static int	acpi_dell_get_brightness(struct acpi_dell_softc *sc);
static int	acpi_dell_set_brightness(struct acpi_dell_softc *sc, int inc);
static void	acpi_dell_powerprofile(void *context);
static int	acpi_dell_get_brightness_levels(struct acpi_dell_softc *sc);

/* Declare lock */
ACPI_SERIAL_DECL(dell, "ACPI DELL extras");

static device_method_t acpi_dell_methods[] = {
	DEVMETHOD(device_probe,		acpi_dell_probe),
	DEVMETHOD(device_attach,	acpi_dell_attach),
	DEVMETHOD(device_detach,	acpi_dell_detach),

	{0, 0}
};

static driver_t acpi_dell_driver = {
	"acpi_dell",
	acpi_dell_methods,
	sizeof(struct acpi_dell_softc),
};

/* supported backlight values for dell mini 1011 (aka dell mini 10v) */
static int acpi_dell_valid_brightness [] = {
	0x64,
	0x41,
	0x19,
	0x1e,
	0x23,
	0x28,
	0x2d,
	0x32,
	0x37,
	0x3c,
	0x41,
	0x46,
	0x4b,
	0x50,
	0x55,
	0x5a,
	0x5f,
	0x64
};

static devclass_t acpi_dell_devclass;
DRIVER_MODULE(acpi_dell, acpi, acpi_dell_driver, acpi_dell_devclass, 0, 0);
MODULE_DEPEND(acpi_dell, acpi, 1, 1, 1);

static int
acpi_dell_probe (device_t dev)
{
	ACPI_HANDLE		devh,h;
	ACPI_OBJECT_TYPE 	t_dos;
	
	devh = acpi_get_handle(dev);
	if (acpi_disabled("dell") || 
	    ACPI_FAILURE(AcpiGetHandle(devh, "_BCL", &h)) ||
	    ACPI_FAILURE(AcpiGetType(h, &t_dos)) ||
	    t_dos != ACPI_TYPE_METHOD) 
		return (ENXIO);
#ifdef	ACPI_DELL_DEBUG
	device_printf(dev, "found device\n");
#endif	
	device_set_desc(dev, "Dell ACPI Video");
	return (0);

}

static int
acpi_dell_attach (device_t dev)
{
	struct acpi_dell_softc 	*sc;
	struct acpi_softc 	*acpi_sc;
	int			brightness;
	
	sc = device_get_softc(dev);
	sc->dev = dev;
	sc->handle = acpi_get_handle(dev);
	sc->num_levels = acpi_dell_get_brightness_levels(sc);
	/* 
	 * if dynamic read of brightness levels did not work 
	 * use static table 
	 */
	if (sc->num_levels == -1) {
#ifdef	ACPI_DELL_DEBUG
		device_printf(dev, "Could not get brightness levels\n");
#endif
		sc->num_levels = 18;
		sc->brightness_levels = acpi_dell_valid_brightness;
	}
	/* set brighness levels for ac and eco power */
	sc->ac_brightness = sc->brightness_levels[0];
	sc->eco_brightness = sc->brightness_levels[1];
	/* get brightness */
	brightness = acpi_dell_get_brightness(sc);
	
	acpi_sc = acpi_device_get_parent_softc(dev);
	
	/* install notifcation and event handler */
	AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
			acpi_dell_notify, sc);

	sc->power_profile = EVENTHANDLER_REGISTER(power_profile_change, 
						  acpi_dell_powerprofile, 
						  sc, 0);

	return (0);
}

static int
acpi_dell_detach( device_t dev)
{
	struct acpi_dell_softc	*sc;
	sc = device_get_softc(dev);
	
	EVENTHANDLER_DEREGISTER(power_profile_change, sc->power_profile);
	AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 
				acpi_dell_notify);
	
	if (sc->brightness_levels != NULL)
		AcpiOsFree(sc->brightness_levels);
	
	return (0);
}



static void
acpi_dell_notify(ACPI_HANDLE h, UINT32 notify, void *context)
{
	struct	acpi_dell_softc *sc;
	/*UINT32	key;*/
	sc = (struct acpi_dell_softc *) context;
	switch (notify) {
		case ACPI_DELL_BRIGHTNESS_INCREASE:
#ifdef ACPI_DELL_DEBUG
			device_printf(sc->dev, 
				"increase brightness notify 0x%x\n", notify);
#endif
			acpi_dell_set_brightness(sc, 1);
			break;
		case ACPI_DELL_BRIGHTNESS_DECREASE:
#ifdef ACPI_DELL_DEBUG
			device_printf(sc->dev, 
				"decrease brightness notify 0x%x\n", notify);
#endif
			acpi_dell_set_brightness(sc, 0);
			break;
		default:
			device_printf(sc->dev, "notify 0x%x\n", notify);
			break;
	}
}

static int
acpi_dell_get_brightness(struct acpi_dell_softc *sc)
{
	int i;
	int brightness;
	acpi_GetInteger(sc->handle, ACPI_DELL_BRIGHTNESS_GET, &brightness);
	sc->brightness = brightness;
	for (i = 2; i < sc->num_levels; i++) 
		if (brightness == acpi_dell_valid_brightness[i])
			break;
	sc->brightness_index = i;
	return (brightness);
}

static int
acpi_dell_set_brightness(struct acpi_dell_softc *sc, int inc)
{
	int brightness;
	int next_br;
	ACPI_SERIAL_BEGIN(dell);
	brightness = acpi_dell_get_brightness(sc);
	if (inc) {
		next_br = sc->brightness_index + 1;
		if (next_br >= sc->num_levels) 
			next_br = sc->num_levels;
	} else {
		next_br = sc->brightness_index - 1;
		if (next_br <= 2) 
			next_br = 2;
	}
	acpi_SetInteger(sc->handle, ACPI_DELL_BRIGHTNESS_SET, 
			acpi_dell_valid_brightness[next_br]);
	sc->brightness = acpi_dell_valid_brightness[next_br];
	sc->brightness_index = next_br;
	ACPI_SERIAL_END(dell);
	return (0);
}

static int
acpi_dell_get_brightness_levels(struct acpi_dell_softc *sc)
{
	ACPI_STATUS	status;
	ACPI_BUFFER	bcl_buf;
	ACPI_OBJECT	*bcl_obj;
	int		i, n, num;
	int		*lvl;

	num = 0;
	bcl_buf.Length = ACPI_ALLOCATE_BUFFER;
	bcl_buf.Pointer = NULL;
	
	status = AcpiEvaluateObject(sc->handle, ACPI_DELL_BRIGHTNESS_SUPPORTED,
				    NULL, &bcl_buf);
	if (ACPI_FAILURE(status)) {
		if (status != AE_NOT_FOUND)
			device_printf(sc->dev, "could not evaluate _BCL\n");
		num = -1;
		goto out;
	} 

	bcl_obj = (ACPI_OBJECT *)bcl_buf.Pointer;
	if (!ACPI_PKG_VALID(bcl_obj,2)) {
		device_printf(sc->dev, "evaluation of _BCL makes no sense\n");
		num = -1;
		goto out;
	}

	num = bcl_obj->Package.Count;
	lvl = AcpiOsAllocate(num * sizeof(*lvl));
	if (lvl == NULL) {
		num = -1;
		goto out;
	}
	
	for (i = 0, n = 0; i < num; i++)
		if (acpi_PkgInt32(bcl_obj, i, &lvl[n]) == 0)
			n++;
	if (n < 2) {
		num = -1;
		AcpiOsFree(lvl);
	} else {
		num = n;
		sc->brightness_levels = lvl;
	}
out:	
	if (bcl_buf.Pointer != NULL)
		AcpiOsFree(bcl_buf.Pointer);
	
	return (num);
}

static void
acpi_dell_powerprofile(void *context)
{
	int 			state;
	struct acpi_dell_softc	*sc;

	sc = context;
	state = power_profile_get_state();
	if (state != POWER_PROFILE_PERFORMANCE &&
	    state != POWER_PROFILE_ECONOMY)
		return ;

	ACPI_SERIAL_BEGIN(dell);
	acpi_SetInteger(sc->handle, ACPI_DELL_BRIGHTNESS_SET, 
			state == POWER_PROFILE_ECONOMY ? 
			sc->eco_brightness : sc->ac_brightness);
	ACPI_SERIAL_END(dell);
}


More information about the freebsd-acpi mailing list