diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index dcdc1f4..9beb427 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -30,6 +30,20 @@ * Yasushi Nagato - changes for linux kernel 2.4 -> 2.5 * Rob Miller - TV out and hotkeys help * + * PLEASE NOTE + * + * This is an experimental version of toshiba_acpi which includes emulation + * of the original toshiba driver's /proc/toshiba and /dev/toshiba, + * allowing Toshiba userspace utilities to work. The relevant code was + * based on toshiba.c (copyright 1996-2001 Jonathan A. Buzzard) and + * incorporated into this driver with help from Gintautas Miliauskas, + * Charles Schwieters, and Christoph Burger-Scheidlin. + * + * Caveats: + * * hotkey status in /proc/toshiba is not implemented + * * to make accesses to /dev/toshiba load this driver instead of + * the original driver, you will have to modify your module + * auto-loading configuration * * TODO * @@ -37,7 +51,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define TOSHIBA_ACPI_VERSION "0.20" +#define TOSHIBA_ACPI_VERSION "experimental-dev-toshiba-test-6" #define PROC_INTERFACE_VERSION 1 #include @@ -46,6 +61,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -126,6 +144,8 @@ struct toshiba_acpi_dev { struct mutex mutex; }; +struct toshiba_acpi_dev* global_dev; + static const struct acpi_device_id toshiba_device_ids[] = { {"TOS6200", 0}, {"TOS6208", 0}, @@ -805,6 +825,207 @@ static const struct file_operations version_proc_fops = { .release = single_release, }; +/* /dev/toshiba and /proc/toshiba handlers {{{ + * + * ISSUE: lots of magic numbers and mysterious code + */ + +#define TOSH_MINOR_DEV 181 +#define OLD_PROC_TOSHIBA "toshiba" + +static int +tosh_acpi_bridge(struct toshiba_acpi_dev *dev, SMMRegisters* regs) +{ + acpi_status status; + + /* assert(sizeof(SMMRegisters) == sizeof(u32)*HCI_WORDS); */ + status = hci_raw(dev, (u32*)regs, (u32*)regs); + if (status == AE_OK && (regs->eax & 0xff00) == HCI_SUCCESS) + return 0; + + return -EINVAL; +} + +static long +tosh_ioctl(struct file* fp, unsigned int cmd, + unsigned long arg) +{ + SMMRegisters regs; + unsigned short ax,bx; + int err; + //FIX: + //struct toshiba_acpi_dev *dev = PDE(fp->f_path.dentry->d_inode)->data; + if ((!arg) || (cmd != TOSH_SMM)) + return -EINVAL; + + if (copy_from_user(®s, (SMMRegisters*)arg, sizeof(SMMRegisters))) + return -EFAULT; + + ax = regs.eax & 0xff00; + bx = regs.ebx & 0xffff; + + /* block HCI calls to read/write memory & PCI devices */ + if (((ax==HCI_SET) || (ax==HCI_GET)) && (bx>0x0069)) + return -EINVAL; + + err = tosh_acpi_bridge(global_dev,®s); + + if (copy_to_user((SMMRegisters*)arg, ®s, sizeof(SMMRegisters))) + return -EFAULT; + + return err; +} + +static int +tosh_get_machine_id(void __iomem *bios) +{ + int id; + unsigned short bx,cx; + unsigned long address; + + id = (0x100*(int) readb(bios+0xfffe))+((int) readb(bios+0xfffa)); + + /* do we have a SCTTable machine identication number on our hands */ + if (id==0xfc2f) { + bx = 0xe6f5; /* cheat */ + /* now twiddle with our pointer a bit */ + address = 0x00000000 + bx; + cx = readw(bios + address); + address = 0x00000009 + bx + cx; + cx = readw(bios + address); + address = 0x0000000a + cx; + cx = readw(bios + address); + /* now construct our machine identification number */ + id = ((cx & 0xff)<<8)+((cx & 0xff00)>>8); + } + + return id; +} + +static int tosh_id; +static int tosh_bios; +static int tosh_date; +static int tosh_sci; + +static struct file_operations tosh_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = tosh_ioctl +}; + +static struct miscdevice tosh_device = { + TOSH_MINOR_DEV, + "toshiba", + &tosh_fops +}; + +static void +setup_tosh_info(void __iomem *bios) +{ + int major, minor; + int day, month, year; + + tosh_id = tosh_get_machine_id(bios); + + /* get the BIOS version */ + major = readb(bios + 0xe009)-'0'; + minor = ((readb(bios + 0xe00b)-'0')*10)+(readb(bios + 0xe00c)-'0'); + tosh_bios = (major*0x100)+minor; + + /* get the BIOS date */ + day = ((readb(bios + 0xfff5)-'0')*10)+(readb(bios + 0xfff6)-'0'); + month = ((readb(bios + 0xfff8)-'0')*10)+(readb(bios + 0xfff9)-'0'); + year = ((readb(bios + 0xfffb)-'0')*10)+(readb(bios + 0xfffc)-'0'); + tosh_date = (((year-90) & 0x1f)<<10) | ((month & 0xf)<<6) + | ((day & 0x1f)<<1); +} + +/* /proc/toshiba read handler */ +static int +tosh_proc_show(struct seq_file *m, void *v) +{ + /* TODO: tosh_fn_status() */ + int key = 0; + + /* Format: + * 0) Linux driver version (this will change if format changes) + * 1) Machine ID + * 2) SCI version + * 3) BIOS version (major, minor) + * 4) BIOS date (in SCI date format) + * 5) Fn Key status + */ + + seq_printf(m, "1.1 0x%04x %d.%d %d.%d 0x%04x 0x%02x\n", + tosh_id, + (tosh_sci & 0xff00)>>8, + tosh_sci & 0xff, + (tosh_bios & 0xff00)>>8, + tosh_bios & 0xff, + tosh_date, + key); + + return 0; +} + +static int tosh_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, tosh_proc_show, NULL); +} + +static const struct file_operations tosh_proc_fops = { + .owner = THIS_MODULE, + .open = tosh_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init +old_driver_emulation_init(void) +{ + int status; + void __iomem *bios = ioremap(0xf0000, 0x10000); + if (!bios) + return -ENOMEM; + + if ((status = misc_register(&tosh_device))) { + pr_err("failed to register misc device %d (\"%s\")\n", + tosh_device.minor, tosh_device.name); + return status; + } + + setup_tosh_info(bios); + proc_create(OLD_PROC_TOSHIBA, 0, NULL, &tosh_proc_fops); + + iounmap(bios); + + return 0; +} + +static void __exit +old_driver_emulation_exit(void) +{ + remove_proc_entry(OLD_PROC_TOSHIBA, NULL); + misc_deregister(&tosh_device); +} + +/* }}} end of /dev/toshiba and /proc/toshiba handlers */ + +/* /dev/toshiba and /proc/toshiba handlers {{{ + * + * ISSUE: lots of magic numbers and mysterious code + */ + +#define TOSH_MINOR_DEV 181 +#define OLD_PROC_TOSHIBA "toshiba" + +static int tosh_id; +static int tosh_bios; +static int tosh_date; +static int tosh_sci; + +/* }}} end of /dev/toshiba and /proc/toshiba handlers */ + /* proc and module init */ @@ -939,6 +1160,7 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) u32 hci_result; u32 dummy; bool bt_present; + // int status2; int ret = 0; struct backlight_properties props; @@ -952,6 +1174,7 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) } dev = kzalloc(sizeof(*dev), GFP_KERNEL); + global_dev = dev; if (!dev) return -ENOMEM; dev->acpi_dev = acpi_dev; @@ -1086,6 +1309,8 @@ static struct acpi_driver toshiba_acpi_driver = { static int __init toshiba_acpi_init(void) { int ret; + int status2; + toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); if (!toshiba_proc_dir) { @@ -1099,12 +1324,17 @@ static int __init toshiba_acpi_init(void) remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); } + if ((status2 = old_driver_emulation_init())) + return status2; return ret; } static void __exit toshiba_acpi_exit(void) { acpi_bus_unregister_driver(&toshiba_acpi_driver); + + old_driver_emulation_exit(); + if (toshiba_proc_dir) remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); }