Commit 33124340 authored by Vojtech Pavlik's avatar Vojtech Pavlik

Add support for PS/2 Active Multiplexing Spec, updates for PS/2 mouse

and keyboard handling - proper cleanup on reboot, allow USB-emulated
AT keyboards, option to restrict PS/2 mouse to generic mode.
parent e0fcd3bc
...@@ -22,9 +22,15 @@ ...@@ -22,9 +22,15 @@
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("AT and PS/2 keyboard driver"); MODULE_DESCRIPTION("AT and PS/2 keyboard driver");
MODULE_PARM(atkbd_set, "1i"); MODULE_PARM(atkbd_set, "1i");
MODULE_PARM(atkbd_reset, "1i");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static int atkbd_set = 2; static int atkbd_set = 2;
#if defined(__i386__) || defined (__x86_64__)
static int atkbd_reset;
#else
static int atkbd_reset = 1;
#endif
/* /*
* Scancode to keycode tables. These are just the default setting, and * Scancode to keycode tables. These are just the default setting, and
...@@ -89,6 +95,7 @@ static unsigned char atkbd_set3_keycode[512] = { ...@@ -89,6 +95,7 @@ static unsigned char atkbd_set3_keycode[512] = {
#define ATKBD_CMD_RESEND 0x00fe #define ATKBD_CMD_RESEND 0x00fe
#define ATKBD_CMD_EX_ENABLE 0x10ea #define ATKBD_CMD_EX_ENABLE 0x10ea
#define ATKBD_CMD_EX_SETLEDS 0x20eb #define ATKBD_CMD_EX_SETLEDS 0x20eb
#define ATKBD_CMD_OK_GETID 0x02e8
#define ATKBD_RET_ACK 0xfa #define ATKBD_RET_ACK 0xfa
#define ATKBD_RET_NAK 0xfe #define ATKBD_RET_NAK 0xfe
...@@ -113,6 +120,7 @@ struct atkbd { ...@@ -113,6 +120,7 @@ struct atkbd {
unsigned char cmdbuf[4]; unsigned char cmdbuf[4];
unsigned char cmdcnt; unsigned char cmdcnt;
unsigned char set; unsigned char set;
unsigned char oldset;
unsigned char release; unsigned char release;
signed char ack; signed char ack;
unsigned char emul; unsigned char emul;
...@@ -134,7 +142,6 @@ static void atkbd_interrupt(struct serio *serio, unsigned char data, unsigned in ...@@ -134,7 +142,6 @@ static void atkbd_interrupt(struct serio *serio, unsigned char data, unsigned in
printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags); printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);
#endif #endif
/* Interface error. Request that the keyboard resend. */
if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && atkbd->write) { if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && atkbd->write) {
printk("atkbd.c: frame/parity error: %02x\n", flags); printk("atkbd.c: frame/parity error: %02x\n", flags);
serio_write(serio, ATKBD_CMD_RESEND); serio_write(serio, ATKBD_CMD_RESEND);
...@@ -205,7 +212,9 @@ static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte) ...@@ -205,7 +212,9 @@ static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte)
#ifdef ATKBD_DEBUG #ifdef ATKBD_DEBUG
printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte); printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte);
#endif #endif
serio_write(atkbd->serio, byte); if (serio_write(atkbd->serio, byte))
return -1;
while (!atkbd->ack && timeout--) udelay(10); while (!atkbd->ack && timeout--) udelay(10);
return -(atkbd->ack <= 0); return -(atkbd->ack <= 0);
...@@ -289,34 +298,41 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co ...@@ -289,34 +298,41 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co
static int atkbd_set_3(struct atkbd *atkbd) static int atkbd_set_3(struct atkbd *atkbd)
{ {
unsigned char param; unsigned char param[2];
/*
* Remember original scancode set value, so that we can restore it on exit.
*/
if (atkbd_command(atkbd, &atkbd->oldset, ATKBD_CMD_GSCANSET))
atkbd->oldset = 2;
/* /*
* For known special keyboards we can go ahead and set the correct set. * For known special keyboards we can go ahead and set the correct set.
* We check for NCD PS/2 Sun, NorthGate OmniKey 101 and IBM RapidAccess
* keyboards.
*/ */
if (atkbd->id == 0xaca1) { if (atkbd->id == 0xaca1) {
param = 3; param[0] = 3;
atkbd_command(atkbd, &param, ATKBD_CMD_SSCANSET); atkbd_command(atkbd, param, ATKBD_CMD_SSCANSET);
return 3; return 3;
} }
/* if (!atkbd_command(atkbd, param, ATKBD_CMD_OK_GETID)) {
* We check for the extra keys on an some keyboards that need extra atkbd->id = param[0] << 8 | param[1];
* command to get enabled. This shouldn't harm any keyboards not return 2;
* knowing the command. }
*/
param = 0x71; param[0] = 0x71;
if (!atkbd_command(atkbd, &param, ATKBD_CMD_EX_ENABLE)) if (!atkbd_command(atkbd, param, ATKBD_CMD_EX_ENABLE))
return 4; return 4;
/* /*
* Try to set the set we want. * Try to set the set we want.
*/ */
param = atkbd_set; if (atkbd_command(atkbd, &atkbd_set, ATKBD_CMD_SSCANSET))
if (atkbd_command(atkbd, &param, ATKBD_CMD_SSCANSET))
return 2; return 2;
/* /*
...@@ -327,8 +343,8 @@ static int atkbd_set_3(struct atkbd *atkbd) ...@@ -327,8 +343,8 @@ static int atkbd_set_3(struct atkbd *atkbd)
* In that case we time out, and return 2. * In that case we time out, and return 2.
*/ */
param = 0; param[0] = 0;
if (atkbd_command(atkbd, &param, ATKBD_CMD_GSCANSET)) if (atkbd_command(atkbd, param, ATKBD_CMD_GSCANSET))
return 2; return 2;
/* /*
...@@ -336,7 +352,7 @@ static int atkbd_set_3(struct atkbd *atkbd) ...@@ -336,7 +352,7 @@ static int atkbd_set_3(struct atkbd *atkbd)
* itself. * itself.
*/ */
return (param == 3) ? 3 : 2; return (param[0] == 3) ? 3 : 2;
} }
/* /*
...@@ -353,10 +369,9 @@ static int atkbd_probe(struct atkbd *atkbd) ...@@ -353,10 +369,9 @@ static int atkbd_probe(struct atkbd *atkbd)
* these systems the BIOS also usually doesn't do it for us. * these systems the BIOS also usually doesn't do it for us.
*/ */
#ifdef CONFIG_KEYBOARD_ATKBD_RESET if (atkbd_reset)
if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_BAT)) if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_BAT))
printk(KERN_WARNING "atkbd.c: keyboard reset failed\n"); printk(KERN_WARNING "atkbd.c: keyboard reset failed\n");
#endif
/* /*
* Next we check we can set LEDs on the keyboard. This should work on every * Next we check we can set LEDs on the keyboard. This should work on every
...@@ -405,7 +420,18 @@ static int atkbd_probe(struct atkbd *atkbd) ...@@ -405,7 +420,18 @@ static int atkbd_probe(struct atkbd *atkbd)
} }
/* /*
* atkbd_disconnect() cleans up behind us ... * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
* reboot.
*/
static void atkbd_cleanup(struct serio *serio)
{
struct atkbd *atkbd = serio->private;
atkbd_command(atkbd, &atkbd->oldset, ATKBD_CMD_SSCANSET);
}
/*
* atkbd_disconnect() closes and frees.
*/ */
static void atkbd_disconnect(struct serio *serio) static void atkbd_disconnect(struct serio *serio)
...@@ -508,18 +534,28 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev) ...@@ -508,18 +534,28 @@ static void atkbd_connect(struct serio *serio, struct serio_dev *dev)
static struct serio_dev atkbd_dev = { static struct serio_dev atkbd_dev = {
.interrupt = atkbd_interrupt, .interrupt = atkbd_interrupt,
.connect = atkbd_connect, .connect = atkbd_connect,
.disconnect = atkbd_disconnect .disconnect = atkbd_disconnect,
.cleanup = atkbd_cleanup,
}; };
#ifndef MODULE #ifndef MODULE
static int __init atkbd_setup(char *str) static int __init atkbd_setup_set(char *str)
{ {
int ints[4]; int ints[4];
str = get_options(str, ARRAY_SIZE(ints), ints); str = get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] > 0) atkbd_set = ints[1]; if (ints[0] > 0) atkbd_set = ints[1];
return 1; return 1;
} }
__setup("atkbd_set=", atkbd_setup); static int __init atkbd_setup_reset(char *str)
{
int ints[4];
str = get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] > 0) atkbd_reset = ints[1];
return 1;
}
__setup("atkbd_set=", atkbd_setup_set);
__setup("atkbd_reset", atkbd_setup_reset);
#endif #endif
int __init atkbd_init(void) int __init atkbd_init(void)
......
...@@ -21,8 +21,11 @@ ...@@ -21,8 +21,11 @@
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("PS/2 mouse driver"); MODULE_DESCRIPTION("PS/2 mouse driver");
MODULE_PARM(psmouse_noext, "1i");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static int psmouse_noext;
#define PSMOUSE_CMD_SETSCALE11 0x00e6 #define PSMOUSE_CMD_SETSCALE11 0x00e6
#define PSMOUSE_CMD_SETRES 0x10e8 #define PSMOUSE_CMD_SETRES 0x10e8
#define PSMOUSE_CMD_GETINFO 0x03e9 #define PSMOUSE_CMD_GETINFO 0x03e9
...@@ -33,6 +36,7 @@ MODULE_LICENSE("GPL"); ...@@ -33,6 +36,7 @@ MODULE_LICENSE("GPL");
#define PSMOUSE_CMD_SETRATE 0x10f3 #define PSMOUSE_CMD_SETRATE 0x10f3
#define PSMOUSE_CMD_ENABLE 0x00f4 #define PSMOUSE_CMD_ENABLE 0x00f4
#define PSMOUSE_CMD_RESET_DIS 0x00f6 #define PSMOUSE_CMD_RESET_DIS 0x00f6
#define PSMOUSE_CMD_RESET_BAT 0x02ff
#define PSMOUSE_RET_BAT 0xaa #define PSMOUSE_RET_BAT 0xaa
#define PSMOUSE_RET_ACK 0xfa #define PSMOUSE_RET_ACK 0xfa
...@@ -222,7 +226,11 @@ static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte) ...@@ -222,7 +226,11 @@ static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte)
psmouse->ack = 0; psmouse->ack = 0;
psmouse->acking = 1; psmouse->acking = 1;
serio_write(psmouse->serio, byte); if (serio_write(psmouse->serio, byte)) {
psmouse->acking = 0;
return -1;
}
while (!psmouse->ack && timeout--) udelay(10); while (!psmouse->ack && timeout--) udelay(10);
return -(psmouse->ack <= 0); return -(psmouse->ack <= 0);
...@@ -302,6 +310,9 @@ static int psmouse_extensions(struct psmouse *psmouse) ...@@ -302,6 +310,9 @@ static int psmouse_extensions(struct psmouse *psmouse)
psmouse->name = "Mouse"; psmouse->name = "Mouse";
psmouse->model = 0; psmouse->model = 0;
if (psmouse_noext)
return PSMOUSE_PS2;
/* /*
* Try Genius NetMouse magic init. * Try Genius NetMouse magic init.
*/ */
...@@ -529,15 +540,24 @@ static void psmouse_initialize(struct psmouse *psmouse) ...@@ -529,15 +540,24 @@ static void psmouse_initialize(struct psmouse *psmouse)
* Last, we enable the mouse so that we get reports from it. * Last, we enable the mouse so that we get reports from it.
*/ */
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE)) { if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE))
printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys); printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys);
}
} }
/* /*
* psmouse_disconnect() cleans up after we don't want talk * psmouse_cleanup() resets the mouse into power-on state.
* to the mouse anymore. */
static void psmouse_cleanup(struct serio *serio)
{
struct psmouse *psmouse = serio->private;
unsigned char param[2];
psmouse_command(psmouse, param, PSMOUSE_CMD_RESET_BAT);
}
/*
* psmouse_disconnect() closes and frees.
*/ */
static void psmouse_disconnect(struct serio *serio) static void psmouse_disconnect(struct serio *serio)
...@@ -607,9 +627,19 @@ static void psmouse_connect(struct serio *serio, struct serio_dev *dev) ...@@ -607,9 +627,19 @@ static void psmouse_connect(struct serio *serio, struct serio_dev *dev)
static struct serio_dev psmouse_dev = { static struct serio_dev psmouse_dev = {
.interrupt = psmouse_interrupt, .interrupt = psmouse_interrupt,
.connect = psmouse_connect, .connect = psmouse_connect,
.disconnect = psmouse_disconnect .disconnect = psmouse_disconnect,
.cleanup = psmouse_cleanup,
}; };
#ifndef MODULE
static int __init psmouse_setup(char *str)
{
psmouse_noext = 1;
return 1;
}
__setup("psmouse_noext", psmouse_setup);
#endif
int __init psmouse_init(void) int __init psmouse_init(void)
{ {
serio_register_device(&psmouse_dev); serio_register_device(&psmouse_dev);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#define I8042_KBD_PHYS_DESC "isa0060/serio0" #define I8042_KBD_PHYS_DESC "isa0060/serio0"
#define I8042_AUX_PHYS_DESC "isa0060/serio1" #define I8042_AUX_PHYS_DESC "isa0060/serio1"
#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
/* /*
* IRQs. * IRQs.
...@@ -64,9 +65,13 @@ static inline int i8042_platform_init(void) ...@@ -64,9 +65,13 @@ static inline int i8042_platform_init(void)
*/ */
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__) && !defined(__x86_64__) #if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__) && !defined(__x86_64__)
if (!request_region(I8042_DATA_REG, 16, "i8042")) if (!request_region(I8042_DATA_REG, 16, "i8042"))
return 0; return -1;
#endif #endif
return 1;
#if !defined(__i386__) && !defined(__x86_64__)
i8042_reset = 1;
#endif
return 0;
} }
static inline void i8042_platform_exit(void) static inline void i8042_platform_exit(void)
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define I8042_KBD_PHYS_DESC "walnutps2/serio0" #define I8042_KBD_PHYS_DESC "walnutps2/serio0"
#define I8042_AUX_PHYS_DESC "walnutps2/serio1" #define I8042_AUX_PHYS_DESC "walnutps2/serio1"
#define I8042_MUX_PHYS_DESC "walnutps2/serio%d"
extern void *kb_cs; extern void *kb_cs;
extern void *kb_data; extern void *kb_data;
...@@ -34,18 +35,17 @@ static inline int i8042_read_status(void) ...@@ -34,18 +35,17 @@ static inline int i8042_read_status(void)
static inline void i8042_write_data(int val) static inline void i8042_write_data(int val)
{ {
writeb(val, kb_data); writeb(val, kb_data);
return;
} }
static inline void i8042_write_command(int val) static inline void i8042_write_command(int val)
{ {
writeb(val, kb_cs); writeb(val, kb_cs);
return;
} }
static inline int i8042_platform_init(void) static inline int i8042_platform_init(void)
{ {
return 1; i8042_reset = 1;
return 0;
} }
static inline void i8042_platform_exit(void) static inline void i8042_platform_exit(void)
...@@ -59,6 +59,7 @@ static inline void i8042_platform_exit(void) ...@@ -59,6 +59,7 @@ static inline void i8042_platform_exit(void)
#define I8042_KBD_PHYS_DESC "spruceps2/serio0" #define I8042_KBD_PHYS_DESC "spruceps2/serio0"
#define I8042_AUX_PHYS_DESC "spruceps2/serio1" #define I8042_AUX_PHYS_DESC "spruceps2/serio1"
#define I8042_MUX_PHYS_DESC "spruceps2/serio%d"
#define I8042_COMMAND_REG 0xff810000 #define I8042_COMMAND_REG 0xff810000
#define I8042_DATA_REG 0xff810001 #define I8042_DATA_REG 0xff810001
...@@ -109,18 +110,17 @@ static inline int i8042_read_status(void) ...@@ -109,18 +110,17 @@ static inline int i8042_read_status(void)
static inline void i8042_write_data(int val) static inline void i8042_write_data(int val)
{ {
*((unsigned char *)0xff810000) = (char)val; *((unsigned char *)0xff810000) = (char)val;
return;
} }
static inline void i8042_write_command(int val) static inline void i8042_write_command(int val)
{ {
*((unsigned char *)0xff810001) = (char)val; *((unsigned char *)0xff810001) = (char)val;
return;
} }
static inline int i8042_platform_init(void) static inline int i8042_platform_init(void)
{ {
return 1; i8042_reset = 1;
return 0;
} }
static inline void i8042_platform_exit(void) static inline void i8042_platform_exit(void)
......
...@@ -11,6 +11,7 @@ static int i8042_aux_irq = -1; ...@@ -11,6 +11,7 @@ static int i8042_aux_irq = -1;
#define I8042_KBD_PHYS_DESC "sparcps2/serio0" #define I8042_KBD_PHYS_DESC "sparcps2/serio0"
#define I8042_AUX_PHYS_DESC "sparcps2/serio1" #define I8042_AUX_PHYS_DESC "sparcps2/serio1"
#define I8042_MUX_PHYS_DESC "sparcps2/serio%d"
static unsigned long kbd_iobase; static unsigned long kbd_iobase;
...@@ -50,14 +51,14 @@ static int i8042_platform_init(void) ...@@ -50,14 +51,14 @@ static int i8042_platform_init(void)
len = prom_getproperty(prom_root_node, "name", prop, sizeof(prop)); len = prom_getproperty(prom_root_node, "name", prop, sizeof(prop));
if (len < 0) { if (len < 0) {
printk("i8042: Cannot get name property of root OBP node.\n"); printk("i8042: Cannot get name property of root OBP node.\n");
return 0; return -1;
} }
if (strncmp(prop, "SUNW,JavaStation-1", len) == 0) { if (strncmp(prop, "SUNW,JavaStation-1", len) == 0) {
/* Hardcoded values for MrCoffee. */ /* Hardcoded values for MrCoffee. */
i8042_kbd_irq = i8042_aux_irq = 13 | 0x20; i8042_kbd_irq = i8042_aux_irq = 13 | 0x20;
kbd_iobase = (unsigned long) ioremap(0x71300060, 8); kbd_iobase = (unsigned long) ioremap(0x71300060, 8);
if (!kbd_iobase) if (!kbd_iobase)
return 0; return -1;
} else { } else {
struct linux_ebus *ebus; struct linux_ebus *ebus;
struct linux_ebus_device *edev; struct linux_ebus_device *edev;
...@@ -69,7 +70,7 @@ static int i8042_platform_init(void) ...@@ -69,7 +70,7 @@ static int i8042_platform_init(void)
goto edev_found; goto edev_found;
} }
} }
return 0; return -1;
edev_found: edev_found:
for_each_edevchild(edev, child) { for_each_edevchild(edev, child) {
...@@ -87,11 +88,13 @@ static int i8042_platform_init(void) ...@@ -87,11 +88,13 @@ static int i8042_platform_init(void)
i8042_aux_irq == -1) { i8042_aux_irq == -1) {
printk("i8042: Error, 8042 device lacks both kbd and " printk("i8042: Error, 8042 device lacks both kbd and "
"mouse nodes.\n"); "mouse nodes.\n");
return 0; return -1;
} }
} }
return 1; i8042_reset = 1;
return 0;
} }
static inline void i8042_platform_exit(void) static inline void i8042_platform_exit(void)
......
This diff is collapsed.
...@@ -21,13 +21,6 @@ ...@@ -21,13 +21,6 @@
#include "i8042-io.h" #include "i8042-io.h"
#endif #endif
/*
* If you want to trace all the i/o the i8042 module does for
* debugging purposes, define this.
*/
#undef I8042_DEBUG_IO
/* /*
* This is in 50us units, the time we wait for the i8042 to react. This * This is in 50us units, the time we wait for the i8042 to react. This
* has to be long enough for the i8042 itself to timeout on sending a byte * has to be long enough for the i8042 itself to timeout on sending a byte
...@@ -54,6 +47,7 @@ ...@@ -54,6 +47,7 @@
#define I8042_STR_AUXDATA 0x20 #define I8042_STR_AUXDATA 0x20
#define I8042_STR_KEYLOCK 0x10 #define I8042_STR_KEYLOCK 0x10
#define I8042_STR_CMDDAT 0x08 #define I8042_STR_CMDDAT 0x08
#define I8042_STR_MUXERR 0x04
#define I8042_STR_IBF 0x02 #define I8042_STR_IBF 0x02
#define I8042_STR_OBF 0x01 #define I8042_STR_OBF 0x01
...@@ -87,6 +81,9 @@ ...@@ -87,6 +81,9 @@
#define I8042_CMD_AUX_SEND 0x10d4 #define I8042_CMD_AUX_SEND 0x10d4
#define I8042_CMD_AUX_LOOP 0x11d3 #define I8042_CMD_AUX_LOOP 0x11d3
#define I8042_CMD_MUX_PFX 0x0090
#define I8042_CMD_MUX_SEND 0x1090
/* /*
* Return codes. * Return codes.
*/ */
...@@ -100,4 +97,18 @@ ...@@ -100,4 +97,18 @@
#define I8042_BUFFER_SIZE 32 #define I8042_BUFFER_SIZE 32
/*
* Debug.
*/
#ifdef DEBUG
static unsigned long i8042_start;
#define dbg_init() do { i8042_start = jiffies; } while (0);
#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "[%d]\n" ,\
## arg, (int) (jiffies - i8042_start))
#else
#define dbg_init() do { } while (0);
#define dbg(format, arg...) do {} while (0)
#endif
#endif /* _I8042_H */ #endif /* _I8042_H */
...@@ -69,6 +69,7 @@ struct serio_dev { ...@@ -69,6 +69,7 @@ struct serio_dev {
void (*interrupt)(struct serio *, unsigned char, unsigned int); void (*interrupt)(struct serio *, unsigned char, unsigned int);
void (*connect)(struct serio *, struct serio_dev *dev); void (*connect)(struct serio *, struct serio_dev *dev);
void (*disconnect)(struct serio *); void (*disconnect)(struct serio *);
void (*cleanup)(struct serio *);
struct serio_dev *next; struct serio_dev *next;
}; };
...@@ -85,7 +86,10 @@ void serio_unregister_device(struct serio_dev *dev); ...@@ -85,7 +86,10 @@ void serio_unregister_device(struct serio_dev *dev);
static __inline__ int serio_write(struct serio *serio, unsigned char data) static __inline__ int serio_write(struct serio *serio, unsigned char data)
{ {
return serio->write(serio, data); if (serio->write)
return serio->write(serio, data);
else
return -1;
} }
static __inline__ void serio_dev_write_wakeup(struct serio *serio) static __inline__ void serio_dev_write_wakeup(struct serio *serio)
...@@ -94,6 +98,12 @@ static __inline__ void serio_dev_write_wakeup(struct serio *serio) ...@@ -94,6 +98,12 @@ static __inline__ void serio_dev_write_wakeup(struct serio *serio)
serio->dev->write_wakeup(serio); serio->dev->write_wakeup(serio);
} }
static __inline__ void serio_cleanup(struct serio *serio)
{
if (serio->dev && serio->dev->cleanup)
serio->dev->cleanup(serio);
}
/* /*
* bit masks for use in "interrupt" flags (3rd argument) * bit masks for use in "interrupt" flags (3rd argument)
*/ */
......
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