Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * Input device TTY line discipline
4 : : *
5 : : * Copyright (c) 1999-2002 Vojtech Pavlik
6 : : *
7 : : * This is a module that converts a tty line into a much simpler
8 : : * 'serial io port' abstraction that the input device drivers use.
9 : : */
10 : :
11 : :
12 : : #include <linux/uaccess.h>
13 : : #include <linux/kernel.h>
14 : : #include <linux/sched.h>
15 : : #include <linux/slab.h>
16 : : #include <linux/module.h>
17 : : #include <linux/init.h>
18 : : #include <linux/serio.h>
19 : : #include <linux/tty.h>
20 : : #include <linux/compat.h>
21 : :
22 : : MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
23 : : MODULE_DESCRIPTION("Input device TTY line discipline");
24 : : MODULE_LICENSE("GPL");
25 : : MODULE_ALIAS_LDISC(N_MOUSE);
26 : :
27 : : #define SERPORT_BUSY 1
28 : : #define SERPORT_ACTIVE 2
29 : : #define SERPORT_DEAD 3
30 : :
31 : : struct serport {
32 : : struct tty_struct *tty;
33 : : wait_queue_head_t wait;
34 : : struct serio *serio;
35 : : struct serio_device_id id;
36 : : spinlock_t lock;
37 : : unsigned long flags;
38 : : };
39 : :
40 : : /*
41 : : * Callback functions from the serio code.
42 : : */
43 : :
44 : 0 : static int serport_serio_write(struct serio *serio, unsigned char data)
45 : : {
46 : 0 : struct serport *serport = serio->port_data;
47 : 0 : return -(serport->tty->ops->write(serport->tty, &data, 1) != 1);
48 : : }
49 : :
50 : 0 : static int serport_serio_open(struct serio *serio)
51 : : {
52 : 0 : struct serport *serport = serio->port_data;
53 : 0 : unsigned long flags;
54 : :
55 : 0 : spin_lock_irqsave(&serport->lock, flags);
56 : 0 : set_bit(SERPORT_ACTIVE, &serport->flags);
57 : 0 : spin_unlock_irqrestore(&serport->lock, flags);
58 : :
59 : 0 : return 0;
60 : : }
61 : :
62 : :
63 : 0 : static void serport_serio_close(struct serio *serio)
64 : : {
65 : 0 : struct serport *serport = serio->port_data;
66 : 0 : unsigned long flags;
67 : :
68 : 0 : spin_lock_irqsave(&serport->lock, flags);
69 : 0 : clear_bit(SERPORT_ACTIVE, &serport->flags);
70 : 0 : spin_unlock_irqrestore(&serport->lock, flags);
71 : 0 : }
72 : :
73 : : /*
74 : : * serport_ldisc_open() is the routine that is called upon setting our line
75 : : * discipline on a tty. It prepares the serio struct.
76 : : */
77 : :
78 : 0 : static int serport_ldisc_open(struct tty_struct *tty)
79 : : {
80 : 0 : struct serport *serport;
81 : :
82 [ # # ]: 0 : if (!capable(CAP_SYS_ADMIN))
83 : : return -EPERM;
84 : :
85 : 0 : serport = kzalloc(sizeof(struct serport), GFP_KERNEL);
86 [ # # ]: 0 : if (!serport)
87 : : return -ENOMEM;
88 : :
89 : 0 : serport->tty = tty;
90 : 0 : spin_lock_init(&serport->lock);
91 : 0 : init_waitqueue_head(&serport->wait);
92 : :
93 : 0 : tty->disc_data = serport;
94 : 0 : tty->receive_room = 256;
95 : 0 : set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
96 : :
97 : 0 : return 0;
98 : : }
99 : :
100 : : /*
101 : : * serport_ldisc_close() is the opposite of serport_ldisc_open()
102 : : */
103 : :
104 : 0 : static void serport_ldisc_close(struct tty_struct *tty)
105 : : {
106 : 0 : struct serport *serport = (struct serport *) tty->disc_data;
107 : :
108 : 0 : kfree(serport);
109 : 0 : }
110 : :
111 : : /*
112 : : * serport_ldisc_receive() is called by the low level tty driver when characters
113 : : * are ready for us. We forward the characters and flags, one by one to the
114 : : * 'interrupt' routine.
115 : : */
116 : :
117 : 0 : static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
118 : : {
119 : 0 : struct serport *serport = (struct serport*) tty->disc_data;
120 : 0 : unsigned long flags;
121 : 0 : unsigned int ch_flags = 0;
122 : 0 : int i;
123 : :
124 : 0 : spin_lock_irqsave(&serport->lock, flags);
125 : :
126 [ # # ]: 0 : if (!test_bit(SERPORT_ACTIVE, &serport->flags))
127 : 0 : goto out;
128 : :
129 [ # # ]: 0 : for (i = 0; i < count; i++) {
130 [ # # ]: 0 : if (fp) {
131 [ # # # ]: 0 : switch (fp[i]) {
132 : : case TTY_FRAME:
133 : : ch_flags = SERIO_FRAME;
134 : : break;
135 : :
136 : 0 : case TTY_PARITY:
137 : 0 : ch_flags = SERIO_PARITY;
138 : 0 : break;
139 : :
140 : 0 : default:
141 : 0 : ch_flags = 0;
142 : 0 : break;
143 : : }
144 : 0 : }
145 : :
146 : 0 : serio_interrupt(serport->serio, cp[i], ch_flags);
147 : : }
148 : :
149 : 0 : out:
150 : 0 : spin_unlock_irqrestore(&serport->lock, flags);
151 : 0 : }
152 : :
153 : : /*
154 : : * serport_ldisc_read() just waits indefinitely if everything goes well.
155 : : * However, when the serio driver closes the serio port, it finishes,
156 : : * returning 0 characters.
157 : : */
158 : :
159 : 0 : static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char __user * buf, size_t nr)
160 : : {
161 : 0 : struct serport *serport = (struct serport*) tty->disc_data;
162 : 0 : struct serio *serio;
163 : :
164 [ # # ]: 0 : if (test_and_set_bit(SERPORT_BUSY, &serport->flags))
165 : : return -EBUSY;
166 : :
167 : 0 : serport->serio = serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
168 [ # # ]: 0 : if (!serio)
169 : : return -ENOMEM;
170 : :
171 : 0 : strlcpy(serio->name, "Serial port", sizeof(serio->name));
172 : 0 : snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty));
173 : 0 : serio->id = serport->id;
174 : 0 : serio->id.type = SERIO_RS232;
175 : 0 : serio->write = serport_serio_write;
176 : 0 : serio->open = serport_serio_open;
177 : 0 : serio->close = serport_serio_close;
178 : 0 : serio->port_data = serport;
179 : 0 : serio->dev.parent = tty->dev;
180 : :
181 : 0 : serio_register_port(serport->serio);
182 : 0 : printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty));
183 : :
184 [ # # # # : 0 : wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags));
# # ]
185 : 0 : serio_unregister_port(serport->serio);
186 : 0 : serport->serio = NULL;
187 : :
188 : 0 : clear_bit(SERPORT_DEAD, &serport->flags);
189 : 0 : clear_bit(SERPORT_BUSY, &serport->flags);
190 : :
191 : 0 : return 0;
192 : : }
193 : :
194 : 0 : static void serport_set_type(struct tty_struct *tty, unsigned long type)
195 : : {
196 : 0 : struct serport *serport = tty->disc_data;
197 : :
198 : 0 : serport->id.proto = type & 0x000000ff;
199 : 0 : serport->id.id = (type & 0x0000ff00) >> 8;
200 : 0 : serport->id.extra = (type & 0x00ff0000) >> 16;
201 : : }
202 : :
203 : : /*
204 : : * serport_ldisc_ioctl() allows to set the port protocol, and device ID
205 : : */
206 : :
207 : 0 : static int serport_ldisc_ioctl(struct tty_struct *tty, struct file *file,
208 : : unsigned int cmd, unsigned long arg)
209 : : {
210 [ # # ]: 0 : if (cmd == SPIOCSTYPE) {
211 : 0 : unsigned long type;
212 : :
213 [ # # ]: 0 : if (get_user(type, (unsigned long __user *) arg))
214 : : return -EFAULT;
215 : :
216 : 0 : serport_set_type(tty, type);
217 : 0 : return 0;
218 : : }
219 : :
220 : : return -EINVAL;
221 : : }
222 : :
223 : : #ifdef CONFIG_COMPAT
224 : : #define COMPAT_SPIOCSTYPE _IOW('q', 0x01, compat_ulong_t)
225 : 0 : static int serport_ldisc_compat_ioctl(struct tty_struct *tty,
226 : : struct file *file,
227 : : unsigned int cmd, unsigned long arg)
228 : : {
229 [ # # ]: 0 : if (cmd == COMPAT_SPIOCSTYPE) {
230 : 0 : void __user *uarg = compat_ptr(arg);
231 : 0 : compat_ulong_t compat_type;
232 : :
233 [ # # ]: 0 : if (get_user(compat_type, (compat_ulong_t __user *)uarg))
234 : : return -EFAULT;
235 : :
236 : 0 : serport_set_type(tty, compat_type);
237 : 0 : return 0;
238 : : }
239 : :
240 : : return -EINVAL;
241 : : }
242 : : #endif
243 : :
244 : 0 : static int serport_ldisc_hangup(struct tty_struct *tty)
245 : : {
246 : 0 : struct serport *serport = (struct serport *) tty->disc_data;
247 : 0 : unsigned long flags;
248 : :
249 : 0 : spin_lock_irqsave(&serport->lock, flags);
250 : 0 : set_bit(SERPORT_DEAD, &serport->flags);
251 : 0 : spin_unlock_irqrestore(&serport->lock, flags);
252 : :
253 : 0 : wake_up_interruptible(&serport->wait);
254 : 0 : return 0;
255 : : }
256 : :
257 : 0 : static void serport_ldisc_write_wakeup(struct tty_struct * tty)
258 : : {
259 : 0 : struct serport *serport = (struct serport *) tty->disc_data;
260 : 0 : unsigned long flags;
261 : :
262 : 0 : spin_lock_irqsave(&serport->lock, flags);
263 [ # # ]: 0 : if (test_bit(SERPORT_ACTIVE, &serport->flags))
264 [ # # ]: 0 : serio_drv_write_wakeup(serport->serio);
265 : 0 : spin_unlock_irqrestore(&serport->lock, flags);
266 : 0 : }
267 : :
268 : : /*
269 : : * The line discipline structure.
270 : : */
271 : :
272 : : static struct tty_ldisc_ops serport_ldisc = {
273 : : .owner = THIS_MODULE,
274 : : .name = "input",
275 : : .open = serport_ldisc_open,
276 : : .close = serport_ldisc_close,
277 : : .read = serport_ldisc_read,
278 : : .ioctl = serport_ldisc_ioctl,
279 : : #ifdef CONFIG_COMPAT
280 : : .compat_ioctl = serport_ldisc_compat_ioctl,
281 : : #endif
282 : : .receive_buf = serport_ldisc_receive,
283 : : .hangup = serport_ldisc_hangup,
284 : : .write_wakeup = serport_ldisc_write_wakeup
285 : : };
286 : :
287 : : /*
288 : : * The functions for insering/removing us as a module.
289 : : */
290 : :
291 : 30 : static int __init serport_init(void)
292 : : {
293 : 30 : int retval;
294 : 30 : retval = tty_register_ldisc(N_MOUSE, &serport_ldisc);
295 [ - + ]: 30 : if (retval)
296 : 0 : printk(KERN_ERR "serport.c: Error registering line discipline.\n");
297 : :
298 : 30 : return retval;
299 : : }
300 : :
301 : 0 : static void __exit serport_exit(void)
302 : : {
303 : 0 : tty_unregister_ldisc(N_MOUSE);
304 : 0 : }
305 : :
306 : : module_init(serport_init);
307 : : module_exit(serport_exit);
|