Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v5.3 233 lines 6.7 kB view raw
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 __user *buf, size_t nr) { 122 return -EINVAL; 123} 124 125/** 126 * n_tracerouter_write() - Function that allows write() in userspace. 127 * @tty: terminal device passed into the ldisc. 128 * @file: pointer to open file object. 129 * @buf: pointer to the data buffer that gets eventually returned. 130 * @nr: number of bytes of the data buffer that is returned. 131 * 132 * By default if this is not implemented, it returns -EIO. 133 * This should not be implemented, ever, because 134 * 1. this driver is functioning like a router via 135 * n_tracerouter_receivebuf() 136 * 2. No writes to HW will ever go through this line discpline driver. 137 * However, an error return value other than -EIO should be used 138 * just to show that there was an intent not to have this function 139 * implemented. Return value based on write() man pages. 140 * 141 * Return: 142 * -EINVAL 143 */ 144static ssize_t n_tracerouter_write(struct tty_struct *tty, struct file *file, 145 const unsigned char *buf, size_t nr) { 146 return -EINVAL; 147} 148 149/** 150 * n_tracerouter_receivebuf() - Routing function for driver. 151 * @tty: terminal device passed into the ldisc. It's assumed 152 * tty will never be NULL. 153 * @cp: buffer, block of characters to be eventually read by 154 * someone, somewhere (user read() call or some kernel function). 155 * @fp: flag buffer. 156 * @count: number of characters (aka, bytes) in cp. 157 * 158 * This function takes the input buffer, cp, and passes it to 159 * an external API function for processing. 160 */ 161static void n_tracerouter_receivebuf(struct tty_struct *tty, 162 const unsigned char *cp, 163 char *fp, int count) 164{ 165 mutex_lock(&routelock); 166 n_tracesink_datadrain((u8 *) cp, count); 167 mutex_unlock(&routelock); 168} 169 170/* 171 * Flush buffer is not impelemented as the ldisc has no internal buffering 172 * so the tty_driver_flush_buffer() is sufficient for this driver's needs. 173 */ 174 175static struct tty_ldisc_ops tty_ptirouter_ldisc = { 176 .owner = THIS_MODULE, 177 .magic = TTY_LDISC_MAGIC, 178 .name = DRIVERNAME, 179 .open = n_tracerouter_open, 180 .close = n_tracerouter_close, 181 .read = n_tracerouter_read, 182 .write = n_tracerouter_write, 183 .receive_buf = n_tracerouter_receivebuf 184}; 185 186/** 187 * n_tracerouter_init - module initialisation 188 * 189 * Registers this module as a line discipline driver. 190 * 191 * Return: 192 * 0 for success, any other value error. 193 */ 194static int __init n_tracerouter_init(void) 195{ 196 int retval; 197 198 tr_data = kzalloc(sizeof(struct tracerouter_data), GFP_KERNEL); 199 if (tr_data == NULL) 200 return -ENOMEM; 201 202 203 /* Note N_TRACEROUTER is defined in linux/tty.h */ 204 retval = tty_register_ldisc(N_TRACEROUTER, &tty_ptirouter_ldisc); 205 if (retval < 0) { 206 pr_err("%s: Registration failed: %d\n", __func__, retval); 207 kfree(tr_data); 208 } 209 return retval; 210} 211 212/** 213 * n_tracerouter_exit - module unload 214 * 215 * Removes this module as a line discipline driver. 216 */ 217static void __exit n_tracerouter_exit(void) 218{ 219 int retval = tty_unregister_ldisc(N_TRACEROUTER); 220 221 if (retval < 0) 222 pr_err("%s: Unregistration failed: %d\n", __func__, retval); 223 else 224 kfree(tr_data); 225} 226 227module_init(n_tracerouter_init); 228module_exit(n_tracerouter_exit); 229 230MODULE_LICENSE("GPL"); 231MODULE_AUTHOR("Jay Freyensee"); 232MODULE_ALIAS_LDISC(N_TRACEROUTER); 233MODULE_DESCRIPTION("Trace router ldisc driver");