LCOV - code coverage report
Current view: top level - drivers/input/serio - serport.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 4 98 4.1 %
Date: 2023-04-06 08:38:28 Functions: 1 12 8.3 %

          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             :         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             :         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             :         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,
     118             :                 const unsigned char *cp, const char *fp, int count)
     119             : {
     120           0 :         struct serport *serport = (struct serport*) tty->disc_data;
     121             :         unsigned long flags;
     122           0 :         unsigned int ch_flags = 0;
     123             :         int i;
     124             : 
     125           0 :         spin_lock_irqsave(&serport->lock, flags);
     126             : 
     127           0 :         if (!test_bit(SERPORT_ACTIVE, &serport->flags))
     128             :                 goto out;
     129             : 
     130           0 :         for (i = 0; i < count; i++) {
     131           0 :                 if (fp) {
     132           0 :                         switch (fp[i]) {
     133             :                         case TTY_FRAME:
     134             :                                 ch_flags = SERIO_FRAME;
     135             :                                 break;
     136             : 
     137             :                         case TTY_PARITY:
     138           0 :                                 ch_flags = SERIO_PARITY;
     139           0 :                                 break;
     140             : 
     141             :                         default:
     142           0 :                                 ch_flags = 0;
     143           0 :                                 break;
     144             :                         }
     145             :                 }
     146             : 
     147           0 :                 serio_interrupt(serport->serio, cp[i], ch_flags);
     148             :         }
     149             : 
     150             : out:
     151           0 :         spin_unlock_irqrestore(&serport->lock, flags);
     152           0 : }
     153             : 
     154             : /*
     155             :  * serport_ldisc_read() just waits indefinitely if everything goes well.
     156             :  * However, when the serio driver closes the serio port, it finishes,
     157             :  * returning 0 characters.
     158             :  */
     159             : 
     160           0 : static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file,
     161             :                                   unsigned char *kbuf, size_t nr,
     162             :                                   void **cookie, unsigned long offset)
     163             : {
     164           0 :         struct serport *serport = (struct serport*) tty->disc_data;
     165             :         struct serio *serio;
     166             : 
     167           0 :         if (test_and_set_bit(SERPORT_BUSY, &serport->flags))
     168             :                 return -EBUSY;
     169             : 
     170           0 :         serport->serio = serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
     171           0 :         if (!serio)
     172             :                 return -ENOMEM;
     173             : 
     174           0 :         strscpy(serio->name, "Serial port", sizeof(serio->name));
     175           0 :         snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty));
     176           0 :         serio->id = serport->id;
     177           0 :         serio->id.type = SERIO_RS232;
     178           0 :         serio->write = serport_serio_write;
     179           0 :         serio->open = serport_serio_open;
     180           0 :         serio->close = serport_serio_close;
     181           0 :         serio->port_data = serport;
     182           0 :         serio->dev.parent = tty->dev;
     183             : 
     184           0 :         serio_register_port(serport->serio);
     185           0 :         printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty));
     186             : 
     187           0 :         wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags));
     188           0 :         serio_unregister_port(serport->serio);
     189           0 :         serport->serio = NULL;
     190             : 
     191           0 :         clear_bit(SERPORT_DEAD, &serport->flags);
     192           0 :         clear_bit(SERPORT_BUSY, &serport->flags);
     193             : 
     194           0 :         return 0;
     195             : }
     196             : 
     197             : static void serport_set_type(struct tty_struct *tty, unsigned long type)
     198             : {
     199           0 :         struct serport *serport = tty->disc_data;
     200             : 
     201           0 :         serport->id.proto = type & 0x000000ff;
     202           0 :         serport->id.id    = (type & 0x0000ff00) >> 8;
     203           0 :         serport->id.extra = (type & 0x00ff0000) >> 16;
     204             : }
     205             : 
     206             : /*
     207             :  * serport_ldisc_ioctl() allows to set the port protocol, and device ID
     208             :  */
     209             : 
     210           0 : static int serport_ldisc_ioctl(struct tty_struct *tty, unsigned int cmd,
     211             :                                unsigned long arg)
     212             : {
     213           0 :         if (cmd == SPIOCSTYPE) {
     214             :                 unsigned long type;
     215             : 
     216           0 :                 if (get_user(type, (unsigned long __user *) arg))
     217             :                         return -EFAULT;
     218             : 
     219           0 :                 serport_set_type(tty, type);
     220           0 :                 return 0;
     221             :         }
     222             : 
     223             :         return -EINVAL;
     224             : }
     225             : 
     226             : #ifdef CONFIG_COMPAT
     227             : #define COMPAT_SPIOCSTYPE       _IOW('q', 0x01, compat_ulong_t)
     228             : static int serport_ldisc_compat_ioctl(struct tty_struct *tty,
     229             :                                        unsigned int cmd, unsigned long arg)
     230             : {
     231             :         if (cmd == COMPAT_SPIOCSTYPE) {
     232             :                 void __user *uarg = compat_ptr(arg);
     233             :                 compat_ulong_t compat_type;
     234             : 
     235             :                 if (get_user(compat_type, (compat_ulong_t __user *)uarg))
     236             :                         return -EFAULT;
     237             : 
     238             :                 serport_set_type(tty, compat_type);
     239             :                 return 0;
     240             :         }
     241             : 
     242             :         return -EINVAL;
     243             : }
     244             : #endif
     245             : 
     246           0 : static void serport_ldisc_hangup(struct tty_struct *tty)
     247             : {
     248           0 :         struct serport *serport = (struct serport *) tty->disc_data;
     249             :         unsigned long flags;
     250             : 
     251           0 :         spin_lock_irqsave(&serport->lock, flags);
     252           0 :         set_bit(SERPORT_DEAD, &serport->flags);
     253           0 :         spin_unlock_irqrestore(&serport->lock, flags);
     254             : 
     255           0 :         wake_up_interruptible(&serport->wait);
     256           0 : }
     257             : 
     258           0 : static void serport_ldisc_write_wakeup(struct tty_struct * tty)
     259             : {
     260           0 :         struct serport *serport = (struct serport *) tty->disc_data;
     261             :         unsigned long flags;
     262             : 
     263           0 :         spin_lock_irqsave(&serport->lock, flags);
     264           0 :         if (test_bit(SERPORT_ACTIVE, &serport->flags))
     265           0 :                 serio_drv_write_wakeup(serport->serio);
     266           0 :         spin_unlock_irqrestore(&serport->lock, flags);
     267           0 : }
     268             : 
     269             : /*
     270             :  * The line discipline structure.
     271             :  */
     272             : 
     273             : static struct tty_ldisc_ops serport_ldisc = {
     274             :         .owner =        THIS_MODULE,
     275             :         .num =          N_MOUSE,
     276             :         .name =         "input",
     277             :         .open =         serport_ldisc_open,
     278             :         .close =        serport_ldisc_close,
     279             :         .read =         serport_ldisc_read,
     280             :         .ioctl =        serport_ldisc_ioctl,
     281             : #ifdef CONFIG_COMPAT
     282             :         .compat_ioctl = serport_ldisc_compat_ioctl,
     283             : #endif
     284             :         .receive_buf =  serport_ldisc_receive,
     285             :         .hangup =       serport_ldisc_hangup,
     286             :         .write_wakeup = serport_ldisc_write_wakeup
     287             : };
     288             : 
     289             : /*
     290             :  * The functions for insering/removing us as a module.
     291             :  */
     292             : 
     293           1 : static int __init serport_init(void)
     294             : {
     295             :         int retval;
     296           1 :         retval = tty_register_ldisc(&serport_ldisc);
     297           1 :         if (retval)
     298           0 :                 printk(KERN_ERR "serport.c: Error registering line discipline.\n");
     299             : 
     300           1 :         return  retval;
     301             : }
     302             : 
     303           0 : static void __exit serport_exit(void)
     304             : {
     305           0 :         tty_unregister_ldisc(&serport_ldisc);
     306           0 : }
     307             : 
     308             : module_init(serport_init);
     309             : module_exit(serport_exit);

Generated by: LCOV version 1.14