linux/drivers/tty/n_tracesink.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *  n_tracesink.c - Trace data router and sink path through tty space.
   4 *
   5 *  Copyright (C) Intel 2011
   6 *
   7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   8 *
   9 * The trace sink uses the Linux line discipline framework to receive
  10 * trace data coming from the PTI source line discipline driver
  11 * to a user-desired tty port, like USB.
  12 * This is to provide a way to extract modem trace data on
  13 * devices that do not have a PTI HW module, or just need modem
  14 * trace data to come out of a different HW output port.
  15 * This is part of a solution for the P1149.7, compact JTAG, standard.
  16 */
  17
  18#include <linux/init.h>
  19#include <linux/kernel.h>
  20#include <linux/module.h>
  21#include <linux/types.h>
  22#include <linux/ioctl.h>
  23#include <linux/tty.h>
  24#include <linux/tty_ldisc.h>
  25#include <linux/errno.h>
  26#include <linux/string.h>
  27#include <linux/bug.h>
  28#include "n_tracesink.h"
  29
  30/*
  31 * Other ldisc drivers use 65536 which basically means,
  32 * 'I can always accept 64k' and flow control is off.
  33 * This number is deemed appropriate for this driver.
  34 */
  35#define RECEIVE_ROOM    65536
  36#define DRIVERNAME      "n_tracesink"
  37
  38/*
  39 * there is a quirk with this ldisc is he can write data
  40 * to a tty from anyone calling his kernel API, which
  41 * meets customer requirements in the drivers/misc/pti.c
  42 * project.  So he needs to know when he can and cannot write when
  43 * the API is called. In theory, the API can be called
  44 * after an init() but before a successful open() which
  45 * would crash the system if tty is not checked.
  46 */
  47static struct tty_struct *this_tty;
  48static DEFINE_MUTEX(writelock);
  49
  50/**
  51 * n_tracesink_open() - Called when a tty is opened by a SW entity.
  52 * @tty: terminal device to the ldisc.
  53 *
  54 * Return:
  55 *      0 for success,
  56 *      -EFAULT = couldn't get a tty kref n_tracesink will sit
  57 *       on top of
  58 *      -EEXIST = open() called successfully once and it cannot
  59 *      be called again.
  60 *
  61 * Caveats: open() should only be successful the first time a
  62 * SW entity calls it.
  63 */
  64static int n_tracesink_open(struct tty_struct *tty)
  65{
  66        int retval = -EEXIST;
  67
  68        mutex_lock(&writelock);
  69        if (this_tty == NULL) {
  70                this_tty = tty_kref_get(tty);
  71                if (this_tty == NULL) {
  72                        retval = -EFAULT;
  73                } else {
  74                        tty->disc_data = this_tty;
  75                        tty_driver_flush_buffer(tty);
  76                        retval = 0;
  77                }
  78        }
  79        mutex_unlock(&writelock);
  80
  81        return retval;
  82}
  83
  84/**
  85 * n_tracesink_close() - close connection
  86 * @tty: terminal device to the ldisc.
  87 *
  88 * Called when a software entity wants to close a connection.
  89 */
  90static void n_tracesink_close(struct tty_struct *tty)
  91{
  92        mutex_lock(&writelock);
  93        tty_driver_flush_buffer(tty);
  94        tty_kref_put(this_tty);
  95        this_tty = NULL;
  96        tty->disc_data = NULL;
  97        mutex_unlock(&writelock);
  98}
  99
 100/**
 101 * n_tracesink_read() - read request from user space
 102 * @tty:  terminal device passed into the ldisc.
 103 * @file: pointer to open file object.
 104 * @buf:  pointer to the data buffer that gets eventually returned.
 105 * @nr:   number of bytes of the data buffer that is returned.
 106 *
 107 * function that allows read() functionality in userspace. By default if this
 108 * is not implemented it returns -EIO. This module is functioning like a
 109 * router via n_tracesink_receivebuf(), and there is no real requirement
 110 * to implement this function. However, an error return value other than
 111 * -EIO should be used just to show that there was an intent not to have
 112 * this function implemented.  Return value based on read() man pages.
 113 *
 114 * Return:
 115 *       -EINVAL
 116 */
 117static ssize_t n_tracesink_read(struct tty_struct *tty, struct file *file,
 118                                unsigned char __user *buf, size_t nr) {
 119        return -EINVAL;
 120}
 121
 122/**
 123 * n_tracesink_write() - Function that allows write() in userspace.
 124 * @tty:  terminal device passed into the ldisc.
 125 * @file: pointer to open file object.
 126 * @buf:  pointer to the data buffer that gets eventually returned.
 127 * @nr:   number of bytes of the data buffer that is returned.
 128 *
 129 * By default if this is not implemented, it returns -EIO.
 130 * This should not be implemented, ever, because
 131 * 1. this driver is functioning like a router via
 132 *    n_tracesink_receivebuf()
 133 * 2. No writes to HW will ever go through this line discpline driver.
 134 * However, an error return value other than -EIO should be used
 135 * just to show that there was an intent not to have this function
 136 * implemented.  Return value based on write() man pages.
 137 *
 138 * Return:
 139 *      -EINVAL
 140 */
 141static ssize_t n_tracesink_write(struct tty_struct *tty, struct file *file,
 142                                 const unsigned char *buf, size_t nr) {
 143        return -EINVAL;
 144}
 145
 146/**
 147 * n_tracesink_datadrain() - Kernel API function used to route
 148 *                           trace debugging data to user-defined
 149 *                           port like USB.
 150 *
 151 * @buf:   Trace debuging data buffer to write to tty target
 152 *         port. Null value will return with no write occurring.
 153 * @count: Size of buf. Value of 0 or a negative number will
 154 *         return with no write occuring.
 155 *
 156 * Caveat: If this line discipline does not set the tty it sits
 157 * on top of via an open() call, this API function will not
 158 * call the tty's write() call because it will have no pointer
 159 * to call the write().
 160 */
 161void n_tracesink_datadrain(u8 *buf, int count)
 162{
 163        mutex_lock(&writelock);
 164
 165        if ((buf != NULL) && (count > 0) && (this_tty != NULL))
 166                this_tty->ops->write(this_tty, buf, count);
 167
 168        mutex_unlock(&writelock);
 169}
 170EXPORT_SYMBOL_GPL(n_tracesink_datadrain);
 171
 172/*
 173 * Flush buffer is not impelemented as the ldisc has no internal buffering
 174 * so the tty_driver_flush_buffer() is sufficient for this driver's needs.
 175 */
 176
 177/*
 178 * tty_ldisc function operations for this driver.
 179 */
 180static struct tty_ldisc_ops tty_n_tracesink = {
 181        .owner          = THIS_MODULE,
 182        .magic          = TTY_LDISC_MAGIC,
 183        .name           = DRIVERNAME,
 184        .open           = n_tracesink_open,
 185        .close          = n_tracesink_close,
 186        .read           = n_tracesink_read,
 187        .write          = n_tracesink_write
 188};
 189
 190/**
 191 * n_tracesink_init-    module initialisation
 192 *
 193 * Registers this module as a line discipline driver.
 194 *
 195 * Return:
 196 *      0 for success, any other value error.
 197 */
 198static int __init n_tracesink_init(void)
 199{
 200        /* Note N_TRACESINK is defined in linux/tty.h */
 201        int retval = tty_register_ldisc(N_TRACESINK, &tty_n_tracesink);
 202
 203        if (retval < 0)
 204                pr_err("%s: Registration failed: %d\n", __func__, retval);
 205
 206        return retval;
 207}
 208
 209/**
 210 * n_tracesink_exit -   module unload
 211 *
 212 * Removes this module as a line discipline driver.
 213 */
 214static void __exit n_tracesink_exit(void)
 215{
 216        int retval = tty_unregister_ldisc(N_TRACESINK);
 217
 218        if (retval < 0)
 219                pr_err("%s: Unregistration failed: %d\n", __func__,  retval);
 220}
 221
 222module_init(n_tracesink_init);
 223module_exit(n_tracesink_exit);
 224
 225MODULE_LICENSE("GPL");
 226MODULE_AUTHOR("Jay Freyensee");
 227MODULE_ALIAS_LDISC(N_TRACESINK);
 228MODULE_DESCRIPTION("Trace sink ldisc driver");
 229