Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  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");