Contents of /alx-src/tags/kernel26-2.6.12-alx-r9/drivers/char/qtronix.c
Parent Directory
|
Revision Log
Revision 630 -
(show annotations)
(download)
Wed Mar 4 11:03:09 2009 UTC (15 years, 3 months ago) by niro
File MIME type: text/plain
File size: 13512 byte(s)
Wed Mar 4 11:03:09 2009 UTC (15 years, 3 months ago) by niro
File MIME type: text/plain
File size: 13512 byte(s)
Tag kernel26-2.6.12-alx-r9
1 | /* |
2 | * |
3 | * BRIEF MODULE DESCRIPTION |
4 | * Qtronix 990P infrared keyboard driver. |
5 | * |
6 | * |
7 | * Copyright 2001 MontaVista Software Inc. |
8 | * Author: MontaVista Software, Inc. |
9 | * ppopov@mvista.com or source@mvista.com |
10 | * |
11 | * |
12 | * The bottom portion of this driver was take from |
13 | * pc_keyb.c Please see that file for copyrights. |
14 | * |
15 | * This program is free software; you can redistribute it and/or modify it |
16 | * under the terms of the GNU General Public License as published by the |
17 | * Free Software Foundation; either version 2 of the License, or (at your |
18 | * option) any later version. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
21 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
22 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
23 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
24 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
25 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
26 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
27 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
29 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | * |
31 | * You should have received a copy of the GNU General Public License along |
32 | * with this program; if not, write to the Free Software Foundation, Inc., |
33 | * 675 Mass Ave, Cambridge, MA 02139, USA. |
34 | */ |
35 | |
36 | #include <linux/config.h> |
37 | |
38 | /* |
39 | * NOTE: |
40 | * |
41 | * This driver has only been tested with the Consumer IR |
42 | * port of the ITE 8172 system controller. |
43 | * |
44 | * You do not need this driver if you are using the ps/2 or |
45 | * USB adapter that the keyboard ships with. You only need |
46 | * this driver if your board has a IR port and the keyboard |
47 | * data is being sent directly to the IR. In that case, |
48 | * you also need some low-level IR support. See it8172_cir.c. |
49 | * |
50 | */ |
51 | |
52 | #ifdef CONFIG_QTRONIX_KEYBOARD |
53 | |
54 | #include <linux/module.h> |
55 | #include <linux/types.h> |
56 | #include <linux/pci.h> |
57 | #include <linux/kernel.h> |
58 | |
59 | #include <asm/it8172/it8172.h> |
60 | #include <asm/it8172/it8172_int.h> |
61 | #include <asm/it8172/it8172_cir.h> |
62 | |
63 | #include <linux/spinlock.h> |
64 | #include <linux/sched.h> |
65 | #include <linux/interrupt.h> |
66 | #include <linux/tty.h> |
67 | #include <linux/mm.h> |
68 | #include <linux/signal.h> |
69 | #include <linux/init.h> |
70 | #include <linux/kbd_ll.h> |
71 | #include <linux/delay.h> |
72 | #include <linux/poll.h> |
73 | #include <linux/miscdevice.h> |
74 | #include <linux/slab.h> |
75 | #include <linux/kbd_kern.h> |
76 | #include <linux/smp_lock.h> |
77 | #include <asm/io.h> |
78 | #include <linux/pc_keyb.h> |
79 | |
80 | #include <asm/keyboard.h> |
81 | #include <linux/bitops.h> |
82 | #include <asm/uaccess.h> |
83 | #include <asm/irq.h> |
84 | #include <asm/system.h> |
85 | |
86 | #define leading1 0 |
87 | #define leading2 0xF |
88 | |
89 | #define KBD_CIR_PORT 0 |
90 | #define AUX_RECONNECT 170 /* scancode when ps2 device is plugged (back) in */ |
91 | |
92 | static int data_index; |
93 | struct cir_port *cir; |
94 | static unsigned char kbdbytes[5]; |
95 | static unsigned char cir_data[32]; /* we only need 16 chars */ |
96 | |
97 | static void kbd_int_handler(int irq, void *dev_id, struct pt_regs *regs); |
98 | static int handle_data(unsigned char *p_data); |
99 | static inline void handle_mouse_event(unsigned char scancode); |
100 | static inline void handle_keyboard_event(unsigned char scancode, int down); |
101 | static int __init psaux_init(void); |
102 | |
103 | static struct aux_queue *queue; /* Mouse data buffer. */ |
104 | static int aux_count = 0; |
105 | |
106 | /* |
107 | * Keys accessed through the 'Fn' key |
108 | * The Fn key does not produce a key-up sequence. So, the first |
109 | * time the user presses it, it will be key-down event. The key |
110 | * stays down until the user presses it again. |
111 | */ |
112 | #define NUM_FN_KEYS 56 |
113 | static unsigned char fn_keys[NUM_FN_KEYS] = { |
114 | 0,0,0,0,0,0,0,0, /* 0 7 */ |
115 | 8,9,10,93,0,0,0,0, /* 8 15 */ |
116 | 0,0,0,0,0,0,0,5, /* 16 23 */ |
117 | 6,7,91,0,0,0,0,0, /* 24 31 */ |
118 | 0,0,0,0,0,2,3,4, /* 32 39 */ |
119 | 92,0,0,0,0,0,0,0, /* 40 47 */ |
120 | 0,0,0,0,11,0,94,95 /* 48 55 */ |
121 | |
122 | }; |
123 | |
124 | void __init init_qtronix_990P_kbd(void) |
125 | { |
126 | int retval; |
127 | |
128 | cir = (struct cir_port *)kmalloc(sizeof(struct cir_port), GFP_KERNEL); |
129 | if (!cir) { |
130 | printk("Unable to initialize Qtronix keyboard\n"); |
131 | return; |
132 | } |
133 | |
134 | /* |
135 | * revisit |
136 | * this should be programmable, somehow by the, by the user. |
137 | */ |
138 | cir->port = KBD_CIR_PORT; |
139 | cir->baud_rate = 0x1d; |
140 | cir->rdwos = 0; |
141 | cir->rxdcr = 0x3; |
142 | cir->hcfs = 0; |
143 | cir->fifo_tl = 0; |
144 | cir->cfq = 0x1d; |
145 | cir_port_init(cir); |
146 | |
147 | retval = request_irq(IT8172_CIR0_IRQ, kbd_int_handler, |
148 | (unsigned long )(SA_INTERRUPT|SA_SHIRQ), |
149 | (const char *)"Qtronix IR Keyboard", (void *)cir); |
150 | |
151 | if (retval) { |
152 | printk("unable to allocate cir %d irq %d\n", |
153 | cir->port, IT8172_CIR0_IRQ); |
154 | } |
155 | #ifdef CONFIG_PSMOUSE |
156 | psaux_init(); |
157 | #endif |
158 | } |
159 | |
160 | static inline unsigned char BitReverse(unsigned short key) |
161 | { |
162 | unsigned char rkey = 0; |
163 | rkey |= (key & 0x1) << 7; |
164 | rkey |= (key & 0x2) << 5; |
165 | rkey |= (key & 0x4) << 3; |
166 | rkey |= (key & 0x8) << 1; |
167 | rkey |= (key & 0x10) >> 1; |
168 | rkey |= (key & 0x20) >> 3; |
169 | rkey |= (key & 0x40) >> 5; |
170 | rkey |= (key & 0x80) >> 7; |
171 | return rkey; |
172 | |
173 | } |
174 | |
175 | |
176 | static inline u_int8_t UpperByte(u_int8_t data) |
177 | { |
178 | return (data >> 4); |
179 | } |
180 | |
181 | |
182 | static inline u_int8_t LowerByte(u_int8_t data) |
183 | { |
184 | return (data & 0xF); |
185 | } |
186 | |
187 | |
188 | int CheckSumOk(u_int8_t byte1, u_int8_t byte2, |
189 | u_int8_t byte3, u_int8_t byte4, u_int8_t byte5) |
190 | { |
191 | u_int8_t CheckSum; |
192 | |
193 | CheckSum = (byte1 & 0x0F) + byte2 + byte3 + byte4 + byte5; |
194 | if ( LowerByte(UpperByte(CheckSum) + LowerByte(CheckSum)) != UpperByte(byte1) ) |
195 | return 0; |
196 | else |
197 | return 1; |
198 | } |
199 | |
200 | |
201 | static void kbd_int_handler(int irq, void *dev_id, struct pt_regs *regs) |
202 | { |
203 | struct cir_port *cir; |
204 | int j; |
205 | unsigned char int_status; |
206 | |
207 | cir = (struct cir_port *)dev_id; |
208 | int_status = get_int_status(cir); |
209 | if (int_status & 0x4) { |
210 | clear_fifo(cir); |
211 | return; |
212 | } |
213 | |
214 | while (cir_get_rx_count(cir)) { |
215 | |
216 | cir_data[data_index] = cir_read_data(cir); |
217 | |
218 | if (data_index == 0) {/* expecting first byte */ |
219 | if (cir_data[data_index] != leading1) { |
220 | //printk("!leading byte %x\n", cir_data[data_index]); |
221 | set_rx_active(cir); |
222 | clear_fifo(cir); |
223 | continue; |
224 | } |
225 | } |
226 | if (data_index == 1) { |
227 | if ((cir_data[data_index] & 0xf) != leading2) { |
228 | set_rx_active(cir); |
229 | data_index = 0; /* start over */ |
230 | clear_fifo(cir); |
231 | continue; |
232 | } |
233 | } |
234 | |
235 | if ( (cir_data[data_index] == 0xff)) { /* last byte */ |
236 | //printk("data_index %d\n", data_index); |
237 | set_rx_active(cir); |
238 | #if 0 |
239 | for (j=0; j<=data_index; j++) { |
240 | printk("rx_data %d: %x\n", j, cir_data[j]); |
241 | } |
242 | #endif |
243 | data_index = 0; |
244 | handle_data(cir_data); |
245 | return; |
246 | } |
247 | else if (data_index>16) { |
248 | set_rx_active(cir); |
249 | #if 0 |
250 | printk("warning: data_index %d\n", data_index); |
251 | for (j=0; j<=data_index; j++) { |
252 | printk("rx_data %d: %x\n", j, cir_data[j]); |
253 | } |
254 | #endif |
255 | data_index = 0; |
256 | clear_fifo(cir); |
257 | return; |
258 | } |
259 | data_index++; |
260 | } |
261 | } |
262 | |
263 | |
264 | #define NUM_KBD_BYTES 5 |
265 | static int handle_data(unsigned char *p_data) |
266 | { |
267 | u_int32_t bit_bucket; |
268 | u_int32_t i, j; |
269 | u_int32_t got_bits, next_byte; |
270 | int down = 0; |
271 | |
272 | /* Reorganize the bit stream */ |
273 | for (i=0; i<16; i++) |
274 | p_data[i] = BitReverse(~p_data[i]); |
275 | |
276 | /* |
277 | * We've already previously checked that p_data[0] |
278 | * is equal to leading1 and that (p_data[1] & 0xf) |
279 | * is equal to leading2. These twelve bits are the |
280 | * leader code. We can now throw them away (the 12 |
281 | * bits) and continue parsing the stream. |
282 | */ |
283 | bit_bucket = p_data[1] << 12; |
284 | got_bits = 4; |
285 | next_byte = 2; |
286 | |
287 | /* |
288 | * Process four bits at a time |
289 | */ |
290 | for (i=0; i<NUM_KBD_BYTES; i++) { |
291 | |
292 | kbdbytes[i]=0; |
293 | |
294 | for (j=0; j<8; j++) /* 8 bits per byte */ |
295 | { |
296 | if (got_bits < 4) { |
297 | bit_bucket |= (p_data[next_byte++] << (8 - got_bits)); |
298 | got_bits += 8; |
299 | } |
300 | |
301 | if ((bit_bucket & 0xF000) == 0x8000) { |
302 | /* Convert 1000b to 1 */ |
303 | kbdbytes[i] = 0x80 | (kbdbytes[i] >> 1); |
304 | got_bits -= 4; |
305 | bit_bucket = bit_bucket << 4; |
306 | } |
307 | else if ((bit_bucket & 0xC000) == 0x8000) { |
308 | /* Convert 10b to 0 */ |
309 | kbdbytes[i] = kbdbytes[i] >> 1; |
310 | got_bits -= 2; |
311 | bit_bucket = bit_bucket << 2; |
312 | } |
313 | else { |
314 | /* bad serial stream */ |
315 | return 1; |
316 | } |
317 | |
318 | if (next_byte > 16) { |
319 | //printk("error: too many bytes\n"); |
320 | return 1; |
321 | } |
322 | } |
323 | } |
324 | |
325 | |
326 | if (!CheckSumOk(kbdbytes[0], kbdbytes[1], |
327 | kbdbytes[2], kbdbytes[3], kbdbytes[4])) { |
328 | //printk("checksum failed\n"); |
329 | return 1; |
330 | } |
331 | |
332 | if (kbdbytes[1] & 0x08) { |
333 | //printk("m: %x %x %x\n", kbdbytes[1], kbdbytes[2], kbdbytes[3]); |
334 | handle_mouse_event(kbdbytes[1]); |
335 | handle_mouse_event(kbdbytes[2]); |
336 | handle_mouse_event(kbdbytes[3]); |
337 | } |
338 | else { |
339 | if (kbdbytes[2] == 0) down = 1; |
340 | #if 0 |
341 | if (down) |
342 | printk("down %d\n", kbdbytes[3]); |
343 | else |
344 | printk("up %d\n", kbdbytes[3]); |
345 | #endif |
346 | handle_keyboard_event(kbdbytes[3], down); |
347 | } |
348 | return 0; |
349 | } |
350 | |
351 | |
352 | DEFINE_SPINLOCK(kbd_controller_lock); |
353 | static unsigned char handle_kbd_event(void); |
354 | |
355 | |
356 | int kbd_setkeycode(unsigned int scancode, unsigned int keycode) |
357 | { |
358 | printk("kbd_setkeycode scancode %x keycode %x\n", scancode, keycode); |
359 | return 0; |
360 | } |
361 | |
362 | int kbd_getkeycode(unsigned int scancode) |
363 | { |
364 | return scancode; |
365 | } |
366 | |
367 | |
368 | int kbd_translate(unsigned char scancode, unsigned char *keycode, |
369 | char raw_mode) |
370 | { |
371 | static int prev_scancode = 0; |
372 | |
373 | if (scancode == 0x00 || scancode == 0xff) { |
374 | prev_scancode = 0; |
375 | return 0; |
376 | } |
377 | |
378 | /* todo */ |
379 | if (!prev_scancode && scancode == 160) { /* Fn key down */ |
380 | //printk("Fn key down\n"); |
381 | prev_scancode = 160; |
382 | return 0; |
383 | } |
384 | else if (prev_scancode && scancode == 160) { /* Fn key up */ |
385 | //printk("Fn key up\n"); |
386 | prev_scancode = 0; |
387 | return 0; |
388 | } |
389 | |
390 | /* todo */ |
391 | if (prev_scancode == 160) { |
392 | if (scancode <= NUM_FN_KEYS) { |
393 | *keycode = fn_keys[scancode]; |
394 | //printk("fn keycode %d\n", *keycode); |
395 | } |
396 | else |
397 | return 0; |
398 | } |
399 | else if (scancode <= 127) { |
400 | *keycode = scancode; |
401 | } |
402 | else |
403 | return 0; |
404 | |
405 | |
406 | return 1; |
407 | } |
408 | |
409 | char kbd_unexpected_up(unsigned char keycode) |
410 | { |
411 | //printk("kbd_unexpected_up\n"); |
412 | return 0; |
413 | } |
414 | |
415 | static unsigned char kbd_exists = 1; |
416 | |
417 | static inline void handle_keyboard_event(unsigned char scancode, int down) |
418 | { |
419 | kbd_exists = 1; |
420 | handle_scancode(scancode, down); |
421 | tasklet_schedule(&keyboard_tasklet); |
422 | } |
423 | |
424 | |
425 | void kbd_leds(unsigned char leds) |
426 | { |
427 | } |
428 | |
429 | /* dummy */ |
430 | void kbd_init_hw(void) |
431 | { |
432 | } |
433 | |
434 | |
435 | |
436 | static inline void handle_mouse_event(unsigned char scancode) |
437 | { |
438 | if(scancode == AUX_RECONNECT){ |
439 | queue->head = queue->tail = 0; /* Flush input queue */ |
440 | // __aux_write_ack(AUX_ENABLE_DEV); /* ping the mouse :) */ |
441 | return; |
442 | } |
443 | |
444 | if (aux_count) { |
445 | int head = queue->head; |
446 | |
447 | queue->buf[head] = scancode; |
448 | head = (head + 1) & (AUX_BUF_SIZE-1); |
449 | if (head != queue->tail) { |
450 | queue->head = head; |
451 | kill_fasync(&queue->fasync, SIGIO, POLL_IN); |
452 | wake_up_interruptible(&queue->proc_list); |
453 | } |
454 | } |
455 | } |
456 | |
457 | static unsigned char get_from_queue(void) |
458 | { |
459 | unsigned char result; |
460 | unsigned long flags; |
461 | |
462 | spin_lock_irqsave(&kbd_controller_lock, flags); |
463 | result = queue->buf[queue->tail]; |
464 | queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1); |
465 | spin_unlock_irqrestore(&kbd_controller_lock, flags); |
466 | return result; |
467 | } |
468 | |
469 | |
470 | static inline int queue_empty(void) |
471 | { |
472 | return queue->head == queue->tail; |
473 | } |
474 | |
475 | static int fasync_aux(int fd, struct file *filp, int on) |
476 | { |
477 | int retval; |
478 | |
479 | //printk("fasync_aux\n"); |
480 | retval = fasync_helper(fd, filp, on, &queue->fasync); |
481 | if (retval < 0) |
482 | return retval; |
483 | return 0; |
484 | } |
485 | |
486 | |
487 | /* |
488 | * Random magic cookie for the aux device |
489 | */ |
490 | #define AUX_DEV ((void *)queue) |
491 | |
492 | static int release_aux(struct inode * inode, struct file * file) |
493 | { |
494 | fasync_aux(-1, file, 0); |
495 | aux_count--; |
496 | return 0; |
497 | } |
498 | |
499 | static int open_aux(struct inode * inode, struct file * file) |
500 | { |
501 | if (aux_count++) { |
502 | return 0; |
503 | } |
504 | queue->head = queue->tail = 0; /* Flush input queue */ |
505 | return 0; |
506 | } |
507 | |
508 | /* |
509 | * Put bytes from input queue to buffer. |
510 | */ |
511 | |
512 | static ssize_t read_aux(struct file * file, char * buffer, |
513 | size_t count, loff_t *ppos) |
514 | { |
515 | DECLARE_WAITQUEUE(wait, current); |
516 | ssize_t i = count; |
517 | unsigned char c; |
518 | |
519 | if (queue_empty()) { |
520 | if (file->f_flags & O_NONBLOCK) |
521 | return -EAGAIN; |
522 | add_wait_queue(&queue->proc_list, &wait); |
523 | repeat: |
524 | set_current_state(TASK_INTERRUPTIBLE); |
525 | if (queue_empty() && !signal_pending(current)) { |
526 | schedule(); |
527 | goto repeat; |
528 | } |
529 | current->state = TASK_RUNNING; |
530 | remove_wait_queue(&queue->proc_list, &wait); |
531 | } |
532 | while (i > 0 && !queue_empty()) { |
533 | c = get_from_queue(); |
534 | put_user(c, buffer++); |
535 | i--; |
536 | } |
537 | if (count-i) { |
538 | struct inode *inode = file->f_dentry->d_inode; |
539 | inode->i_atime = current_fs_time(inode->i_sb); |
540 | return count-i; |
541 | } |
542 | if (signal_pending(current)) |
543 | return -ERESTARTSYS; |
544 | return 0; |
545 | } |
546 | |
547 | /* |
548 | * Write to the aux device. |
549 | */ |
550 | |
551 | static ssize_t write_aux(struct file * file, const char * buffer, |
552 | size_t count, loff_t *ppos) |
553 | { |
554 | /* |
555 | * The ITE boards this was tested on did not have the |
556 | * transmit wires connected. |
557 | */ |
558 | return count; |
559 | } |
560 | |
561 | static unsigned int aux_poll(struct file *file, poll_table * wait) |
562 | { |
563 | poll_wait(file, &queue->proc_list, wait); |
564 | if (!queue_empty()) |
565 | return POLLIN | POLLRDNORM; |
566 | return 0; |
567 | } |
568 | |
569 | struct file_operations psaux_fops = { |
570 | .read = read_aux, |
571 | .write = write_aux, |
572 | .poll = aux_poll, |
573 | .open = open_aux, |
574 | .release = release_aux, |
575 | .fasync = fasync_aux, |
576 | }; |
577 | |
578 | /* |
579 | * Initialize driver. |
580 | */ |
581 | static struct miscdevice psaux_mouse = { |
582 | PSMOUSE_MINOR, "psaux", &psaux_fops |
583 | }; |
584 | |
585 | static int __init psaux_init(void) |
586 | { |
587 | int retval; |
588 | |
589 | retval = misc_register(&psaux_mouse); |
590 | if(retval < 0) |
591 | return retval; |
592 | |
593 | queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); |
594 | memset(queue, 0, sizeof(*queue)); |
595 | queue->head = queue->tail = 0; |
596 | init_waitqueue_head(&queue->proc_list); |
597 | |
598 | return 0; |
599 | } |
600 | module_init(init_qtronix_990P_kbd); |
601 | #endif |