xref: /OK3568_Linux_fs/kernel/drivers/input/serio/serport.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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