update.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. #include "update.h"
  2. #include "update_loader_download.h"
  3. #include "asm/crc16.h"
  4. #include "asm/wdt.h"
  5. #include "os/os_api.h"
  6. #include "app_config.h"
  7. #include "cpu.h"
  8. #include "syscfg_id.h"
  9. #include "btcontroller_modules.h"
  10. #include "system/includes.h"
  11. #include "uart_update.h"
  12. #include "dual_bank_updata_api.h"
  13. #include "btstack/avctp_user.h"
  14. #if TCFG_UI_ENABLE
  15. #include "ui/ui_api.h"
  16. #endif
  17. #if RCSP_BTMATE_EN
  18. #include "rcsp_user_update.h"
  19. #elif RCSP_ADV_EN
  20. #include "rcsp_adv_user_update.h"
  21. #endif
  22. #ifdef UPDATE_VOICE_REMIND
  23. #include "tone_player.h"
  24. #include "audio_config.h"
  25. #endif
  26. #ifdef UPDATE_LED_REMIND
  27. #include "asm/pwm_led.h"
  28. #endif
  29. #include "custom_cfg.h"
  30. #define LOG_TAG "[APP-UPDATE]"
  31. #define LOG_INFO_ENABLE
  32. #define LOG_ERROR_ENABLE
  33. #include "system/debug.h"
  34. #if (JL_SMART_BOX_EXTRA_FLASH_OPT)
  35. #include "smartbox_extra_flash_opt.h"
  36. #endif
  37. #define LOADER_NAME "LOADER.BIN"
  38. #define DEVICE_UPDATE_KEY_ERR BIT(30)
  39. #define DEVICE_FIRST_START BIT(31)
  40. extern void update_module_init(void (*cbk)(update_mode_info_t *, u32, void *));
  41. extern void testbox_update_init(void);
  42. extern void ll_hci_destory(void);
  43. extern void hci_controller_destory(void);
  44. extern const int support_norflash_update_en;
  45. extern void ram_protect_close(void);
  46. extern void hwi_all_close(void);
  47. extern void wifi_det_close();
  48. __attribute__((weak))
  49. void wifi_det_close()
  50. {
  51. printf("tmp weak func wifi_det_close\n");
  52. }
  53. extern void *dev_update_get_parm(int type);
  54. extern u8 get_ota_status();
  55. extern int get_nor_update_param(void *buf);
  56. extern bool get_tws_phone_connect_state(void);
  57. extern void tws_sniff_controle_check_disable(void);
  58. extern void tws_tx_unsniff_req(void);
  59. extern void sys_auto_sniff_controle(u8 enable, u8 *addr);
  60. extern void app_audio_set_wt_volume(s16 volume);
  61. extern u8 get_max_sys_vol(void);
  62. extern u8 get_ldo_trim_res(u8 *res);
  63. #ifdef DEV_UPDATE_SUPPORT_JUMP
  64. extern void __JUMP_TO_MASKROM();
  65. extern void save_spi_port();
  66. extern s32 sd1_unmount(void);
  67. extern void usb_sie_close_all(void);
  68. #endif //endif DEV_UPDATE_SUPPORT_JUMP
  69. extern const int support_norflash_update_en;
  70. const u8 loader_file_path[] = "mnt/norflash/C/"LOADER_NAME"";
  71. //升级文件路径必须是短文件名(8+3)结构,仅支持2层目录
  72. /* const char updata_file_name[] = "/UPDATA/JL_692X.BFU"; */
  73. const char updata_file_name[] = "/*.UFW";
  74. static u32 g_updata_flag = 0;
  75. static volatile u8 ota_status = 0;
  76. static succ_report_t succ_report;
  77. extern const int support_dual_bank_update_en;
  78. extern const int support_norflash_update_en;
  79. extern const int support_vm_data_keep;
  80. u16 update_result_get(void)
  81. {
  82. u16 ret = UPDATA_NON;
  83. if (!UPDATE_SUPPORT_DEV_IS_NULL()) {
  84. UPDATA_PARM *p = UPDATA_FLAG_ADDR;
  85. u16 crc_cal;
  86. crc_cal = CRC16(((u8 *)p) + 2, sizeof(UPDATA_PARM) - 2); //2 : crc_val
  87. if (crc_cal && crc_cal == p->parm_crc) {
  88. ret = p->parm_result;
  89. }
  90. g_updata_flag = ret;
  91. g_updata_flag |= ((u32)(p->magic)) << 16;
  92. memset(p, 0x00, sizeof(UPDATA_PARM));
  93. }
  94. return ret;
  95. }
  96. void update_result_set(u16 result)
  97. {
  98. if (!UPDATE_SUPPORT_DEV_IS_NULL()) {
  99. UPDATA_PARM *p = UPDATA_FLAG_ADDR;
  100. memset(p, 0x00, sizeof(UPDATA_PARM));
  101. p->parm_result = result;
  102. p->parm_crc = CRC16(((u8 *)p) + 2, sizeof(UPDATA_PARM) - 2);
  103. }
  104. #if (RCSP_UPDATE_EN && SMART_BOX_EN && JL_SMART_BOX_EXTRA_FLASH_OPT)
  105. if (UPDATA_SUCC == result) {
  106. smartbox_eflash_update_flag_set(0);
  107. smartbox_eflash_flag_set(0);
  108. extern void set_update_ex_flash_flag(u8 update_flag);
  109. set_update_ex_flash_flag(0);
  110. }
  111. #endif
  112. }
  113. bool vm_need_recover(void)
  114. {
  115. return ((g_updata_flag & 0xffff) == UPDATA_SUCC) ? true : false;
  116. }
  117. void update_clear_result()
  118. {
  119. g_updata_flag = 0;
  120. }
  121. bool update_success_boot_check(void)
  122. {
  123. if (!UPDATE_SUPPORT_DEV_IS_NULL()) {
  124. u16 result = g_updata_flag & 0xffff;
  125. u16 up_tpye = g_updata_flag >> 16;
  126. if ((UPDATA_SUCC == result) && ((SD0_UPDATA == up_tpye) || (SD1_UPDATA == up_tpye) || (USB_UPDATA == up_tpye))) {
  127. return true;
  128. }
  129. }
  130. return false;
  131. }
  132. bool device_is_first_start()
  133. {
  134. log_info("g_updata_flag=0x%x\n", g_updata_flag);
  135. if ((g_updata_flag & DEVICE_FIRST_START) || (g_updata_flag & DEVICE_UPDATE_KEY_ERR) || (g_updata_flag == UPDATA_SUCC)) {
  136. puts("\n=================device_is_first_start=========================\n");
  137. return true;
  138. }
  139. return false;
  140. }
  141. void led_update_start(void)
  142. {
  143. #ifdef UPDATE_LED_REMIND
  144. puts("led_update_start\n");
  145. pwm_led_mode_set(PWM_LED_ALL_OFF);
  146. #endif
  147. }
  148. void led_update_finish(void)
  149. {
  150. #ifdef UPDATE_LED_REMIND
  151. puts("led_update_finish\n");
  152. pwm_led_mode_set(PWM_LED0_LED1_FAST_FLASH);
  153. #endif
  154. }
  155. static inline void dev_update_close_ui()
  156. {
  157. #if (TCFG_UI_ENABLE&&(CONFIG_UI_STYLE == STYLE_JL_LED7))
  158. u8 count = 0;
  159. UI_SHOW_WINDOW(ID_WINDOW_POWER_OFF);
  160. __retry:
  161. if (UI_GET_WINDOW_ID() != ID_WINDOW_POWER_OFF) {
  162. os_time_dly(10);//增加延时防止没有关显示
  163. if (count < 3) {
  164. goto __retry;
  165. }
  166. count++;
  167. }
  168. #endif
  169. }
  170. #ifdef UPDATE_VOICE_REMIND
  171. void update_tone_event_clear()
  172. {
  173. struct sys_event e = {0};
  174. e.type = SYS_DEVICE_EVENT;
  175. e.arg = (void *)DEVICE_EVENT_FROM_TONE;
  176. sys_event_clear(&e);
  177. }
  178. #endif
  179. int update_result_deal()
  180. {
  181. #ifdef CONFIG_FPGA_ENABLE
  182. return 0;
  183. #endif
  184. u8 key_voice_cnt = 0;
  185. u16 result = 0;
  186. result = (g_updata_flag & 0xffff);
  187. log_info("<--------update_result_deal=0x%x %x--------->\n", result, g_updata_flag >> 16);
  188. #ifdef CONFIG_DEBUG_ENABLE
  189. #if TCFG_APP_BT_EN
  190. u8 check_update_param_len(void);
  191. ASSERT(check_update_param_len(), "UPDATE_PARAM_LEN ERROR");
  192. #endif
  193. #endif
  194. if (result == UPDATA_NON || 0 == result) {
  195. return 0;
  196. }
  197. #ifdef UPDATE_VOICE_REMIND
  198. #endif
  199. if (result == UPDATA_SUCC) {
  200. #if(JL_EARPHONE_APP_EN && RCSP_UPDATE_EN)
  201. u8 clear_update_flag = 0;
  202. syscfg_write(VM_UPDATE_FLAG, &clear_update_flag, 1);
  203. #endif
  204. #ifdef UPDATE_LED_REMIND
  205. led_update_finish();
  206. #endif
  207. }
  208. int voice_max_cnt = 5;
  209. while (1) {
  210. wdt_clear();
  211. key_voice_cnt++;
  212. #ifdef UPDATE_VOICE_REMIND
  213. if (result == UPDATA_SUCC) {
  214. puts("<<<<<<UPDATA_SUCC");
  215. app_audio_set_volume(APP_AUDIO_STATE_WTONE, get_max_sys_vol() / 2, 1);
  216. tone_play(TONE_SIN_NORMAL, 1);
  217. os_time_dly(25);
  218. puts(">>>>>>>>>>>\n");
  219. update_tone_event_clear();
  220. } else {
  221. voice_max_cnt = 20; //区分下升级失败提示音
  222. log_info("!!!!!!!!!!!!!!!updata waring !!!!!!!!!!!=0x%x\n", result);
  223. app_audio_set_volume(APP_AUDIO_STATE_WTONE, get_max_sys_vol(), 1);
  224. tone_play(TONE_SIN_NORMAL, 1);
  225. os_time_dly(10);
  226. update_tone_event_clear();
  227. }
  228. #endif
  229. if (key_voice_cnt > voice_max_cnt) {
  230. key_voice_cnt = 0;
  231. puts("enter_sys_soft_poweroff\n");
  232. break;
  233. //注:关机要慎重,要设置开机键
  234. //enter_sys_soft_poweroff();
  235. }
  236. }
  237. return 1;
  238. }
  239. void clr_update_ram_info(void)
  240. {
  241. UPDATA_PARM *p = UPDATA_FLAG_ADDR;
  242. memset(p, 0x00, sizeof(UPDATA_PARM));
  243. }
  244. void update_close_hw(void *filter_name)
  245. {
  246. const struct update_target *p;
  247. list_for_each_update_target(p) {
  248. if (memcmp(filter_name, p->name, strlen(filter_name)) != 0) {
  249. printf("close Hw Name : %s\n", p->name);
  250. p->driver_close();
  251. }
  252. }
  253. }
  254. static void update_before_jump_common_handle(UPDATA_TYPE up_type)
  255. {
  256. dev_update_close_ui();
  257. #if TCFG_AUDIO_ANC_ENABLE
  258. extern void audio_anc_hw_close();
  259. audio_anc_hw_close();
  260. #endif
  261. #if (CPU_CORE_NUM == 1) //双核在跳转前关中断lock_set后会和maskrom 初始化中断冲突导致ilock err
  262. local_irq_disable();
  263. #endif
  264. hwi_all_close();
  265. #if defined(CONFIG_CPU_BD29)
  266. void breakpoint_uninit(void);
  267. breakpoint_uninit();
  268. #endif
  269. #ifdef CONFIG_SUPPORT_WIFI_DETECT
  270. wifi_det_close();
  271. #endif
  272. /*跳转的时候遇到死掉的情况很可能是硬件模块没关导致,加上保护可以判断哪个异常,保护的地址根据不同SDK而定*/
  273. /* u8 inv = 0; */
  274. /* mpu_set(1, (u32)&test_pro_addr, (u32)test_pro_addr, inv, "0r", DBG_FM); */
  275. }
  276. //ota.bin 放到exflash升级的方式,parm_priv存放了norflash的参数,对应实际升级方式的参数需要放在norflash参数之后
  277. void update_param_priv_fill(UPDATA_PARM *p, void *priv, u16 priv_len)
  278. {
  279. int parm_offset = 0;
  280. if (support_norflash_update_en) {
  281. parm_offset = get_nor_update_param(p->parm_priv); //如果loader放在外挂norflash,parm_priv前面放norflash参数,后面才是升级类型本身的参数
  282. }
  283. memcpy(p->parm_priv + parm_offset, priv, priv_len);
  284. }
  285. void update_param_ext_fill(UPDATA_PARM *p, u8 ext_type, u8 *ext_data, u8 ext_len)
  286. {
  287. struct ext_arg_t ext_arg;
  288. ext_arg.type = ext_type;
  289. ext_arg.len = ext_len;
  290. ext_arg.data = ext_data;
  291. memcpy((u8 *)p + sizeof(UPDATA_PARM) + p->ext_arg_len, &ext_arg, 2); //2byte:type + len
  292. memcpy((u8 *)p + sizeof(UPDATA_PARM) + p->ext_arg_len + 2, ext_arg.data, ext_arg.len);
  293. log_info("ext_fill :");
  294. log_info_hexdump((u8 *)p + sizeof(UPDATA_PARM) + p->ext_arg_len, ext_arg.len + 2);
  295. p->ext_arg_len += (2 + ext_arg.len);
  296. p->ext_arg_crc = CRC16((u8 *)p + sizeof(UPDATA_PARM), p->ext_arg_len);
  297. }
  298. u8 *update_param_ext_get(UPDATA_PARM *p, u8 ext_type)
  299. {
  300. u8 info_len;
  301. u8 ext_arg_len = p->ext_arg_len;
  302. u8 *pExt_arg = (u8 *)p + sizeof(UPDATA_PARM);
  303. /* r_printf(">>>[test]:ext_len = %d\n", p->ext_arg_len); */
  304. /* put_buf(pExt_arg, p->ext_arg_len); */
  305. if (p->ext_arg_crc != CRC16(pExt_arg, p->ext_arg_len)) { //crc not match
  306. return NULL;
  307. }
  308. while (1) {
  309. info_len = *(pExt_arg + 1) + 2;
  310. /* r_printf(">>>[test]:ext arg = %d, ext_type = %d, info_len = %d\n", *pExt_arg, ext_type, info_len); */
  311. if (*pExt_arg == ext_type) {
  312. return pExt_arg + 2; //2Byte: type + len
  313. }
  314. if (ext_arg_len < info_len) {
  315. break;
  316. }
  317. ext_arg_len -= info_len;
  318. pExt_arg += info_len; // + len
  319. }
  320. return NULL; //not find ext_type
  321. }
  322. //fill common content \ private content \ crc16
  323. static void update_param_content_fill(int type, UPDATA_PARM *p, void (*priv_param_fill_hdl)(UPDATA_PARM *P))
  324. {
  325. u8 ext_len = 0;
  326. u8 *ext_data = NULL;
  327. memset((u8 *)p, 0x00, sizeof(UPDATA_PARM));
  328. if (support_norflash_update_en) {
  329. p->parm_type = NORFLASH_UPDATA; //uboot通过该标识从外挂flash读取ota.bin
  330. *((u16 *)((u8 *)p + sizeof(UPDATA_PARM) + 32)) = (u16)type; //将实际的升级类型保存到UPDATA_PARM后
  331. } else {
  332. p->parm_type = (u16)type;
  333. }
  334. p->parm_result = (u16)UPDATA_READY;
  335. p->magic = UPDATE_PARAM_MAGIC;
  336. p->ota_addr = succ_report.loader_saddr;
  337. //支持loader放到外挂flash里ota_addr为0
  338. if (0 == p->ota_addr && !support_norflash_update_en) {
  339. log_error("ota addr err\n");
  340. return;
  341. }
  342. if (priv_param_fill_hdl) {
  343. priv_param_fill_hdl(p);
  344. }
  345. #ifdef CONFIG_CPU_BR23
  346. #if TCFG_APP_BT_EN
  347. if (type == BT_UPDATA || type == BLE_APP_UPDATA || type == SPP_APP_UPDATA || type == BLE_TEST_UPDATA) { //D版芯片蓝牙相关的升级需要保存LDO_TRIM_RES
  348. ext_data = malloc(128);
  349. if (ext_data != NULL) {
  350. ext_len = get_ldo_trim_res(ext_data);
  351. update_param_ext_fill(p, EXT_LDO_TRIM_RES, ext_data, ext_len);
  352. free(ext_data);
  353. }
  354. }
  355. #endif
  356. #endif
  357. u8 ext_flag = 0;
  358. ext_len = 1;
  359. #if CONFIG_UPDATE_JUMP_TO_MASK
  360. ext_flag = 1;
  361. #endif
  362. update_param_ext_fill(p, EXT_JUMP_FLAG, &ext_flag, ext_len);
  363. /* u8 *flag = update_param_ext_get(p, EXT_JUMP_FLAG); */
  364. /* r_printf(">>>[test]:flag = %d\n", flag[0]); */
  365. p->parm_crc = CRC16(((u8 *)p) + 2, sizeof(UPDATA_PARM) - 2); //2 : crc_val
  366. }
  367. static void update_param_ram_set(u8 *buf, u16 len)
  368. {
  369. u8 *update_ram = UPDATA_FLAG_ADDR;
  370. memcpy(update_ram, (u8 *)buf, len);
  371. }
  372. void update_mode_api_v2(UPDATA_TYPE type, void (*priv_param_fill_hdl)(UPDATA_PARM *p), void (*priv_update_jump_handle)(int type))
  373. {
  374. u16 update_param_len = sizeof(UPDATA_PARM) + UPDATE_PRIV_PARAM_LEN;
  375. UPDATA_PARM *p = malloc(update_param_len);
  376. if (p) {
  377. update_param_content_fill(type, p, priv_param_fill_hdl);
  378. if (succ_report.update_param_write_hdl) {
  379. succ_report.update_param_write_hdl(succ_report.priv_param, (u8 *)p, update_param_len);
  380. }
  381. #ifdef UPDATE_LED_REMIND
  382. led_update_start();
  383. #endif
  384. update_param_ram_set((u8 *)p, update_param_len);
  385. #if CPU_CORE_NUM > 1 //双核需要把CPU1关掉
  386. printf("Before Suspend Current Cpu ID:%d Cpu In Irq?:%d\n", current_cpu_id(), cpu_in_irq());
  387. if (current_cpu_id() == 1) {
  388. os_suspend_other_core();
  389. }
  390. ASSERT(current_cpu_id() == 0); //确保跳转前CPU1已经停止运行
  391. cpu_suspend_other_core(0x55);
  392. printf("After Suspend Current Cpu ID:%d\n", current_cpu_id());
  393. #endif
  394. update_before_jump_common_handle(type);
  395. if (priv_update_jump_handle) {
  396. priv_update_jump_handle(type);
  397. }
  398. free(p);
  399. } else {
  400. ASSERT(p, "malloc update param err \n");
  401. }
  402. }
  403. int update_check_sniff_en(void)
  404. {
  405. if (!UPDATE_SUPPORT_DEV_IS_NULL()) {
  406. if (get_ota_status()) {
  407. log_info("ota ing...");
  408. return 0;
  409. } else {
  410. return 1;
  411. }
  412. }
  413. return 1;
  414. }
  415. void set_ota_status(u8 stu)
  416. {
  417. ota_status = stu;
  418. }
  419. u8 get_ota_status()
  420. {
  421. return ota_status;
  422. }
  423. static u8 ota_idle_query(void)
  424. {
  425. return !ota_status;
  426. }
  427. //防止升级过程进入powerdown
  428. REGISTER_LP_TARGET(ota_lp_target) = {
  429. .name = "ota",
  430. .is_idle = ota_idle_query,
  431. };
  432. extern void tws_sync_update_api_register(const update_op_tws_api_t *op);
  433. extern update_op_tws_api_t *get_tws_update_api(void);
  434. extern const int support_dual_bank_update_en;
  435. extern int tws_ota_init(void);
  436. extern void sys_auto_shut_down_disable(void);
  437. extern void sys_auto_shut_down_enable(void);
  438. extern void tws_api_auto_role_switch_disable();
  439. extern void tws_api_auto_role_switch_enable();
  440. static void update_init_common_handle(int type)
  441. {
  442. ota_status = 1;
  443. if (UPDATE_DUAL_BANK_IS_SUPPORT()) {
  444. #if TCFG_AUTO_SHUT_DOWN_TIME
  445. sys_auto_shut_down_disable();
  446. #endif
  447. #if OTA_TWS_SAME_TIME_ENABLE
  448. tws_api_auto_role_switch_disable();
  449. tws_sync_update_api_register(get_tws_update_api());
  450. tws_ota_init();
  451. #endif
  452. }
  453. }
  454. static void update_exit_common_handle(int type, void *priv)
  455. {
  456. update_ret_code_t *ret_code = (update_ret_code_t *)priv;
  457. #if TCFG_AUTO_SHUT_DOWN_TIME
  458. sys_auto_shut_down_enable();
  459. #endif
  460. #if OTA_TWS_SAME_TIME_ENABLE
  461. if (UPDATE_DUAL_BANK_IS_SUPPORT()) {
  462. tws_api_auto_role_switch_enable();
  463. }
  464. #endif
  465. ota_status = 0;
  466. }
  467. static void update_common_state_cbk(update_mode_info_t *info, u32 state, void *priv)
  468. {
  469. int type = info->type;
  470. log_info("type:%x state:%x code:%x\n", type, state, priv);
  471. switch (state) {
  472. case UPDATE_CH_INIT:
  473. memset((u8 *)&succ_report, 0x00, sizeof(succ_report_t));
  474. update_init_common_handle(info->type);
  475. break;
  476. case UPDATE_CH_SUCESS_REPORT:
  477. log_info("succ report stored\n");
  478. memcpy((u8 *)&succ_report, (u8 *)priv, sizeof(succ_report_t));
  479. break;
  480. }
  481. if (info->state_cbk) {
  482. info->state_cbk(type, state, priv);
  483. }
  484. switch (state) {
  485. case UPDATE_CH_EXIT:
  486. update_exit_common_handle(info->type, priv);
  487. break;
  488. }
  489. }
  490. static int app_update_init(void)
  491. {
  492. update_module_init(update_common_state_cbk);
  493. testbox_update_init();
  494. printf("app_update_cfg:%d,%d,%d\n", support_dual_bank_update_en, support_norflash_update_en, support_vm_data_keep);
  495. return 0;
  496. }
  497. __initcall(app_update_init);
  498. void update_start_exit_sniff(void)
  499. {
  500. #if TCFG_USER_TWS_ENABLE
  501. volatile u8 wait_tws_sniff_exit = 1;
  502. if (get_tws_phone_connect_state() == TRUE) {
  503. g_printf("exit sniff mode...\n");
  504. user_send_cmd_prepare(USER_CTRL_ALL_SNIFF_EXIT, 0, NULL);
  505. } else {
  506. tws_tx_unsniff_req();
  507. }
  508. tws_sniff_controle_check_disable();
  509. #else
  510. user_send_cmd_prepare(USER_CTRL_ALL_SNIFF_EXIT, 0, NULL);
  511. #endif
  512. sys_auto_sniff_controle(0, NULL);
  513. }