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