n_tracesink.c 6.6 KB

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