1 use core::{ 2 ffi::{c_uint, c_void}, 3 mem::MaybeUninit, 4 sync::atomic::AtomicBool, 5 }; 6 7 use alloc::{ 8 string::{String, ToString}, 9 sync::{Arc, Weak}, 10 vec::Vec, 11 }; 12 use system_error::SystemError; 13 use unified_init::macros::unified_init; 14 15 use crate::{ 16 arch::MMArch, 17 driver::{ 18 base::{ 19 class::Class, 20 device::{ 21 bus::Bus, device_manager, driver::Driver, Device, DeviceState, DeviceType, IdTable, 22 }, 23 kobject::{KObjType, KObject, KObjectState, LockedKObjectState}, 24 kset::KSet, 25 platform::{ 26 platform_device::{platform_device_manager, PlatformDevice}, 27 platform_driver::{platform_driver_manager, PlatformDriver}, 28 CompatibleTable, 29 }, 30 }, 31 tty::serial::serial8250::send_to_default_serial8250_port, 32 video::fbdev::base::{fbmem::frame_buffer_manager, FbVisual}, 33 }, 34 filesystem::{ 35 kernfs::KernFSInode, 36 sysfs::{file::sysfs_emit_str, Attribute, AttributeGroup, SysFSOpsSupport}, 37 vfs::syscall::ModeType, 38 }, 39 include::bindings::bindings::{ 40 multiboot2_get_Framebuffer_info, multiboot2_iter, multiboot_tag_framebuffer_info_t, 41 FRAME_BUFFER_MAPPING_OFFSET, 42 }, 43 init::{boot_params, initcall::INITCALL_DEVICE}, 44 libs::{ 45 align::page_align_up, 46 once::Once, 47 rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, 48 spinlock::SpinLock, 49 }, 50 mm::{ 51 allocator::page_frame::PageFrameCount, no_init::pseudo_map_phys, MemoryManagementArch, 52 PhysAddr, VirtAddr, 53 }, 54 }; 55 56 use super::base::{ 57 fbmem::FbDevice, BlankMode, BootTimeVideoType, FbAccel, FbActivateFlags, FbId, FbState, FbType, 58 FbVModeFlags, FbVarScreenInfo, FbVideoMode, FixedScreenInfo, FrameBuffer, FrameBufferInfo, 59 FrameBufferOps, 60 }; 61 62 /// 当前机器上面是否有vesa帧缓冲区 63 static HAS_VESA_FB: AtomicBool = AtomicBool::new(false); 64 65 lazy_static! { 66 static ref VESAFB_FIX_INFO: RwLock<FixedScreenInfo> = RwLock::new(FixedScreenInfo { 67 id: FixedScreenInfo::name2id("VESA VGA"), 68 fb_type: FbType::PackedPixels, 69 accel: FbAccel::None, 70 ..Default::default() 71 }); 72 static ref VESAFB_DEFINED: RwLock<FbVarScreenInfo> = RwLock::new(FbVarScreenInfo { 73 activate: FbActivateFlags::FB_ACTIVATE_NOW, 74 height: None, 75 width: None, 76 right_margin: 32, 77 upper_margin: 16, 78 lower_margin: 4, 79 vsync_len: 4, 80 vmode: FbVModeFlags::FB_VMODE_NONINTERLACED, 81 82 ..Default::default() 83 }); 84 } 85 86 #[derive(Debug)] 87 #[cast_to([sync] Device)] 88 #[cast_to([sync] PlatformDevice)] 89 pub struct VesaFb { 90 inner: SpinLock<InnerVesaFb>, 91 kobj_state: LockedKObjectState, 92 } 93 94 impl VesaFb { 95 pub const NAME: &'static str = "vesa_vga"; 96 pub fn new() -> Self { 97 return Self { 98 inner: SpinLock::new(InnerVesaFb { 99 bus: None, 100 class: None, 101 driver: None, 102 kern_inode: None, 103 parent: None, 104 kset: None, 105 kobj_type: None, 106 device_state: DeviceState::NotInitialized, 107 pdev_id: 0, 108 pdev_id_auto: false, 109 fb_id: FbId::INIT, 110 fb_device: None, 111 fb_state: FbState::Suspended, 112 }), 113 kobj_state: LockedKObjectState::new(None), 114 }; 115 } 116 } 117 118 #[derive(Debug)] 119 struct InnerVesaFb { 120 bus: Option<Weak<dyn Bus>>, 121 class: Option<Arc<dyn Class>>, 122 driver: Option<Weak<dyn Driver>>, 123 kern_inode: Option<Arc<KernFSInode>>, 124 parent: Option<Weak<dyn KObject>>, 125 kset: Option<Arc<KSet>>, 126 kobj_type: Option<&'static dyn KObjType>, 127 device_state: DeviceState, 128 pdev_id: i32, 129 pdev_id_auto: bool, 130 fb_id: FbId, 131 fb_device: Option<Arc<FbDevice>>, 132 fb_state: FbState, 133 } 134 135 impl FrameBuffer for VesaFb { 136 fn fb_id(&self) -> FbId { 137 self.inner.lock().fb_id 138 } 139 140 fn set_fb_id(&self, id: FbId) { 141 self.inner.lock().fb_id = id; 142 } 143 } 144 145 impl PlatformDevice for VesaFb { 146 fn pdev_name(&self) -> &str { 147 Self::NAME 148 } 149 150 fn set_pdev_id(&self, id: i32) { 151 self.inner.lock().pdev_id = id; 152 } 153 154 fn set_pdev_id_auto(&self, id_auto: bool) { 155 self.inner.lock().pdev_id_auto = id_auto; 156 } 157 158 fn compatible_table(&self) -> CompatibleTable { 159 todo!() 160 } 161 162 fn is_initialized(&self) -> bool { 163 self.inner.lock().device_state == DeviceState::Initialized 164 } 165 166 fn set_state(&self, set_state: DeviceState) { 167 self.inner.lock().device_state = set_state; 168 } 169 } 170 171 impl Device for VesaFb { 172 fn dev_type(&self) -> DeviceType { 173 DeviceType::Char 174 } 175 176 fn id_table(&self) -> IdTable { 177 IdTable::new(self.name(), None) 178 } 179 180 fn bus(&self) -> Option<Weak<dyn Bus>> { 181 self.inner.lock().bus.clone() 182 } 183 184 fn set_bus(&self, bus: Option<Weak<dyn Bus>>) { 185 self.inner.lock().bus = bus; 186 } 187 188 fn set_class(&self, class: Option<Arc<dyn Class>>) { 189 self.inner.lock().class = class; 190 } 191 192 fn driver(&self) -> Option<Arc<dyn Driver>> { 193 self.inner.lock().driver.clone()?.upgrade() 194 } 195 196 fn set_driver(&self, driver: Option<Weak<dyn Driver>>) { 197 self.inner.lock().driver = driver; 198 } 199 200 fn is_dead(&self) -> bool { 201 false 202 } 203 204 fn can_match(&self) -> bool { 205 true 206 } 207 208 fn set_can_match(&self, _can_match: bool) {} 209 210 fn state_synced(&self) -> bool { 211 true 212 } 213 } 214 215 impl KObject for VesaFb { 216 fn as_any_ref(&self) -> &dyn core::any::Any { 217 self 218 } 219 220 fn set_inode(&self, inode: Option<Arc<KernFSInode>>) { 221 self.inner.lock().kern_inode = inode; 222 } 223 224 fn inode(&self) -> Option<Arc<KernFSInode>> { 225 self.inner.lock().kern_inode.clone() 226 } 227 228 fn parent(&self) -> Option<Weak<dyn KObject>> { 229 self.inner.lock().parent.clone() 230 } 231 232 fn set_parent(&self, parent: Option<Weak<dyn KObject>>) { 233 self.inner.lock().parent = parent; 234 } 235 236 fn kset(&self) -> Option<Arc<KSet>> { 237 self.inner.lock().kset.clone() 238 } 239 240 fn set_kset(&self, kset: Option<Arc<KSet>>) { 241 self.inner.lock().kset = kset; 242 } 243 244 fn kobj_type(&self) -> Option<&'static dyn KObjType> { 245 self.inner.lock().kobj_type 246 } 247 248 fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { 249 self.inner.lock().kobj_type = ktype; 250 } 251 252 fn name(&self) -> String { 253 Self::NAME.to_string() 254 } 255 256 fn set_name(&self, _name: String) { 257 // do nothing 258 } 259 260 fn kobj_state(&self) -> RwLockReadGuard<KObjectState> { 261 self.kobj_state.read() 262 } 263 264 fn kobj_state_mut(&self) -> RwLockWriteGuard<KObjectState> { 265 self.kobj_state.write() 266 } 267 268 fn set_kobj_state(&self, state: KObjectState) { 269 *self.kobj_state.write() = state; 270 } 271 } 272 273 impl FrameBufferOps for VesaFb { 274 fn fb_open(&self, _user: bool) { 275 todo!() 276 } 277 278 fn fb_release(&self, _user: bool) { 279 todo!() 280 } 281 282 fn fb_set_color_register( 283 &self, 284 _regno: u16, 285 _red: u16, 286 _green: u16, 287 _blue: u16, 288 ) -> Result<(), SystemError> { 289 todo!() 290 } 291 292 fn fb_blank(&self, _blank_mode: BlankMode) -> Result<(), SystemError> { 293 todo!() 294 } 295 296 fn fb_destroy(&self) { 297 todo!() 298 } 299 300 fn fb_read(&self, buf: &mut [u8], pos: usize) -> Result<usize, SystemError> { 301 let bp = boot_params().read(); 302 303 let vaddr = bp.screen_info.lfb_virt_base.ok_or(SystemError::ENODEV)?; 304 let size = self.current_fb_fix().smem_len; 305 drop(bp); 306 if pos >= size { 307 return Ok(0); 308 } 309 310 let pos = pos as i64; 311 let size = size as i64; 312 313 let len = core::cmp::min(size - pos, buf.len() as i64) as usize; 314 315 let slice = unsafe { core::slice::from_raw_parts(vaddr.as_ptr::<u8>(), size as usize) }; 316 buf[..len].copy_from_slice(&slice[pos as usize..(pos as usize + len)]); 317 318 return Ok(len); 319 } 320 321 fn fb_write(&self, buf: &[u8], pos: usize) -> Result<usize, SystemError> { 322 let bp = boot_params().read(); 323 324 let vaddr = bp.screen_info.lfb_virt_base.ok_or(SystemError::ENODEV)?; 325 let size = self.current_fb_fix().smem_len; 326 327 if pos >= size { 328 return Ok(0); 329 } 330 331 let pos = pos as i64; 332 let size = size as i64; 333 334 let len = core::cmp::min(size - pos, buf.len() as i64) as usize; 335 336 let slice = unsafe { core::slice::from_raw_parts_mut(vaddr.as_ptr::<u8>(), size as usize) }; 337 slice[pos as usize..(pos as usize + len)].copy_from_slice(&buf[..len]); 338 339 return Ok(len); 340 } 341 } 342 343 impl FrameBufferInfo for VesaFb { 344 fn fb_device(&self) -> Option<Arc<FbDevice>> { 345 self.inner.lock().fb_device.clone() 346 } 347 348 fn set_fb_device(&self, device: Option<Arc<FbDevice>>) { 349 self.inner.lock().fb_device = device; 350 } 351 352 fn screen_size(&self) -> usize { 353 todo!() 354 } 355 356 fn current_fb_var(&self) -> FbVarScreenInfo { 357 VESAFB_DEFINED.read().clone() 358 } 359 360 fn current_fb_fix(&self) -> FixedScreenInfo { 361 VESAFB_FIX_INFO.read().clone() 362 } 363 364 fn video_mode(&self) -> Option<&FbVideoMode> { 365 todo!() 366 } 367 368 fn state(&self) -> FbState { 369 self.inner.lock().fb_state 370 } 371 } 372 373 #[derive(Debug)] 374 #[cast_to([sync] PlatformDriver)] 375 struct VesaFbDriver { 376 inner: SpinLock<InnerVesaFbDriver>, 377 kobj_state: LockedKObjectState, 378 } 379 380 impl VesaFbDriver { 381 pub fn new() -> Arc<Self> { 382 let r = Arc::new(Self { 383 inner: SpinLock::new(InnerVesaFbDriver { 384 ktype: None, 385 kset: None, 386 parent: None, 387 kernfs_inode: None, 388 devices: Vec::new(), 389 bus: None, 390 self_ref: Weak::new(), 391 }), 392 kobj_state: LockedKObjectState::new(None), 393 }); 394 395 r.inner.lock().self_ref = Arc::downgrade(&r); 396 397 return r; 398 } 399 } 400 401 #[derive(Debug)] 402 struct InnerVesaFbDriver { 403 ktype: Option<&'static dyn KObjType>, 404 kset: Option<Arc<KSet>>, 405 parent: Option<Weak<dyn KObject>>, 406 kernfs_inode: Option<Arc<KernFSInode>>, 407 devices: Vec<Arc<dyn Device>>, 408 bus: Option<Weak<dyn Bus>>, 409 410 self_ref: Weak<VesaFbDriver>, 411 } 412 413 impl VesaFbDriver { 414 const NAME: &'static str = "vesa-framebuffer"; 415 } 416 417 impl PlatformDriver for VesaFbDriver { 418 fn probe(&self, device: &Arc<dyn PlatformDevice>) -> Result<(), SystemError> { 419 let device = device 420 .clone() 421 .arc_any() 422 .downcast::<VesaFb>() 423 .map_err(|_| SystemError::EINVAL)?; 424 425 device.set_driver(Some(self.inner.lock_irqsave().self_ref.clone())); 426 427 return Ok(()); 428 } 429 430 fn remove(&self, _device: &Arc<dyn PlatformDevice>) -> Result<(), SystemError> { 431 todo!() 432 } 433 434 fn shutdown(&self, _device: &Arc<dyn PlatformDevice>) -> Result<(), SystemError> { 435 // do nothing 436 return Ok(()); 437 } 438 439 fn suspend(&self, _device: &Arc<dyn PlatformDevice>) -> Result<(), SystemError> { 440 // do nothing 441 return Ok(()); 442 } 443 444 fn resume(&self, _device: &Arc<dyn PlatformDevice>) -> Result<(), SystemError> { 445 todo!() 446 } 447 } 448 449 impl Driver for VesaFbDriver { 450 fn id_table(&self) -> Option<IdTable> { 451 Some(IdTable::new(VesaFb::NAME.to_string(), None)) 452 } 453 454 fn devices(&self) -> Vec<Arc<dyn Device>> { 455 self.inner.lock().devices.clone() 456 } 457 458 fn add_device(&self, device: Arc<dyn Device>) { 459 let mut guard = self.inner.lock(); 460 // check if the device is already in the list 461 if guard.devices.iter().any(|dev| Arc::ptr_eq(dev, &device)) { 462 return; 463 } 464 465 guard.devices.push(device); 466 } 467 468 fn delete_device(&self, device: &Arc<dyn Device>) { 469 let mut guard = self.inner.lock(); 470 guard.devices.retain(|dev| !Arc::ptr_eq(dev, device)); 471 } 472 473 fn set_bus(&self, bus: Option<Weak<dyn Bus>>) { 474 self.inner.lock().bus = bus; 475 } 476 477 fn bus(&self) -> Option<Weak<dyn Bus>> { 478 self.inner.lock().bus.clone() 479 } 480 481 fn dev_groups(&self) -> &'static [&'static dyn AttributeGroup] { 482 return &[&VesaFbAnonAttributeGroup]; 483 } 484 } 485 486 impl KObject for VesaFbDriver { 487 fn as_any_ref(&self) -> &dyn core::any::Any { 488 self 489 } 490 491 fn set_inode(&self, inode: Option<Arc<KernFSInode>>) { 492 self.inner.lock().kernfs_inode = inode; 493 } 494 495 fn inode(&self) -> Option<Arc<KernFSInode>> { 496 self.inner.lock().kernfs_inode.clone() 497 } 498 499 fn parent(&self) -> Option<Weak<dyn KObject>> { 500 self.inner.lock().parent.clone() 501 } 502 503 fn set_parent(&self, parent: Option<Weak<dyn KObject>>) { 504 self.inner.lock().parent = parent; 505 } 506 507 fn kset(&self) -> Option<Arc<KSet>> { 508 self.inner.lock().kset.clone() 509 } 510 511 fn set_kset(&self, kset: Option<Arc<KSet>>) { 512 self.inner.lock().kset = kset; 513 } 514 515 fn kobj_type(&self) -> Option<&'static dyn KObjType> { 516 self.inner.lock().ktype 517 } 518 519 fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { 520 self.inner.lock().ktype = ktype; 521 } 522 523 fn name(&self) -> String { 524 Self::NAME.to_string() 525 } 526 527 fn set_name(&self, _name: String) { 528 // do nothing 529 } 530 531 fn kobj_state(&self) -> RwLockReadGuard<KObjectState> { 532 self.kobj_state.read() 533 } 534 535 fn kobj_state_mut(&self) -> RwLockWriteGuard<KObjectState> { 536 self.kobj_state.write() 537 } 538 539 fn set_kobj_state(&self, state: KObjectState) { 540 *self.kobj_state.write() = state; 541 } 542 } 543 544 #[derive(Debug)] 545 struct VesaFbAnonAttributeGroup; 546 547 impl AttributeGroup for VesaFbAnonAttributeGroup { 548 fn name(&self) -> Option<&str> { 549 None 550 } 551 552 fn attrs(&self) -> &[&'static dyn Attribute] { 553 &[&AnonAttrPhysAddr as &'static dyn Attribute] 554 } 555 556 fn is_visible( 557 &self, 558 _kobj: Arc<dyn KObject>, 559 attr: &'static dyn Attribute, 560 ) -> Option<ModeType> { 561 Some(attr.mode()) 562 } 563 } 564 565 #[derive(Debug)] 566 struct AnonAttrPhysAddr; 567 568 impl Attribute for AnonAttrPhysAddr { 569 fn name(&self) -> &str { 570 "smem_start" 571 } 572 573 fn mode(&self) -> ModeType { 574 ModeType::S_IRUGO 575 } 576 577 fn support(&self) -> SysFSOpsSupport { 578 SysFSOpsSupport::ATTR_SHOW 579 } 580 581 fn show(&self, _kobj: Arc<dyn KObject>, buf: &mut [u8]) -> Result<usize, SystemError> { 582 sysfs_emit_str( 583 buf, 584 format!( 585 "0x{:x}\n", 586 VESAFB_FIX_INFO 587 .read() 588 .smem_start 589 .unwrap_or(PhysAddr::new(0)) 590 .data() 591 ) 592 .as_str(), 593 ) 594 } 595 } 596 597 #[unified_init(INITCALL_DEVICE)] 598 pub fn vesa_fb_driver_init() -> Result<(), SystemError> { 599 let driver = VesaFbDriver::new(); 600 601 platform_driver_manager().register(driver)?; 602 603 return Ok(()); 604 } 605 606 /// 在内存管理初始化之前,初始化vesafb 607 pub fn vesafb_early_init() -> Result<VirtAddr, SystemError> { 608 let mut _reserved: u32 = 0; 609 610 let mut fb_info: MaybeUninit<multiboot_tag_framebuffer_info_t> = MaybeUninit::uninit(); 611 //从multiboot2中读取帧缓冲区信息至fb_info 612 613 // todo: 换成rust的,并且检测是否成功获取 614 unsafe { 615 multiboot2_iter( 616 Some(multiboot2_get_Framebuffer_info), 617 fb_info.as_mut_ptr() as usize as *mut c_void, 618 &mut _reserved as *mut c_uint, 619 ) 620 }; 621 unsafe { fb_info.assume_init() }; 622 let fb_info: multiboot_tag_framebuffer_info_t = unsafe { core::mem::transmute(fb_info) }; 623 624 // todo: 判断是否有vesa帧缓冲区,这里暂时直接设置true 625 HAS_VESA_FB.store(true, core::sync::atomic::Ordering::SeqCst); 626 627 let width = fb_info.framebuffer_width; 628 let height = fb_info.framebuffer_height; 629 630 let mut boot_params_guard = boot_params().write(); 631 let boottime_screen_info = &mut boot_params_guard.screen_info; 632 633 boottime_screen_info.is_vga = true; 634 635 boottime_screen_info.lfb_base = PhysAddr::new(fb_info.framebuffer_addr as usize); 636 637 if fb_info.framebuffer_type == 2 { 638 //当type=2时,width与height用字符数表示,故depth=8 639 boottime_screen_info.origin_video_cols = width as u8; 640 boottime_screen_info.origin_video_lines = height as u8; 641 boottime_screen_info.video_type = BootTimeVideoType::Mda; 642 boottime_screen_info.lfb_depth = 8; 643 } else { 644 //否则为图像模式,depth应参照帧缓冲区信息里面的每个像素的位数 645 boottime_screen_info.lfb_width = width; 646 boottime_screen_info.lfb_height = height; 647 boottime_screen_info.video_type = BootTimeVideoType::Vlfb; 648 boottime_screen_info.lfb_depth = fb_info.framebuffer_bpp as u8; 649 } 650 651 boottime_screen_info.lfb_size = 652 (width * height * ((fb_info.framebuffer_bpp as u32 + 7) / 8)) as usize; 653 654 // let buf_vaddr = VirtAddr::new(0xffff800003200000); 655 let buf_vaddr = VirtAddr::new( 656 crate::include::bindings::bindings::SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE as usize 657 + FRAME_BUFFER_MAPPING_OFFSET as usize, 658 ); 659 boottime_screen_info.lfb_virt_base = Some(buf_vaddr); 660 661 let init_text = "Video driver to map.\n\0"; 662 send_to_default_serial8250_port(init_text.as_bytes()); 663 664 // 地址映射 665 let paddr = PhysAddr::new(fb_info.framebuffer_addr as usize); 666 let count = 667 PageFrameCount::new(page_align_up(boottime_screen_info.lfb_size) / MMArch::PAGE_SIZE); 668 unsafe { pseudo_map_phys(buf_vaddr, paddr, count) }; 669 return Ok(buf_vaddr); 670 } 671 672 #[unified_init(INITCALL_DEVICE)] 673 fn vesa_fb_device_init() -> Result<(), SystemError> { 674 // 如果没有vesa帧缓冲区,直接返回 675 if !HAS_VESA_FB.load(core::sync::atomic::Ordering::SeqCst) { 676 return Ok(()); 677 } 678 679 static INIT: Once = Once::new(); 680 INIT.call_once(|| { 681 kinfo!("vesa fb device init"); 682 683 let mut fix_info_guard = VESAFB_FIX_INFO.write_irqsave(); 684 let mut var_info_guard = VESAFB_DEFINED.write_irqsave(); 685 686 let boot_params_guard = boot_params().read(); 687 let boottime_screen_info = &boot_params_guard.screen_info; 688 689 fix_info_guard.smem_start = Some(boottime_screen_info.lfb_base); 690 fix_info_guard.smem_len = boottime_screen_info.lfb_size; 691 692 if boottime_screen_info.video_type == BootTimeVideoType::Mda { 693 fix_info_guard.visual = FbVisual::Mono10; 694 var_info_guard.bits_per_pixel = 8; 695 fix_info_guard.line_length = (boottime_screen_info.origin_video_cols as u32) 696 * (var_info_guard.bits_per_pixel / 8); 697 var_info_guard.xres_virtual = boottime_screen_info.origin_video_cols as u32; 698 var_info_guard.yres_virtual = boottime_screen_info.origin_video_lines as u32; 699 } else { 700 fix_info_guard.visual = FbVisual::TrueColor; 701 var_info_guard.bits_per_pixel = boottime_screen_info.lfb_depth as u32; 702 fix_info_guard.line_length = 703 (boottime_screen_info.lfb_width as u32) * (var_info_guard.bits_per_pixel / 8); 704 var_info_guard.xres_virtual = boottime_screen_info.lfb_width as u32; 705 var_info_guard.yres_virtual = boottime_screen_info.lfb_height as u32; 706 } 707 708 drop(var_info_guard); 709 drop(fix_info_guard); 710 711 let device = Arc::new(VesaFb::new()); 712 device_manager().device_default_initialize(&(device.clone() as Arc<dyn Device>)); 713 714 platform_device_manager() 715 .device_add(device.clone() as Arc<dyn PlatformDevice>) 716 .expect("vesa_fb_device_init: platform_device_manager().device_add failed"); 717 718 frame_buffer_manager() 719 .register_fb(device.clone() as Arc<dyn FrameBuffer>) 720 .expect("vesa_fb_device_init: frame_buffer_manager().register_fb failed"); 721 722 // 设置vesa fb的状态为运行中 723 device.inner.lock().fb_state = FbState::Running; 724 }); 725 726 return Ok(()); 727 } 728