main_mod.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. /******************************************************************************
  2. 版权所有:
  3. 文件名称: main_mod.c
  4. 文件版本: 01.01
  5. 创建作者: sunxi
  6. 创建日期: 2025-06-09
  7. 功能说明: 实时微系统。
  8. 其它说明:
  9. 修改记录:
  10. */
  11. #include <linux/fs.h>
  12. #include <linux/sched.h>
  13. #include <linux/wait.h>
  14. #include "bsp.h"
  15. #include "rt.h"
  16. #include "app.h"
  17. #include "head.h"
  18. #include "tmp/bsp_share/bsp_ustimer.h"
  19. #include <sys/prctl.h>
  20. #include <sys/types.h>
  21. #include <sys/syscall.h>
  22. #include <signal.h>
  23. #include <stdbool.h>
  24. #include <errno.h>
  25. #include "lvgl_app.h"
  26. #include <stdio.h>
  27. // #define __USE_GNU
  28. #define _GNU_SOURCE
  29. #include <sched.h>
  30. #include <pthread.h>
  31. // #include <alchemy/task.h>
  32. #include "bsp_boardcheck.h"
  33. int g_l_shm_init = 0xff;
  34. static int m_reset; // 看门狗复位变量
  35. struct completion *g_exit_completion_gprs_net_IEC_proc;
  36. static int g_exit_flag;
  37. bool SystemInit_finish_flag __attribute__((aligned(8))) = false;
  38. static int sync_cnt = 0;
  39. static pthread_t sync_tid = 0;
  40. enum
  41. {
  42. PTHREAD_MQ = 0, // 消息队列
  43. PTHREAD_C0NSOLE, // 控制台
  44. PTHREAD_PRINTF, // 打印信息
  45. PTHREAD_MAIN_LOOP, // 主循环
  46. PTHREAD_MAIN_APP,
  47. PTHREAD_MAIN_DIDO_ADC,
  48. PTHREAD_RCD, // 录波
  49. PTHREAD_CAL_SAMPLE,
  50. PTHREAD_MAX_NUM
  51. };
  52. pthread_t AW_DTU_PTHREAD_TAB[PTHREAD_MAX_NUM]; // 线程表
  53. struct rt_stat g_stat_periodus_5ms;
  54. int app_main(void *arg);
  55. int di_do_adc(void *arg);
  56. #ifdef ENCRYPT_SM2
  57. int gh_sm2_dev_init(void);
  58. void gh_sm2_dev_exit(void);
  59. #endif
  60. int console_init(void);
  61. void console_exit(void);
  62. int encrypt_test(void);
  63. int snprintf_test(void);
  64. extern void uart_putchar(int channel, char ch);
  65. extern int net_if_test(void);
  66. extern int clk_test(void);
  67. extern int ac_e2prom_test(void);
  68. void watchdog_m_reset(int flag) {}
  69. extern int printf_e907_exit(void);
  70. int kernel_callback(void);
  71. static void main_mod_exit(int signo);
  72. // static DECLARE_WAIT_QUEUE_HEAD(wq);
  73. static int g_soft_irq_flag = 0;
  74. static struct proc_dir_entry *corp_info_proc_entry;
  75. int corp_info_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *data)
  76. {
  77. sprintf(buf, "XXXXXX XXXXXX XXXXXXX XXXXX XXXXXXX Co.,Ltd(XXXXXXXXXX) 2025\r\n");
  78. len = strlen(buf);
  79. return len;
  80. }
  81. pid_t gettid()
  82. {
  83. return syscall(SYS_gettid);
  84. }
  85. int mainloop_soft_isr(int irq, void *dev_id)
  86. {
  87. // int offset=0,tmp,count=1,count2=1,count3=0;
  88. // struct timespec ts;
  89. // struct rtc_time_t rtcs;
  90. struct sched_param sch_par;
  91. sch_par.sched_priority = 6;
  92. pthread_setschedparam(pthread_self(), SCHED_RR, &sch_par); // 设置当前线程优先级
  93. prctl(PR_SET_NAME, "mainloop_soft_isr_thread");
  94. #ifdef RT_THREAD_DEBUG
  95. rt_printf("Thread created successfully: %s,PID: %d LWP: %d\r\n",
  96. "mainloop_soft_isr_thread", (int)getpid(), (int)syscall(SYS_gettid));
  97. #endif
  98. while (1)
  99. {
  100. msleep(100); // 延时100ms
  101. kernel_callback();
  102. #if 0
  103. if(count++>=40)
  104. {
  105. //gps_get_time(&ts);
  106. //timespec_to_rtc(ts, &rtcs, 0);
  107. //tmp=GetUsCounterValue();
  108. //offset=tmp-offset;
  109. //rt_printf("%d---%d-%d-%d,%d:%d:%d mainloop=%d,offset=%d\r\n",ts.tv_sec,rtcs.year,rtcs.month,rtcs.day,rtcs.hour,rtcs.min,rtcs.ms,tmp,offset);
  110. //offset=tmp;
  111. //rt_printf("di=0x%08X\r\n",gpio_get_di());
  112. if(count3==0)
  113. gpio_kout_do(0,DO_OUT7);
  114. else
  115. {
  116. if((count3-1)!=DO_OUT4)
  117. gpio_kout_do(0,count3-1);
  118. }
  119. gpio_kout_do(1,count3);
  120. rt_printf("count=%d\r\n",count3);
  121. ++count3;
  122. if(count3>=DO_NUM)
  123. count3=0;
  124. gpio_kout_do(1,DO_PWM0);
  125. count=1;
  126. }
  127. #endif
  128. }
  129. }
  130. void mainloop_wakeup(void)
  131. {
  132. // rt_sirq_force(11);
  133. }
  134. int kernel_callback(void)
  135. {
  136. static unsigned char init_end_flag = 0; // sunxi 20190416 added
  137. // 延时100ms
  138. // wait_event_interruptible_timeout(wq,g_soft_irq_flag,HZ/10);
  139. g_soft_irq_flag = 0;
  140. // sunxi 20190416 added start
  141. if (!init_end_flag)
  142. {
  143. // corp_info_proc_entry = create_proc_entry("corp_info", 0400, NULL);
  144. if (corp_info_proc_entry)
  145. {
  146. // corp_info_proc_entry->read_proc = corp_info_read_proc;
  147. // corp_info_proc_entry->write_proc = NULL;
  148. }
  149. init_end_flag = 1;
  150. }
  151. // sunxi 20190416 added end
  152. // 检查是否需要退出。
  153. if (g_exit_flag)
  154. {
  155. hzk_exit();
  156. led_exit();
  157. net_debug_exit();
  158. net_debug_s_exit();
  159. if (g_exit_completion_gprs_net_IEC_proc)
  160. {
  161. ; // wait_for_completion(g_exit_completion_gprs_net_IEC_proc);
  162. }
  163. printf_e907_exit();
  164. // complete_and_exit(&exit_completion, 1);
  165. }
  166. return 0;
  167. }
  168. int cal_thread(void *arg)
  169. {
  170. uint32_t us0;
  171. int ms;
  172. struct sched_param sch_par;
  173. sch_par.sched_priority = 8;
  174. pthread_setschedparam(pthread_self(), SCHED_RR, &sch_par); // 设置当前线程优先级暂定 20200910
  175. while (SystemInit_finish_flag == false)
  176. {
  177. msleep(10);
  178. }
  179. uint32_t wdt_id;
  180. int rc = watchdog_add_item("cal_thread", &wdt_id, 120);
  181. if (rc != 0)
  182. {
  183. printf("can not add cal_thread task to wdt\r\n");
  184. return -1;
  185. }
  186. ms = 50;
  187. us0 = ustimer_get_origin();
  188. prctl(PR_SET_NAME, "cal_thread");
  189. #ifdef RT_THREAD_DEBUG
  190. rt_printf("Thread created successfully: %s, PID: %d LWP: %d\r\n",
  191. "cal_thread", (int)getpid(), (int)syscall(SYS_gettid));
  192. #endif
  193. while (1)
  194. {
  195. watchdog_feed(wdt_id);
  196. // 100ms执行一次
  197. ms = 50 - (ustimer_get_duration(us0) / USTIMER_US + 500) / 1000;
  198. if (ms < 0)
  199. {
  200. ms = 0;
  201. }
  202. msleep(ms);
  203. us0 = ustimer_get_origin();
  204. // 检查是否需要退出。
  205. if (g_exit_flag)
  206. {
  207. // complete_and_exit(&exit_completion2, 1);
  208. break;
  209. }
  210. sw_cal_app();
  211. hisory_file_app();
  212. // pw_quality_app();
  213. rec_chnl_zero_drift_handle();
  214. }
  215. watchdog_remove_item(wdt_id);
  216. return 0;
  217. }
  218. int main_mod_is_exit(void)
  219. {
  220. return g_exit_flag;
  221. }
  222. // sunxi 20190417 added start
  223. /*
  224. * 该接口在模块装载时调用,防止我们的模块在之前的内核版本中能被轻易使用。函数名称不要定义跟功能一样。
  225. */
  226. int mod_init(void)
  227. {
  228. char buf[256] = {0x00};
  229. int fd = rt_file_open("/proc/version", O_RDONLY, 0);
  230. if (fd < 0)
  231. return -1;
  232. rt_file_read(fd, buf, 256, NULL);
  233. rt_file_close(fd, 0);
  234. if (strstr(buf, "root@Ubuntu") && strstr(buf, "-LYQ"))
  235. return 0;
  236. return -3;
  237. }
  238. extern void gps_isr(void);
  239. int di_do_adc(void *unused)
  240. {
  241. pid_t tid;
  242. cpu_set_t set;
  243. struct sched_param sch_par;
  244. sch_par.sched_priority = 90;
  245. // Set CPU affinity [T536-CPU-3]
  246. tid = gettid();
  247. CPU_ZERO(&set);
  248. CPU_SET(3, &set);
  249. sched_setaffinity(tid, sizeof(cpu_set_t), &set);
  250. while (SystemInit_finish_flag == false)
  251. {
  252. msleep(10);
  253. }
  254. uint32_t wdt_id;
  255. int rc = watchdog_add_item("di_do_adc", &wdt_id, 120);
  256. if (rc != 0)
  257. {
  258. printf("can not add di_do_adc task to wdt\r\n");
  259. return -1;
  260. }
  261. rt_stat_init(&g_stat_periodus_5ms, "pit_5ms_priod(1us)");
  262. pthread_setschedparam(pthread_self(), SCHED_FIFO, &sch_par); // 设置当前线程优先级
  263. prctl(PR_SET_NAME, "di_do_adc");
  264. #ifdef RT_THREAD_DEBUG
  265. rt_printf("Thread created successfully: %s, PID: %d LWP: %d\r\n",
  266. "di_do_adc", (int)getpid(), (int)syscall(SYS_gettid));
  267. #endif
  268. #ifdef CPU_FUXI
  269. // zhl add
  270. struct shmem_fd shmem_fd;
  271. if (share_mem_init_v2(&shmem_fd) < 0)
  272. {
  273. dp_err_n_c("--->shmem_init fail !!!");
  274. g_l_shm_init = -1;
  275. return -1;
  276. }
  277. else
  278. {
  279. g_l_shm_init = 0;
  280. dp_info_n_c("--->shmem_init success !!!");
  281. }
  282. // 写入毫秒时间戳
  283. struct t_shmdata_timestamp t_rv_msts;
  284. #if (0) /* 统一使用gps接口 */
  285. struct timeval tv;
  286. gettimeofday(&tv, NULL);
  287. uint64_t milliseconds = (uint64_t)(tv.tv_sec * 1000 + tv.tv_usec / 1000);
  288. #else
  289. struct timespec ts_gps;
  290. gps_get_time(&ts_gps);
  291. uint64_t milliseconds = (uint64_t)((ts_gps.tv_sec + 28800) * 1000 + ts_gps.tv_nsec / 1000000);
  292. #endif
  293. t_rv_msts.ull_timestamp = milliseconds;
  294. shm_packet_write_v2(SHM_ADDR_R_TIMESTAMP, (uint8_t *)&t_rv_msts, sizeof(struct t_shmdata_timestamp));
  295. check_shmdata_change();
  296. // 装置类型us_machine_type (分布式:1,集中式:2)
  297. struct t_shmdata_machine_type t_rv_mt = {0};
  298. t_rv_mt.us_machine_type = 1;
  299. shm_packet_write_v2(SHM_ADDR_R_MACHINETYPE, (uint8_t *)&t_rv_mt, sizeof(struct t_shmdata_machine_type));
  300. // printf("write SHM_ADDR_R_MACHINETYPE::0x%X -> us_updata: %d, us_op: %d, us_op_bk: %d, us_machine_type: %d, us_crc: 0x%04x\n\n",
  301. // SHM_ADDR_R_MACHINETYPE, t_rv_mt.us_updata, t_rv_mt.us_op, t_rv_mt.us_op_bk, t_rv_mt.us_machine_type, t_rv_mt.us_crc);
  302. sleep(5); // 注:下发装置类型后,需要等待大于5s,才能读取到R核的ADC初始化标志信息
  303. // ADC初始化标志信息
  304. struct t_shmdata_flag t_rv_adc = {0};
  305. int ret_flag = shm_packet_read_v2(SHM_ADDR_W_FLAG, sizeof(t_rv_adc), (uint8_t *)&t_rv_adc, sizeof(t_rv_adc));
  306. if (t_rv_adc.us_cfg_adc_err == 1 || ret_flag < 0)
  307. {
  308. dp_err_a_c("adc init err!");
  309. rt_err_set(ERR_CODE_INIT_ADC, 0);
  310. }
  311. else
  312. {
  313. dp_info_nt("adc init ok!");
  314. }
  315. // printf("ret_flag: %d, read SHM_ADDR_W_FLAG:0x%X -> us_updata: %u, us_op: %u, us_op_bk: %u, us_cfg_adc_err: %u, us_crc: 0x%04x\n\n",
  316. // ret_flag, SHM_ADDR_W_FLAG, t_rv_adc.us_updata, t_rv_adc.us_op, t_rv_adc.us_op_bk, t_rv_adc.us_cfg_adc_err, t_rv_adc.us_crc);
  317. int l_retrv_di = equ_init_rv_di();
  318. if (0 > l_retrv_di)
  319. {
  320. rt_err_set(ERR_CODE_INIT_SOFTWARE, 0);
  321. }
  322. else
  323. {
  324. l_retrv_di = 0;
  325. dp_info_nt("rvdi init ok!");
  326. }
  327. // dp_info_nt("l_retrv_di = %d", l_retrv_di);
  328. #endif
  329. while (1)
  330. {
  331. static u8 flags = 0;
  332. static unsigned long us1 = 0;
  333. unsigned long origin_ext = 0;
  334. usleep(1000);
  335. // bsp_ustimer_delay(1000); // 1ms
  336. // 检查是否需要退出。
  337. if (g_exit_flag)
  338. {
  339. // complete_and_exit(&exit_completion2, 1);
  340. break;
  341. }
  342. origin_ext = bsp_ustimer_get_origin();
  343. if (flags)
  344. {
  345. rt_stat_in(&g_stat_periodus_5ms, (origin_ext - us1));
  346. }
  347. watchdog_feed(wdt_id);
  348. // adc_isr(0);
  349. if (t_rv_adc.us_cfg_adc_err == 0 && ret_flag > 0)
  350. {
  351. adc_isr_new(0);
  352. freq_get_shm_data();
  353. }
  354. // 保护计算
  355. if (prt_flag)
  356. {
  357. pit_5ms_main(0);
  358. prt_flag = false;
  359. }
  360. #ifdef CPU_FUXI
  361. if (0 == l_retrv_di)
  362. {
  363. if (pRunSet->lb_dint)
  364. {
  365. get_shm_dintdata();
  366. }
  367. get_shm_didata();
  368. }
  369. #endif
  370. // gps_isr(); // zhl upadte 放在另一个线程跑
  371. us1 = origin_ext;
  372. flags = 1;
  373. }
  374. watchdog_remove_item(wdt_id);
  375. return 0;
  376. }
  377. // 当装置修改app文件夹里面的文件时,需要调用该函数,以sync_cnt数减为0时,调用sync,写入flash,保存数文件。
  378. void start_sync(void)
  379. {
  380. sync_cnt = 50; // 500ms
  381. }
  382. static int sync_func(void *arg)
  383. {
  384. // int ret = 0;
  385. uint32_t wdt_id;
  386. int rc = watchdog_add_item("sync_func", &wdt_id, 60);
  387. if (rc != 0)
  388. {
  389. printf("can not add sync_func task to wdt\r\n");
  390. return 0;
  391. }
  392. sync_cnt = 0;
  393. prctl(PR_SET_NAME, "sync_func");
  394. #ifdef RT_THREAD_DEBUG
  395. rt_printf("Thread created successfully: %s, PID: %d LWP: %d\r\n",
  396. "sync_func", (int)getpid(), (int)syscall(SYS_gettid));
  397. #endif
  398. while (1)
  399. {
  400. msleep(10);
  401. watchdog_feed(wdt_id);
  402. if (main_mod_is_exit())
  403. {
  404. break;
  405. }
  406. if (sync_cnt > 0)
  407. {
  408. if (--sync_cnt <= 0)
  409. {
  410. system("sync");
  411. // rt_printf_time("--- sync.\r\n");
  412. }
  413. }
  414. }
  415. watchdog_remove_item(wdt_id);
  416. return 0;
  417. }
  418. int main(void)
  419. {
  420. // pid_t pid;
  421. char *argv[] = {"/sbin/ifconfig", "eth1", "192.168.2.100", NULL};
  422. char *envp[] = {NULL};
  423. // struct task_struct * ts;
  424. // struct sched_param sp;
  425. int ret;
  426. SystemInit_finish_flag = false;
  427. // 停止喂狗,让程序复位 m_reset=1
  428. if (m_reset == 1)
  429. {
  430. rt_printf("watchdog_m_reset(1)...\n");
  431. watchdog_m_reset(1);
  432. return 0;
  433. }
  434. call_usermodehelper("/sbin/ifconfig", argv, envp, UMH_WAIT_PROC);
  435. rt_printf("\r\n");
  436. dp_info_nt("%s %s", __DATE__, __TIME__);
  437. float f_version = ((VER_NUM >> 12) & 0xff) + ((VER_NUM & 0xfff) * 0.001f);
  438. dp_info_nt("版本信息: SV%06.03f %s", f_version, DEVICE_VERSION_INFORMATION);
  439. // if(mod_init() < 0) //jack.liu 20200904 认证信息先不使用
  440. // return -1;
  441. bsp_init(); // bsp必须先初始化
  442. rt_init(); // 实时系统后初始化
  443. app_init(); // 应用初始化最后初始化
  444. adc_init();
  445. bsp_board_init();
  446. // 建立主函数入口线程
  447. ret = pthread_create(&AW_DTU_PTHREAD_TAB[PTHREAD_MAIN_LOOP], NULL, (void *)mainloop_soft_isr, NULL);
  448. if (ret)
  449. {
  450. rt_printf("mainloop_soft_isr thread create failed!\r\n");
  451. rt_printf("ret = %d, err = %s\r\n", ret, strerror(ret));
  452. memset((char *)&AW_DTU_PTHREAD_TAB, 0, sizeof(AW_DTU_PTHREAD_TAB));
  453. return ret;
  454. }
  455. // 创建主循环线程
  456. ret = pthread_create(&AW_DTU_PTHREAD_TAB[PTHREAD_MAIN_APP], NULL, (void *)app_main, NULL);
  457. if (ret)
  458. {
  459. rt_printf("app_main thread create failed!\r\n");
  460. rt_printf("ret = %d, err = %s\r\n", ret, strerror(ret));
  461. AW_DTU_PTHREAD_TAB[PTHREAD_MAIN_APP] = 0;
  462. memset((char *)&AW_DTU_PTHREAD_TAB, 0, sizeof(AW_DTU_PTHREAD_TAB));
  463. return ret;
  464. }
  465. #if 1
  466. // 创建三遥线程
  467. ret = pthread_create(&AW_DTU_PTHREAD_TAB[PTHREAD_MAIN_DIDO_ADC], NULL, (void *)di_do_adc, NULL);
  468. if (ret)
  469. {
  470. rt_printf("di_do_adc thread create failed!\r\n");
  471. rt_printf("ret = %d, err = %s\r\n", ret, strerror(ret));
  472. AW_DTU_PTHREAD_TAB[PTHREAD_MAIN_DIDO_ADC] = 0;
  473. memset((char *)&AW_DTU_PTHREAD_TAB, 0, sizeof(AW_DTU_PTHREAD_TAB));
  474. return ret;
  475. }
  476. // 创建采样数据计算线程
  477. ret = pthread_create(&AW_DTU_PTHREAD_TAB[PTHREAD_CAL_SAMPLE], NULL, (void *)cal_thread, NULL);
  478. if (ret)
  479. {
  480. rt_printf("cal_thread thread create failed!\r\n");
  481. rt_printf("ret = %d, err = %s\r\n", ret, strerror(ret));
  482. AW_DTU_PTHREAD_TAB[PTHREAD_CAL_SAMPLE] = 0;
  483. memset((char *)&AW_DTU_PTHREAD_TAB, 0, sizeof(AW_DTU_PTHREAD_TAB));
  484. return ret;
  485. }
  486. ret = pthread_create(&sync_tid, NULL, (void *)sync_func, NULL);
  487. if (ret)
  488. {
  489. rt_printf("ret = %d, err = %s\r\n", ret, strerror(ret));
  490. sync_tid = 0;
  491. return ret;
  492. }
  493. printf("LVGL application start...\n");
  494. // 启动 LVGL 线程(函数声明在gui/lvgl_app.h中)
  495. start_lvgl_thread();
  496. // 设置进程名字
  497. prctl(PR_SET_NAME, "AW_DTU_CALL", 0, 0, 0);
  498. signal(SIGINT, main_mod_exit);
  499. #endif
  500. #ifdef RT_THREAD_DEBUG
  501. rt_printf("thread create success!\r\n");
  502. #endif
  503. scaio_init_data();
  504. // 主线程阻塞
  505. while (1)
  506. {
  507. bsp_board_check();
  508. rsio_get_runtime();
  509. scaio_save_data();
  510. usleep(1000 * 100);
  511. }
  512. return 0;
  513. }
  514. int net_debug_exit(void);
  515. int net_maintain_exit(void);
  516. int net_104_exit(void);
  517. static void main_mod_exit(int signo)
  518. {
  519. if (corp_info_proc_entry)
  520. {
  521. ; // remove_proc_entry("corp_info", NULL);
  522. }
  523. //
  524. if (m_reset == 1)
  525. {
  526. watchdog_m_reset(0);
  527. rt_printf("watchdog_m_reset(0)...\n");
  528. return;
  529. }
  530. // gh_sm2_dev_exit();
  531. // console_exit();
  532. // watchdog_unregister_all();
  533. // rt_sirq_unregister(RT_SOFT_IRQ_MAINLOOP);
  534. net_maintain_exit();
  535. net_104_exit();
  536. net_104link_exit();
  537. g_exit_flag = 1;
  538. // wait_for_completion(&exit_completion);
  539. // wait_for_completion(&exit_completion2);
  540. app_exit();
  541. rt_exit();
  542. bsp_exit();
  543. // sunxi 20190418 goose_app_exit();
  544. open_inet_port();
  545. rt_printf("main_mod_exit\n");
  546. }