📄 i8042.c
字号:
* used it for a PCI card or somethig else. */ if (i8042_noloop || aux_loop_broken) {/* * Without LOOP command we can't test AUX IRQ delivery. Assume the port * is working and hope we are right. */ retval = 0; goto out; } if (request_irq(I8042_AUX_IRQ, i8042_aux_test_irq, IRQF_SHARED, "i8042", i8042_platform_device)) goto out; irq_registered = 1; if (i8042_enable_aux_port()) goto out; spin_lock_irqsave(&i8042_lock, flags); init_completion(&i8042_aux_irq_delivered); i8042_irq_being_tested = 1; param = 0xa5; retval = __i8042_command(¶m, I8042_CMD_AUX_LOOP & 0xf0ff); spin_unlock_irqrestore(&i8042_lock, flags); if (retval) goto out; if (wait_for_completion_timeout(&i8042_aux_irq_delivered, msecs_to_jiffies(250)) == 0) {/* * AUX IRQ was never delivered so we need to flush the controller to * get rid of the byte we put there; otherwise keyboard may not work. */ i8042_flush(); retval = -1; } out:/* * Disable the interface. */ i8042_ctr |= I8042_CTR_AUXDIS; i8042_ctr &= ~I8042_CTR_AUXINT; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) retval = -1; if (irq_registered) free_irq(I8042_AUX_IRQ, i8042_platform_device); return retval;}static int i8042_controller_check(void){ if (i8042_flush() == I8042_BUFFER_SIZE) { printk(KERN_ERR "i8042.c: No controller found.\n"); return -ENODEV; } return 0;}static int i8042_controller_selftest(void){ unsigned char param; if (!i8042_reset) return 0; if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n"); return -ENODEV; } if (param != I8042_RET_CTL_TEST) { printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n", param, I8042_RET_CTL_TEST); return -EIO; } return 0;}/* * 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){ unsigned long flags;/* * 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 -EIO; } i8042_initial_ctr = i8042_ctr;/* * Disable the keyboard interface and interrupt. */ i8042_ctr |= I8042_CTR_KBDDIS; i8042_ctr &= ~I8042_CTR_KBDINT;/* * Handle keylock. */ spin_lock_irqsave(&i8042_lock, flags); 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"); } spin_unlock_irqrestore(&i8042_lock, flags);/* * 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;/* * 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 -EIO; } return 0;}/* * Reset the controller and reset CRT to the original value set by BIOS. */static void i8042_controller_reset(void){ i8042_flush();/* * Disable both KBD and AUX interfaces so they don't get in the way */ i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS; i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT);/* * Disable MUX mode if present. */ if (i8042_mux_present) i8042_set_mux_mode(0, NULL);/* * Reset the controller if requested. */ i8042_controller_selftest();/* * Restore the original control register setting. */ if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR)) printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");}/* * i8042_panic_blink() will flash the keyboard LEDs and is called when * kernel panics. Flashing LEDs is useful for users running X who may * not see the console and will help distingushing panics from "real" * lockups. * * Note that DELAY has a limit of 10ms so we will not get stuck here * waiting for KBC to free up even if KBD interrupt is off */#define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0)static long i8042_panic_blink(long count){ long delay = 0; static long last_blink; static char led; /* * We expect frequency to be about 1/2s. KDB uses about 1s. * Make sure they are different. */ if (!i8042_blink_frequency) return 0; if (count - last_blink < i8042_blink_frequency) return 0; led ^= 0x01 | 0x04; while (i8042_read_status() & I8042_STR_IBF) DELAY; dbg("%02x -> i8042 (panic blink)", 0xed); i8042_suppress_kbd_ack = 2; i8042_write_data(0xed); /* set leds */ DELAY; while (i8042_read_status() & I8042_STR_IBF) DELAY; DELAY; dbg("%02x -> i8042 (panic blink)", led); i8042_write_data(led); DELAY; last_blink = count; return delay;}#undef DELAY#ifdef CONFIG_PM/* * Here we try to restore the original BIOS settings. We only want to * do that once, when we really suspend, not when we taking memory * snapshot for swsusp (in this case we'll perform required cleanup * as part of shutdown process). */static int i8042_suspend(struct platform_device *dev, pm_message_t state){ if (dev->dev.power.power_state.event != state.event) { if (state.event == PM_EVENT_SUSPEND) i8042_controller_reset(); dev->dev.power.power_state = state; } return 0;}/* * Here we try to reset everything back to a state in which suspended */static int i8042_resume(struct platform_device *dev){ int error;/* * Do not bother with restoring state if we haven't suspened yet */ if (dev->dev.power.power_state.event == PM_EVENT_ON) return 0; error = i8042_controller_check(); if (error) return error; error = i8042_controller_selftest(); if (error) return error;/* * Restore original CTR value and disable all ports */ i8042_ctr = i8042_initial_ctr; if (i8042_direct) i8042_ctr &= ~I8042_CTR_XLATE; i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS; i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT); if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { printk(KERN_ERR "i8042: Can't write CTR to resume\n"); return -EIO; } if (i8042_mux_present) { if (i8042_set_mux_mode(1, NULL) || i8042_enable_mux_ports()) printk(KERN_WARNING "i8042: failed to resume active multiplexor, " "mouse won't work.\n"); } else if (i8042_ports[I8042_AUX_PORT_NO].serio) i8042_enable_aux_port(); if (i8042_ports[I8042_KBD_PORT_NO].serio) i8042_enable_kbd_port(); i8042_interrupt(0, NULL); dev->dev.power.power_state = PMSG_ON; return 0;}#endif /* CONFIG_PM *//* * We need to reset the 8042 back to original mode on system shutdown, * because otherwise BIOSes will be confused. */static void i8042_shutdown(struct platform_device *dev){ i8042_controller_reset();}static int __devinit i8042_create_kbd_port(void){ struct serio *serio; struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO]; serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!serio) return -ENOMEM; serio->id.type = i8042_direct ? SERIO_8042 : SERIO_8042_XL; serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write; serio->start = i8042_start; serio->stop = i8042_stop; serio->port_data = port; serio->dev.parent = &i8042_platform_device->dev; strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name)); strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys)); port->serio = serio; port->irq = I8042_KBD_IRQ; return 0;}static int __devinit i8042_create_aux_port(int idx){ struct serio *serio; int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx; struct i8042_port *port = &i8042_ports[port_no]; serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!serio) return -ENOMEM; serio->id.type = SERIO_8042; serio->write = i8042_aux_write; serio->start = i8042_start; serio->stop = i8042_stop; serio->port_data = port; serio->dev.parent = &i8042_platform_device->dev; if (idx < 0) { strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name)); strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); } else { snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx); snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1); } port->serio = serio; port->mux = idx; port->irq = I8042_AUX_IRQ; return 0;}static void __devinit i8042_free_kbd_port(void){ kfree(i8042_ports[I8042_KBD_PORT_NO].serio); i8042_ports[I8042_KBD_PORT_NO].serio = NULL;}static void __devinit i8042_free_aux_ports(void){ int i; for (i = I8042_AUX_PORT_NO; i < I8042_NUM_PORTS; i++) { kfree(i8042_ports[i].serio); i8042_ports[i].serio = NULL; }}static void __devinit i8042_register_ports(void){ int i; for (i = 0; i < I8042_NUM_PORTS; i++) { if (i8042_ports[i].serio) { printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n", i8042_ports[i].serio->name, (unsigned long) I8042_DATA_REG, (unsigned long) I8042_COMMAND_REG, i8042_ports[i].irq); serio_register_port(i8042_ports[i].serio); } }}static void __devexit i8042_unregister_ports(void){ int i; for (i = 0; i < I8042_NUM_PORTS; i++) { if (i8042_ports[i].serio) { serio_unregister_port(i8042_ports[i].serio); i8042_ports[i].serio = NULL; } }}static void i8042_free_irqs(void){ if (i8042_aux_irq_registered) free_irq(I8042_AUX_IRQ, i8042_platform_device); if (i8042_kbd_irq_registered) free_irq(I8042_KBD_IRQ, i8042_platform_device); i8042_aux_irq_registered = i8042_kbd_irq_registered = 0;}static int __devinit i8042_setup_aux(void){ int (*aux_enable)(void); int error; int i; if (i8042_check_aux()) return -ENODEV; if (i8042_nomux || i8042_check_mux()) { error = i8042_create_aux_port(-1); if (error) goto err_free_ports; aux_enable = i8042_enable_aux_port; } else { for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { error = i8042_create_aux_port(i); if (error) goto err_free_ports; } aux_enable = i8042_enable_mux_ports; } error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED, "i8042", i8042_platform_device); if (error) goto err_free_ports; if (aux_enable()) goto err_free_irq; i8042_aux_irq_registered = 1; return 0; err_free_irq: free_irq(I8042_AUX_IRQ, i8042_platform_device); err_free_ports: i8042_free_aux_ports(); return error;}static int __devinit i8042_setup_kbd(void){ int error; error = i8042_create_kbd_port(); if (error) return error; error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, "i8042", i8042_platform_device); if (error) goto err_free_port; error = i8042_enable_kbd_port(); if (error) goto err_free_irq; i8042_kbd_irq_registered = 1; return 0; err_free_irq: free_irq(I8042_KBD_IRQ, i8042_platform_device); err_free_port: i8042_free_kbd_port(); return error;}static int __devinit i8042_probe(struct platform_device *dev){ int error; error = i8042_controller_selftest(); if (error) return error; error = i8042_controller_init(); if (error) return error; if (!i8042_noaux) { error = i8042_setup_aux(); if (error && error != -ENODEV && error != -EBUSY) goto out_fail; } if (!i8042_nokbd) { error = i8042_setup_kbd(); if (error) goto out_fail; }/* * Ok, everything is ready, let's register all serio ports */ i8042_register_ports(); return 0; out_fail: i8042_free_aux_ports(); /* in case KBD failed but AUX not */ i8042_free_irqs(); i8042_controller_reset(); return error;}static int __devexit i8042_remove(struct platform_device *dev){ i8042_unregister_ports(); i8042_free_irqs(); i8042_controller_reset(); return 0;}static struct platform_driver i8042_driver = { .driver = { .name = "i8042", .owner = THIS_MODULE, }, .probe = i8042_probe, .remove = __devexit_p(i8042_remove), .shutdown = i8042_shutdown,#ifdef CONFIG_PM .suspend = i8042_suspend, .resume = i8042_resume,#endif};static int __init i8042_init(void){ int err; dbg_init(); err = i8042_platform_init(); if (err) return err; err = i8042_controller_check(); if (err) goto err_platform_exit; err = platform_driver_register(&i8042_driver); if (err) goto err_platform_exit; i8042_platform_device = platform_device_alloc("i8042", -1); if (!i8042_platform_device) { err = -ENOMEM; goto err_unregister_driver; } err = platform_device_add(i8042_platform_device); if (err) goto err_free_device; panic_blink = i8042_panic_blink; return 0; err_free_device: platform_device_put(i8042_platform_device); err_unregister_driver: platform_driver_unregister(&i8042_driver); err_platform_exit: i8042_platform_exit(); return err;}static void __exit i8042_exit(void){ platform_device_unregister(i8042_platform_device); platform_driver_unregister(&i8042_driver); i8042_platform_exit(); panic_blink = NULL;}module_init(i8042_init);module_exit(i8042_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -