Advertisement
xosski

Rs485 flow control

Jan 9th, 2025
9
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.27 KB | None | 0 0
  1. #include <asm/io.h>
  2. #include <linux/amba/bus.h>
  3. #include <linux/delay.h>
  4. #include <linux/dmaengine.h>
  5. #include <linux/kprobes.h>
  6. #include <linux/serial_core.h>
  7. #include <linux/slab.h>
  8. #include <linux/tty.h>
  9. #include <linux/workqueue.h>
  10.  
  11. #ifndef MODULE_NAME
  12. #define MODULE_NAME "r1000v1_rs485_flow_control"
  13. #endif
  14.  
  15. #ifndef MODULE_VER
  16. #define MODULE_VER "1.0"
  17. #endif
  18.  
  19. MODULE_DESCRIPTION("This module manages RS-485 flow control on reComputer R1000 v1.0 by hooking `uart_write` function.");
  20. MODULE_AUTHOR("Joshua Lee <chengxun.li@seeed.cc>");
  21. MODULE_LICENSE("Dual MIT/GPL");
  22. MODULE_VERSION(MODULE_VER);
  23.  
  24. #define BCM2711_GPIO_BASE (0xfe000000 + 0x200000)
  25.  
  26. volatile unsigned int *GPFSEL0, *GPFSEL1, *GPFSEL2;
  27. volatile unsigned int *GPSET0, *GPCLR0;
  28. volatile unsigned int *GPIO_PUP_PDN_CNTRL_REG0, *GPIO_PUP_PDN_CNTRL_REG1;
  29.  
  30. static void rs485_dtr_init(void)
  31. {
  32. // Remap GPIO registers for the DTR pins
  33. GPFSEL0 = (volatile unsigned int*)ioremap(BCM2711_GPIO_BASE + 0x00, 4);
  34. GPFSEL1 = (volatile unsigned int*)ioremap(BCM2711_GPIO_BASE + 0x04, 4);
  35. GPFSEL2 = (volatile unsigned int*)ioremap(BCM2711_GPIO_BASE + 0x08, 4);
  36. GPSET0 = (volatile unsigned int*)ioremap(BCM2711_GPIO_BASE + 0x1c, 4);
  37. GPCLR0 = (volatile unsigned int*)ioremap(BCM2711_GPIO_BASE + 0x28, 4);
  38. GPIO_PUP_PDN_CNTRL_REG0 = (volatile unsigned int*)ioremap(BCM2711_GPIO_BASE + 0xe4, 4);
  39. GPIO_PUP_PDN_CNTRL_REG1 = (volatile unsigned int*)ioremap(BCM2711_GPIO_BASE + 0xe8, 4);
  40.  
  41. // Configure RS-485 DTR pins (GPIO 6, 17, 24) to output mode with no pull-up/down
  42. *GPFSEL0 = (*GPFSEL0 & ~(7 << 18)) | (1 << 18); // GPIO 6
  43. *GPFSEL1 = (*GPFSEL1 & ~(7 << 21)) | (1 << 21); // GPIO 17
  44. *GPFSEL2 = (*GPFSEL2 & ~(7 << 12)) | (1 << 12); // GPIO 24
  45.  
  46. *GPIO_PUP_PDN_CNTRL_REG0 &= ~(3 << 12); // No pull on GPIO 6
  47. *GPIO_PUP_PDN_CNTRL_REG1 &= ~(3 << 2); // No pull on GPIO 17
  48. *GPIO_PUP_PDN_CNTRL_REG1 &= ~(3 << 16); // No pull on GPIO 24
  49.  
  50. // Set all DTR pins to low initially
  51. *GPCLR0 = (1 << 6) | (1 << 17) | (1 << 24);
  52. }
  53.  
  54. static void rs485_dtr_deinit(void)
  55. {
  56. // Set all DTR pins to low
  57. *GPCLR0 = (1 << 6) | (1 << 17) | (1 << 24);
  58.  
  59. // Unmap GPIO registers
  60. iounmap(GPFSEL0);
  61. iounmap(GPFSEL1);
  62. iounmap(GPFSEL2);
  63. iounmap(GPSET0);
  64. iounmap(GPCLR0);
  65. iounmap(GPIO_PUP_PDN_CNTRL_REG0);
  66. iounmap(GPIO_PUP_PDN_CNTRL_REG1);
  67. }
  68.  
  69. static bool rs485_is_builtin_dev(struct tty_struct *tty)
  70. {
  71. return strcmp(tty->driver->name, "ttyAMA") == 0;
  72. }
  73.  
  74. static void rs485_dtr_set(int dev_num, bool enable)
  75. {
  76. // Set the appropriate DTR pin based on the device number
  77. switch (dev_num) {
  78. case 2: // ttyAMA2
  79. if (enable) {
  80. *GPSET0 = (1 << 6);
  81. } else {
  82. *GPCLR0 = (1 << 6);
  83. }
  84. break;
  85. case 3: // ttyAMA3
  86. if (enable) {
  87. *GPSET0 = (1 << 17);
  88. } else {
  89. *GPCLR0 = (1 << 17);
  90. }
  91. break;
  92. case 5: // ttyAMA5
  93. if (enable) {
  94. *GPSET0 = (1 << 24);
  95. } else {
  96. *GPCLR0 = (1 << 24);
  97. }
  98. break;
  99. }
  100. }
  101.  
  102. static int rs485_get_dev_num(struct tty_struct *tty)
  103. {
  104. if (tty->index == 2 || tty->index == 3 || tty->index == 5) {
  105. return tty->index;
  106. }
  107. return -EINVAL;
  108. }
  109.  
  110. struct rs485_worker_t {
  111. struct delayed_work work;
  112. struct tty_struct *tty;
  113. };
  114.  
  115. static struct workqueue_struct *rs485_worker_queues[3]; // Worker queues for each RS-485 interface (ttyAMA2, ttyAMA3, ttyAMA5)
  116.  
  117. static int rs485_get_worker_index(int dev_num)
  118. {
  119. switch (dev_num) {
  120. case 2: return 0;
  121. case 3: return 1;
  122. case 5: return 2;
  123. default: return -EINVAL;
  124. }
  125. }
  126.  
  127. static void rs485_worker_oncomplete(struct work_struct *work)
  128. {
  129. struct rs485_worker_t *rs485_worker = container_of(work, struct rs485_worker_t, work.work);
  130.  
  131. // Wait until the write room is available, then set DTR to low
  132. if (rs485_worker->tty->ops->write_room(rs485_worker->tty) == 0) {
  133. schedule_delayed_work(&rs485_worker->work, usecs_to_jiffies(1));
  134. return;
  135. }
  136.  
  137. rs485_dtr_set(rs485_worker->tty->index, false);
  138. kfree(rs485_worker);
  139. }
  140.  
  141. static void hook_uart_write_onstart(struct kprobe *p, struct pt_regs *regs)
  142. {
  143. struct tty_struct *tty = (struct tty_struct *)regs->regs[0];
  144. if (rs485_is_builtin_dev(tty)) {
  145. int dev_num = rs485_get_dev_num(tty);
  146. rs485_dtr_set(dev_num, true);
  147. }
  148. }
  149.  
  150. static void hook_uart_write_onreturn(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
  151. {
  152. struct tty_struct *tty = (struct tty_struct *)regs->regs[0];
  153. if (rs485_is_builtin_dev(tty)) {
  154. int dev_num = rs485_get_dev_num(tty);
  155. if (dev_num != -EINVAL) {
  156. struct rs485_worker_t *rs485_worker = kmalloc(sizeof(*rs485_worker), GFP_KERNEL);
  157. rs485_worker->tty = tty;
  158. if (rs485_worker) {
  159. INIT_DELAYED_WORK(&rs485_worker->work, rs485_worker_oncomplete);
  160. int queue_index = rs485_get_worker_index(dev_num);
  161. if (queue_index != -EINVAL) {
  162. queue_delayed_work(rs485_worker_queues[queue_index], &rs485_worker->work, 0);
  163. }
  164. }
  165. }
  166. }
  167. }
  168.  
  169. static unsigned long get_fn_addr(const char *symbol_name)
  170. {
  171. struct kprobe temp_kp = {.symbol_name = symbol_name};
  172. int ret = register_kprobe(&temp_kp);
  173. unsigned long fn_addr = (unsigned long)temp_kp.addr;
  174.  
  175. unregister_kprobe(&temp_kp);
  176. if (ret < 0) {
  177. return ret;
  178. }
  179. if (temp_kp.addr == NULL) {
  180. return -EFAULT;
  181. }
  182.  
  183. return fn_addr;
  184. }
  185.  
  186. static int module_init_fn(void)
  187. {
  188. rs485_dtr_init();
  189.  
  190. // Create worker queues for each RS-485 interface
  191. for (int i = 0; i < 3; i++) {
  192. rs485_worker_queues[i] = create_singlethread_workqueue(MODULE_NAME "_worker_queue_" + i);
  193. if (!rs485_worker_queues[i]) {
  194. pr_err(LOG_PREFIX "Failed to create worker queue for ttyAMA%d\n", i + 2);
  195. return -ENOMEM;
  196. }
  197. }
  198.  
  199. // Hook uart_write function
  200. unsigned long target_fn_addr = get_fn_addr("uart_write");
  201. if (target_fn_addr < 0) {
  202. pr_err(LOG_PREFIX "Failed to get address for `uart_write`, returned code: %ld\n", target_fn_addr);
  203. return target_fn_addr;
  204. }
  205.  
  206. hook_uart_write.addr = (kprobe_opcode_t*)target_fn_addr;
  207. hook_uart_write.pre_handler = hook_uart_write_onstart;
  208. hook_uart_write.post_handler = hook_uart_write_onreturn;
  209.  
  210. int ret = register_kprobe(&hook_uart_write);
  211. if (ret < 0) {
  212. pr_err(LOG_PREFIX "Failed to register kprobe for `uart_write`, returned code: %d\n", ret);
  213. return ret;
  214. }
  215.  
  216. pr_info(LOG_PREFIX "RS-485 flow control has been hooked successfully\n");
  217. return 0;
  218. }
  219.  
  220. static void module_exit_fn(void)
  221. {
  222. unregister_kprobe(&hook_uart_write);
  223. for (int i = 0; i < 3; i++) {
  224. if (rs485_worker_queues[i]) {
  225. destroy_workqueue(rs485_worker_queues[i]);
  226. }
  227. }
  228. rs485_dtr_deinit();
  229.  
  230. pr_info(LOG_PREFIX "RS-485 flow control has been unhooked successfully\n");
  231. }
  232.  
  233. module_init(module_init_fn);
  234. module_exit(module_exit_fn);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement