📄 keyboard_driver.c
字号:
input_dev->name = atkbd->name;
input_dev->phys = atkbd->phys;
input_dev->id.bustype = BUS_I8042;
input_dev->id.vendor = 0x0001;
input_dev->id.product = atkbd->translated ? 1 : atkbd->set;
input_dev->id.version = atkbd->id;
input_dev->event = atkbd_event;
input_dev->private = atkbd;
input_dev->cdev.dev = &atkbd->ps2dev.serio->dev;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_MSC);
if (atkbd->write) {
input_dev->evbit[0] |= BIT(EV_LED);
input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
}
if (atkbd->extra)
input_dev->ledbit[0] |= BIT(LED_COMPOSE) | BIT(LED_SUSPEND) |
BIT(LED_SLEEP) | BIT(LED_MUTE) | BIT(LED_MISC);
if (!atkbd->softrepeat) {
input_dev->rep[REP_DELAY] = 250;
input_dev->rep[REP_PERIOD] = 33;
}
input_dev->mscbit[0] = atkbd->softraw ? BIT(MSC_SCAN) : BIT(MSC_RAW) | BIT(MSC_SCAN);
if (atkbd->scroll) {
input_dev->evbit[0] |= BIT(EV_REL);
input_dev->relbit[0] = BIT(REL_WHEEL) | BIT(REL_HWHEEL);
set_bit(BTN_MIDDLE, input_dev->keybit);
}
input_dev->keycode = atkbd->keycode;
input_dev->keycodesize = sizeof(unsigned char);
input_dev->keycodemax = ARRAY_SIZE(atkbd_set2_keycode);
for (i = 0; i < 512; i++)
if (atkbd->keycode[i] && atkbd->keycode[i] < ATKBD_SPECIAL)
set_bit(atkbd->keycode[i], input_dev->keybit);
}
/*
* atkbd_connect() is called when the serio module finds an interface
* that isn't handled yet by an appropriate device driver. We check if
* there is an AT keyboard out there and if yes, we register ourselves
* to the input module.
*/
static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct atkbd *atkbd;
struct input_dev *dev;
int err = -ENOMEM;
atkbd = kzalloc(sizeof(struct atkbd), GFP_KERNEL);
dev = input_allocate_device();
if (!atkbd || !dev)
goto fail;
atkbd->dev = dev;
ps2_init(&atkbd->ps2dev, serio);
INIT_WORK(&atkbd->event_work, atkbd_event_work, atkbd);
mutex_init(&atkbd->event_mutex);
switch (serio->id.type) {
case SERIO_8042_XL:
atkbd->translated = 1;
case SERIO_8042:
if (serio->write)
atkbd->write = 1;
break;
}
atkbd->softraw = atkbd_softraw;
atkbd->softrepeat = atkbd_softrepeat;
atkbd->scroll = atkbd_scroll;
if (atkbd->softrepeat)
atkbd->softraw = 1;
serio_set_drvdata(serio, atkbd);
err = serio_open(serio, drv);
if (err)
goto fail;
if (atkbd->write) {
if (atkbd_probe(atkbd)) {
serio_close(serio);
err = -ENODEV;
goto fail;
}
atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
atkbd_activate(atkbd);
} else {
atkbd->set = 2;
atkbd->id = 0xab00;
}
atkbd_set_keycode_table(atkbd);
atkbd_set_device_attrs(atkbd);
device_create_file(&serio->dev, &atkbd_attr_extra);
device_create_file(&serio->dev, &atkbd_attr_scroll);
device_create_file(&serio->dev, &atkbd_attr_set);
device_create_file(&serio->dev, &atkbd_attr_softrepeat);
device_create_file(&serio->dev, &atkbd_attr_softraw);
atkbd_enable(atkbd);
input_register_device(atkbd->dev);
return 0;
fail: serio_set_drvdata(serio, NULL);
input_free_device(dev);
kfree(atkbd);
return err;
}
/*
* atkbd_reconnect() tries to restore keyboard into a sane state and is
* most likely called on resume.
*/
static int atkbd_reconnect(struct serio *serio)
{
struct atkbd *atkbd = serio_get_drvdata(serio);
struct serio_driver *drv = serio->drv;
if (!atkbd || !drv) {
printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n");
return -1;
}
atkbd_disable(atkbd);
if (atkbd->write) {
if (atkbd_probe(atkbd))
return -1;
if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
return -1;
atkbd_activate(atkbd);
/*
* Restore repeat rate and LEDs (that were reset by atkbd_activate)
* to pre-resume state
*/
if (!atkbd->softrepeat)
atkbd_set_repeat_rate(atkbd);
atkbd_set_leds(atkbd);
}
atkbd_enable(atkbd);
return 0;
}
static struct serio_device_id atkbd_serio_ids[] = {
{
.type = SERIO_8042,
.proto = SERIO_ANY,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{
.type = SERIO_8042_XL,
.proto = SERIO_ANY,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{
.type = SERIO_RS232,
.proto = SERIO_PS2SER,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, atkbd_serio_ids);
static struct serio_driver atkbd_drv = {
.driver = {
.name = "atkbd",
},
.description = DRIVER_DESC,
.id_table = atkbd_serio_ids,
.interrupt = atkbd_interrupt,
.connect = atkbd_connect,
.reconnect = atkbd_reconnect,
.disconnect = atkbd_disconnect,
.cleanup = atkbd_cleanup,
};
static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
ssize_t (*handler)(struct atkbd *, char *))
{
struct serio *serio = to_serio_port(dev);
int retval;
retval = serio_pin_driver(serio);
if (retval)
return retval;
if (serio->drv != &atkbd_drv) {
retval = -ENODEV;
goto out;
}
retval = handler((struct atkbd *)serio_get_drvdata(serio), buf);
out:
serio_unpin_driver(serio);
return retval;
}
static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count,
ssize_t (*handler)(struct atkbd *, const char *, size_t))
{
struct serio *serio = to_serio_port(dev);
struct atkbd *atkbd;
int retval;
retval = serio_pin_driver(serio);
if (retval)
return retval;
if (serio->drv != &atkbd_drv) {
retval = -ENODEV;
goto out;
}
atkbd = serio_get_drvdata(serio);
atkbd_disable(atkbd);
retval = handler(atkbd, buf, count);
atkbd_enable(atkbd);
out:
serio_unpin_driver(serio);
return retval;
}
static ssize_t atkbd_show_extra(struct atkbd *atkbd, char *buf)
{
return sprintf(buf, "%d\n", atkbd->extra ? 1 : 0);
}
static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t count)
{
struct input_dev *new_dev;
unsigned long value;
char *rest;
if (!atkbd->write)
return -EIO;
value = simple_strtoul(buf, &rest, 10);
if (*rest || value > 1)
return -EINVAL;
if (atkbd->extra != value) {
/*
* Since device's properties will change we need to
* unregister old device. But allocate new one first
* to make sure we have it.
*/
if (!(new_dev = input_allocate_device()))
return -ENOMEM;
input_unregister_device(atkbd->dev);
atkbd->dev = new_dev;
atkbd->set = atkbd_select_set(atkbd, atkbd->set, value);
atkbd_activate(atkbd);
atkbd_set_device_attrs(atkbd);
input_register_device(atkbd->dev);
}
return count;
}
static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf)
{
return sprintf(buf, "%d\n", atkbd->scroll ? 1 : 0);
}
static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t count)
{
struct input_dev *new_dev;
unsigned long value;
char *rest;
value = simple_strtoul(buf, &rest, 10);
if (*rest || value > 1)
return -EINVAL;
if (atkbd->scroll != value) {
if (!(new_dev = input_allocate_device()))
return -ENOMEM;
input_unregister_device(atkbd->dev);
atkbd->dev = new_dev;
atkbd->scroll = value;
atkbd_set_keycode_table(atkbd);
atkbd_set_device_attrs(atkbd);
input_register_device(atkbd->dev);
}
return count;
}
static ssize_t atkbd_show_set(struct atkbd *atkbd, char *buf)
{
return sprintf(buf, "%d\n", atkbd->set);
}
static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count)
{
struct input_dev *new_dev;
unsigned long value;
char *rest;
if (!atkbd->write)
return -EIO;
value = simple_strtoul(buf, &rest, 10);
if (*rest || (value != 2 && value != 3))
return -EINVAL;
if (atkbd->set != value) {
if (!(new_dev = input_allocate_device()))
return -ENOMEM;
input_unregister_device(atkbd->dev);
atkbd->dev = new_dev;
atkbd->set = atkbd_select_set(atkbd, value, atkbd->extra);
atkbd_activate(atkbd);
atkbd_set_keycode_table(atkbd);
atkbd_set_device_attrs(atkbd);
input_register_device(atkbd->dev);
}
return count;
}
static ssize_t atkbd_show_softrepeat(struct atkbd *atkbd, char *buf)
{
return sprintf(buf, "%d\n", atkbd->softrepeat ? 1 : 0);
}
static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t count)
{
struct input_dev *new_dev;
unsigned long value;
char *rest;
if (!atkbd->write)
return -EIO;
value = simple_strtoul(buf, &rest, 10);
if (*rest || value > 1)
return -EINVAL;
if (atkbd->softrepeat != value) {
if (!(new_dev = input_allocate_device()))
return -ENOMEM;
input_unregister_device(atkbd->dev);
atkbd->dev = new_dev;
atkbd->softrepeat = value;
if (atkbd->softrepeat)
atkbd->softraw = 1;
atkbd_set_device_attrs(atkbd);
input_register_device(atkbd->dev);
}
return count;
}
static ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf)
{
return sprintf(buf, "%d\n", atkbd->softraw ? 1 : 0);
}
static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t count)
{
struct input_dev *new_dev;
unsigned long value;
char *rest;
value = simple_strtoul(buf, &rest, 10);
if (*rest || value > 1)
return -EINVAL;
if (atkbd->softraw != value) {
if (!(new_dev = input_allocate_device()))
return -ENOMEM;
input_unregister_device(atkbd->dev);
atkbd->dev = new_dev;
atkbd->softraw = value;
atkbd_set_device_attrs(atkbd);
input_register_device(atkbd->dev);
}
return count;
}
static int __init atkbd_init(void)
{
serio_register_driver(&atkbd_drv);
return 0;
}
static void __exit atkbd_exit(void)
{
serio_unregister_driver(&atkbd_drv);
}
module_init(atkbd_init);
module_exit(atkbd_exit);
/*
* PS/2 mouse driver
*
* Copyright (c) 1999-2002 Vojtech Pavlik
* Copyright (c) 2003-2004 Dmitry Torokhov
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -