Commit d15fbf71 authored by Andy Grover's avatar Andy Grover

By John Belmonte - improvements to Toshiba ACPI driver:

1) Fix sscanf
2) Add TV out support
3) Add hotkey status
4) Add version info
parent 2baa231c
...@@ -26,15 +26,15 @@ ...@@ -26,15 +26,15 @@
* Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse * Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse
* engineering the Windows drivers * engineering the Windows drivers
* Yasushi Nagato - changes for linux kernel 2.4 -> 2.5 * Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
* Rob Miller - TV out and hotkeys help
* *
* *
* TODO * TODO
* provide version info in proc
* add Fn key status
* *
*/ */
#define TOSHIBA_ACPI_VERSION "0.12" #define TOSHIBA_ACPI_VERSION "0.13"
#define PROC_INTERFACE_VERSION 1
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -43,9 +43,10 @@ ...@@ -43,9 +43,10 @@
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/version.h> #include <linux/version.h>
#define KERNEL24 (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) #include <acconfig.h>
#define OLD_ACPI_INTERFACE (ACPI_CA_VERSION < 0x20020000)
#if KERNEL24 #if OLD_ACPI_INTERFACE
#include <acpi.h> #include <acpi.h>
extern struct proc_dir_entry* bm_proc_root; extern struct proc_dir_entry* bm_proc_root;
#define acpi_root_dir bm_proc_root #define acpi_root_dir bm_proc_root
...@@ -82,10 +83,13 @@ MODULE_LICENSE("GPL"); ...@@ -82,10 +83,13 @@ MODULE_LICENSE("GPL");
#define HCI_SUCCESS 0x0000 #define HCI_SUCCESS 0x0000
#define HCI_FAILURE 0x1000 #define HCI_FAILURE 0x1000
#define HCI_NOT_SUPPORTED 0x8000 #define HCI_NOT_SUPPORTED 0x8000
#define HCI_EMPTY 0x8c00
/* registers */ /* registers */
#define HCI_FAN 0x0004 #define HCI_FAN 0x0004
#define HCI_SYSTEM_EVENT 0x0016
#define HCI_VIDEO_OUT 0x001c #define HCI_VIDEO_OUT 0x001c
#define HCI_HOTKEY_EVENT 0x001e
#define HCI_LCD_BRIGHTNESS 0x002a #define HCI_LCD_BRIGHTNESS 0x002a
/* field definitions */ /* field definitions */
...@@ -94,6 +98,7 @@ MODULE_LICENSE("GPL"); ...@@ -94,6 +98,7 @@ MODULE_LICENSE("GPL");
#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS)
#define HCI_VIDEO_OUT_LCD 0x1 #define HCI_VIDEO_OUT_LCD 0x1
#define HCI_VIDEO_OUT_CRT 0x2 #define HCI_VIDEO_OUT_CRT 0x2
#define HCI_VIDEO_OUT_TV 0x4
/* utility /* utility
*/ */
...@@ -104,6 +109,23 @@ _set_bit(u32* word, u32 mask, int value) ...@@ -104,6 +109,23 @@ _set_bit(u32* word, u32 mask, int value)
*word = (*word & ~mask) | (mask * value); *word = (*word & ~mask) | (mask * value);
} }
/* an sscanf that takes explicit string length */
static int
snscanf(const char* str, int n, const char* format, ...)
{
va_list args;
int result;
char* str2 = kmalloc(n + 1, GFP_KERNEL);
if (str2 == 0) return 0;
strncpy(str2, str, n);
str2[n] = 0;
va_start(args, format);
result = vsscanf(str2, format, args);
va_end(args);
kfree(str2);
return result;
}
/* This is the common code at the end of every proc read handler. I don't /* This is the common code at the end of every proc read handler. I don't
* understand it yet. * understand it yet.
*/ */
...@@ -226,14 +248,15 @@ hci_read1(u32 reg, u32* out1, u32* result) ...@@ -226,14 +248,15 @@ hci_read1(u32 reg, u32* out1, u32* result)
#define PROC_LCD "lcd" #define PROC_LCD "lcd"
#define PROC_VIDEO "video" #define PROC_VIDEO "video"
#define PROC_FAN "fan" #define PROC_FAN "fan"
#define PROC_KEYS "keys"
#define PROC_VERSION "version"
static struct proc_dir_entry* toshiba_proc_dir = NULL; static struct proc_dir_entry* toshiba_proc_dir = NULL;
static int force_fan; static int force_fan;
static int last_key_event;
static int key_event_valid;
/* proc file handlers /* proc file handlers
*
* WARNING: The write handlers are using sscanf on non-zero-terminated
* buffers. This may result in memory reads past the buffer bounds.
*/ */
static int static int
...@@ -270,7 +293,7 @@ proc_write_lcd(struct file* file, const char* buffer, unsigned long count, ...@@ -270,7 +293,7 @@ proc_write_lcd(struct file* file, const char* buffer, unsigned long count,
u32 hci_result; u32 hci_result;
/* ISSUE: %i doesn't work with hex values as advertised */ /* ISSUE: %i doesn't work with hex values as advertised */
if (sscanf(buffer, " brightness : %i", &value) == 1 && if (snscanf(buffer, count, " brightness : %i", &value) == 1 &&
value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) {
value = value << HCI_LCD_BRIGHTNESS_SHIFT; value = value << HCI_LCD_BRIGHTNESS_SHIFT;
hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result); hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);
...@@ -297,8 +320,10 @@ proc_read_video(char* page, char** start, off_t off, int count, int* eof, ...@@ -297,8 +320,10 @@ proc_read_video(char* page, char** start, off_t off, int count, int* eof,
if (hci_result == HCI_SUCCESS) { if (hci_result == HCI_SUCCESS) {
int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
int is_tv = (value & HCI_VIDEO_OUT_TV ) ? 1 : 0;
p += sprintf(p, "lcd_out: %d\n", is_lcd); p += sprintf(p, "lcd_out: %d\n", is_lcd);
p += sprintf(p, "crt_out: %d\n", is_crt); p += sprintf(p, "crt_out: %d\n", is_crt);
p += sprintf(p, "tv_out: %d\n", is_tv);
} else { } else {
p += sprintf(p, "ERROR\n"); p += sprintf(p, "ERROR\n");
goto end; goto end;
...@@ -316,15 +341,18 @@ proc_write_video(struct file* file, const char* buffer, unsigned long count, ...@@ -316,15 +341,18 @@ proc_write_video(struct file* file, const char* buffer, unsigned long count,
const char* buffer_end = buffer + count; const char* buffer_end = buffer + count;
int lcd_out = -1; int lcd_out = -1;
int crt_out = -1; int crt_out = -1;
int tv_out = -1;
u32 hci_result; u32 hci_result;
int video_out; int video_out;
/* scan expression. Multiple expressions may be delimited with ; */ /* scan expression. Multiple expressions may be delimited with ; */
do { do {
if (sscanf(buffer, " lcd_out : %i", &value) == 1) if (snscanf(buffer, count, " lcd_out : %i", &value) == 1)
lcd_out = value & 1; lcd_out = value & 1;
else if (sscanf(buffer, " crt_out : %i", &value) == 1) else if (snscanf(buffer, count, " crt_out : %i", &value) == 1)
crt_out = value & 1; crt_out = value & 1;
else if (snscanf(buffer, count, " tv_out : %i", &value) == 1)
tv_out = value & 1;
/* advance to one character past the next ; */ /* advance to one character past the next ; */
do ++buffer; do ++buffer;
while ((buffer < buffer_end) && (*(buffer-1) != ';')); while ((buffer < buffer_end) && (*(buffer-1) != ';'));
...@@ -337,6 +365,8 @@ proc_write_video(struct file* file, const char* buffer, unsigned long count, ...@@ -337,6 +365,8 @@ proc_write_video(struct file* file, const char* buffer, unsigned long count,
_set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out); _set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out);
if (crt_out != -1) if (crt_out != -1)
_set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out); _set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out);
if (tv_out != -1)
_set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out);
/* To avoid unnecessary video disruption, only write the new /* To avoid unnecessary video disruption, only write the new
* video setting if something changed. */ * video setting if something changed. */
if (new_video_out != video_out) if (new_video_out != video_out)
...@@ -376,7 +406,7 @@ proc_write_fan(struct file* file, const char* buffer, unsigned long count, ...@@ -376,7 +406,7 @@ proc_write_fan(struct file* file, const char* buffer, unsigned long count,
int value; int value;
u32 hci_result; u32 hci_result;
if (sscanf(buffer, " force_on : %i", &value) == 1 && if (snscanf(buffer, count, " force_on : %i", &value) == 1 &&
value >= 0 && value <= 1) { value >= 0 && value <= 1) {
hci_write1(HCI_FAN, value, &hci_result); hci_write1(HCI_FAN, value, &hci_result);
if (hci_result != HCI_SUCCESS) if (hci_result != HCI_SUCCESS)
...@@ -390,6 +420,68 @@ proc_write_fan(struct file* file, const char* buffer, unsigned long count, ...@@ -390,6 +420,68 @@ proc_write_fan(struct file* file, const char* buffer, unsigned long count,
return count; return count;
} }
static int
proc_read_keys(char* page, char** start, off_t off, int count, int* eof,
void* context)
{
char* p = page;
u32 hci_result;
u32 value;
if (off != 0) goto end;
if (!key_event_valid) {
hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
if (hci_result == HCI_SUCCESS) {
key_event_valid = 1;
last_key_event = value;
} else if (hci_result == HCI_EMPTY) {
/* better luck next time */
} else {
p += sprintf(p, "ERROR\n");
goto end;
}
}
p += sprintf(p, "hotkey_ready: %d\n", key_event_valid);
p += sprintf(p, "hotkey: 0x%04x\n", last_key_event);
end:
return end_proc_read(p, page, off, count, start, eof);
}
static int
proc_write_keys(struct file* file, const char* buffer, unsigned long count,
void* data)
{
int value;
if (snscanf(buffer, count, " hotkey_ready : %i", &value) == 1 &&
value == 0) {
key_event_valid = 0;
} else {
return -EINVAL;
}
return count;
}
static int
proc_read_version(char* page, char** start, off_t off, int count, int* eof,
void* context)
{
char* p = page;
if (off != 0) goto end;
p += sprintf(p, "driver: %s\n", TOSHIBA_ACPI_VERSION);
p += sprintf(p, "proc_interface: %d\n",
PROC_INTERFACE_VERSION);
end:
return end_proc_read(p, page, off, count, start, eof);
}
/* proc and module init /* proc and module init
*/ */
...@@ -410,6 +502,13 @@ add_device(void) ...@@ -410,6 +502,13 @@ add_device(void)
toshiba_proc_dir, proc_read_fan, 0); toshiba_proc_dir, proc_read_fan, 0);
if (proc) proc->write_proc = proc_write_fan; if (proc) proc->write_proc = proc_write_fan;
proc = create_proc_read_entry(PROC_KEYS, S_IFREG | S_IRUGO | S_IWUSR,
toshiba_proc_dir, proc_read_keys, 0);
if (proc) proc->write_proc = proc_write_keys;
proc = create_proc_read_entry(PROC_VERSION, S_IFREG | S_IRUGO | S_IWUSR,
toshiba_proc_dir, proc_read_version, 0);
return(AE_OK); return(AE_OK);
} }
...@@ -419,6 +518,8 @@ remove_device(void) ...@@ -419,6 +518,8 @@ remove_device(void)
remove_proc_entry(PROC_LCD, toshiba_proc_dir); remove_proc_entry(PROC_LCD, toshiba_proc_dir);
remove_proc_entry(PROC_VIDEO, toshiba_proc_dir); remove_proc_entry(PROC_VIDEO, toshiba_proc_dir);
remove_proc_entry(PROC_FAN, toshiba_proc_dir); remove_proc_entry(PROC_FAN, toshiba_proc_dir);
remove_proc_entry(PROC_KEYS, toshiba_proc_dir);
remove_proc_entry(PROC_VERSION, toshiba_proc_dir);
return(AE_OK); return(AE_OK);
} }
...@@ -437,6 +538,10 @@ toshiba_acpi_init(void) ...@@ -437,6 +538,10 @@ toshiba_acpi_init(void)
printk("Toshiba Laptop ACPI Extras version %s\n", TOSHIBA_ACPI_VERSION); printk("Toshiba Laptop ACPI Extras version %s\n", TOSHIBA_ACPI_VERSION);
force_fan = 0; force_fan = 0;
key_event_valid = 0;
/* enable event fifo */
hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
if (!toshiba_proc_dir) { if (!toshiba_proc_dir) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment