예제 #1
0
s32 sys_spu_thread_bind_queue(u32 id, u32 spuq, u32 spuq_num)
{
	sys_spu.warning("sys_spu_thread_bind_queue(id=0x%x, spuq=0x%x, spuq_num=0x%x)", id, spuq, spuq_num);

	LV2_LOCK;

	const auto thread = idm::get<SPUThread>(id);
	const auto queue = idm::get<lv2_event_queue>(spuq);

	if (!thread || !queue)
	{
		return CELL_ESRCH;
	}

	if (queue->type != SYS_SPU_QUEUE)
	{
		return CELL_EINVAL;
	}

	for (auto& v : thread->spuq)
	{
		if (auto q = v.second.lock())
		{
			if (v.first == spuq_num || q == queue)
			{
				return CELL_EBUSY;
			}
		}
	}

	for (auto& v : thread->spuq)
	{
		if (v.second.expired())
		{
			v.first = spuq_num;
			v.second = queue;

			return CELL_OK;
		}
	}

	return CELL_EAGAIN;
}
예제 #2
0
s32 _sys_snprintf(ppu_thread& ppu, vm::ptr<char> dst, u32 count, vm::cptr<char> fmt, ppu_va_args_t va_args)
{
	sysPrxForUser.warning("_sys_snprintf(dst=*0x%x, count=%d, fmt=%s, ...)", dst, count, fmt);

	std::string result = ps3_fmt(ppu, fmt, va_args.count);

	if (!count)
	{
		return 0; // ???
	}
	else
	{
		count = (u32)std::min<size_t>(count - 1, result.size());

		std::memcpy(dst.get_ptr(), result.c_str(), count);
		dst[count] = 0;
		return count;
	}
}
예제 #3
0
파일: cellMouse.cpp 프로젝트: KitoHo/rpcs3
s32 cellMouseInit(u32 max_connect)
{
	sys_io.warning("cellMouseInit(max_connect=%d)", max_connect);

	if (max_connect > 7)
	{
		return CELL_MOUSE_ERROR_INVALID_PARAMETER;
	}

	const auto handler = fxm::import<MouseHandlerBase>(Emu.GetCallbacks().get_mouse_handler);

	if (!handler)
	{
		return CELL_MOUSE_ERROR_ALREADY_INITIALIZED;
	}

	handler->Init(max_connect);
	return CELL_OK;
}
예제 #4
0
s32 sys_spu_thread_get_exit_status(u32 id, vm::ptr<u32> status)
{
	sys_spu.warning("sys_spu_thread_get_exit_status(id=0x%x, status=*0x%x)", id, status);

	LV2_LOCK;

	const auto thread = idm::get<SPUThread>(id);

	if (!thread)
	{
		return CELL_ESRCH;
	}

	// TODO: check CELL_ESTAT condition

	*status = thread->ch_out_mbox.pop(*thread);

	return CELL_OK;
}
예제 #5
0
s32 _sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2)
{
	sys_interrupt.warning("_sys_interrupt_thread_establish(ih=*0x%x, intrtag=0x%x, intrthread=0x%x, arg1=0x%llx, arg2=0x%llx)", ih, intrtag, intrthread, arg1, arg2);

	LV2_LOCK;

	// Get interrupt tag
	const auto tag = idm::get<lv2_int_tag_t>(intrtag);

	if (!tag)
	{
		return CELL_ESRCH;
	}

	// Get interrupt thread
	const auto it = idm::get<ppu_thread>(intrthread);

	if (!it)
	{
		return CELL_ESRCH;
	}

	// If interrupt thread is running, it's already established on another interrupt tag
	if (!test(it->state & cpu_flag::stop))
	{
		return CELL_EAGAIN;
	}

	// It's unclear if multiple handlers can be established on single interrupt tag
	if (tag->handler)
	{
		sys_interrupt.error("_sys_interrupt_thread_establish(): handler service already exists (intrtag=0x%x) -> CELL_ESTAT", intrtag);
		return CELL_ESTAT;
	}

	tag->handler = idm::make_ptr<lv2_int_serv_t>(it, arg1, arg2);

	it->run();

	*ih = tag->handler->id;

	return CELL_OK;
}
예제 #6
0
파일: cellSail.cpp 프로젝트: DHrpcs3/rpcs3
s32 cellSailDescriptorCreateDatabase(vm::ptr<CellSailDescriptor> pSelf, vm::ptr<void> pDatabase, u32 size, u64 arg)
{
	cellSail.warning("cellSailDescriptorCreateDatabase(pSelf=*0x%x, pDatabase=*0x%x, size=0x%x, arg=0x%llx)", pSelf, pDatabase, size, arg);

	switch ((s32)pSelf->streamType)
	{
		case CELL_SAIL_STREAM_PAMF:
		{
			u32 addr = pSelf->sp_;
			auto ptr = vm::ptr<CellPamfReader>::make(addr);
			memcpy(pDatabase.get_ptr(), ptr.get_ptr(), sizeof(CellPamfReader));
			break;
		}
		default:
			cellSail.error("Unhandled stream type: %d", pSelf->streamType);
	}

	return CELL_OK;
}
예제 #7
0
파일: sys_prx.cpp 프로젝트: 4iDragon/rpcs3
s32 sys_prx_unload_module(s32 id, u64 flags, vm::ptr<sys_prx_unload_module_option_t> pOpt)
{
	sys_prx.warning("sys_prx_unload_module(id=0x%x, flags=0x%llx, pOpt=*0x%x)", id, flags, pOpt);

	// Get the PRX, free the used memory and delete the object and its ID
	const auto prx = idm::get<lv2_prx_t>(id);

	if (!prx)
	{
		return CELL_ESRCH;
	}

	//Memory.Free(prx->address);

	//s32 result = prx->exit ? prx->exit() : CELL_OK;
	idm::remove<lv2_prx_t>(id);
	
	return CELL_OK;
}
예제 #8
0
파일: sys_fs.cpp 프로젝트: Zangetsu38/rpcs3
error_code sys_fs_mkdir(vm::cptr<char> path, s32 mode)
{
	sys_fs.warning("sys_fs_mkdir(path=%s, mode=%#o)", path, mode);

	const std::string& local_path = vfs::get(path.get_ptr());

	if (fs::is_dir(local_path))
	{
		return CELL_EEXIST;
	}

	if (!fs::create_path(local_path))
	{
		return CELL_EIO; // ???
	}

	sys_fs.notice("sys_fs_mkdir(): directory %s created", path);
	return CELL_OK;
}
예제 #9
0
파일: sys_prx.cpp 프로젝트: 4iDragon/rpcs3
s32 sys_prx_start_module(s32 id, u64 flags, vm::ptr<sys_prx_start_module_option_t> pOpt)
{
	sys_prx.warning("sys_prx_start_module(id=0x%x, flags=0x%llx, pOpt=*0x%x)", id, flags, pOpt);

	const auto prx = idm::get<lv2_prx_t>(id);

	if (!prx)
	{
		return CELL_ESRCH;
	}

	//if (prx->is_started)
	//	return CELL_PRX_ERROR_ALREADY_STARTED;

	//prx->is_started = true;
	pOpt->entry_point.set(prx->start ? prx->start.addr() : ~0ull);

	return CELL_OK;
}
예제 #10
0
error_code _sys_prx_stop_module(u32 id, u64 flags, vm::ptr<sys_prx_start_stop_module_option_t> pOpt)
{
	sys_prx.warning("_sys_prx_stop_module(id=0x%x, flags=0x%x, pOpt=*0x%x)", id, flags, pOpt);

	const auto prx = idm::get<lv2_obj, lv2_prx>(id);

	if (!prx)
	{
		return CELL_ESRCH;
	}

	//if (!prx->is_started)
	//	return CELL_PRX_ERROR_ALREADY_STOPPED;

	//prx->is_started = false;
	pOpt->entry.set(prx->stop ? prx->stop.addr() : ~0ull);

	return CELL_OK;
}
예제 #11
0
s32 sys_get_random_number(vm::ptr<void> addr, u64 size)
{
	sysPrxForUser.warning("sys_get_random_number(addr=*0x%x, size=%d)", addr, size);

	if (size > 0x1000)
	{
		return CELL_EINVAL;
	}

	switch (u32 rs = sys_ss_random_number_generator(2, addr, size))
	{
	case 0x80010501: return CELL_ENOMEM;
	case 0x80010503: return CELL_EAGAIN;
	case 0x80010509: return CELL_EINVAL;
	default: if (rs) return CELL_EABORT;
	}

	return CELL_OK;
}
예제 #12
0
파일: sys_spu_.cpp 프로젝트: 4iDragon/rpcs3
s32 sys_spu_image_close(vm::ptr<sys_spu_image_t> img)
{
	sysPrxForUser.warning("sys_spu_image_close(img=*0x%x)", img);

	if (img->type == SYS_SPU_IMAGE_TYPE_USER)
	{
		//_sys_free(img->segs.addr());
	}
	else if (img->type == SYS_SPU_IMAGE_TYPE_KERNEL)
	{
		//return syscall_158(img);
	}
	else
	{
		return CELL_EINVAL;
	}

	VERIFY(vm::dealloc(img->segs.addr(), vm::main)); // Current rough implementation
	return CELL_OK;
}
예제 #13
0
s32 sys_timer_get_information(u32 timer_id, vm::ptr<sys_timer_information_t> info)
{
	sys_timer.warning("sys_timer_get_information(timer_id=0x%x, info=*0x%x)", timer_id, info);

	LV2_LOCK;

	const auto timer = idm::get<lv2_timer_t>(timer_id);

	if (!timer)
	{
		return CELL_ESRCH;
	}

	info->next_expiration_time = timer->expire;

	info->period      = timer->period;
	info->timer_state = timer->state;

	return CELL_OK;
}
예제 #14
0
error_code sys_memory_container_get_size(vm::ptr<sys_memory_info_t> mem_info, u32 cid)
{
	sys_memory.warning("sys_memory_container_get_size(mem_info=*0x%x, cid=0x%x)", mem_info, cid);

	const auto ct = idm::get<lv2_memory_container>(cid);

	if (!ct)
	{
		return CELL_ESRCH;
	}

	mem_info->total_user_memory = ct->size; // Total container memory
	// Available container memory, minus a hidden 'buffer' 
	// This buffer seems to be used by the PS3 OS for c style 'mallocs'
	// Todo: Research this more, even though we dont use this buffer, it helps out games when calculating 
	//	     expected memory they can use allowing them to boot
	mem_info->available_user_memory = ct->size - ct->used - 0x1000000; 

	return CELL_OK;
}
예제 #15
0
s32 cellSysmoduleLoadModule(u16 id)
{
	cellSysmodule.warning("cellSysmoduleLoadModule(id=%s)", get_module_id(id));

	const auto name = get_module_name(id);

	if (!name)
	{
		cellSysmodule.error("cellSysmoduleLoadModule() failed: unknown module 0x%04X", id);
		return CELL_SYSMODULE_ERROR_UNKNOWN;
	}

	//if (Module<>* m = Emu.GetModuleManager().GetModuleById(id))
	//{
	//	// CELL_SYSMODULE_ERROR_DUPLICATED shouldn't be returned
	//	m->Load();
	//}

	return CELL_OK;
}
예제 #16
0
s32 sys_spu_thread_group_create(vm::ptr<u32> id, u32 num, s32 prio, vm::ptr<sys_spu_thread_group_attribute> attr)
{
	sys_spu.warning("sys_spu_thread_group_create(id=*0x%x, num=%d, prio=%d, attr=*0x%x)", id, num, prio, attr);

	// TODO: max num value should be affected by sys_spu_initialize() settings

	if (!num || num > 6 || prio < 16 || prio > 255)
	{
		return CELL_EINVAL;
	}

	if (attr->type)
	{
		sys_spu.todo("Unsupported SPU Thread Group type (0x%x)", attr->type);
	}

	*id = idm::make<lv2_spu_group_t>(std::string{ attr->name.get_ptr(), attr->nsize - 1 }, num, prio, attr->type, attr->ct);

	return CELL_OK;
}
예제 #17
0
파일: sceNp.cpp 프로젝트: 4iDragon/rpcs3
s32 sceNpInit(u32 poolsize, vm::ptr<void> poolptr)
{
	sceNp.warning("sceNpInit(poolsize=0x%x, poolptr=*0x%x)", poolsize, poolptr);

	if (poolsize == 0)
	{
		return SCE_NP_ERROR_INVALID_ARGUMENT;
	}
	else if (poolsize < 128 * 1024)
	{
		return SCE_NP_ERROR_INSUFFICIENT_BUFFER;
	}

	if (!poolptr)
	{
		return SCE_NP_ERROR_INVALID_ARGUMENT;
	}

	return CELL_OK;
}
예제 #18
0
파일: cellFs.cpp 프로젝트: 4iDragon/rpcs3
s32 cellFsAioWrite(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
{
	cellFs.warning("cellFsAioWrite(aio=*0x%x, id=*0x%x, func=*0x%x)", aio, id, func);

	// TODO: detect mount point and send AIO request to the AIO thread of this mount point

	const s32 xid = (*id = ++g_fs_aio_id);

	const auto m = fxm::get_always<fs_aio_manager>();

	m->thread->cmd_list
	({
		{ 2, xid },
		{ aio, func },
	});

	m->thread->lock_notify();

	return CELL_OK;
}
예제 #19
0
파일: sys_prx.cpp 프로젝트: 4iDragon/rpcs3
s32 prx_load_module(std::string path, u64 flags, vm::ptr<sys_prx_load_module_option_t> pOpt)
{
	sys_prx.warning("prx_load_module(path='%s', flags=0x%llx, pOpt=*0x%x)", path.c_str(), flags, pOpt);

	const ppu_prx_object obj = fs::file(vfs::get(path));

	if (obj != elf_error::ok)
	{
		return CELL_PRX_ERROR_ILLEGAL_LIBRARY;
	}

	const auto prx = ppu_load_prx(obj);

	if (!prx)
	{
		return CELL_PRX_ERROR_ILLEGAL_LIBRARY;
	}

	return prx->id;
}
예제 #20
0
s32 cellVideoOutGetScreenSize(u32 videoOut, vm::ptr<f32> screenSize)
{
	cellAvconfExt.warning("cellVideoOutGetScreenSize(videoOut=%d, screenSize=*0x%x)", videoOut, screenSize);

	if (videoOut != CELL_VIDEO_OUT_PRIMARY)
	{
		return CELL_VIDEO_OUT_ERROR_UNSUPPORTED_VIDEO_OUT;
	}

	//TODO: Use virtual screen size
#ifdef _WIN32
	//HDC screen = GetDC(NULL);
	//float diagonal = roundf(sqrtf((powf(float(GetDeviceCaps(screen, HORZSIZE)), 2) + powf(float(GetDeviceCaps(screen, VERTSIZE)), 2))) * 0.0393f);
#else
	// TODO: Linux implementation, without using wx
	// float diagonal = roundf(sqrtf((powf(wxGetDisplaySizeMM().GetWidth(), 2) + powf(wxGetDisplaySizeMM().GetHeight(), 2))) * 0.0393f);
#endif

	return CELL_VIDEO_OUT_ERROR_VALUE_IS_NOT_SET;
}
예제 #21
0
파일: sys_spu_.cpp 프로젝트: AniLeo/rpcs3
error_code sys_spu_elf_get_segments(u32 elf_img, vm::ptr<sys_spu_segment> segments, s32 nseg)
{
	sysPrxForUser.warning("sys_spu_elf_get_segments(elf_img=0x%x, segments=*0x%x, nseg=0x%x)", elf_img, segments, nseg);

	// Initialize ELF loader
	vm::var<spu_elf_info> info({0});

	if (auto res = info->init(vm::cast(elf_img)))
	{
		return res;
	}

	// Load ELF header
	vm::var<elf_ehdr<elf_be, u64>> ehdr({0});

	if (info->ldr->get_ehdr(ehdr) || ehdr->e_machine != elf_machine::spu || !ehdr->e_phnum)
	{
		return CELL_ENOEXEC;
	}

	// Load program headers
	vm::var<elf_phdr<elf_be, u64>[]> phdr(ehdr->e_phnum);

	if (info->ldr->get_phdr(phdr, ehdr->e_phnum))
	{
		return CELL_ENOEXEC;
	}

	const s32 num_segs = sys_spu_image::fill<false>(segments, nseg, phdr, elf_img);

	if (num_segs == -2)
	{
		return CELL_ENOMEM;
	}
	else if (num_segs < 0)
	{
		return CELL_ENOEXEC;
	}

	return CELL_OK;
}
예제 #22
0
파일: sys_fs.cpp 프로젝트: Zangetsu38/rpcs3
error_code sys_fs_opendir(vm::cptr<char> path, vm::ptr<u32> fd)
{
	sys_fs.warning("sys_fs_opendir(path=%s, fd=*0x%x)", path, fd);

	const std::string& local_path = vfs::get(path.get_ptr());

	if (local_path.empty())
	{
		sys_fs.error("sys_fs_opendir(%s) failed: device not mounted", path);
		return CELL_ENOTMOUNTED;
	}

	// TODO: other checks for path

	if (fs::is_file(local_path))
	{
		sys_fs.error("sys_fs_opendir(%s) failed: path is a file", path);
		return CELL_ENOTDIR;
	}

	fs::dir dir(local_path);

	if (!dir)
	{
		sys_fs.error("sys_fs_opendir(%s): failed to open directory", path);
		return CELL_ENOENT;
	}

	const auto _dir = idm::make_ptr<lv2_dir>(path.get_ptr(), std::move(dir));

	if (!_dir)
	{
		// out of file descriptors
		return CELL_EMFILE;
	}

	*fd = _dir->id;
	sys_fs.notice("sys_fs_opendir(%s) -> lv2_fs_id %d", path, _dir->id);

	return CELL_OK;
}
예제 #23
0
error_code sys_event_port_connect_local(u32 eport_id, u32 equeue_id)
{
	sys_event.warning("sys_event_port_connect_local(eport_id=0x%x, equeue_id=0x%x)", eport_id, equeue_id);

	const auto queue = idm::get<lv2_event_queue>(equeue_id);

	if (!queue)
	{
		return CELL_ESRCH;
	}

	const auto port = idm::check<lv2_event_port>(eport_id, [&](lv2_event_port& port) -> CellError
	{
		if (port.type != SYS_EVENT_PORT_LOCAL)
		{
			return CELL_EINVAL;
		}

		lv2_lock lock{port};

		if (!port.queue.expired())
		{
			return CELL_EISCONN;
		}

		port.queue = queue;
		return {};
	});

	if (!port)
	{
		return CELL_ESRCH;
	}

	if (port.value)
	{
		return port.value;	
	}

	return CELL_OK;
}
예제 #24
0
s32 sys_raw_spu_create(vm::ptr<u32> id, vm::ptr<void> attr)
{
	sys_spu.warning("sys_raw_spu_create(id=*0x%x, attr=*0x%x)", id, attr);

	LV2_LOCK;

	// TODO: check number set by sys_spu_initialize()

	const auto thread = idm::make_ptr<RawSPUThread>("");

	if (!thread)
	{
		return CELL_EAGAIN;
	}

	thread->cpu_init();

	*id = thread->index;

	return CELL_OK;
}
예제 #25
0
파일: sys_spu_.cpp 프로젝트: AniLeo/rpcs3
s32 sys_raw_spu_load(s32 id, vm::cptr<char> path, vm::ptr<u32> entry)
{
	sysPrxForUser.warning("sys_raw_spu_load(id=%d, path=%s, entry=*0x%x)", id, path, entry);

	const fs::file elf_file = fs::file(vfs::get(path.get_ptr()));

	if (!elf_file)
	{
		sysPrxForUser.error("sys_raw_spu_load() error: %s not found!", path);
		return CELL_ENOENT;
	}

	sys_spu_image img;
	img.load(elf_file);
	img.deploy(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * id, img.segs.get_ptr(), img.nsegs);
	img.free();

	*entry = img.entry_point;

	return CELL_OK;
}
예제 #26
0
파일: sys_spu_.cpp 프로젝트: AniLeo/rpcs3
error_code sys_spu_image_close(vm::ptr<sys_spu_image> img)
{
	sysPrxForUser.warning("sys_spu_image_close(img=*0x%x)", img);

	if (img->type == SYS_SPU_IMAGE_TYPE_USER)
	{
		//_sys_free(img->segs.addr());
		vm::dealloc_verbose_nothrow(img->segs.addr(), vm::main);
	}
	else if (img->type == SYS_SPU_IMAGE_TYPE_KERNEL)
	{
		// Call the syscall
		return _sys_spu_image_close(img);
	}
	else
	{
		return CELL_EINVAL;
	}

	return CELL_OK;
}
예제 #27
0
s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr<u64> r13)
{
	sys_interrupt.warning("_sys_interrupt_thread_disestablish(ih=0x%x, r13=*0x%x)", ih, r13);

	LV2_LOCK;

	const auto handler = idm::get<lv2_int_serv_t>(ih);

	if (!handler)
	{
		return CELL_ESRCH;
	}

	// Wait for sys_interrupt_thread_eoi() and destroy interrupt thread
	handler->join(ppu, lv2_lock);

	// Save TLS base
	*r13 = handler->thread->gpr[13];

	return CELL_OK;
}
예제 #28
0
error_code cellVideoOutConfigure(u32 videoOut, vm::ptr<CellVideoOutConfiguration> config, vm::ptr<CellVideoOutOption> option, u32 waitForEvent)
{
    cellSysutil.warning("cellVideoOutConfigure(videoOut=%d, config=*0x%x, option=*0x%x, waitForEvent=%d)", videoOut, config, option, waitForEvent);

    switch (videoOut)
    {
    case CELL_VIDEO_OUT_PRIMARY:
        if (config->resolutionId != g_cfg_video_out_resolution.get()
                || config->format != CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8R8G8B8
                || (config->aspect != CELL_VIDEO_OUT_ASPECT_AUTO && config->aspect != g_cfg_video_out_aspect_ratio.get()))
        {
            return CELL_VIDEO_OUT_ERROR_ILLEGAL_CONFIGURATION;
        }
        return CELL_OK;

    case CELL_VIDEO_OUT_SECONDARY:
        return CELL_OK;
    }

    return CELL_VIDEO_OUT_ERROR_UNSUPPORTED_VIDEO_OUT;
}
예제 #29
0
파일: sys_spu_.cpp 프로젝트: 4iDragon/rpcs3
s32 sys_raw_spu_image_load(ppu_thread& ppu, s32 id, vm::ptr<sys_spu_image_t> img)
{
	sysPrxForUser.warning("sys_raw_spu_image_load(id=%d, img=*0x%x)", id, img);

	// TODO: use segment info

	const auto stamp0 = get_system_time();

	std::memcpy(vm::base(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * id), img->segs.get_ptr(), 256 * 1024);

	const auto stamp1 = get_system_time();

	vm::write32(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * id + RAW_SPU_PROB_OFFSET + SPU_NPC_offs, img->entry_point | 1);

	const auto stamp2 = get_system_time();

	sysPrxForUser.error("memcpy() latency: %lldus", (stamp1 - stamp0));
	sysPrxForUser.error("MMIO latency: %lldus", (stamp2 - stamp1));

	return CELL_OK;
}
예제 #30
0
error_code sys_timer_connect_event_queue(u32 timer_id, u32 queue_id, u64 name, u64 data1, u64 data2)
{
	sys_timer.warning("sys_timer_connect_event_queue(timer_id=0x%x, queue_id=0x%x, name=0x%llx, data1=0x%llx, data2=0x%llx)", timer_id, queue_id, name, data1, data2);

	const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer) -> CellError
	{
		const auto found = idm::find_unlocked<lv2_obj, lv2_event_queue>(queue_id);

		if (!found)
		{
			return CELL_ESRCH;
		}

		semaphore_lock lock(timer.mutex);

		if (!timer.port.expired())
		{
			return CELL_EISCONN;
		}

		// Connect event queue
		timer.port   = std::static_pointer_cast<lv2_event_queue>(found->second);
		timer.source = name ? name : ((u64)process_getpid() << 32) | timer_id;
		timer.data1  = data1;
		timer.data2  = data2;
		return {};
	});

	if (!timer)
	{
		return CELL_ESRCH;
	}

	if (timer.ret)
	{
		return timer.ret;
	}

	return CELL_OK;
}