diff options
author | malattia@linux.it <malattia@linux.it> | 2007-04-09 04:19:08 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2007-04-10 16:01:19 -0400 |
commit | 1549ee6fb122400c0767b5f3da2c42abbc4f750a (patch) | |
tree | e7c3b3c19f34e42a9bff6d7e36f66175904cfd01 /drivers/misc | |
parent | 33a04454527edd33d4a6332a2944d2b4f46fbb18 (diff) |
sony-laptop: Unify the input subsystem event forwarding
SNC and SPIC events are forwarded to the same input devices
and are thus handled together.
Signed-off-by: Mattia Dongili <malattia@linux.it>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/sony-laptop.c | 541 |
1 files changed, 276 insertions, 265 deletions
diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index b797c8cb47df..cf8d7927dc5c 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c | |||
@@ -97,18 +97,266 @@ MODULE_PARM_DESC(no_spic, | |||
97 | static int compat; /* = 0 */ | 97 | static int compat; /* = 0 */ |
98 | module_param(compat, int, 0444); | 98 | module_param(compat, int, 0444); |
99 | MODULE_PARM_DESC(compat, | 99 | MODULE_PARM_DESC(compat, |
100 | "set this if you want to enable backward compatibility mode"); | 100 | "set this if you want to enable backward compatibility mode for SPIC"); |
101 | |||
102 | static int force_jog; /* = 0 */ | ||
103 | module_param(force_jog, int, 0444); | ||
104 | MODULE_PARM_DESC(force_jog, | ||
105 | "set this if the driver doesn't detect your jogdial"); | ||
106 | 101 | ||
107 | static unsigned long mask = 0xffffffff; | 102 | static unsigned long mask = 0xffffffff; |
108 | module_param(mask, ulong, 0644); | 103 | module_param(mask, ulong, 0644); |
109 | MODULE_PARM_DESC(mask, | 104 | MODULE_PARM_DESC(mask, |
110 | "set this to the mask of event you want to enable (see doc)"); | 105 | "set this to the mask of event you want to enable (see doc)"); |
111 | 106 | ||
107 | /*********** Input Devices ***********/ | ||
108 | |||
109 | #define SONY_LAPTOP_BUF_SIZE 128 | ||
110 | struct sony_laptop_input_s { | ||
111 | atomic_t users; | ||
112 | struct input_dev *jog_dev; | ||
113 | struct input_dev *key_dev; | ||
114 | struct kfifo *fifo; | ||
115 | spinlock_t fifo_lock; | ||
116 | struct workqueue_struct *wq; | ||
117 | }; | ||
118 | static struct sony_laptop_input_s sony_laptop_input = { | ||
119 | .users = ATOMIC_INIT(0), | ||
120 | }; | ||
121 | |||
122 | struct sony_laptop_keypress { | ||
123 | struct input_dev *dev; | ||
124 | int key; | ||
125 | }; | ||
126 | |||
127 | /* Correspondance table between sonypi events and input layer events */ | ||
128 | static struct { | ||
129 | int sonypiev; | ||
130 | int inputev; | ||
131 | } sony_laptop_inputkeys[] = { | ||
132 | { SONYPI_EVENT_CAPTURE_PRESSED, KEY_CAMERA }, | ||
133 | { SONYPI_EVENT_FNKEY_ONLY, KEY_FN }, | ||
134 | { SONYPI_EVENT_FNKEY_ESC, KEY_FN_ESC }, | ||
135 | { SONYPI_EVENT_FNKEY_F1, KEY_FN_F1 }, | ||
136 | { SONYPI_EVENT_FNKEY_F2, KEY_FN_F2 }, | ||
137 | { SONYPI_EVENT_FNKEY_F3, KEY_FN_F3 }, | ||
138 | { SONYPI_EVENT_FNKEY_F4, KEY_FN_F4 }, | ||
139 | { SONYPI_EVENT_FNKEY_F5, KEY_FN_F5 }, | ||
140 | { SONYPI_EVENT_FNKEY_F6, KEY_FN_F6 }, | ||
141 | { SONYPI_EVENT_FNKEY_F7, KEY_FN_F7 }, | ||
142 | { SONYPI_EVENT_FNKEY_F8, KEY_FN_F8 }, | ||
143 | { SONYPI_EVENT_FNKEY_F9, KEY_FN_F9 }, | ||
144 | { SONYPI_EVENT_FNKEY_F10, KEY_FN_F10 }, | ||
145 | { SONYPI_EVENT_FNKEY_F11, KEY_FN_F11 }, | ||
146 | { SONYPI_EVENT_FNKEY_F12, KEY_FN_F12 }, | ||
147 | { SONYPI_EVENT_FNKEY_1, KEY_FN_1 }, | ||
148 | { SONYPI_EVENT_FNKEY_2, KEY_FN_2 }, | ||
149 | { SONYPI_EVENT_FNKEY_D, KEY_FN_D }, | ||
150 | { SONYPI_EVENT_FNKEY_E, KEY_FN_E }, | ||
151 | { SONYPI_EVENT_FNKEY_F, KEY_FN_F }, | ||
152 | { SONYPI_EVENT_FNKEY_S, KEY_FN_S }, | ||
153 | { SONYPI_EVENT_FNKEY_B, KEY_FN_B }, | ||
154 | { SONYPI_EVENT_BLUETOOTH_PRESSED, KEY_BLUE }, | ||
155 | { SONYPI_EVENT_BLUETOOTH_ON, KEY_BLUE }, | ||
156 | { SONYPI_EVENT_PKEY_P1, KEY_PROG1 }, | ||
157 | { SONYPI_EVENT_PKEY_P2, KEY_PROG2 }, | ||
158 | { SONYPI_EVENT_PKEY_P3, KEY_PROG3 }, | ||
159 | { SONYPI_EVENT_BACK_PRESSED, KEY_BACK }, | ||
160 | { SONYPI_EVENT_HELP_PRESSED, KEY_HELP }, | ||
161 | { SONYPI_EVENT_ZOOM_PRESSED, KEY_ZOOM }, | ||
162 | { SONYPI_EVENT_THUMBPHRASE_PRESSED, BTN_THUMB }, | ||
163 | { 0, 0 }, | ||
164 | }; | ||
165 | |||
166 | /* release buttons after a short delay if pressed */ | ||
167 | static void do_sony_laptop_release_key(struct work_struct *work) | ||
168 | { | ||
169 | struct sony_laptop_keypress kp; | ||
170 | |||
171 | while (kfifo_get(sony_laptop_input.fifo, (unsigned char *)&kp, | ||
172 | sizeof(kp)) == sizeof(kp)) { | ||
173 | msleep(10); | ||
174 | input_report_key(kp.dev, kp.key, 0); | ||
175 | input_sync(kp.dev); | ||
176 | } | ||
177 | } | ||
178 | static DECLARE_WORK(sony_laptop_release_key_work, | ||
179 | do_sony_laptop_release_key); | ||
180 | |||
181 | /* forward event to the input subsytem */ | ||
182 | static void sony_laptop_report_input_event(u8 event) | ||
183 | { | ||
184 | struct input_dev *jog_dev = sony_laptop_input.jog_dev; | ||
185 | struct input_dev *key_dev = sony_laptop_input.key_dev; | ||
186 | struct sony_laptop_keypress kp = { NULL }; | ||
187 | int i; | ||
188 | |||
189 | if (event == SONYPI_EVENT_FNKEY_RELEASED) { | ||
190 | /* Nothing, not all VAIOs generate this event */ | ||
191 | return; | ||
192 | } | ||
193 | |||
194 | /* report events */ | ||
195 | switch (event) { | ||
196 | /* jog_dev events */ | ||
197 | case SONYPI_EVENT_JOGDIAL_UP: | ||
198 | case SONYPI_EVENT_JOGDIAL_UP_PRESSED: | ||
199 | input_report_rel(jog_dev, REL_WHEEL, 1); | ||
200 | input_sync(jog_dev); | ||
201 | return; | ||
202 | |||
203 | case SONYPI_EVENT_JOGDIAL_DOWN: | ||
204 | case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED: | ||
205 | input_report_rel(jog_dev, REL_WHEEL, -1); | ||
206 | input_sync(jog_dev); | ||
207 | return; | ||
208 | |||
209 | /* key_dev events */ | ||
210 | case SONYPI_EVENT_JOGDIAL_PRESSED: | ||
211 | kp.key = BTN_MIDDLE; | ||
212 | kp.dev = jog_dev; | ||
213 | break; | ||
214 | |||
215 | default: | ||
216 | for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++) | ||
217 | if (event == sony_laptop_inputkeys[i].sonypiev) { | ||
218 | kp.dev = key_dev; | ||
219 | kp.key = sony_laptop_inputkeys[i].inputev; | ||
220 | break; | ||
221 | } | ||
222 | break; | ||
223 | } | ||
224 | |||
225 | if (kp.dev) { | ||
226 | input_report_key(kp.dev, kp.key, 1); | ||
227 | input_sync(kp.dev); | ||
228 | kfifo_put(sony_laptop_input.fifo, | ||
229 | (unsigned char *)&kp, sizeof(kp)); | ||
230 | |||
231 | if (!work_pending(&sony_laptop_release_key_work)) | ||
232 | queue_work(sony_laptop_input.wq, | ||
233 | &sony_laptop_release_key_work); | ||
234 | } else | ||
235 | dprintk("unknown input event %.2x\n", event); | ||
236 | } | ||
237 | |||
238 | static int sony_laptop_setup_input(void) | ||
239 | { | ||
240 | struct input_dev *jog_dev; | ||
241 | struct input_dev *key_dev; | ||
242 | int i; | ||
243 | int error; | ||
244 | |||
245 | /* don't run again if already initialized */ | ||
246 | if (atomic_add_return(1, &sony_laptop_input.users) > 1) | ||
247 | return 0; | ||
248 | |||
249 | /* kfifo */ | ||
250 | spin_lock_init(&sony_laptop_input.fifo_lock); | ||
251 | sony_laptop_input.fifo = | ||
252 | kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL, | ||
253 | &sony_laptop_input.fifo_lock); | ||
254 | if (IS_ERR(sony_laptop_input.fifo)) { | ||
255 | printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); | ||
256 | error = PTR_ERR(sony_laptop_input.fifo); | ||
257 | goto err_dec_users; | ||
258 | } | ||
259 | |||
260 | /* init workqueue */ | ||
261 | sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop"); | ||
262 | if (!sony_laptop_input.wq) { | ||
263 | printk(KERN_ERR DRV_PFX | ||
264 | "Unabe to create workqueue.\n"); | ||
265 | error = -ENXIO; | ||
266 | goto err_free_kfifo; | ||
267 | } | ||
268 | |||
269 | /* input keys */ | ||
270 | key_dev = input_allocate_device(); | ||
271 | if (!key_dev) { | ||
272 | error = -ENOMEM; | ||
273 | goto err_destroy_wq; | ||
274 | } | ||
275 | |||
276 | key_dev->name = "Sony Vaio Keys"; | ||
277 | key_dev->id.bustype = BUS_ISA; | ||
278 | key_dev->id.vendor = PCI_VENDOR_ID_SONY; | ||
279 | |||
280 | /* Initialize the Input Drivers: special keys */ | ||
281 | key_dev->evbit[0] = BIT(EV_KEY); | ||
282 | for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++) | ||
283 | if (sony_laptop_inputkeys[i].inputev) | ||
284 | set_bit(sony_laptop_inputkeys[i].inputev, | ||
285 | key_dev->keybit); | ||
286 | |||
287 | error = input_register_device(key_dev); | ||
288 | if (error) | ||
289 | goto err_free_keydev; | ||
290 | |||
291 | sony_laptop_input.key_dev = key_dev; | ||
292 | |||
293 | /* jogdial */ | ||
294 | jog_dev = input_allocate_device(); | ||
295 | if (!jog_dev) { | ||
296 | error = -ENOMEM; | ||
297 | goto err_unregister_keydev; | ||
298 | } | ||
299 | |||
300 | jog_dev->name = "Sony Vaio Jogdial"; | ||
301 | jog_dev->id.bustype = BUS_ISA; | ||
302 | jog_dev->id.vendor = PCI_VENDOR_ID_SONY; | ||
303 | |||
304 | jog_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); | ||
305 | jog_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_MIDDLE); | ||
306 | jog_dev->relbit[0] = BIT(REL_WHEEL); | ||
307 | |||
308 | error = input_register_device(jog_dev); | ||
309 | if (error) | ||
310 | goto err_free_jogdev; | ||
311 | |||
312 | sony_laptop_input.jog_dev = jog_dev; | ||
313 | |||
314 | return 0; | ||
315 | |||
316 | err_free_jogdev: | ||
317 | input_free_device(jog_dev); | ||
318 | |||
319 | err_unregister_keydev: | ||
320 | input_unregister_device(key_dev); | ||
321 | /* to avoid kref underflow below at input_free_device */ | ||
322 | key_dev = NULL; | ||
323 | |||
324 | err_free_keydev: | ||
325 | input_free_device(key_dev); | ||
326 | |||
327 | err_destroy_wq: | ||
328 | destroy_workqueue(sony_laptop_input.wq); | ||
329 | |||
330 | err_free_kfifo: | ||
331 | kfifo_free(sony_laptop_input.fifo); | ||
332 | |||
333 | err_dec_users: | ||
334 | atomic_dec(&sony_laptop_input.users); | ||
335 | return error; | ||
336 | } | ||
337 | |||
338 | static void sony_laptop_remove_input(void) | ||
339 | { | ||
340 | /* cleanup only after the last user has gone */ | ||
341 | if (!atomic_dec_and_test(&sony_laptop_input.users)) | ||
342 | return; | ||
343 | |||
344 | /* flush workqueue first */ | ||
345 | flush_workqueue(sony_laptop_input.wq); | ||
346 | |||
347 | /* destroy input devs */ | ||
348 | input_unregister_device(sony_laptop_input.key_dev); | ||
349 | sony_laptop_input.key_dev = NULL; | ||
350 | |||
351 | if (sony_laptop_input.jog_dev) { | ||
352 | input_unregister_device(sony_laptop_input.jog_dev); | ||
353 | sony_laptop_input.jog_dev = NULL; | ||
354 | } | ||
355 | |||
356 | destroy_workqueue(sony_laptop_input.wq); | ||
357 | kfifo_free(sony_laptop_input.fifo); | ||
358 | } | ||
359 | |||
112 | /*********** Platform Device ***********/ | 360 | /*********** Platform Device ***********/ |
113 | 361 | ||
114 | static atomic_t sony_pf_users = ATOMIC_INIT(0); | 362 | static atomic_t sony_pf_users = ATOMIC_INIT(0); |
@@ -428,6 +676,7 @@ static struct backlight_ops sony_backlight_ops = { | |||
428 | static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) | 676 | static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) |
429 | { | 677 | { |
430 | dprintk("sony_acpi_notify, event: %d\n", event); | 678 | dprintk("sony_acpi_notify, event: %d\n", event); |
679 | sony_laptop_report_input_event(event); | ||
431 | acpi_bus_generate_event(sony_nc_acpi_device, 1, event); | 680 | acpi_bus_generate_event(sony_nc_acpi_device, 1, event); |
432 | } | 681 | } |
433 | 682 | ||
@@ -490,13 +739,21 @@ static int sony_nc_add(struct acpi_device *device) | |||
490 | } | 739 | } |
491 | } | 740 | } |
492 | 741 | ||
742 | /* setup input devices and helper fifo */ | ||
743 | result = sony_laptop_setup_input(); | ||
744 | if (result) { | ||
745 | printk(KERN_ERR DRV_PFX | ||
746 | "Unabe to create input devices.\n"); | ||
747 | goto outwalk; | ||
748 | } | ||
749 | |||
493 | status = acpi_install_notify_handler(sony_nc_acpi_handle, | 750 | status = acpi_install_notify_handler(sony_nc_acpi_handle, |
494 | ACPI_DEVICE_NOTIFY, | 751 | ACPI_DEVICE_NOTIFY, |
495 | sony_acpi_notify, NULL); | 752 | sony_acpi_notify, NULL); |
496 | if (ACPI_FAILURE(status)) { | 753 | if (ACPI_FAILURE(status)) { |
497 | printk(LOG_PFX "unable to install notify handler\n"); | 754 | printk(LOG_PFX "unable to install notify handler\n"); |
498 | result = -ENODEV; | 755 | result = -ENODEV; |
499 | goto outwalk; | 756 | goto outinput; |
500 | } | 757 | } |
501 | 758 | ||
502 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) { | 759 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) { |
@@ -568,6 +825,7 @@ static int sony_nc_add(struct acpi_device *device) | |||
568 | device_remove_file(&sony_pf_device->dev, &item->devattr); | 825 | device_remove_file(&sony_pf_device->dev, &item->devattr); |
569 | } | 826 | } |
570 | sony_pf_remove(); | 827 | sony_pf_remove(); |
828 | |||
571 | outbacklight: | 829 | outbacklight: |
572 | if (sony_backlight_device) | 830 | if (sony_backlight_device) |
573 | backlight_device_unregister(sony_backlight_device); | 831 | backlight_device_unregister(sony_backlight_device); |
@@ -577,6 +835,10 @@ static int sony_nc_add(struct acpi_device *device) | |||
577 | sony_acpi_notify); | 835 | sony_acpi_notify); |
578 | if (ACPI_FAILURE(status)) | 836 | if (ACPI_FAILURE(status)) |
579 | printk(LOG_PFX "unable to remove notify handler\n"); | 837 | printk(LOG_PFX "unable to remove notify handler\n"); |
838 | |||
839 | outinput: | ||
840 | sony_laptop_remove_input(); | ||
841 | |||
580 | outwalk: | 842 | outwalk: |
581 | return result; | 843 | return result; |
582 | } | 844 | } |
@@ -602,6 +864,7 @@ static int sony_nc_remove(struct acpi_device *device, int type) | |||
602 | } | 864 | } |
603 | 865 | ||
604 | sony_pf_remove(); | 866 | sony_pf_remove(); |
867 | sony_laptop_remove_input(); | ||
605 | 868 | ||
606 | printk(KERN_INFO SONY_NC_DRIVER_NAME " successfully removed\n"); | 869 | printk(KERN_INFO SONY_NC_DRIVER_NAME " successfully removed\n"); |
607 | 870 | ||
@@ -626,13 +889,8 @@ static struct acpi_driver sony_nc_driver = { | |||
626 | #define SONYPI_DEVICE_TYPE2 0x00000002 | 889 | #define SONYPI_DEVICE_TYPE2 0x00000002 |
627 | #define SONYPI_DEVICE_TYPE3 0x00000004 | 890 | #define SONYPI_DEVICE_TYPE3 0x00000004 |
628 | 891 | ||
629 | #define SONY_EC_JOGB 0x82 | ||
630 | #define SONY_EC_JOGB_MASK 0x02 | ||
631 | |||
632 | #define SONY_PIC_EV_MASK 0xff | 892 | #define SONY_PIC_EV_MASK 0xff |
633 | 893 | ||
634 | #define SONYPI_BUF_SIZE 128 | ||
635 | |||
636 | struct sony_pic_ioport { | 894 | struct sony_pic_ioport { |
637 | struct acpi_resource_io io; | 895 | struct acpi_resource_io io; |
638 | struct list_head list; | 896 | struct list_head list; |
@@ -650,12 +908,6 @@ struct sony_pic_dev { | |||
650 | struct sony_pic_ioport *cur_ioport; | 908 | struct sony_pic_ioport *cur_ioport; |
651 | struct list_head interrupts; | 909 | struct list_head interrupts; |
652 | struct list_head ioports; | 910 | struct list_head ioports; |
653 | |||
654 | struct input_dev *input_jog_dev; | ||
655 | struct input_dev *input_key_dev; | ||
656 | struct kfifo *input_fifo; | ||
657 | spinlock_t input_fifo_lock; | ||
658 | struct workqueue_struct *sony_pic_wq; | ||
659 | }; | 911 | }; |
660 | 912 | ||
661 | static struct sony_pic_dev spic_dev = { | 913 | static struct sony_pic_dev spic_dev = { |
@@ -929,250 +1181,9 @@ static u8 sony_pic_call2(u8 dev, u8 fn) | |||
929 | return v1; | 1181 | return v1; |
930 | } | 1182 | } |
931 | 1183 | ||
932 | /***************** | 1184 | /* |
933 | * | ||
934 | * INPUT Device | ||
935 | * | ||
936 | *****************/ | ||
937 | struct sony_pic_keypress { | ||
938 | struct input_dev *dev; | ||
939 | int key; | ||
940 | }; | ||
941 | |||
942 | /* Correspondance table between sonypi events and input layer events */ | ||
943 | static struct { | ||
944 | int sonypiev; | ||
945 | int inputev; | ||
946 | } sony_pic_inputkeys[] = { | ||
947 | { SONYPI_EVENT_CAPTURE_PRESSED, KEY_CAMERA }, | ||
948 | { SONYPI_EVENT_FNKEY_ONLY, KEY_FN }, | ||
949 | { SONYPI_EVENT_FNKEY_ESC, KEY_FN_ESC }, | ||
950 | { SONYPI_EVENT_FNKEY_F1, KEY_FN_F1 }, | ||
951 | { SONYPI_EVENT_FNKEY_F2, KEY_FN_F2 }, | ||
952 | { SONYPI_EVENT_FNKEY_F3, KEY_FN_F3 }, | ||
953 | { SONYPI_EVENT_FNKEY_F4, KEY_FN_F4 }, | ||
954 | { SONYPI_EVENT_FNKEY_F5, KEY_FN_F5 }, | ||
955 | { SONYPI_EVENT_FNKEY_F6, KEY_FN_F6 }, | ||
956 | { SONYPI_EVENT_FNKEY_F7, KEY_FN_F7 }, | ||
957 | { SONYPI_EVENT_FNKEY_F8, KEY_FN_F8 }, | ||
958 | { SONYPI_EVENT_FNKEY_F9, KEY_FN_F9 }, | ||
959 | { SONYPI_EVENT_FNKEY_F10, KEY_FN_F10 }, | ||
960 | { SONYPI_EVENT_FNKEY_F11, KEY_FN_F11 }, | ||
961 | { SONYPI_EVENT_FNKEY_F12, KEY_FN_F12 }, | ||
962 | { SONYPI_EVENT_FNKEY_1, KEY_FN_1 }, | ||
963 | { SONYPI_EVENT_FNKEY_2, KEY_FN_2 }, | ||
964 | { SONYPI_EVENT_FNKEY_D, KEY_FN_D }, | ||
965 | { SONYPI_EVENT_FNKEY_E, KEY_FN_E }, | ||
966 | { SONYPI_EVENT_FNKEY_F, KEY_FN_F }, | ||
967 | { SONYPI_EVENT_FNKEY_S, KEY_FN_S }, | ||
968 | { SONYPI_EVENT_FNKEY_B, KEY_FN_B }, | ||
969 | { SONYPI_EVENT_BLUETOOTH_PRESSED, KEY_BLUE }, | ||
970 | { SONYPI_EVENT_BLUETOOTH_ON, KEY_BLUE }, | ||
971 | { SONYPI_EVENT_PKEY_P1, KEY_PROG1 }, | ||
972 | { SONYPI_EVENT_PKEY_P2, KEY_PROG2 }, | ||
973 | { SONYPI_EVENT_PKEY_P3, KEY_PROG3 }, | ||
974 | { SONYPI_EVENT_BACK_PRESSED, KEY_BACK }, | ||
975 | { SONYPI_EVENT_HELP_PRESSED, KEY_HELP }, | ||
976 | { SONYPI_EVENT_ZOOM_PRESSED, KEY_ZOOM }, | ||
977 | { SONYPI_EVENT_THUMBPHRASE_PRESSED, BTN_THUMB }, | ||
978 | { 0, 0 }, | ||
979 | }; | ||
980 | |||
981 | /* release buttons after a short delay if pressed */ | ||
982 | static void do_sony_pic_release_key(struct work_struct *work) | ||
983 | { | ||
984 | struct sony_pic_keypress kp; | ||
985 | |||
986 | while (kfifo_get(spic_dev.input_fifo, (unsigned char *)&kp, | ||
987 | sizeof(kp)) == sizeof(kp)) { | ||
988 | msleep(10); | ||
989 | input_report_key(kp.dev, kp.key, 0); | ||
990 | input_sync(kp.dev); | ||
991 | } | ||
992 | } | ||
993 | static DECLARE_WORK(sony_pic_release_key_work, | ||
994 | do_sony_pic_release_key); | ||
995 | |||
996 | /* forward event to the input subsytem */ | ||
997 | static void sony_pic_report_input_event(u8 event) | ||
998 | { | ||
999 | struct input_dev *jog_dev = spic_dev.input_jog_dev; | ||
1000 | struct input_dev *key_dev = spic_dev.input_key_dev; | ||
1001 | struct sony_pic_keypress kp = { NULL }; | ||
1002 | int i; | ||
1003 | |||
1004 | if (event == SONYPI_EVENT_FNKEY_RELEASED) { | ||
1005 | /* Nothing, not all VAIOs generate this event */ | ||
1006 | return; | ||
1007 | } | ||
1008 | |||
1009 | /* report jog_dev events */ | ||
1010 | if (jog_dev) { | ||
1011 | switch (event) { | ||
1012 | case SONYPI_EVENT_JOGDIAL_UP: | ||
1013 | case SONYPI_EVENT_JOGDIAL_UP_PRESSED: | ||
1014 | input_report_rel(jog_dev, REL_WHEEL, 1); | ||
1015 | input_sync(jog_dev); | ||
1016 | return; | ||
1017 | |||
1018 | case SONYPI_EVENT_JOGDIAL_DOWN: | ||
1019 | case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED: | ||
1020 | input_report_rel(jog_dev, REL_WHEEL, -1); | ||
1021 | input_sync(jog_dev); | ||
1022 | return; | ||
1023 | |||
1024 | default: | ||
1025 | break; | ||
1026 | } | ||
1027 | } | ||
1028 | |||
1029 | switch (event) { | ||
1030 | case SONYPI_EVENT_JOGDIAL_PRESSED: | ||
1031 | kp.key = BTN_MIDDLE; | ||
1032 | kp.dev = jog_dev; | ||
1033 | break; | ||
1034 | |||
1035 | default: | ||
1036 | for (i = 0; sony_pic_inputkeys[i].sonypiev; i++) | ||
1037 | if (event == sony_pic_inputkeys[i].sonypiev) { | ||
1038 | kp.dev = key_dev; | ||
1039 | kp.key = sony_pic_inputkeys[i].inputev; | ||
1040 | break; | ||
1041 | } | ||
1042 | break; | ||
1043 | } | ||
1044 | |||
1045 | if (kp.dev) { | ||
1046 | input_report_key(kp.dev, kp.key, 1); | ||
1047 | input_sync(kp.dev); | ||
1048 | kfifo_put(spic_dev.input_fifo, | ||
1049 | (unsigned char *)&kp, sizeof(kp)); | ||
1050 | |||
1051 | if (!work_pending(&sony_pic_release_key_work)) | ||
1052 | queue_work(spic_dev.sony_pic_wq, | ||
1053 | &sony_pic_release_key_work); | ||
1054 | } | ||
1055 | } | ||
1056 | |||
1057 | static int sony_pic_setup_input(void) | ||
1058 | { | ||
1059 | struct input_dev *jog_dev; | ||
1060 | struct input_dev *key_dev; | ||
1061 | int i; | ||
1062 | int error; | ||
1063 | u8 jog_present = 0; | ||
1064 | |||
1065 | /* kfifo */ | ||
1066 | spin_lock_init(&spic_dev.input_fifo_lock); | ||
1067 | spic_dev.input_fifo = | ||
1068 | kfifo_alloc(SONYPI_BUF_SIZE, GFP_KERNEL, | ||
1069 | &spic_dev.input_fifo_lock); | ||
1070 | if (IS_ERR(spic_dev.input_fifo)) { | ||
1071 | printk(KERN_ERR "sonypi: kfifo_alloc failed\n"); | ||
1072 | return PTR_ERR(spic_dev.input_fifo); | ||
1073 | } | ||
1074 | |||
1075 | /* init workqueue */ | ||
1076 | spic_dev.sony_pic_wq = create_singlethread_workqueue("sony-pic"); | ||
1077 | if (!spic_dev.sony_pic_wq) { | ||
1078 | printk(KERN_ERR DRV_PFX | ||
1079 | "Unabe to create workqueue.\n"); | ||
1080 | error = -ENXIO; | ||
1081 | goto err_free_kfifo; | ||
1082 | } | ||
1083 | |||
1084 | /* input keys */ | ||
1085 | key_dev = input_allocate_device(); | ||
1086 | if (!key_dev) { | ||
1087 | error = -ENOMEM; | ||
1088 | goto err_destroy_wq; | ||
1089 | } | ||
1090 | |||
1091 | key_dev->name = "Sony Vaio Keys"; | ||
1092 | key_dev->id.bustype = BUS_ISA; | ||
1093 | key_dev->id.vendor = PCI_VENDOR_ID_SONY; | ||
1094 | |||
1095 | /* Initialize the Input Drivers: special keys */ | ||
1096 | key_dev->evbit[0] = BIT(EV_KEY); | ||
1097 | for (i = 0; sony_pic_inputkeys[i].sonypiev; i++) | ||
1098 | if (sony_pic_inputkeys[i].inputev) | ||
1099 | set_bit(sony_pic_inputkeys[i].inputev, key_dev->keybit); | ||
1100 | |||
1101 | error = input_register_device(key_dev); | ||
1102 | if (error) | ||
1103 | goto err_free_keydev; | ||
1104 | |||
1105 | spic_dev.input_key_dev = key_dev; | ||
1106 | |||
1107 | /* jogdial - really reliable ? */ | ||
1108 | ec_read(SONY_EC_JOGB, &jog_present); | ||
1109 | if (jog_present & SONY_EC_JOGB_MASK || force_jog) { | ||
1110 | jog_dev = input_allocate_device(); | ||
1111 | if (!jog_dev) { | ||
1112 | error = -ENOMEM; | ||
1113 | goto err_unregister_keydev; | ||
1114 | } | ||
1115 | |||
1116 | jog_dev->name = "Sony Vaio Jogdial"; | ||
1117 | jog_dev->id.bustype = BUS_ISA; | ||
1118 | jog_dev->id.vendor = PCI_VENDOR_ID_SONY; | ||
1119 | |||
1120 | jog_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); | ||
1121 | jog_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_MIDDLE); | ||
1122 | jog_dev->relbit[0] = BIT(REL_WHEEL); | ||
1123 | |||
1124 | error = input_register_device(jog_dev); | ||
1125 | if (error) | ||
1126 | goto err_free_jogdev; | ||
1127 | |||
1128 | spic_dev.input_jog_dev = jog_dev; | ||
1129 | } | ||
1130 | |||
1131 | return 0; | ||
1132 | |||
1133 | err_free_jogdev: | ||
1134 | input_free_device(jog_dev); | ||
1135 | |||
1136 | err_unregister_keydev: | ||
1137 | input_unregister_device(key_dev); | ||
1138 | /* to avoid kref underflow below at input_free_device */ | ||
1139 | key_dev = NULL; | ||
1140 | |||
1141 | err_free_keydev: | ||
1142 | input_free_device(key_dev); | ||
1143 | |||
1144 | err_destroy_wq: | ||
1145 | destroy_workqueue(spic_dev.sony_pic_wq); | ||
1146 | |||
1147 | err_free_kfifo: | ||
1148 | kfifo_free(spic_dev.input_fifo); | ||
1149 | |||
1150 | return error; | ||
1151 | } | ||
1152 | |||
1153 | static void sony_pic_remove_input(void) | ||
1154 | { | ||
1155 | /* flush workqueue first */ | ||
1156 | flush_workqueue(spic_dev.sony_pic_wq); | ||
1157 | |||
1158 | /* destroy input devs */ | ||
1159 | input_unregister_device(spic_dev.input_key_dev); | ||
1160 | spic_dev.input_key_dev = NULL; | ||
1161 | |||
1162 | if (spic_dev.input_jog_dev) { | ||
1163 | input_unregister_device(spic_dev.input_jog_dev); | ||
1164 | spic_dev.input_jog_dev = NULL; | ||
1165 | } | ||
1166 | |||
1167 | destroy_workqueue(spic_dev.sony_pic_wq); | ||
1168 | kfifo_free(spic_dev.input_fifo); | ||
1169 | } | ||
1170 | |||
1171 | /******************** | ||
1172 | * | ||
1173 | * ACPI callbacks | 1185 | * ACPI callbacks |
1174 | * | 1186 | */ |
1175 | ********************/ | ||
1176 | static acpi_status | 1187 | static acpi_status |
1177 | sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) | 1188 | sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) |
1178 | { | 1189 | { |
@@ -1409,7 +1420,7 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id) | |||
1409 | return IRQ_HANDLED; | 1420 | return IRQ_HANDLED; |
1410 | 1421 | ||
1411 | found: | 1422 | found: |
1412 | sony_pic_report_input_event(device_event); | 1423 | sony_laptop_report_input_event(device_event); |
1413 | acpi_bus_generate_event(spic_dev.acpi_dev, 1, device_event); | 1424 | acpi_bus_generate_event(spic_dev.acpi_dev, 1, device_event); |
1414 | 1425 | ||
1415 | return IRQ_HANDLED; | 1426 | return IRQ_HANDLED; |
@@ -1434,7 +1445,7 @@ static int sony_pic_remove(struct acpi_device *device, int type) | |||
1434 | release_region(spic_dev.cur_ioport->io.minimum, | 1445 | release_region(spic_dev.cur_ioport->io.minimum, |
1435 | spic_dev.cur_ioport->io.address_length); | 1446 | spic_dev.cur_ioport->io.address_length); |
1436 | 1447 | ||
1437 | sony_pic_remove_input(); | 1448 | sony_laptop_remove_input(); |
1438 | 1449 | ||
1439 | list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) { | 1450 | list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) { |
1440 | list_del(&io->list); | 1451 | list_del(&io->list); |
@@ -1474,7 +1485,7 @@ static int sony_pic_add(struct acpi_device *device) | |||
1474 | } | 1485 | } |
1475 | 1486 | ||
1476 | /* setup input devices and helper fifo */ | 1487 | /* setup input devices and helper fifo */ |
1477 | result = sony_pic_setup_input(); | 1488 | result = sony_laptop_setup_input(); |
1478 | if (result) { | 1489 | if (result) { |
1479 | printk(KERN_ERR DRV_PFX | 1490 | printk(KERN_ERR DRV_PFX |
1480 | "Unabe to create input devices.\n"); | 1491 | "Unabe to create input devices.\n"); |
@@ -1535,7 +1546,7 @@ err_release_region: | |||
1535 | spic_dev.cur_ioport->io.address_length); | 1546 | spic_dev.cur_ioport->io.address_length); |
1536 | 1547 | ||
1537 | err_remove_input: | 1548 | err_remove_input: |
1538 | sony_pic_remove_input(); | 1549 | sony_laptop_remove_input(); |
1539 | 1550 | ||
1540 | err_free_resources: | 1551 | err_free_resources: |
1541 | list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) { | 1552 | list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) { |