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