linux/drivers/tty/n_tracerouter.c
<<
>>
Prefs
   1/*
   2 *  n_tracerouter.c - Trace data router through tty space
   3 *
   4 *  Copyright (C) Intel 2011
   5 *
   6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   7 *
   8 *  This program is free software; you can redistribute it and/or modify
   9 *  it under the terms of the GNU General Public License version 2
  10 *  as published by the Free Software Foundation.
  11 *
  12 *  This program is distributed in the hope that it will be useful,
  13 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 *  GNU General Public License for more details.
  16 *
  17 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  18 *
  19 * This trace router uses the Linux line discipline framework to route
  20 * trace data coming from a HW Modem to a PTI (Parallel Trace Module) port.
  21 * The solution is not specific to a HW modem and this line disciple can
  22 * be used to route any stream of data in kernel space.
  23 * This is part of a solution for the P1149.7, compact JTAG, standard.
  24 */
  25
  26#include <linux/init.h>
  27#include <linux/kernel.h>
  28#include <linux/module.h>
  29#include <linux/types.h>
  30#include <linux/ioctl.h>
  31#include <linux/tty.h>
  32#include <linux/tty_ldisc.h>
  33#include <linux/errno.h>
  34#include <linux/string.h>
  35#include <linux/mutex.h>
  36#include <linux/slab.h>
  37#include <asm-generic/bug.h>
  38#include "n_tracesink.h"
  39
  40/*
  41 * Other ldisc drivers use 65536 which basically means,
  42 * 'I can always accept 64k' and flow control is off.
  43 * This number is deemed appropriate for this driver.
  44 */
  45#define RECEIVE_ROOM    65536
  46#define DRIVERNAME      "n_tracerouter"
  47
  48/*
  49 * struct to hold private configuration data for this ldisc.
  50 * opencalled is used to hold if this ldisc has been opened.
  51 * kref_tty holds the tty reference the ldisc sits on top of.
  52 */
  53struct tracerouter_data {
  54        u8 opencalled;
  55        struct tty_struct *kref_tty;
  56};
  57static struct tracerouter_data *tr_data;
  58
  59/* lock for when tty reference is being used */
  60static DEFINE_MUTEX(routelock);
  61
  62/**
  63 * n_tracerouter_open() - Called when a tty is opened by a SW entity.
  64 * @tty: terminal device to the ldisc.
  65 *
  66 * Return:
  67 *      0 for success.
  68 *
  69 * Caveats: This should only be opened one time per SW entity.
  70 */
  71static int n_tracerouter_open(struct tty_struct *tty)
  72{
  73        int retval = -EEXIST;
  74
  75        mutex_lock(&routelock);
  76        if (tr_data->opencalled == 0) {
  77
  78                tr_data->kref_tty = tty_kref_get(tty);
  79                if (tr_data->kref_tty == NULL) {
  80                        retval = -EFAULT;
  81                } else {
  82                        tr_data->opencalled = 1;
  83                        tty->disc_data      = tr_data;
  84                        tty->receive_room   = RECEIVE_ROOM;
  85                        tty_driver_flush_buffer(tty);
  86                        retval = 0;
  87                }
  88        }
  89        mutex_unlock(&routelock);
  90        return retval;
  91}
  92
  93/**
  94 * n_tracerouter_close() - close connection
  95 * @tty: terminal device to the ldisc.
  96 *
  97 * Called when a software entity wants to close a connection.
  98 */
  99static void n_tracerouter_close(struct tty_struct *tty)
 100{
 101        struct tracerouter_data *tptr = tty->disc_data;
 102
 103        mutex_lock(&routelock);
 104        WARN_ON(tptr->kref_tty != tr_data->kref_tty);
 105        tty_driver_flush_buffer(tty);
 106        tty_kref_put(tr_data->kref_tty);
 107        tr_data->kref_tty = NULL;
 108        tr_data->opencalled = 0;
 109        tty->disc_data = NULL;
 110        mutex_unlock(&routelock);
 111}
 112
 113/**
 114 * n_tracerouter_read() - read request from user space
 115 * @tty:  terminal device passed into the ldisc.
 116 * @file: pointer to open file object.
 117 * @buf:  pointer to the data buffer that gets eventually returned.
 118 * @nr:   number of bytes of the data buffer that is returned.
 119 *
 120 * function that allows read() functionality in userspace. By default if this
 121 * is not implemented it returns -EIO. This module is functioning like a
 122 * router via n_tracerouter_receivebuf(), and there is no real requirement
 123 * to implement this function. However, an error return value other than
 124 * -EIO should be used just to show that there was an intent not to have
 125 * this function implemented.  Return value based on read() man pages.
 126 *
 127 * Return:
 128 *       -EINVAL
 129 */
 130static ssize_t n_tracerouter_read(struct tty_struct *tty, struct file *file,
 131                                  unsigned char __user *buf, size_t nr) {
 132        return -EINVAL;
 133}
 134
 135/**
 136 * n_tracerouter_write() - Function that allows write() in userspace.
 137 * @tty:  terminal device passed into the ldisc.
 138 * @file: pointer to open file object.
 139 * @buf:  pointer to the data buffer that gets eventually returned.
 140 * @nr:   number of bytes of the data buffer that is returned.
 141 *
 142 * By default if this is not implemented, it returns -EIO.
 143 * This should not be implemented, ever, because
 144 * 1. this driver is functioning like a router via
 145 *    n_tracerouter_receivebuf()
 146 * 2. No writes to HW will ever go through this line discpline driver.
 147 * However, an error return value other than -EIO should be used
 148 * just to show that there was an intent not to have this function
 149 * implemented.  Return value based on write() man pages.
 150 *
 151 * Return:
 152 *      -EINVAL
 153 */
 154static ssize_t n_tracerouter_write(struct tty_struct *tty, struct file *file,
 155                                   const unsigned char *buf, size_t nr) {
 156        return -EINVAL;
 157}
 158
 159/**
 160 * n_tracerouter_receivebuf() - Routing function for driver.
 161 * @tty: terminal device passed into the ldisc.  It's assumed
 162 *       tty will never be NULL.
 163 * @cp:  buffer, block of characters to be eventually read by
 164 *       someone, somewhere (user read() call or some kernel function).
 165 * @fp:  flag buffer.
 166 * @count: number of characters (aka, bytes) in cp.
 167 *
 168 * This function takes the input buffer, cp, and passes it to
 169 * an external API function for processing.
 170 */
 171static void n_tracerouter_receivebuf(struct tty_struct *tty,
 172                                        const unsigned char *cp,
 173                                        char *fp, int count)
 174{
 175        mutex_lock(&routelock);
 176        n_tracesink_datadrain((u8 *) cp, count);
 177        mutex_unlock(&routelock);
 178}
 179
 180/*
 181 * Flush buffer is not impelemented as the ldisc has no internal buffering
 182 * so the tty_driver_flush_buffer() is sufficient for this driver's needs.
 183 */
 184
 185static struct tty_ldisc_ops tty_ptirouter_ldisc = {
 186        .owner          = THIS_MODULE,
 187        .magic          = TTY_LDISC_MAGIC,
 188        .name           = DRIVERNAME,
 189        .open           = n_tracerouter_open,
 190        .close          = n_tracerouter_close,
 191        .read           = n_tracerouter_read,
 192        .write          = n_tracerouter_write,
 193        .receive_buf    = n_tracerouter_receivebuf
 194};
 195
 196/**
 197 * n_tracerouter_init - module initialisation
 198 *
 199 * Registers this module as a line discipline driver.
 200 *
 201 * Return:
 202 *      0 for success, any other value error.
 203 */
 204static int __init n_tracerouter_init(void)
 205{
 206        int retval;
 207
 208        tr_data = kzalloc(sizeof(struct tracerouter_data), GFP_KERNEL);
 209        if (tr_data == NULL)
 210                return -ENOMEM;
 211
 212
 213        /* Note N_TRACEROUTER is defined in linux/tty.h */
 214        retval = tty_register_ldisc(N_TRACEROUTER, &tty_ptirouter_ldisc);
 215        if (retval < 0) {
 216                pr_err("%s: Registration failed: %d\n", __func__, retval);
 217                kfree(tr_data);
 218        }
 219        return retval;
 220}
 221
 222/**
 223 * n_tracerouter_exit - module unload
 224 *
 225 * Removes this module as a line discipline driver.
 226 */
 227static void __exit n_tracerouter_exit(void)
 228{
 229        int retval = tty_unregister_ldisc(N_TRACEROUTER);
 230
 231        if (retval < 0)
 232                pr_err("%s: Unregistration failed: %d\n", __func__,  retval);
 233        else
 234                kfree(tr_data);
 235}
 236
 237module_init(n_tracerouter_init);
 238module_exit(n_tracerouter_exit);
 239
 240MODULE_LICENSE("GPL");
 241MODULE_AUTHOR("Jay Freyensee");
 242MODULE_ALIAS_LDISC(N_TRACEROUTER);
 243MODULE_DESCRIPTION("Trace router ldisc driver");
 244