Advertisement
xosski

Rs485_dtr_hook

Jan 9th, 2025
10
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.41 KB | None | 0 0
  1. #include <asm/io.h>
  2. #include <linux/kprobes.h>
  3. #include <linux/slab.h>
  4. #include <linux/tty.h>
  5. #include <linux/workqueue.h>
  6.  
  7. #define MODULE_NAME "rs485_dtr_hook"
  8. #define LOG_PREFIX MODULE_NAME ": "
  9.  
  10. MODULE_DESCRIPTION("This module manages the RS-485 interface on reComputer R1000 by hooking uart_write function.");
  11. MODULE_AUTHOR("Joshua Lee <chengxun.li@seeed.cc>");
  12. MODULE_LICENSE("Dual MIT/GPL");
  13. MODULE_VERSION("0.0.1");
  14.  
  15. #define BCM2711_GPIO_BASE (0xfe000000 + 0x200000)
  16.  
  17. volatile unsigned int* GPFSEL0; // Function selector for GPIO 0-9, for CM4_RS485_1_DTR at GPIO_6.
  18. volatile unsigned int* GPFSEL1; // Function selector for GPIO 10-19, for CM4_RS485_2_DTR at GPIO_17.
  19. volatile unsigned int* GPFSEL2; // Function selector for GPIO 20-29, for CM4_RS485_3_DTR at GPIO_24.
  20. volatile unsigned int* GPSET0; // Register to set GPIO 0-31 to high.
  21. volatile unsigned int* GPCLR0; // Register to set GPIO 0-31 to low.
  22. volatile unsigned int* GPIO_PUP_PDN_CNTRL_REG0; // Register to set pull up/down control of GPIO 0-15.
  23. volatile unsigned int* GPIO_PUP_PDN_CNTRL_REG1; // Register to set pull up/down control of GPIO 16-31.
  24.  
  25. static void rs485_dtr_init(void) {
  26. // Re-map GPIO registers, offsets are given in the datasheet
  27. GPFSEL0 = ioremap(BCM2711_GPIO_BASE + 0x00, 4);
  28. GPFSEL1 = ioremap(BCM2711_GPIO_BASE + 0x04, 4);
  29. GPFSEL2 = ioremap(BCM2711_GPIO_BASE + 0x08, 4);
  30. GPSET0 = ioremap(BCM2711_GPIO_BASE + 0x1c, 4);
  31. GPCLR0 = ioremap(BCM2711_GPIO_BASE + 0x28, 4);
  32. GPIO_PUP_PDN_CNTRL_REG0 = ioremap(BCM2711_GPIO_BASE + 0xe4, 4);
  33. GPIO_PUP_PDN_CNTRL_REG1 = ioremap(BCM2711_GPIO_BASE + 0xe8, 4);
  34.  
  35. if (!GPFSEL0 || !GPFSEL1 || !GPFSEL2 || !GPSET0 || !GPCLR0 ||
  36. !GPIO_PUP_PDN_CNTRL_REG0 || !GPIO_PUP_PDN_CNTRL_REG1) {
  37. pr_err(LOG_PREFIX "Failed to remap GPIO registers\n");
  38. return;
  39. }
  40.  
  41. // Initialize GPIO pins for RS-485 DTR signals
  42. *GPFSEL0 &= ~(7 << 18); *GPFSEL0 |= (1 << 18); // Set GPIO_6 to output (RS485_1)
  43. *GPFSEL1 &= ~(7 << 21); *GPFSEL1 |= (1 << 21); // Set GPIO_17 to output (RS485_2)
  44. *GPFSEL2 &= ~(7 << 12); *GPFSEL2 |= (1 << 12); // Set GPIO_24 to output (RS485_3)
  45.  
  46. *GPIO_PUP_PDN_CNTRL_REG0 &= ~(3 << 12); *GPIO_PUP_PDN_CNTRL_REG0 |= (0 << 12); // No pull-up/down for GPIO_6
  47. *GPIO_PUP_PDN_CNTRL_REG1 &= ~(3 << 2); *GPIO_PUP_PDN_CNTRL_REG1 |= (0 << 2); // No pull-up/down for GPIO_17
  48. *GPIO_PUP_PDN_CNTRL_REG1 &= ~(3 << 16); *GPIO_PUP_PDN_CNTRL_REG1 |= (0 << 16); // No pull-up/down for GPIO_24
  49.  
  50. // Initialize all DTR pins to low
  51. *GPCLR0 = (1 << 6) | (1 << 17) | (1 << 24);
  52. }
  53.  
  54. static void rs485_dtr_deinit(void) {
  55. // Set all DTR pins to low
  56. *GPCLR0 = (1 << 6) | (1 << 17) | (1 << 24);
  57.  
  58. // Unmap GPIO registers
  59. iounmap(GPFSEL0);
  60. iounmap(GPFSEL1);
  61. iounmap(GPFSEL2);
  62. iounmap(GPSET0);
  63. iounmap(GPCLR0);
  64. iounmap(GPIO_PUP_PDN_CNTRL_REG0);
  65. iounmap(GPIO_PUP_PDN_CNTRL_REG1);
  66. }
  67.  
  68. static void rs485_dtr_set(int dev_num, bool enable) {
  69. // Set corresponding GPIO DTR pin for given tty device number
  70. switch (dev_num) {
  71. case 2: *GPSET0 = enable ? (1 << 6) : (1 << 6); break;
  72. case 3: *GPSET0 = enable ? (1 << 17) : (1 << 17); break;
  73. case 5: *GPSET0 = enable ? (1 << 24) : (1 << 24); break;
  74. }
  75. }
  76.  
  77. static int rs485_get_dev_num(struct tty_struct* tty) {
  78. if (tty->index == 2 || tty->index == 3 || tty->index == 5) {
  79. return tty->index;
  80. }
  81. return -EINVAL;
  82. }
  83.  
  84. static bool rs485_filter_driver(struct tty_struct* tty) {
  85. return strcmp(tty->driver->name, "ttyAMA") == 0;
  86. }
  87.  
  88. struct rs485_worker_t {
  89. struct work_struct work;
  90. struct tty_struct* tty;
  91. int dev_num;
  92. };
  93.  
  94. static struct workqueue_struct* rs485_worker_queues[3]; // Queues for 3 ttyAMA devices
  95.  
  96. static void hook_uart_write_oncomplete(struct work_struct* work) {
  97. struct rs485_worker_t* rs485_worker = container_of(work, struct rs485_worker_t, work);
  98.  
  99. // Wait until data is sent, then set DTR to low
  100. while (rs485_worker->tty->ops->write_room(rs485_worker->tty) == 0) {
  101. cpu_relax();
  102. }
  103. rs485_dtr_set(rs485_worker->dev_num, false);
  104. kfree(rs485_worker);
  105. }
  106.  
  107. static void hook_uart_write_onreturn(struct kprobe* p, struct pt_regs* regs, unsigned long flags) {
  108. struct tty_struct* tty = (struct tty_struct*)regs->regs[0];
  109. if (rs485_filter_driver(tty)) {
  110. int dev_num = rs485_get_dev_num(tty);
  111. if (dev_num != -EINVAL) {
  112. struct rs485_worker_t* rs485_worker = kmalloc(sizeof(*rs485_worker), GFP_KERNEL);
  113. if (rs485_worker) {
  114. rs485_worker->tty = tty;
  115. rs485_worker->dev_num = dev_num;
  116. INIT_WORK(&rs485_worker->work, hook_uart_write_oncomplete);
  117. int queue_index = (dev_num == 2) ? 0 : (dev_num == 3) ? 1 : 2;
  118. queue_work(rs485_worker_queues[queue_index], &rs485_worker->work);
  119. } else {
  120. pr_err(LOG_PREFIX "Failed to allocate memory for RS-485 worker\n");
  121. }
  122. }
  123. }
  124. }
  125.  
  126. static int hook_uart_write_onstart(struct kprobe* p, struct pt_regs* regs) {
  127. struct tty_struct* tty = (struct tty_struct*)regs->regs[0];
  128. if (rs485_filter_driver(tty)) {
  129. int dev_num = rs485_get_dev_num(tty);
  130. rs485_dtr_set(dev_num, true);
  131. }
  132. return 0;
  133. }
  134.  
  135. static unsigned long get_fn_addr(const char* symbol_name) {
  136. struct kprobe temp_kp = {.symbol_name = symbol_name};
  137. int ret = register_kprobe(&temp_kp);
  138. unsigned long fn_addr = (unsigned long)temp_kp.addr;
  139.  
  140. unregister_kprobe(&temp_kp);
  141. if (ret < 0) {
  142. return ret;
  143. }
  144. return fn_addr ? fn_addr : -EFAULT;
  145. }
  146.  
  147. struct kprobe hook_uart_write;
  148.  
  149. static int module_init_fn(void) {
  150. rs485_dtr_init();
  151.  
  152. rs485_worker_queues[0] = create_singlethread_workqueue(MODULE_NAME "_worker_queue_2");
  153. rs485_worker_queues[1] = create_singlethread_workqueue(MODULE_NAME "_worker_queue_3");
  154. rs485_worker_queues[2] = create_singlethread_workqueue(MODULE_NAME "_worker_queue_5");
  155.  
  156. if (!rs485_worker_queues[0] || !rs485_worker_queues[1] || !rs485_worker_queues[2]) {
  157. pr_err(LOG_PREFIX "Failed to create workqueues\n");
  158. return -ENOMEM;
  159. }
  160.  
  161. unsigned long target_fn_addr = get_fn_addr("uart_write");
  162. if (target_fn_addr < 0) {
  163. pr_err(LOG_PREFIX "Failed to get address for uart_write, returned code: %ld\n", target_fn_addr);
  164. return target_fn_addr;
  165. }
  166.  
  167. hook_uart_write.addr = (kprobe_opcode_t*)target_fn_addr;
  168. hook_uart_write.pre_handler = hook_uart_write_onstart;
  169. hook_uart_write.post_handler = hook_uart_write_onreturn;
  170.  
  171. int ret = register_kprobe(&hook_uart_write);
  172. if (ret < 0) {
  173. pr_err(LOG_PREFIX "Failed to register kprobe for uart_write, returned code: %d\n", ret);
  174. return ret;
  175. }
  176.  
  177. pr_info(LOG_PREFIX "RS-485 interface has been hooked successfully\n");
  178. return 0;
  179. }
  180.  
  181. static void module_exit_fn(void) {
  182. unregister_kprobe(&hook_uart_write);
  183. for (int i = 0; i < sizeof(rs485_worker_queues) / sizeof(rs485_worker_queues[0]); i++) {
  184. if (rs485_worker_queues[i]) {
  185. destroy_workqueue(rs485_worker_queues[i]);
  186. }
  187. }
  188. rs485_dtr_deinit();
  189.  
  190. pr_info(LOG_PREFIX "RS-485 interface has been unhooked successfully\n");
  191. }
  192.  
  193. module_init(module_init_fn);
  194. module_exit(module_exit_fn);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement