diff options
Diffstat (limited to 'drivers/input/serio')
| -rw-r--r-- | drivers/input/serio/Kconfig | 29 | ||||
| -rw-r--r-- | drivers/input/serio/Makefile | 1 | ||||
| -rw-r--r-- | drivers/input/serio/altera_ps2.c | 1 | ||||
| -rw-r--r-- | drivers/input/serio/ambakmi.c | 6 | ||||
| -rw-r--r-- | drivers/input/serio/apbps2.c | 2 | ||||
| -rw-r--r-- | drivers/input/serio/hp_sdc.c | 2 | ||||
| -rw-r--r-- | drivers/input/serio/hyperv-keyboard.c | 439 | ||||
| -rw-r--r-- | drivers/input/serio/i8042-x86ia64io.h | 24 | ||||
| -rw-r--r-- | drivers/input/serio/i8042.c | 31 | ||||
| -rw-r--r-- | drivers/input/serio/libps2.c | 1 | ||||
| -rw-r--r-- | drivers/input/serio/olpc_apsp.c | 3 | ||||
| -rw-r--r-- | drivers/input/serio/pcips2.c | 2 | ||||
| -rw-r--r-- | drivers/input/serio/q40kbd.c | 1 | ||||
| -rw-r--r-- | drivers/input/serio/rpckbd.c | 1 | ||||
| -rw-r--r-- | drivers/input/serio/serio.c | 94 | ||||
| -rw-r--r-- | drivers/input/serio/serio_raw.c | 1 | ||||
| -rw-r--r-- | drivers/input/serio/serport.c | 28 | ||||
| -rw-r--r-- | drivers/input/serio/xilinx_ps2.c | 9 | 
18 files changed, 590 insertions, 85 deletions
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 33b3e88fe4a..bc2d47431bd 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -2,7 +2,7 @@  # Input core configuration  #  config SERIO -	tristate "Serial I/O support" if EXPERT || !X86 +	tristate "Serial I/O support"  	default y  	help  	  Say Yes here if you have any input device that uses serial I/O to @@ -16,14 +16,19 @@ config SERIO  	  To compile this driver as a module, choose M here: the  	  module will be called serio. +config ARCH_MIGHT_HAVE_PC_SERIO +	bool +	help +	  Select this config option from the architecture Kconfig if +	  the architecture might use a PC serio device (i8042) to +	  communicate with keyboard, mouse, etc. +  if SERIO  config SERIO_I8042 -	tristate "i8042 PC Keyboard controller" if EXPERT || !X86 +	tristate "i8042 PC Keyboard controller"  	default y -	depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \ -		   (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !S390 && \ -		   !ARC +	depends on ARCH_MIGHT_HAVE_PC_SERIO  	help  	  i8042 is the chip over which the standard AT keyboard and PS/2  	  mouse are connected to the computer. If you use these devices, @@ -170,7 +175,7 @@ config SERIO_MACEPS2  	  module will be called maceps2.  config SERIO_LIBPS2 -	tristate "PS/2 driver library" if EXPERT +	tristate "PS/2 driver library"  	depends on SERIO_I8042 || SERIO_I8042=n  	help  	  Say Y here if you are using a driver for device connected @@ -258,7 +263,7 @@ config SERIO_APBPS2  config SERIO_OLPC_APSP  	tristate "OLPC AP-SP input support" -	depends on OF +	depends on OLPC || COMPILE_TEST  	help  	  Say Y here if you want support for the keyboard and touchpad included  	  in the OLPC XO-1.75 and XO-4 laptops. @@ -266,4 +271,14 @@ config SERIO_OLPC_APSP  	  To compile this driver as a module, choose M here: the module will  	  be called olpc_apsp. +config HYPERV_KEYBOARD +	tristate "Microsoft Synthetic Keyboard driver" +	depends on HYPERV +	default HYPERV +	help +	  Select this option to enable the Hyper-V Keyboard driver. + +	  To compile this driver as a module, choose M here: the module will +	  be called hyperv_keyboard. +  endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 12298b1c0e7..815d874fe72 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -28,3 +28,4 @@ obj-$(CONFIG_SERIO_ALTERA_PS2)	+= altera_ps2.o  obj-$(CONFIG_SERIO_ARC_PS2)	+= arc_ps2.o  obj-$(CONFIG_SERIO_APBPS2)	+= apbps2.o  obj-$(CONFIG_SERIO_OLPC_APSP)	+= olpc_apsp.o +obj-$(CONFIG_HYPERV_KEYBOARD)	+= hyperv-keyboard.o diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c index 4777a73cd39..cce69d6b958 100644 --- a/drivers/input/serio/altera_ps2.c +++ b/drivers/input/serio/altera_ps2.c @@ -12,7 +12,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/input.h>  #include <linux/serio.h>  #include <linux/interrupt.h> diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c index 4e2fd44865e..8b748d99b93 100644 --- a/drivers/input/serio/ambakmi.c +++ b/drivers/input/serio/ambakmi.c @@ -10,7 +10,6 @@   * (at your option) any later version.   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/serio.h>  #include <linux/errno.h>  #include <linux/interrupt.h> @@ -80,7 +79,8 @@ static int amba_kmi_open(struct serio *io)  	writeb(divisor, KMICLKDIV);  	writeb(KMICR_EN, KMICR); -	ret = request_irq(kmi->irq, amba_kmi_int, 0, "kmi-pl050", kmi); +	ret = request_irq(kmi->irq, amba_kmi_int, IRQF_SHARED, "kmi-pl050", +			  kmi);  	if (ret) {  		printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq);  		writeb(0, KMICR); @@ -167,8 +167,6 @@ static int amba_kmi_remove(struct amba_device *dev)  {  	struct amba_kmi_port *kmi = amba_get_drvdata(dev); -	amba_set_drvdata(dev, NULL); -  	serio_unregister_port(kmi->io);  	clk_put(kmi->clk);  	iounmap(kmi->base); diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c index 17e01a807dd..98be824544a 100644 --- a/drivers/input/serio/apbps2.c +++ b/drivers/input/serio/apbps2.c @@ -203,7 +203,7 @@ static int apbps2_of_remove(struct platform_device *of_dev)  	return 0;  } -static struct of_device_id apbps2_of_match[] = { +static const struct of_device_id apbps2_of_match[] = {  	{ .name = "GAISLER_APBPS2", },  	{ .name = "01_060", },  	{} diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index d7a7e54f646..852858e5d8d 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -984,7 +984,7 @@ static void hp_sdc_exit(void)  	free_irq(hp_sdc.irq, &hp_sdc);  	write_unlock_irq(&hp_sdc.lock); -	del_timer(&hp_sdc.kicker); +	del_timer_sync(&hp_sdc.kicker);  	tasklet_kill(&hp_sdc.task); diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c new file mode 100644 index 00000000000..61326199462 --- /dev/null +++ b/drivers/input/serio/hyperv-keyboard.c @@ -0,0 +1,439 @@ +/* + *  Copyright (c) 2013, Microsoft Corporation. + * + *  This program is free software; you can redistribute it and/or modify it + *  under the terms and conditions of the GNU General Public License, + *  version 2, as published by the Free Software Foundation. + * + *  This program is distributed in the hope it will be useful, but WITHOUT + *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + *  more details. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/completion.h> +#include <linux/hyperv.h> +#include <linux/serio.h> +#include <linux/slab.h> + +/* + * Current version 1.0 + * + */ +#define SYNTH_KBD_VERSION_MAJOR 1 +#define SYNTH_KBD_VERSION_MINOR	0 +#define SYNTH_KBD_VERSION		(SYNTH_KBD_VERSION_MINOR | \ +					 (SYNTH_KBD_VERSION_MAJOR << 16)) + + +/* + * Message types in the synthetic input protocol + */ +enum synth_kbd_msg_type { +	SYNTH_KBD_PROTOCOL_REQUEST = 1, +	SYNTH_KBD_PROTOCOL_RESPONSE = 2, +	SYNTH_KBD_EVENT = 3, +	SYNTH_KBD_LED_INDICATORS = 4, +}; + +/* + * Basic message structures. + */ +struct synth_kbd_msg_hdr { +	__le32 type; +}; + +struct synth_kbd_msg { +	struct synth_kbd_msg_hdr header; +	char data[]; /* Enclosed message */ +}; + +union synth_kbd_version { +	__le32 version; +}; + +/* + * Protocol messages + */ +struct synth_kbd_protocol_request { +	struct synth_kbd_msg_hdr header; +	union synth_kbd_version version_requested; +}; + +#define PROTOCOL_ACCEPTED	BIT(0) +struct synth_kbd_protocol_response { +	struct synth_kbd_msg_hdr header; +	__le32 proto_status; +}; + +#define IS_UNICODE	BIT(0) +#define IS_BREAK	BIT(1) +#define IS_E0		BIT(2) +#define IS_E1		BIT(3) +struct synth_kbd_keystroke { +	struct synth_kbd_msg_hdr header; +	__le16 make_code; +	__le16 reserved0; +	__le32 info; /* Additional information */ +}; + + +#define HK_MAXIMUM_MESSAGE_SIZE 256 + +#define KBD_VSC_SEND_RING_BUFFER_SIZE		(10 * PAGE_SIZE) +#define KBD_VSC_RECV_RING_BUFFER_SIZE		(10 * PAGE_SIZE) + +#define XTKBD_EMUL0     0xe0 +#define XTKBD_EMUL1     0xe1 +#define XTKBD_RELEASE   0x80 + + +/* + * Represents a keyboard device + */ +struct hv_kbd_dev { +	struct hv_device *hv_dev; +	struct serio *hv_serio; +	struct synth_kbd_protocol_request protocol_req; +	struct synth_kbd_protocol_response protocol_resp; +	/* Synchronize the request/response if needed */ +	struct completion wait_event; +	spinlock_t lock; /* protects 'started' field */ +	bool started; +}; + +static void hv_kbd_on_receive(struct hv_device *hv_dev, +			      struct synth_kbd_msg *msg, u32 msg_length) +{ +	struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); +	struct synth_kbd_keystroke *ks_msg; +	unsigned long flags; +	u32 msg_type = __le32_to_cpu(msg->header.type); +	u32 info; +	u16 scan_code; + +	switch (msg_type) { +	case SYNTH_KBD_PROTOCOL_RESPONSE: +		/* +		 * Validate the information provided by the host. +		 * If the host is giving us a bogus packet, +		 * drop the packet (hoping the problem +		 * goes away). +		 */ +		if (msg_length < sizeof(struct synth_kbd_protocol_response)) { +			dev_err(&hv_dev->device, +				"Illegal protocol response packet (len: %d)\n", +				msg_length); +			break; +		} + +		memcpy(&kbd_dev->protocol_resp, msg, +			sizeof(struct synth_kbd_protocol_response)); +		complete(&kbd_dev->wait_event); +		break; + +	case SYNTH_KBD_EVENT: +		/* +		 * Validate the information provided by the host. +		 * If the host is giving us a bogus packet, +		 * drop the packet (hoping the problem +		 * goes away). +		 */ +		if (msg_length < sizeof(struct  synth_kbd_keystroke)) { +			dev_err(&hv_dev->device, +				"Illegal keyboard event packet (len: %d)\n", +				msg_length); +			break; +		} + +		ks_msg = (struct synth_kbd_keystroke *)msg; +		info = __le32_to_cpu(ks_msg->info); + +		/* +		 * Inject the information through the serio interrupt. +		 */ +		spin_lock_irqsave(&kbd_dev->lock, flags); +		if (kbd_dev->started) { +			if (info & IS_E0) +				serio_interrupt(kbd_dev->hv_serio, +						XTKBD_EMUL0, 0); +			if (info & IS_E1) +				serio_interrupt(kbd_dev->hv_serio, +						XTKBD_EMUL1, 0); +			scan_code = __le16_to_cpu(ks_msg->make_code); +			if (info & IS_BREAK) +				scan_code |= XTKBD_RELEASE; + +			serio_interrupt(kbd_dev->hv_serio, scan_code, 0); +		} +		spin_unlock_irqrestore(&kbd_dev->lock, flags); +		break; + +	default: +		dev_err(&hv_dev->device, +			"unhandled message type %d\n", msg_type); +	} +} + +static void hv_kbd_handle_received_packet(struct hv_device *hv_dev, +					  struct vmpacket_descriptor *desc, +					  u32 bytes_recvd, +					  u64 req_id) +{ +	struct synth_kbd_msg *msg; +	u32 msg_sz; + +	switch (desc->type) { +	case VM_PKT_COMP: +		break; + +	case VM_PKT_DATA_INBAND: +		/* +		 * We have a packet that has "inband" data. The API used +		 * for retrieving the packet guarantees that the complete +		 * packet is read. So, minimally, we should be able to +		 * parse the payload header safely (assuming that the host +		 * can be trusted.  Trusting the host seems to be a +		 * reasonable assumption because in a virtualized +		 * environment there is not whole lot you can do if you +		 * don't trust the host. +		 * +		 * Nonetheless, let us validate if the host can be trusted +		 * (in a trivial way).  The interesting aspect of this +		 * validation is how do you recover if we discover that the +		 * host is not to be trusted? Simply dropping the packet, I +		 * don't think is an appropriate recovery.  In the interest +		 * of failing fast, it may be better to crash the guest. +		 * For now, I will just drop the packet! +		 */ + +		msg_sz = bytes_recvd - (desc->offset8 << 3); +		if (msg_sz <= sizeof(struct synth_kbd_msg_hdr)) { +			/* +			 * Drop the packet and hope +			 * the problem magically goes away. +			 */ +			dev_err(&hv_dev->device, +				"Illegal packet (type: %d, tid: %llx, size: %d)\n", +				desc->type, req_id, msg_sz); +			break; +		} + +		msg = (void *)desc + (desc->offset8 << 3); +		hv_kbd_on_receive(hv_dev, msg, msg_sz); +		break; + +	default: +		dev_err(&hv_dev->device, +			"unhandled packet type %d, tid %llx len %d\n", +			desc->type, req_id, bytes_recvd); +		break; +	} +} + +static void hv_kbd_on_channel_callback(void *context) +{ +	struct hv_device *hv_dev = context; +	void *buffer; +	int bufferlen = 0x100; /* Start with sensible size */ +	u32 bytes_recvd; +	u64 req_id; +	int error; + +	buffer = kmalloc(bufferlen, GFP_ATOMIC); +	if (!buffer) +		return; + +	while (1) { +		error = vmbus_recvpacket_raw(hv_dev->channel, buffer, bufferlen, +					     &bytes_recvd, &req_id); +		switch (error) { +		case 0: +			if (bytes_recvd == 0) { +				kfree(buffer); +				return; +			} + +			hv_kbd_handle_received_packet(hv_dev, buffer, +						      bytes_recvd, req_id); +			break; + +		case -ENOBUFS: +			kfree(buffer); +			/* Handle large packet */ +			bufferlen = bytes_recvd; +			buffer = kmalloc(bytes_recvd, GFP_ATOMIC); +			if (!buffer) +				return; +			break; +		} +	} +} + +static int hv_kbd_connect_to_vsp(struct hv_device *hv_dev) +{ +	struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); +	struct synth_kbd_protocol_request *request; +	struct synth_kbd_protocol_response *response; +	u32 proto_status; +	int error; + +	request = &kbd_dev->protocol_req; +	memset(request, 0, sizeof(struct synth_kbd_protocol_request)); +	request->header.type = __cpu_to_le32(SYNTH_KBD_PROTOCOL_REQUEST); +	request->version_requested.version = __cpu_to_le32(SYNTH_KBD_VERSION); + +	error = vmbus_sendpacket(hv_dev->channel, request, +				 sizeof(struct synth_kbd_protocol_request), +				 (unsigned long)request, +				 VM_PKT_DATA_INBAND, +				 VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); +	if (error) +		return error; + +	if (!wait_for_completion_timeout(&kbd_dev->wait_event, 10 * HZ)) +		return -ETIMEDOUT; + +	response = &kbd_dev->protocol_resp; +	proto_status = __le32_to_cpu(response->proto_status); +	if (!(proto_status & PROTOCOL_ACCEPTED)) { +		dev_err(&hv_dev->device, +			"synth_kbd protocol request failed (version %d)\n", +		        SYNTH_KBD_VERSION); +		return -ENODEV; +	} + +	return 0; +} + +static int hv_kbd_start(struct serio *serio) +{ +	struct hv_kbd_dev *kbd_dev = serio->port_data; +	unsigned long flags; + +	spin_lock_irqsave(&kbd_dev->lock, flags); +	kbd_dev->started = true; +	spin_unlock_irqrestore(&kbd_dev->lock, flags); + +	return 0; +} + +static void hv_kbd_stop(struct serio *serio) +{ +	struct hv_kbd_dev *kbd_dev = serio->port_data; +	unsigned long flags; + +	spin_lock_irqsave(&kbd_dev->lock, flags); +	kbd_dev->started = false; +	spin_unlock_irqrestore(&kbd_dev->lock, flags); +} + +static int hv_kbd_probe(struct hv_device *hv_dev, +			const struct hv_vmbus_device_id *dev_id) +{ +	struct hv_kbd_dev *kbd_dev; +	struct serio *hv_serio; +	int error; + +	kbd_dev = kzalloc(sizeof(struct hv_kbd_dev), GFP_KERNEL); +	hv_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); +	if (!kbd_dev || !hv_serio) { +		error = -ENOMEM; +		goto err_free_mem; +	} + +	kbd_dev->hv_dev = hv_dev; +	kbd_dev->hv_serio = hv_serio; +	spin_lock_init(&kbd_dev->lock); +	init_completion(&kbd_dev->wait_event); +	hv_set_drvdata(hv_dev, kbd_dev); + +	hv_serio->dev.parent  = &hv_dev->device; +	hv_serio->id.type = SERIO_8042_XL; +	hv_serio->port_data = kbd_dev; +	strlcpy(hv_serio->name, dev_name(&hv_dev->device), +		sizeof(hv_serio->name)); +	strlcpy(hv_serio->phys, dev_name(&hv_dev->device), +		sizeof(hv_serio->phys)); + +	hv_serio->start = hv_kbd_start; +	hv_serio->stop = hv_kbd_stop; + +	error = vmbus_open(hv_dev->channel, +			   KBD_VSC_SEND_RING_BUFFER_SIZE, +			   KBD_VSC_RECV_RING_BUFFER_SIZE, +			   NULL, 0, +			   hv_kbd_on_channel_callback, +			   hv_dev); +	if (error) +		goto err_free_mem; + +	error = hv_kbd_connect_to_vsp(hv_dev); +	if (error) +		goto err_close_vmbus; + +	serio_register_port(kbd_dev->hv_serio); +	return 0; + +err_close_vmbus: +	vmbus_close(hv_dev->channel); +err_free_mem: +	kfree(hv_serio); +	kfree(kbd_dev); +	return error; +} + +static int hv_kbd_remove(struct hv_device *hv_dev) +{ +	struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); + +	serio_unregister_port(kbd_dev->hv_serio); +	vmbus_close(hv_dev->channel); +	kfree(kbd_dev); + +	hv_set_drvdata(hv_dev, NULL); + +	return 0; +} + +/* + * Keyboard GUID + * {f912ad6d-2b17-48ea-bd65-f927a61c7684} + */ +#define HV_KBD_GUID \ +	.guid = { \ +			0x6d, 0xad, 0x12, 0xf9, 0x17, 0x2b, 0xea, 0x48, \ +			0xbd, 0x65, 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84 \ +	} + +static const struct hv_vmbus_device_id id_table[] = { +	/* Keyboard guid */ +	{ HV_KBD_GUID, }, +	{ }, +}; + +MODULE_DEVICE_TABLE(vmbus, id_table); + +static struct  hv_driver hv_kbd_drv = { +	.name = KBUILD_MODNAME, +	.id_table = id_table, +	.probe = hv_kbd_probe, +	.remove = hv_kbd_remove, +}; + +static int __init hv_kbd_init(void) +{ +	return vmbus_driver_register(&hv_kbd_drv); +} + +static void __exit hv_kbd_exit(void) +{ +	vmbus_driver_unregister(&hv_kbd_drv); +} + +MODULE_LICENSE("GPL"); +module_init(hv_kbd_init); +module_exit(hv_kbd_exit); diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 5f306f79da0..136b7b204f5 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -402,6 +402,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {  		},  	},  	{ +		/* Acer Aspire 5710 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "Acer"), +			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710"), +		}, +	}, +	{  		/* Gericom Bellagio */  		.matches = {  			DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), @@ -702,6 +709,17 @@ static int i8042_pnp_aux_irq;  static char i8042_pnp_kbd_name[32];  static char i8042_pnp_aux_name[32]; +static void i8042_pnp_id_to_string(struct pnp_id *id, char *dst, int dst_size) +{ +	strlcpy(dst, "PNP:", dst_size); + +	while (id) { +		strlcat(dst, " ", dst_size); +		strlcat(dst, id->id, dst_size); +		id = id->next; +	} +} +  static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *did)  {  	if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1) @@ -718,6 +736,8 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *  		strlcat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name));  		strlcat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name));  	} +	i8042_pnp_id_to_string(dev->id, i8042_kbd_firmware_id, +			       sizeof(i8042_kbd_firmware_id));  	/* Keyboard ports are always supposed to be wakeup-enabled */  	device_set_wakeup_enable(&dev->dev, true); @@ -742,6 +762,8 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id *  		strlcat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name));  		strlcat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name));  	} +	i8042_pnp_id_to_string(dev->id, i8042_aux_firmware_id, +			       sizeof(i8042_aux_firmware_id));  	i8042_pnp_aux_devices++;  	return 0; @@ -765,6 +787,7 @@ static struct pnp_device_id pnp_kbd_devids[] = {  	{ .id = "CPQA0D7", .driver_data = 0 },  	{ .id = "", },  }; +MODULE_DEVICE_TABLE(pnp, pnp_kbd_devids);  static struct pnp_driver i8042_pnp_kbd_driver = {  	.name           = "i8042 kbd", @@ -786,6 +809,7 @@ static struct pnp_device_id pnp_aux_devids[] = {  	{ .id = "SYN0801", .driver_data = 0 },  	{ .id = "", },  }; +MODULE_DEVICE_TABLE(pnp, pnp_aux_devids);  static struct pnp_driver i8042_pnp_aux_driver = {  	.name           = "i8042 aux", diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 78e4de42efa..3807c3e971c 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -87,6 +87,8 @@ MODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off");  #endif  static bool i8042_bypass_aux_irq_test; +static char i8042_kbd_firmware_id[128]; +static char i8042_aux_firmware_id[128];  #include "i8042.h" @@ -223,21 +225,26 @@ static int i8042_flush(void)  {  	unsigned long flags;  	unsigned char data, str; -	int i = 0; +	int count = 0; +	int retval = 0;  	spin_lock_irqsave(&i8042_lock, flags); -	while (((str = i8042_read_status()) & I8042_STR_OBF) && (i < I8042_BUFFER_SIZE)) { -		udelay(50); -		data = i8042_read_data(); -		i++; -		dbg("%02x <- i8042 (flush, %s)\n", -		    data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); +	while ((str = i8042_read_status()) & I8042_STR_OBF) { +		if (count++ < I8042_BUFFER_SIZE) { +			udelay(50); +			data = i8042_read_data(); +			dbg("%02x <- i8042 (flush, %s)\n", +			    data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); +		} else { +			retval = -EIO; +			break; +		}  	}  	spin_unlock_irqrestore(&i8042_lock, flags); -	return i; +	return retval;  }  /* @@ -849,7 +856,7 @@ static int __init i8042_check_aux(void)  static int i8042_controller_check(void)  { -	if (i8042_flush() == I8042_BUFFER_SIZE) { +	if (i8042_flush()) {  		pr_err("No controller found\n");  		return -ENODEV;  	} @@ -1031,7 +1038,7 @@ static void i8042_controller_reset(bool force_reset)  /*   * i8042_panic_blink() will turn the keyboard LEDs on or off 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" + * not see the console and will help distinguishing panics from "real"   * lockups.   *   * Note that DELAY has a limit of 10ms so we will not get stuck here @@ -1213,6 +1220,8 @@ static int __init i8042_create_kbd_port(void)  	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)); +	strlcpy(serio->firmware_id, i8042_kbd_firmware_id, +		sizeof(serio->firmware_id));  	port->serio = serio;  	port->irq = I8042_KBD_IRQ; @@ -1239,6 +1248,8 @@ static int __init i8042_create_aux_port(int idx)  	if (idx < 0) {  		strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name));  		strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); +		strlcpy(serio->firmware_id, i8042_aux_firmware_id, +			sizeof(serio->firmware_id));  		serio->close = i8042_port_close;  	} else {  		snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx); diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 07a8363f3c5..75516996db2 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -18,7 +18,6 @@  #include <linux/input.h>  #include <linux/serio.h>  #include <linux/i8042.h> -#include <linux/init.h>  #include <linux/libps2.h>  #define DRIVER_DESC	"PS/2 driver library" diff --git a/drivers/input/serio/olpc_apsp.c b/drivers/input/serio/olpc_apsp.c index 51b1d40cc28..d906f3ebc8c 100644 --- a/drivers/input/serio/olpc_apsp.c +++ b/drivers/input/serio/olpc_apsp.c @@ -16,7 +16,6 @@  #include <linux/module.h>  #include <linux/interrupt.h> -#include <linux/init.h>  #include <linux/serio.h>  #include <linux/err.h>  #include <linux/platform_device.h> @@ -263,7 +262,7 @@ static int olpc_apsp_remove(struct platform_device *pdev)  	return 0;  } -static struct of_device_id olpc_apsp_dt_ids[] = { +static const struct of_device_id olpc_apsp_dt_ids[] = {  	{ .compatible = "olpc,ap-sp", },  	{}  }; diff --git a/drivers/input/serio/pcips2.c b/drivers/input/serio/pcips2.c index 76f83836fd5..e862c6ea9d9 100644 --- a/drivers/input/serio/pcips2.c +++ b/drivers/input/serio/pcips2.c @@ -16,7 +16,6 @@  #include <linux/input.h>  #include <linux/pci.h>  #include <linux/slab.h> -#include <linux/init.h>  #include <linux/serio.h>  #include <linux/delay.h>  #include <asm/io.h> @@ -181,7 +180,6 @@ static void pcips2_remove(struct pci_dev *dev)  	struct pcips2_data *ps2if = pci_get_drvdata(dev);  	serio_unregister_port(ps2if->io); -	pci_set_drvdata(dev, NULL);  	kfree(ps2if);  	pci_release_regions(dev);  	pci_disable_device(dev); diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index 7a65a1bc522..594256c3855 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -30,7 +30,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/serio.h>  #include <linux/interrupt.h>  #include <linux/err.h> diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index 567566ae0da..e462e7791bb 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -29,7 +29,6 @@  #include <linux/module.h>  #include <linux/interrupt.h> -#include <linux/init.h>  #include <linux/serio.h>  #include <linux/err.h>  #include <linux/platform_device.h> diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 2b56855c2c7..b29134de983 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -365,7 +365,7 @@ static ssize_t serio_show_description(struct device *dev, struct device_attribut  	return sprintf(buf, "%s\n", serio->name);  } -static ssize_t serio_show_modalias(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)  {  	struct serio *serio = to_serio_port(dev); @@ -373,54 +373,31 @@ static ssize_t serio_show_modalias(struct device *dev, struct device_attribute *  			serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);  } -static ssize_t serio_show_id_type(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf)  {  	struct serio *serio = to_serio_port(dev);  	return sprintf(buf, "%02x\n", serio->id.type);  } -static ssize_t serio_show_id_proto(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t proto_show(struct device *dev, struct device_attribute *attr, char *buf)  {  	struct serio *serio = to_serio_port(dev);  	return sprintf(buf, "%02x\n", serio->id.proto);  } -static ssize_t serio_show_id_id(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf)  {  	struct serio *serio = to_serio_port(dev);  	return sprintf(buf, "%02x\n", serio->id.id);  } -static ssize_t serio_show_id_extra(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t extra_show(struct device *dev, struct device_attribute *attr, char *buf)  {  	struct serio *serio = to_serio_port(dev);  	return sprintf(buf, "%02x\n", serio->id.extra);  } -static DEVICE_ATTR(type, S_IRUGO, serio_show_id_type, NULL); -static DEVICE_ATTR(proto, S_IRUGO, serio_show_id_proto, NULL); -static DEVICE_ATTR(id, S_IRUGO, serio_show_id_id, NULL); -static DEVICE_ATTR(extra, S_IRUGO, serio_show_id_extra, NULL); - -static struct attribute *serio_device_id_attrs[] = { -	&dev_attr_type.attr, -	&dev_attr_proto.attr, -	&dev_attr_id.attr, -	&dev_attr_extra.attr, -	NULL -}; - -static struct attribute_group serio_id_attr_group = { -	.name	= "id", -	.attrs	= serio_device_id_attrs, -}; - -static const struct attribute_group *serio_device_attr_groups[] = { -	&serio_id_attr_group, -	NULL -}; - -static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t drvctl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)  {  	struct serio *serio = to_serio_port(dev);  	struct device_driver *drv; @@ -474,14 +451,55 @@ static ssize_t serio_set_bind_mode(struct device *dev, struct device_attribute *  	return retval;  } -static struct device_attribute serio_device_attrs[] = { -	__ATTR(description, S_IRUGO, serio_show_description, NULL), -	__ATTR(modalias, S_IRUGO, serio_show_modalias, NULL), -	__ATTR(drvctl, S_IWUSR, NULL, serio_rebind_driver), -	__ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode), -	__ATTR_NULL +static ssize_t firmware_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ +	struct serio *serio = to_serio_port(dev); + +	return sprintf(buf, "%s\n", serio->firmware_id); +} + +static DEVICE_ATTR_RO(type); +static DEVICE_ATTR_RO(proto); +static DEVICE_ATTR_RO(id); +static DEVICE_ATTR_RO(extra); + +static struct attribute *serio_device_id_attrs[] = { +	&dev_attr_type.attr, +	&dev_attr_proto.attr, +	&dev_attr_id.attr, +	&dev_attr_extra.attr, +	NULL  }; +static struct attribute_group serio_id_attr_group = { +	.name	= "id", +	.attrs	= serio_device_id_attrs, +}; + +static DEVICE_ATTR_RO(modalias); +static DEVICE_ATTR_WO(drvctl); +static DEVICE_ATTR(description, S_IRUGO, serio_show_description, NULL); +static DEVICE_ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode); +static DEVICE_ATTR_RO(firmware_id); + +static struct attribute *serio_device_attrs[] = { +	&dev_attr_modalias.attr, +	&dev_attr_description.attr, +	&dev_attr_drvctl.attr, +	&dev_attr_bind_mode.attr, +	&dev_attr_firmware_id.attr, +	NULL +}; + +static struct attribute_group serio_device_attr_group = { +	.attrs	= serio_device_attrs, +}; + +static const struct attribute_group *serio_device_attr_groups[] = { +	&serio_id_attr_group, +	&serio_device_attr_group, +	NULL +};  static void serio_release_port(struct device *dev)  { @@ -912,9 +930,14 @@ static int serio_uevent(struct device *dev, struct kobj_uevent_env *env)  	SERIO_ADD_UEVENT_VAR("SERIO_PROTO=%02x", serio->id.proto);  	SERIO_ADD_UEVENT_VAR("SERIO_ID=%02x", serio->id.id);  	SERIO_ADD_UEVENT_VAR("SERIO_EXTRA=%02x", serio->id.extra); +  	SERIO_ADD_UEVENT_VAR("MODALIAS=serio:ty%02Xpr%02Xid%02Xex%02X",  				serio->id.type, serio->id.proto, serio->id.id, serio->id.extra); +	if (serio->firmware_id[0]) +		SERIO_ADD_UEVENT_VAR("SERIO_FIRMWARE_ID=%s", +				     serio->firmware_id); +  	return 0;  }  #undef SERIO_ADD_UEVENT_VAR @@ -996,7 +1019,6 @@ EXPORT_SYMBOL(serio_interrupt);  static struct bus_type serio_bus = {  	.name		= "serio", -	.dev_attrs	= serio_device_attrs,  	.drv_groups	= serio_driver_groups,  	.match		= serio_bus_match,  	.uevent		= serio_uevent, diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index 59df2e7317a..c9a02fe5757 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -15,7 +15,6 @@  #include <linux/poll.h>  #include <linux/module.h>  #include <linux/serio.h> -#include <linux/init.h>  #include <linux/major.h>  #include <linux/device.h>  #include <linux/miscdevice.h> diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 8755f5f3ad3..0cb7ef59071 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -124,7 +124,7 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c  {  	struct serport *serport = (struct serport*) tty->disc_data;  	unsigned long flags; -	unsigned int ch_flags; +	unsigned int ch_flags = 0;  	int i;  	spin_lock_irqsave(&serport->lock, flags); @@ -133,18 +133,20 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c  		goto out;  	for (i = 0; i < count; i++) { -		switch (fp[i]) { -		case TTY_FRAME: -			ch_flags = SERIO_FRAME; -			break; - -		case TTY_PARITY: -			ch_flags = SERIO_PARITY; -			break; - -		default: -			ch_flags = 0; -			break; +		if (fp) { +			switch (fp[i]) { +			case TTY_FRAME: +				ch_flags = SERIO_FRAME; +				break; + +			case TTY_PARITY: +				ch_flags = SERIO_PARITY; +				break; + +			default: +				ch_flags = 0; +				break; +			}  		}  		serio_interrupt(serport->serio, cp[i], ch_flags); diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c index 4b7662a17ae..e6cf52ebad8 100644 --- a/drivers/input/serio/xilinx_ps2.c +++ b/drivers/input/serio/xilinx_ps2.c @@ -20,11 +20,11 @@  #include <linux/interrupt.h>  #include <linux/errno.h>  #include <linux/slab.h> -#include <linux/init.h>  #include <linux/list.h>  #include <linux/io.h>  #include <linux/of_address.h>  #include <linux/of_device.h> +#include <linux/of_irq.h>  #include <linux/of_platform.h>  #define DRIVER_NAME		"xilinx_ps2" @@ -235,12 +235,12 @@ static void sxps2_close(struct serio *pserio)   */  static int xps2_of_probe(struct platform_device *ofdev)  { -	struct resource r_irq; /* Interrupt resources */  	struct resource r_mem; /* IO mem resources */  	struct xps2data *drvdata;  	struct serio *serio;  	struct device *dev = &ofdev->dev;  	resource_size_t remap_size, phys_addr; +	unsigned int irq;  	int error;  	dev_info(dev, "Device Tree Probing \'%s\'\n", @@ -254,7 +254,8 @@ static int xps2_of_probe(struct platform_device *ofdev)  	}  	/* Get IRQ for the device */ -	if (!of_irq_to_resource(ofdev->dev.of_node, 0, &r_irq)) { +	irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); +	if (!irq) {  		dev_err(dev, "no IRQ found\n");  		return -ENODEV;  	} @@ -267,7 +268,7 @@ static int xps2_of_probe(struct platform_device *ofdev)  	}  	spin_lock_init(&drvdata->lock); -	drvdata->irq = r_irq.start; +	drvdata->irq = irq;  	drvdata->serio = serio;  	drvdata->dev = dev;  | 
