Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/input/serio/parkbd.c
15111 views
1
/*
2
* Parallel port to Keyboard port adapter driver for Linux
3
*
4
* Copyright (c) 1999-2004 Vojtech Pavlik
5
*/
6
7
/*
8
* This program is free software; you can redistribute it and/or modify it
9
* under the terms of the GNU General Public License version 2 as published by
10
* the Free Software Foundation.
11
*/
12
13
/*
14
* To connect an AT or XT keyboard to the parallel port, a fairly simple adapter
15
* can be made:
16
*
17
* Parallel port Keyboard port
18
*
19
* +5V --------------------- +5V (4)
20
*
21
* ______
22
* +5V -------|______|--.
23
* |
24
* ACK (10) ------------|
25
* |--- KBD CLOCK (5)
26
* STROBE (1) ---|<|----'
27
*
28
* ______
29
* +5V -------|______|--.
30
* |
31
* BUSY (11) -----------|
32
* |--- KBD DATA (1)
33
* AUTOFD (14) --|<|----'
34
*
35
* GND (18-25) ------------- GND (3)
36
*
37
* The diodes can be fairly any type, and the resistors should be somewhere
38
* around 5 kOhm, but the adapter will likely work without the resistors,
39
* too.
40
*
41
* The +5V source can be taken either from USB, from mouse or keyboard ports,
42
* or from a joystick port. Unfortunately, the parallel port of a PC doesn't
43
* have a +5V pin, and feeding the keyboard from signal pins is out of question
44
* with 300 mA power reqirement of a typical AT keyboard.
45
*/
46
47
#include <linux/module.h>
48
#include <linux/parport.h>
49
#include <linux/slab.h>
50
#include <linux/init.h>
51
#include <linux/serio.h>
52
53
MODULE_AUTHOR("Vojtech Pavlik <[email protected]>");
54
MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver");
55
MODULE_LICENSE("GPL");
56
57
static unsigned int parkbd_pp_no;
58
module_param_named(port, parkbd_pp_no, int, 0);
59
MODULE_PARM_DESC(port, "Parallel port the adapter is connected to (default is 0)");
60
61
static unsigned int parkbd_mode = SERIO_8042;
62
module_param_named(mode, parkbd_mode, uint, 0);
63
MODULE_PARM_DESC(mode, "Mode of operation: XT = 0/AT = 1 (default)");
64
65
#define PARKBD_CLOCK 0x01 /* Strobe & Ack */
66
#define PARKBD_DATA 0x02 /* AutoFd & Busy */
67
68
static int parkbd_buffer;
69
static int parkbd_counter;
70
static unsigned long parkbd_last;
71
static int parkbd_writing;
72
static unsigned long parkbd_start;
73
74
static struct pardevice *parkbd_dev;
75
static struct serio *parkbd_port;
76
77
static int parkbd_readlines(void)
78
{
79
return (parport_read_status(parkbd_dev->port) >> 6) ^ 2;
80
}
81
82
static void parkbd_writelines(int data)
83
{
84
parport_write_control(parkbd_dev->port, (~data & 3) | 0x10);
85
}
86
87
static int parkbd_write(struct serio *port, unsigned char c)
88
{
89
unsigned char p;
90
91
if (!parkbd_mode) return -1;
92
93
p = c ^ (c >> 4);
94
p = p ^ (p >> 2);
95
p = p ^ (p >> 1);
96
97
parkbd_counter = 0;
98
parkbd_writing = 1;
99
parkbd_buffer = c | (((int) (~p & 1)) << 8) | 0x600;
100
101
parkbd_writelines(2);
102
103
return 0;
104
}
105
106
static void parkbd_interrupt(void *dev_id)
107
{
108
109
if (parkbd_writing) {
110
111
if (parkbd_counter && ((parkbd_counter == 11) || time_after(jiffies, parkbd_last + HZ/100))) {
112
parkbd_counter = 0;
113
parkbd_buffer = 0;
114
parkbd_writing = 0;
115
parkbd_writelines(3);
116
return;
117
}
118
119
parkbd_writelines(((parkbd_buffer >> parkbd_counter++) & 1) | 2);
120
121
if (parkbd_counter == 11) {
122
parkbd_counter = 0;
123
parkbd_buffer = 0;
124
parkbd_writing = 0;
125
parkbd_writelines(3);
126
}
127
128
} else {
129
130
if ((parkbd_counter == parkbd_mode + 10) || time_after(jiffies, parkbd_last + HZ/100)) {
131
parkbd_counter = 0;
132
parkbd_buffer = 0;
133
}
134
135
parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++;
136
137
if (parkbd_counter == parkbd_mode + 10)
138
serio_interrupt(parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0);
139
}
140
141
parkbd_last = jiffies;
142
}
143
144
static int parkbd_getport(void)
145
{
146
struct parport *pp;
147
148
pp = parport_find_number(parkbd_pp_no);
149
150
if (pp == NULL) {
151
printk(KERN_ERR "parkbd: no such parport\n");
152
return -ENODEV;
153
}
154
155
parkbd_dev = parport_register_device(pp, "parkbd", NULL, NULL, parkbd_interrupt, PARPORT_DEV_EXCL, NULL);
156
parport_put_port(pp);
157
158
if (!parkbd_dev)
159
return -ENODEV;
160
161
if (parport_claim(parkbd_dev)) {
162
parport_unregister_device(parkbd_dev);
163
return -EBUSY;
164
}
165
166
parkbd_start = jiffies;
167
168
return 0;
169
}
170
171
static struct serio * __init parkbd_allocate_serio(void)
172
{
173
struct serio *serio;
174
175
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
176
if (serio) {
177
serio->id.type = parkbd_mode;
178
serio->write = parkbd_write,
179
strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name));
180
snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name);
181
}
182
183
return serio;
184
}
185
186
static int __init parkbd_init(void)
187
{
188
int err;
189
190
err = parkbd_getport();
191
if (err)
192
return err;
193
194
parkbd_port = parkbd_allocate_serio();
195
if (!parkbd_port) {
196
parport_release(parkbd_dev);
197
return -ENOMEM;
198
}
199
200
parkbd_writelines(3);
201
202
serio_register_port(parkbd_port);
203
204
printk(KERN_INFO "serio: PARKBD %s adapter on %s\n",
205
parkbd_mode ? "AT" : "XT", parkbd_dev->port->name);
206
207
return 0;
208
}
209
210
static void __exit parkbd_exit(void)
211
{
212
parport_release(parkbd_dev);
213
serio_unregister_port(parkbd_port);
214
parport_unregister_device(parkbd_dev);
215
}
216
217
module_init(parkbd_init);
218
module_exit(parkbd_exit);
219
220