📄 i8042.c
字号:
}/* * Enable all muxed ports. */ for (i = 0; i < 4; i++) { i8042_command(¶m, I8042_CMD_MUX_PFX + i); i8042_command(¶m, I8042_CMD_AUX_ENABLE); } return 0;}/* * i8042_check_mux() checks whether the controller supports the PS/2 Active * Multiplexing specification by Synaptics, Phoenix, Insyde and * LCS/Telegraphics. */static int __init i8042_check_mux(struct i8042_values *values){ unsigned char mux_version; if (i8042_enable_mux_mode(values, &mux_version)) return -1; /* Workaround for broken chips which seem to support MUX, but in reality don't. */ /* They all report version 10.12 */ if (mux_version == 0xAC) return -1; printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n", (mux_version >> 4) & 0xf, mux_version & 0xf); if (i8042_enable_mux_ports(values)) return -1; i8042_mux_present = 1; return 0;}/* * i8042_check_aux() applies as much paranoia as it can at detecting * the presence of an AUX interface. */static int __init i8042_check_aux(struct i8042_values *values){ unsigned char param; static int i8042_check_aux_cookie;/* * Check if AUX irq is available. If it isn't, then there is no point * in trying to detect AUX presence. */ if (request_irq(values->irq, i8042_interrupt, SA_SHIRQ, "i8042", &i8042_check_aux_cookie)) return -1; free_irq(values->irq, &i8042_check_aux_cookie);/* * Get rid of bytes in the queue. */ i8042_flush();/* * Internal loopback test - filters out AT-type i8042's. Unfortunately * SiS screwed up and their 5597 doesn't support the LOOP command even * though it has an AUX port. */ param = 0x5a; if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0xa5) {/* * External connection test - filters out AT-soldered PS/2 i8042's * 0x00 - no error, 0x01-0x03 - clock/data stuck, 0xff - general error * 0xfa - no error on some notebooks which ignore the spec * Because it's common for chipsets to return error on perfectly functioning * AUX ports, we test for this only when the LOOP command failed. */ if (i8042_command(¶m, I8042_CMD_AUX_TEST) || (param && param != 0xfa && param != 0xff)) return -1; }/* * Bit assignment test - filters out PS/2 i8042's in AT mode */ if (i8042_command(¶m, I8042_CMD_AUX_DISABLE)) return -1; if (i8042_command(¶m, I8042_CMD_CTL_RCTR) || (~param & I8042_CTR_AUXDIS)) { printk(KERN_WARNING "Failed to disable AUX port, but continuing anyway... Is this a SiS?\n"); printk(KERN_WARNING "If AUX port is really absent please use the 'i8042.noaux' option.\n"); } if (i8042_command(¶m, I8042_CMD_AUX_ENABLE)) return -1; if (i8042_command(¶m, I8042_CMD_CTL_RCTR) || (param & I8042_CTR_AUXDIS)) return -1;/* * Disable the interface. */ i8042_ctr |= I8042_CTR_AUXDIS; i8042_ctr &= ~I8042_CTR_AUXINT; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) return -1; return 0;}/* * i8042_port_register() marks the device as existing, * registers it, and reports to the user. */static int __init i8042_port_register(struct i8042_values *values, struct serio *port){ values->exists = 1; i8042_ctr &= ~values->disable; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { printk(KERN_WARNING "i8042.c: Can't write CTR while registering.\n"); values->exists = 0; return -1; } printk(KERN_INFO "serio: i8042 %s port at %#lx,%#lx irq %d\n", values->name, (unsigned long) I8042_DATA_REG, (unsigned long) I8042_COMMAND_REG, values->irq); serio_register_port(port); return 0;}static void i8042_timer_func(unsigned long data){ i8042_interrupt(0, NULL, NULL);}/* * i8042_controller init initializes the i8042 controller, and, * most importantly, sets it into non-xlated mode if that's * desired. */static int i8042_controller_init(void){/* * Test the i8042. We need to know if it thinks it's working correctly * before doing anything else. */ i8042_flush(); if (i8042_reset) { unsigned char param; if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n"); return -1; } if (param != I8042_RET_CTL_TEST) { printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n", param, I8042_RET_CTL_TEST); return -1; } }/* * Save the CTR for restoral on unload / reboot. */ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) { printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n"); return -1; } i8042_initial_ctr = i8042_ctr;/* * Disable the keyboard interface and interrupt. */ i8042_ctr |= I8042_CTR_KBDDIS; i8042_ctr &= ~I8042_CTR_KBDINT;/* * Handle keylock. */ if (~i8042_read_status() & I8042_STR_KEYLOCK) { if (i8042_unlock) i8042_ctr |= I8042_CTR_IGNKEYLOCK; else printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n"); }/* * If the chip is configured into nontranslated mode by the BIOS, don't * bother enabling translating and be happy. */ if (~i8042_ctr & I8042_CTR_XLATE) i8042_direct = 1;/* * Set nontranslated mode for the kbd interface if requested by an option. * After this the kbd interface becomes a simple serial in/out, like the aux * interface is. We don't do this by default, since it can confuse notebook * BIOSes. */ if (i8042_direct) { i8042_ctr &= ~I8042_CTR_XLATE; i8042_kbd_port.type = SERIO_8042; }/* * Write CTR back. */ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n"); return -1; } return 0;}/* * Reset the controller. */void i8042_controller_reset(void){ if (i8042_reset) { unsigned char param; if (i8042_command(¶m, I8042_CMD_CTL_TEST)) printk(KERN_ERR "i8042.c: i8042 controller reset timeout.\n"); }/* * Restore the original control register setting. */ i8042_ctr = i8042_initial_ctr; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");}/* * Here we try to reset everything back to a state in which the BIOS will be * able to talk to the hardware when rebooting. */void i8042_controller_cleanup(void){ int i; i8042_flush();/* * Reset anything that is connected to the ports. */ if (i8042_kbd_values.exists) serio_cleanup(&i8042_kbd_port); if (i8042_aux_values.exists) serio_cleanup(&i8042_aux_port); for (i = 0; i < 4; i++) if (i8042_mux_values[i].exists) serio_cleanup(i8042_mux_port + i); i8042_controller_reset();}/* * Here we try to restore the original BIOS settings */static int i8042_controller_suspend(void){ del_timer_sync(&i8042_timer); i8042_controller_reset(); return 0;}/* * Here we try to reset everything back to a state in which suspended */static int i8042_controller_resume(void){ int i; if (i8042_controller_init()) { printk(KERN_ERR "i8042: resume failed\n"); return -1; } if (i8042_mux_present) if (i8042_enable_mux_mode(&i8042_aux_values, NULL) || i8042_enable_mux_ports(&i8042_aux_values)) { printk(KERN_WARNING "i8042: failed to resume active multiplexor, mouse won't work.\n"); }/* * Reconnect anything that was connected to the ports. */ if (i8042_kbd_values.exists && i8042_activate_port(&i8042_kbd_port) == 0) serio_reconnect(&i8042_kbd_port); if (i8042_aux_values.exists && i8042_activate_port(&i8042_aux_port) == 0) serio_reconnect(&i8042_aux_port); for (i = 0; i < 4; i++) if (i8042_mux_values[i].exists && i8042_activate_port(i8042_mux_port + i) == 0) serio_reconnect(i8042_mux_port + i);/* * Restart timer (for polling "stuck" data) */ mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); return 0;}/* * We need to reset the 8042 back to original mode on system shutdown, * because otherwise BIOSes will be confused. */static int i8042_notify_sys(struct notifier_block *this, unsigned long code, void *unused){ if (code == SYS_DOWN || code == SYS_HALT) i8042_controller_cleanup(); return NOTIFY_DONE;}static struct notifier_block i8042_notifier={ i8042_notify_sys, NULL, 0};/* * Suspend/resume handlers for the new PM scheme (driver model) */static int i8042_suspend(struct sys_device *dev, u32 state){ return i8042_controller_suspend();}static int i8042_resume(struct sys_device *dev){ return i8042_controller_resume();}static struct sysdev_class kbc_sysclass = { set_kset_name("i8042"), .suspend = i8042_suspend, .resume = i8042_resume,};static struct sys_device device_i8042 = { .id = 0, .cls = &kbc_sysclass,};/* * Suspend/resume handler for the old PM scheme (APM) */static int i8042_pm_callback(struct pm_dev *dev, pm_request_t request, void *dummy){ switch (request) { case PM_SUSPEND: return i8042_controller_suspend(); case PM_RESUME: return i8042_controller_resume(); } return 0;}static void __init i8042_init_mux_values(struct i8042_values *values, struct serio *port, int index){ memcpy(port, &i8042_aux_port, sizeof(struct serio)); memcpy(values, &i8042_aux_values, sizeof(struct i8042_values)); sprintf(i8042_mux_names[index], "i8042 Aux-%d Port", index); sprintf(i8042_mux_phys[index], I8042_MUX_PHYS_DESC, index + 1); sprintf(i8042_mux_short[index], "AUX%d", index); port->name = i8042_mux_names[index]; port->phys = i8042_mux_phys[index]; port->driver = values; values->name = i8042_mux_short[index]; values->mux = index;}int __init i8042_init(void){ int i; dbg_init(); init_timer(&i8042_timer); i8042_timer.function = i8042_timer_func; if (i8042_platform_init()) return -EBUSY; i8042_aux_values.irq = I8042_AUX_IRQ; i8042_kbd_values.irq = I8042_KBD_IRQ; if (i8042_controller_init()) return -ENODEV; if (i8042_dumbkbd) i8042_kbd_port.write = NULL; if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values)) { if (!i8042_nomux && !i8042_check_mux(&i8042_aux_values)) for (i = 0; i < 4; i++) { i8042_init_mux_values(i8042_mux_values + i, i8042_mux_port + i, i); i8042_port_register(i8042_mux_values + i, i8042_mux_port + i); } else i8042_port_register(&i8042_aux_values, &i8042_aux_port); } i8042_port_register(&i8042_kbd_values, &i8042_kbd_port); mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); if (sysdev_class_register(&kbc_sysclass) == 0) { if (sysdev_register(&device_i8042) == 0) i8042_sysdev_initialized = 1; else sysdev_class_unregister(&kbc_sysclass); } i8042_pm_dev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, i8042_pm_callback); register_reboot_notifier(&i8042_notifier); return 0;}void __exit i8042_exit(void){ int i; unregister_reboot_notifier(&i8042_notifier); if (i8042_pm_dev) pm_unregister(i8042_pm_dev); if (i8042_sysdev_initialized) { sysdev_unregister(&device_i8042); sysdev_class_unregister(&kbc_sysclass); } i8042_controller_cleanup(); if (i8042_kbd_values.exists) serio_unregister_port(&i8042_kbd_port); if (i8042_aux_values.exists) serio_unregister_port(&i8042_aux_port); for (i = 0; i < 4; i++) if (i8042_mux_values[i].exists) serio_unregister_port(i8042_mux_port + i); del_timer_sync(&i8042_timer); i8042_platform_exit();}module_init(i8042_init);module_exit(i8042_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -