| Terry Lambert | f6a0460 | 2011-10-14 17:18:54 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * HID driver for primax and similar keyboards with in-band modifiers | 
 | 3 |  * | 
 | 4 |  * Copyright 2011 Google Inc. All Rights Reserved | 
 | 5 |  * | 
 | 6 |  * Author: | 
 | 7 |  *	Terry Lambert <tlambert@google.com> | 
 | 8 |  * | 
 | 9 |  * This software is licensed under the terms of the GNU General Public | 
 | 10 |  * License version 2, as published by the Free Software Foundation, and | 
 | 11 |  * may be copied, distributed, and modified under those terms. | 
 | 12 |  * | 
 | 13 |  * This program is distributed in the hope that it will be useful, | 
 | 14 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 15 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 16 |  * GNU General Public License for more details. | 
 | 17 |  */ | 
 | 18 |  | 
 | 19 | #include <linux/device.h> | 
 | 20 | #include <linux/hid.h> | 
 | 21 | #include <linux/module.h> | 
 | 22 |  | 
 | 23 | #include "hid-ids.h" | 
 | 24 |  | 
 | 25 | static int px_raw_event(struct hid_device *hid, struct hid_report *report, | 
 | 26 | 	 u8 *data, int size) | 
 | 27 | { | 
 | 28 | 	int idx = size; | 
 | 29 |  | 
 | 30 | 	switch (report->id) { | 
 | 31 | 	case 0:		/* keyboard input */ | 
 | 32 | 		/* | 
 | 33 | 		 * Convert in-band modifier key values into out of band | 
 | 34 | 		 * modifier bits and pull the key strokes from the report. | 
 | 35 | 		 * Thus a report data set which looked like: | 
 | 36 | 		 * | 
 | 37 | 		 * [00][00][E0][30][00][00][00][00] | 
 | 38 | 		 * (no modifier bits + "Left Shift" key + "1" key) | 
 | 39 | 		 * | 
 | 40 | 		 * Would be converted to: | 
 | 41 | 		 * | 
 | 42 | 		 * [01][00][00][30][00][00][00][00] | 
 | 43 | 		 * (Left Shift modifier bit + "1" key) | 
 | 44 | 		 * | 
 | 45 | 		 * As long as it's in the size range, the upper level | 
 | 46 | 		 * drivers don't particularly care if there are in-band | 
 | 47 | 		 * 0-valued keys, so they don't stop parsing. | 
 | 48 | 		 */ | 
 | 49 | 		while (--idx > 1) { | 
 | 50 | 			if (data[idx] < 0xE0 || data[idx] > 0xE7) | 
 | 51 | 				continue; | 
 | 52 | 			data[0] |= (1 << (data[idx] - 0xE0)); | 
 | 53 | 			data[idx] = 0; | 
 | 54 | 		} | 
 | 55 | 		hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, 0); | 
 | 56 | 		return 1; | 
 | 57 |  | 
 | 58 | 	default:	/* unknown report */ | 
 | 59 | 		/* Unknown report type; pass upstream */ | 
 | 60 | 		hid_info(hid, "unknown report type %d\n", report->id); | 
 | 61 | 		break; | 
 | 62 | 	} | 
 | 63 |  | 
 | 64 | 	return 0; | 
 | 65 | } | 
 | 66 |  | 
 | 67 | static int px_probe(struct hid_device *hid, const struct hid_device_id *id) | 
 | 68 | { | 
 | 69 | 	int ret; | 
 | 70 |  | 
 | 71 | 	ret = hid_parse(hid); | 
 | 72 | 	if (ret) { | 
 | 73 | 		hid_err(hid, "parse failed\n"); | 
 | 74 | 		goto fail; | 
 | 75 | 	} | 
 | 76 |  | 
 | 77 | 	ret = hid_hw_start(hid, HID_CONNECT_DEFAULT); | 
 | 78 | 	if (ret) | 
 | 79 | 		hid_err(hid, "hw start failed\n"); | 
 | 80 |  | 
 | 81 | fail: | 
 | 82 | 	return ret; | 
 | 83 | } | 
 | 84 |  | 
 | 85 | static void px_remove(struct hid_device *hid) | 
 | 86 | { | 
 | 87 | 	hid_hw_stop(hid); | 
 | 88 | } | 
 | 89 |  | 
 | 90 | static const struct hid_device_id px_devices[] = { | 
 | 91 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, | 
 | 92 | 	{ } | 
 | 93 | }; | 
 | 94 | MODULE_DEVICE_TABLE(hid, px_devices); | 
 | 95 |  | 
 | 96 | static struct hid_driver px_driver = { | 
 | 97 | 	.name = "primax", | 
 | 98 | 	.id_table = px_devices, | 
 | 99 | 	.raw_event = px_raw_event, | 
 | 100 | 	.probe = px_probe, | 
 | 101 | 	.remove = px_remove, | 
 | 102 | }; | 
 | 103 |  | 
 | 104 | static int __init px_init(void) | 
 | 105 | { | 
 | 106 | 	return hid_register_driver(&px_driver); | 
 | 107 | } | 
 | 108 |  | 
 | 109 | static void __exit px_exit(void) | 
 | 110 | { | 
 | 111 | 	hid_unregister_driver(&px_driver); | 
 | 112 | } | 
 | 113 |  | 
 | 114 | module_init(px_init); | 
 | 115 | module_exit(px_exit); | 
 | 116 | MODULE_AUTHOR("Terry Lambert <tlambert@google.com>"); | 
 | 117 | MODULE_LICENSE("GPL"); |