1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34#include <linux/module.h>
35#include <linux/kernel.h>
36#include <linux/signal.h>
37#include <linux/errno.h>
38#include <linux/mm.h>
39#include <linux/miscdevice.h>
40#include <linux/ptrace.h>
41#include <linux/poll.h>
42#include <linux/ioport.h>
43#include <linux/interrupt.h>
44#include <linux/smp_lock.h>
45#include <linux/init.h>
46
47#include <asm/signal.h>
48#include <asm/io.h>
49#include <asm/irq.h>
50#include <asm/semaphore.h>
51#include <linux/spinlock.h>
52#include <asm/uaccess.h>
53
54#include "pc110pad.h"
55
56
57static struct pc110pad_params default_params = {
58 mode: PC110PAD_PS2,
59 bounce_interval: 50 MS,
60 tap_interval: 200 MS,
61 irq: 10,
62 io: 0x15E0,
63};
64
65static struct pc110pad_params current_params;
66
67
68
69static wait_queue_head_t queue;
70static struct fasync_struct *asyncptr;
71static int active;
72static struct semaphore reader_lock;
73
74
75
76
77
78
79
80
81
82
83
84static void wake_readers(void)
85{
86 wake_up_interruptible(&queue);
87 kill_fasync(&asyncptr, SIGIO, POLL_IN);
88}
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114static int button_pending;
115static int recent_transition;
116static int transition_count;
117static int synthesize_tap;
118static void tap_timeout(unsigned long data);
119static struct timer_list tap_timer = { function: tap_timeout };
120
121
122
123
124
125
126
127
128
129
130
131
132
133static void tap_timeout(unsigned long data)
134{
135 if(!recent_transition)
136 {
137 printk(KERN_ERR "pc110pad: tap_timeout but no recent transition!\n");
138 }
139 if( transition_count==2 || transition_count==4 || transition_count==6 )
140 {
141 synthesize_tap+=transition_count;
142 button_pending = 1;
143 wake_readers();
144 }
145 recent_transition=0;
146}
147
148
149
150
151
152
153
154
155
156void notify_pad_up_down(void)
157{
158 if(recent_transition)
159 {
160 transition_count++;
161 }
162 else
163 {
164 transition_count=1;
165 recent_transition=1;
166 }
167 mod_timer(&tap_timer, jiffies + current_params.tap_interval);
168
169
170 button_pending = 1;
171 wake_readers();
172}
173
174
175
176
177
178
179
180
181
182static void read_button(int *b)
183{
184 if(synthesize_tap)
185 {
186 *b=--synthesize_tap & 1;
187 }
188 else
189 {
190 *b=(!recent_transition && transition_count==3);
191 }
192 button_pending=(synthesize_tap>0);
193}
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220static int raw_data[3];
221static int raw_data_count;
222static int raw_x, raw_y;
223static int raw_down;
224static int debounced_down;
225static enum { NO_BOUNCE, JUST_GONE_UP, JUST_GONE_DOWN } bounce=NO_BOUNCE;
226
227static int xy_pending;
228
229
230
231
232
233
234static void bounce_timeout(unsigned long data);
235static struct timer_list bounce_timer = { function: bounce_timeout };
236
237
238
239
240
241
242
243
244
245
246
247static void bounce_timeout(unsigned long data)
248{
249 switch(bounce)
250 {
251 case NO_BOUNCE:
252 {
253
254
255
256
257 printk(KERN_WARNING "pc110pad, bounce_timeout: bounce flag not set!\n");
258 break;
259 }
260 case JUST_GONE_UP:
261 {
262
263
264
265
266 bounce=NO_BOUNCE;
267 if(debounced_down==raw_down)
268 {
269 printk(KERN_WARNING "pc110pad, bounce_timeout: raw already debounced!\n");
270 }
271 debounced_down=raw_down;
272
273 notify_pad_up_down();
274 break;
275 }
276 case JUST_GONE_DOWN:
277 {
278
279
280
281
282
283 bounce=NO_BOUNCE;
284 break;
285 }
286 }
287}
288
289
290
291
292
293
294
295
296
297
298
299
300
301static void pad_irq(int irq, void *ptr, struct pt_regs *regs)
302{
303
304
305 {
306 int value=inb_p(current_params.io);
307 int handshake=inb_p(current_params.io+2);
308 outb_p(handshake | 1, current_params.io+2);
309 outb_p(handshake &~1, current_params.io+2);
310 inb_p(0x64);
311
312 raw_data[raw_data_count++]=value;
313 }
314
315 if(raw_data_count==3)
316 {
317 int new_down=raw_data[0]&0x01;
318 int new_x=raw_data[1];
319 int new_y=raw_data[2];
320 if(raw_data[0]&0x10) new_x+=128;
321 if(raw_data[0]&0x80) new_x+=256;
322 if(raw_data[0]&0x08) new_y+=128;
323
324 if( (raw_x!=new_x) || (raw_y!=new_y) )
325 {
326 raw_x=new_x;
327 raw_y=new_y;
328 xy_pending=1;
329 }
330
331 if(new_down != raw_down)
332 {
333
334
335
336 raw_down=new_down;
337
338
339 if(bounce)
340 {
341 del_timer(&bounce_timer);
342 bounce=NO_BOUNCE;
343 }
344
345 if(new_down)
346 {
347 if(debounced_down)
348 {
349
350
351
352
353
354 }
355 else
356 {
357 bounce=JUST_GONE_DOWN;
358 mod_timer(&bounce_timer,
359 jiffies+current_params.bounce_interval);
360
361 debounced_down=new_down;
362 notify_pad_up_down();
363 }
364 }
365 else
366 {
367 if(recent_transition)
368 {
369
370
371
372
373 debounced_down=new_down;
374 notify_pad_up_down();
375 }
376 else
377 {
378
379 bounce=JUST_GONE_UP;
380 mod_timer(&bounce_timer,
381 jiffies+current_params.bounce_interval);
382 }
383 }
384 }
385 wake_readers();
386 raw_data_count=0;
387 }
388}
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403static void read_raw_pad(int *down, int *debounced, int *x, int *y)
404{
405 disable_irq(current_params.irq);
406 {
407 *down=raw_down;
408 *debounced=debounced_down;
409 *x=raw_x;
410 *y=raw_y;
411 xy_pending = 0;
412 }
413 enable_irq(current_params.irq);
414}
415
416
417
418
419
420
421
422
423
424
425
426
427static int read_bytes[3];
428static int read_byte_count;
429
430
431
432
433
434
435
436
437
438static void sample_raw(int d[3])
439{
440 d[0]=raw_data[0];
441 d[1]=raw_data[1];
442 d[2]=raw_data[2];
443}
444
445
446
447
448
449
450
451
452
453
454static void sample_rare(int d[3])
455{
456 int thisd, thisdd, thisx, thisy;
457
458 read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
459
460 d[0]=(thisd?0x80:0)
461 | (thisx/256)<<4
462 | (thisdd?0x08:0)
463 | (thisy/256)
464 ;
465 d[1]=thisx%256;
466 d[2]=thisy%256;
467}
468
469
470
471
472
473
474
475
476
477
478static void sample_debug(int d[3])
479{
480 int thisd, thisdd, thisx, thisy;
481 int b;
482 unsigned long flags;
483
484 save_flags(flags);
485 cli();
486 read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
487 d[0]=(thisd?0x80:0) | (thisdd?0x40:0) | bounce;
488 d[1]=(recent_transition?0x80:0)+transition_count;
489 read_button(&b);
490 d[2]=(synthesize_tap<<4) | (b?0x01:0);
491 restore_flags(flags);
492}
493
494
495
496
497
498
499
500
501
502
503
504static void sample_ps2(int d[3])
505{
506 static int lastx, lasty, lastd;
507
508 int thisd, thisdd, thisx, thisy;
509 int dx, dy, b;
510
511
512
513
514
515
516
517 read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
518 read_button(&b);
519
520
521
522
523 if( (thisd && !lastd)
524 || (bounce!=NO_BOUNCE) )
525 {
526 dx=0;
527 dy=0;
528 }
529 else
530 {
531 dx = (thisx-lastx);
532 dy = -(thisy-lasty);
533 }
534 lastx=thisx;
535 lasty=thisy;
536 lastd=thisd;
537
538
539
540
541
542
543
544
545 d[0]= ((dy<0)?0x20:0)
546 | ((dx<0)?0x10:0)
547 | (b? 0x00:0x08)
548 ;
549 d[1]=dx;
550 d[2]=dy;
551}
552
553
554
555
556
557
558
559
560
561
562
563
564static int fasync_pad(int fd, struct file *filp, int on)
565{
566 int retval;
567
568 retval = fasync_helper(fd, filp, on, &asyncptr);
569 if (retval < 0)
570 return retval;
571 return 0;
572}
573
574
575
576
577
578
579
580
581
582
583
584
585static int close_pad(struct inode * inode, struct file * file)
586{
587 lock_kernel();
588 fasync_pad(-1, file, 0);
589 if (!--active)
590 outb(0x30, current_params.io+2);
591 unlock_kernel();
592 return 0;
593}
594
595
596
597
598
599
600
601
602
603
604
605
606
607static int open_pad(struct inode * inode, struct file * file)
608{
609 unsigned long flags;
610
611 if (active++)
612 return 0;
613
614 save_flags(flags);
615 cli();
616 outb(0x30, current_params.io+2);
617 pad_irq(0,0,0);
618 pad_irq(0,0,0);
619 pad_irq(0,0,0);
620 outb(0x38, current_params.io+2);
621 current_params = default_params;
622 raw_data_count=0;
623 read_byte_count=0;
624 button_pending=0;
625 recent_transition=0;
626 transition_count=0;
627 synthesize_tap=0;
628 del_timer(&bounce_timer);
629 del_timer(&tap_timer);
630 restore_flags(flags);
631
632 return 0;
633}
634
635
636
637
638
639
640
641
642
643
644
645
646
647static ssize_t write_pad(struct file * file, const char * buffer, size_t count, loff_t *ppos)
648{
649 return -EINVAL;
650}
651
652
653
654
655
656
657
658
659
660
661void new_sample(int d[3])
662{
663 switch(current_params.mode)
664 {
665 case PC110PAD_RAW: sample_raw(d); break;
666 case PC110PAD_RARE: sample_rare(d); break;
667 case PC110PAD_DEBUG: sample_debug(d); break;
668 case PC110PAD_PS2: sample_ps2(d); break;
669 }
670}
671
672
673
674
675
676
677
678
679
680
681
682
683
684static ssize_t read_pad(struct file * file, char * buffer, size_t count, loff_t *ppos)
685{
686 int r;
687
688 down(&reader_lock);
689 for(r=0; r<count; r++)
690 {
691 if(!read_byte_count)
692 new_sample(read_bytes);
693 if(put_user(read_bytes[read_byte_count], buffer+r))
694 {
695 r = -EFAULT;
696 break;
697 }
698 read_byte_count = (read_byte_count+1)%3;
699 }
700 up(&reader_lock);
701 return r;
702}
703
704
705
706
707
708
709
710
711
712
713
714
715static unsigned int pad_poll(struct file *file, poll_table * wait)
716{
717 poll_wait(file, &queue, wait);
718 if(button_pending || xy_pending)
719 return POLLIN | POLLRDNORM;
720 return 0;
721}
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738static int pad_ioctl(struct inode *inode, struct file * file,
739 unsigned int cmd, unsigned long arg)
740{
741 struct pc110pad_params new;
742
743 if (!inode)
744 return -EINVAL;
745
746 switch (cmd) {
747 case PC110PADIOCGETP:
748 new = current_params;
749 if(copy_to_user((void *)arg, &new, sizeof(new)))
750 return -EFAULT;
751 return 0;
752
753 case PC110PADIOCSETP:
754 if(copy_from_user(&new, (void *)arg, sizeof(new)))
755 return -EFAULT;
756
757 if( (new.mode<PC110PAD_RAW)
758 || (new.mode>PC110PAD_PS2)
759 || (new.bounce_interval<0)
760 || (new.tap_interval<0)
761 )
762 return -EINVAL;
763
764 current_params.mode = new.mode;
765 current_params.bounce_interval = new.bounce_interval;
766 current_params.tap_interval = new.tap_interval;
767 return 0;
768 }
769 return -ENOTTY;
770}
771
772
773static struct file_operations pad_fops = {
774 owner: THIS_MODULE,
775 read: read_pad,
776 write: write_pad,
777 poll: pad_poll,
778 ioctl: pad_ioctl,
779 open: open_pad,
780 release: close_pad,
781 fasync: fasync_pad,
782};
783
784
785static struct miscdevice pc110_pad = {
786 minor: PC110PAD_MINOR,
787 name: "pc110 pad",
788 fops: &pad_fops,
789};
790
791
792
793
794
795
796
797
798
799
800
801static char banner[] __initdata = KERN_INFO "PC110 digitizer pad at 0x%X, irq %d.\n";
802
803static int __init pc110pad_init_driver(void)
804{
805 init_MUTEX(&reader_lock);
806 current_params = default_params;
807
808 if (request_irq(current_params.irq, pad_irq, 0, "pc110pad", 0)) {
809 printk(KERN_ERR "pc110pad: Unable to get IRQ.\n");
810 return -EBUSY;
811 }
812 if (!request_region(current_params.io, 4, "pc110pad")) {
813 printk(KERN_ERR "pc110pad: I/O area in use.\n");
814 free_irq(current_params.irq,0);
815 return -EBUSY;
816 }
817 init_waitqueue_head(&queue);
818 printk(banner, current_params.io, current_params.irq);
819 misc_register(&pc110_pad);
820 outb(0x30, current_params.io+2);
821 return 0;
822}
823
824
825
826
827
828
829
830
831static void __exit pc110pad_exit_driver(void)
832{
833 outb(0x30, current_params.io+2);
834 if (current_params.irq)
835 free_irq(current_params.irq, 0);
836 current_params.irq = 0;
837 release_region(current_params.io, 4);
838 misc_deregister(&pc110_pad);
839}
840
841module_init(pc110pad_init_driver);
842module_exit(pc110pad_exit_driver);
843
844MODULE_AUTHOR("Alan Cox, Robin O'Leary");
845MODULE_DESCRIPTION("Driver for the touchpad on the IBM PC110 palmtop");
846MODULE_LICENSE("GPL");
847
848EXPORT_NO_SYMBOLS;
849