smux_loopback.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /* drivers/tty/smux_loopback.c
  2. *
  3. * Copyright (c) 2012, The Linux Foundation. All rights reserved.
  4. *
  5. * This software is licensed under the terms of the GNU General Public
  6. * License version 2, as published by the Free Software Foundation, and
  7. * may be copied, distributed, and modified under those terms.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. */
  15. #include <linux/types.h>
  16. #include <linux/err.h>
  17. #include <linux/workqueue.h>
  18. #include <linux/kfifo.h>
  19. #include <linux/slab.h>
  20. #include <linux/smux.h>
  21. #include "smux_private.h"
  22. #define SMUX_LOOP_FIFO_SIZE 128
  23. static void smux_loopback_rx_worker(struct work_struct *work);
  24. static struct workqueue_struct *smux_loopback_wq;
  25. static DECLARE_WORK(smux_loopback_work, smux_loopback_rx_worker);
  26. static struct kfifo smux_loop_pkt_fifo;
  27. static DEFINE_SPINLOCK(hw_fn_lock);
  28. /**
  29. * Initialize loopback framework (called by n_smux.c).
  30. */
  31. int smux_loopback_init(void)
  32. {
  33. int ret = 0;
  34. spin_lock_init(&hw_fn_lock);
  35. smux_loopback_wq = create_singlethread_workqueue("smux_loopback_wq");
  36. if (IS_ERR(smux_loopback_wq)) {
  37. pr_err("%s: failed to create workqueue\n", __func__);
  38. return -ENOMEM;
  39. }
  40. ret |= kfifo_alloc(&smux_loop_pkt_fifo,
  41. SMUX_LOOP_FIFO_SIZE * sizeof(struct smux_pkt_t *),
  42. GFP_KERNEL);
  43. return ret;
  44. }
  45. /**
  46. * Simulate a write to the TTY hardware by duplicating
  47. * the TX packet and putting it into the RX queue.
  48. *
  49. * @pkt Packet to write
  50. *
  51. * @returns 0 on success
  52. */
  53. int smux_tx_loopback(struct smux_pkt_t *pkt_ptr)
  54. {
  55. struct smux_pkt_t *send_pkt;
  56. unsigned long flags;
  57. int i;
  58. int ret;
  59. /* duplicate packet */
  60. send_pkt = smux_alloc_pkt();
  61. send_pkt->hdr = pkt_ptr->hdr;
  62. if (pkt_ptr->hdr.payload_len) {
  63. ret = smux_alloc_pkt_payload(send_pkt);
  64. if (ret) {
  65. ret = -ENOMEM;
  66. goto out;
  67. }
  68. memcpy(send_pkt->payload, pkt_ptr->payload,
  69. pkt_ptr->hdr.payload_len);
  70. }
  71. /* queue duplicate as pseudo-RX data */
  72. spin_lock_irqsave(&hw_fn_lock, flags);
  73. i = kfifo_avail(&smux_loop_pkt_fifo);
  74. if (i < sizeof(struct smux_pkt_t *)) {
  75. pr_err("%s: no space in fifo\n", __func__);
  76. ret = -ENOMEM;
  77. goto unlock;
  78. }
  79. i = kfifo_in(&smux_loop_pkt_fifo,
  80. &send_pkt,
  81. sizeof(struct smux_pkt_t *));
  82. if (i < 0) {
  83. pr_err("%s: fifo error\n", __func__);
  84. ret = -ENOMEM;
  85. goto unlock;
  86. }
  87. queue_work(smux_loopback_wq, &smux_loopback_work);
  88. ret = 0;
  89. unlock:
  90. spin_unlock_irqrestore(&hw_fn_lock, flags);
  91. out:
  92. return ret;
  93. }
  94. /**
  95. * Receive loopback byte processor.
  96. *
  97. * @pkt Incoming packet
  98. */
  99. static void smux_loopback_rx_byte(struct smux_pkt_t *pkt)
  100. {
  101. static int simulated_retry_cnt;
  102. const char ack = SMUX_WAKEUP_ACK;
  103. switch (pkt->hdr.flags) {
  104. case SMUX_WAKEUP_REQ:
  105. /* reply with ACK after appropriate delays */
  106. ++simulated_retry_cnt;
  107. if (simulated_retry_cnt >= smux_simulate_wakeup_delay) {
  108. pr_err("%s: completed %d of %d\n",
  109. __func__, simulated_retry_cnt,
  110. smux_simulate_wakeup_delay);
  111. pr_err("%s: simulated wakeup\n", __func__);
  112. simulated_retry_cnt = 0;
  113. smux_rx_state_machine(&ack, 1, 0);
  114. } else {
  115. /* force retry */
  116. pr_err("%s: dropping wakeup request %d of %d\n",
  117. __func__, simulated_retry_cnt,
  118. smux_simulate_wakeup_delay);
  119. }
  120. break;
  121. case SMUX_WAKEUP_ACK:
  122. /* this shouldn't happen since we don't send requests */
  123. pr_err("%s: wakeup ACK unexpected\n", __func__);
  124. break;
  125. default:
  126. /* invalid character */
  127. pr_err("%s: invalid character 0x%x\n",
  128. __func__, (unsigned)pkt->hdr.flags);
  129. break;
  130. }
  131. }
  132. /**
  133. * Simulated remote hardware used for local loopback testing.
  134. *
  135. * @work Not used
  136. */
  137. static void smux_loopback_rx_worker(struct work_struct *work)
  138. {
  139. struct smux_pkt_t *pkt;
  140. struct smux_pkt_t reply_pkt;
  141. char *data;
  142. int len;
  143. int lcid;
  144. int i;
  145. unsigned long flags;
  146. data = kzalloc(SMUX_MAX_PKT_SIZE, GFP_ATOMIC);
  147. spin_lock_irqsave(&hw_fn_lock, flags);
  148. while (kfifo_len(&smux_loop_pkt_fifo) >= sizeof(struct smux_pkt_t *)) {
  149. i = kfifo_out(&smux_loop_pkt_fifo, &pkt,
  150. sizeof(struct smux_pkt_t *));
  151. spin_unlock_irqrestore(&hw_fn_lock, flags);
  152. if (pkt->hdr.magic != SMUX_MAGIC) {
  153. pr_err("%s: invalid magic %x\n", __func__,
  154. pkt->hdr.magic);
  155. kfree(data);
  156. return;
  157. }
  158. lcid = pkt->hdr.lcid;
  159. switch (pkt->hdr.cmd) {
  160. case SMUX_CMD_OPEN_LCH:
  161. if (smux_assert_lch_id(lcid)) {
  162. pr_err("%s: invalid channel id %d\n",
  163. __func__, lcid);
  164. break;
  165. }
  166. if (pkt->hdr.flags & SMUX_CMD_OPEN_ACK)
  167. break;
  168. /* Reply with Open ACK */
  169. smux_init_pkt(&reply_pkt);
  170. reply_pkt.hdr.lcid = lcid;
  171. reply_pkt.hdr.cmd = SMUX_CMD_OPEN_LCH;
  172. reply_pkt.hdr.flags = SMUX_CMD_OPEN_ACK
  173. | SMUX_CMD_OPEN_POWER_COLLAPSE;
  174. reply_pkt.hdr.payload_len = 0;
  175. reply_pkt.hdr.pad_len = 0;
  176. smux_serialize(&reply_pkt, data, &len);
  177. smux_rx_state_machine(data, len, 0);
  178. /* Send Remote Open */
  179. smux_init_pkt(&reply_pkt);
  180. reply_pkt.hdr.lcid = lcid;
  181. reply_pkt.hdr.cmd = SMUX_CMD_OPEN_LCH;
  182. reply_pkt.hdr.flags = SMUX_CMD_OPEN_POWER_COLLAPSE;
  183. reply_pkt.hdr.payload_len = 0;
  184. reply_pkt.hdr.pad_len = 0;
  185. smux_serialize(&reply_pkt, data, &len);
  186. smux_rx_state_machine(data, len, 0);
  187. break;
  188. case SMUX_CMD_CLOSE_LCH:
  189. if (smux_assert_lch_id(lcid)) {
  190. pr_err("%s: invalid channel id %d\n",
  191. __func__, lcid);
  192. break;
  193. }
  194. if (pkt->hdr.flags == SMUX_CMD_CLOSE_ACK)
  195. break;
  196. /* Reply with Close ACK */
  197. smux_init_pkt(&reply_pkt);
  198. reply_pkt.hdr.lcid = lcid;
  199. reply_pkt.hdr.cmd = SMUX_CMD_CLOSE_LCH;
  200. reply_pkt.hdr.flags = SMUX_CMD_CLOSE_ACK;
  201. reply_pkt.hdr.payload_len = 0;
  202. reply_pkt.hdr.pad_len = 0;
  203. smux_serialize(&reply_pkt, data, &len);
  204. smux_rx_state_machine(data, len, 0);
  205. /* Send Remote Close */
  206. smux_init_pkt(&reply_pkt);
  207. reply_pkt.hdr.lcid = lcid;
  208. reply_pkt.hdr.cmd = SMUX_CMD_CLOSE_LCH;
  209. reply_pkt.hdr.flags = 0;
  210. reply_pkt.hdr.payload_len = 0;
  211. reply_pkt.hdr.pad_len = 0;
  212. smux_serialize(&reply_pkt, data, &len);
  213. smux_rx_state_machine(data, len, 0);
  214. break;
  215. case SMUX_CMD_DATA:
  216. if (smux_assert_lch_id(lcid)) {
  217. pr_err("%s: invalid channel id %d\n",
  218. __func__, lcid);
  219. break;
  220. }
  221. /* Echo back received data */
  222. smux_init_pkt(&reply_pkt);
  223. reply_pkt.hdr.lcid = lcid;
  224. reply_pkt.hdr.cmd = SMUX_CMD_DATA;
  225. reply_pkt.hdr.flags = 0;
  226. reply_pkt.hdr.payload_len = pkt->hdr.payload_len;
  227. reply_pkt.payload = pkt->payload;
  228. reply_pkt.hdr.pad_len = pkt->hdr.pad_len;
  229. smux_serialize(&reply_pkt, data, &len);
  230. smux_rx_state_machine(data, len, 0);
  231. break;
  232. case SMUX_CMD_STATUS:
  233. if (smux_assert_lch_id(lcid)) {
  234. pr_err("%s: invalid channel id %d\n",
  235. __func__, lcid);
  236. break;
  237. }
  238. /* Echo back received status */
  239. smux_init_pkt(&reply_pkt);
  240. reply_pkt.hdr.lcid = lcid;
  241. reply_pkt.hdr.cmd = SMUX_CMD_STATUS;
  242. reply_pkt.hdr.flags = pkt->hdr.flags;
  243. reply_pkt.hdr.payload_len = 0;
  244. reply_pkt.payload = NULL;
  245. reply_pkt.hdr.pad_len = pkt->hdr.pad_len;
  246. smux_serialize(&reply_pkt, data, &len);
  247. smux_rx_state_machine(data, len, 0);
  248. break;
  249. case SMUX_CMD_PWR_CTL:
  250. /* reply with ack */
  251. smux_init_pkt(&reply_pkt);
  252. reply_pkt.hdr.lcid = SMUX_BROADCAST_LCID;
  253. reply_pkt.hdr.cmd = SMUX_CMD_PWR_CTL;
  254. reply_pkt.hdr.flags = SMUX_CMD_PWR_CTL_ACK;
  255. reply_pkt.hdr.payload_len = 0;
  256. reply_pkt.payload = NULL;
  257. reply_pkt.hdr.pad_len = pkt->hdr.pad_len;
  258. smux_serialize(&reply_pkt, data, &len);
  259. smux_rx_state_machine(data, len, 0);
  260. break;
  261. case SMUX_CMD_BYTE:
  262. smux_loopback_rx_byte(pkt);
  263. break;
  264. default:
  265. pr_err("%s: unknown command %d\n",
  266. __func__, pkt->hdr.cmd);
  267. break;
  268. };
  269. smux_free_pkt(pkt);
  270. spin_lock_irqsave(&hw_fn_lock, flags);
  271. }
  272. spin_unlock_irqrestore(&hw_fn_lock, flags);
  273. kfree(data);
  274. }