PageRenderTime 2ms CodeModel.GetById 2ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 0ms

/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp

https://github.com/bunnei/dolphin
C++ | 626 lines | 496 code | 82 blank | 48 comment | 72 complexity | 42b19d22f279dec6b8fd604fb632aa5c MD5 | raw file
  1// Copyright 2013 Dolphin Emulator Project
  2// Licensed under GPLv2
  3// Refer to the license.txt file included.
  4
  5/*
  6This is the main Wii IPC file that handles all incoming IPC calls and directs them
  7to the right function.
  8
  9IPC basics (IOS' usage):
 10
 11Return values for file handles: All IPC calls will generate a return value to 0x04,
 12in case of success they are
 13	Open: DeviceID
 14	Close: 0
 15	Read: Bytes read
 16	Write: Bytes written
 17	Seek: Seek position
 18	Ioctl: 0 (in addition to that there may be messages to the out buffers)
 19	Ioctlv: 0 (in addition to that there may be messages to the out buffers)
 20They will also generate a true or false return for UpdateInterrupts() in WII_IPC.cpp.
 21*/
 22
 23#include <list>
 24#include <map>
 25#include <string>
 26
 27#include "Common/Common.h"
 28#include "Common/CommonPaths.h"
 29#include "Common/FileUtil.h"
 30#include "Common/Thread.h"
 31
 32#include "Core/ConfigManager.h"
 33#include "Core/CoreTiming.h"
 34#include "Core/Debugger/Debugger_SymbolMap.h"
 35#include "Core/HW/CPU.h"
 36#include "Core/HW/Memmap.h"
 37#include "Core/HW/SystemTimers.h"
 38#include "Core/HW/WII_IPC.h"
 39
 40#include "Core/IPC_HLE/WII_IPC_HLE.h"
 41#include "Core/IPC_HLE/WII_IPC_HLE_Device.h"
 42#include "Core/IPC_HLE/WII_IPC_HLE_Device_DI.h"
 43#include "Core/IPC_HLE/WII_IPC_HLE_Device_es.h"
 44#include "Core/IPC_HLE/WII_IPC_HLE_Device_FileIO.h"
 45#include "Core/IPC_HLE/WII_IPC_HLE_Device_fs.h"
 46#include "Core/IPC_HLE/WII_IPC_HLE_Device_net.h"
 47#include "Core/IPC_HLE/WII_IPC_HLE_Device_net_ssl.h"
 48#include "Core/IPC_HLE/WII_IPC_HLE_Device_sdio_slot0.h"
 49#include "Core/IPC_HLE/WII_IPC_HLE_Device_stm.h"
 50#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb.h"
 51#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_kbd.h"
 52
 53#if defined(__LIBUSB__) || defined (_WIN32)
 54	#include "Core/IPC_HLE/WII_IPC_HLE_Device_hid.h"
 55#endif
 56
 57#include "Core/PowerPC/PowerPC.h"
 58
 59
 60namespace WII_IPC_HLE_Interface
 61{
 62
 63typedef std::map<u32, IWII_IPC_HLE_Device*> TDeviceMap;
 64TDeviceMap g_DeviceMap;
 65
 66// STATE_TO_SAVE
 67typedef std::map<u32, std::string> TFileNameMap;
 68
 69#define IPC_MAX_FDS 0x18
 70#define ES_MAX_COUNT 2
 71IWII_IPC_HLE_Device* g_FdMap[IPC_MAX_FDS];
 72bool es_inuse[ES_MAX_COUNT];
 73IWII_IPC_HLE_Device* es_handles[ES_MAX_COUNT];
 74
 75
 76typedef std::deque<u32> ipc_msg_queue;
 77static ipc_msg_queue request_queue; // ppc -> arm
 78static ipc_msg_queue reply_queue;   // arm -> ppc
 79static std::mutex s_reply_queue;
 80
 81static int enque_reply;
 82
 83static u64 last_reply_time;
 84
 85// NOTE: Only call this if you have correctly handled
 86//       CommandAddress+0 and CommandAddress+8.
 87//       Please search for examples of this being called elsewhere.
 88void EnqueReplyCallback(u64 userdata, int)
 89{
 90	std::lock_guard<std::mutex> lk(s_reply_queue);
 91	reply_queue.push_back((u32)userdata);
 92}
 93
 94void Init()
 95{
 96	_dbg_assert_msg_(WII_IPC_HLE, g_DeviceMap.empty(), "DeviceMap isn't empty on init");
 97	CWII_IPC_HLE_Device_es::m_ContentFile = "";
 98
 99	for (IWII_IPC_HLE_Device*& dev : g_FdMap)
100	{
101		dev = nullptr;
102	}
103
104	u32 i = 0;
105	// Build hardware devices
106	g_DeviceMap[i] = new CWII_IPC_HLE_Device_usb_oh1_57e_305(i, "/dev/usb/oh1/57e/305"); i++;
107	g_DeviceMap[i] = new CWII_IPC_HLE_Device_stm_immediate(i, "/dev/stm/immediate"); i++;
108	g_DeviceMap[i] = new CWII_IPC_HLE_Device_stm_eventhook(i, "/dev/stm/eventhook"); i++;
109	g_DeviceMap[i] = new CWII_IPC_HLE_Device_fs(i, "/dev/fs"); i++;
110
111	// IOS allows two ES devices at a time
112	for (u32 j=0; j<ES_MAX_COUNT; j++)
113	{
114		g_DeviceMap[i] = es_handles[j] = new CWII_IPC_HLE_Device_es(i, "/dev/es"); i++;
115		es_inuse[j] = false;
116	}
117
118	g_DeviceMap[i] = new CWII_IPC_HLE_Device_di(i, std::string("/dev/di")); i++;
119	g_DeviceMap[i] = new CWII_IPC_HLE_Device_net_kd_request(i, "/dev/net/kd/request"); i++;
120	g_DeviceMap[i] = new CWII_IPC_HLE_Device_net_kd_time(i, "/dev/net/kd/time"); i++;
121	g_DeviceMap[i] = new CWII_IPC_HLE_Device_net_ncd_manage(i, "/dev/net/ncd/manage"); i++;
122	g_DeviceMap[i] = new CWII_IPC_HLE_Device_net_wd_command(i, "/dev/net/wd/command"); i++;
123	g_DeviceMap[i] = new CWII_IPC_HLE_Device_net_ip_top(i, "/dev/net/ip/top"); i++;
124	g_DeviceMap[i] = new CWII_IPC_HLE_Device_net_ssl(i, "/dev/net/ssl"); i++;
125	g_DeviceMap[i] = new CWII_IPC_HLE_Device_usb_kbd(i, "/dev/usb/kbd"); i++;
126	g_DeviceMap[i] = new CWII_IPC_HLE_Device_sdio_slot0(i, "/dev/sdio/slot0"); i++;
127	g_DeviceMap[i] = new CWII_IPC_HLE_Device_stub(i, "/dev/sdio/slot1"); i++;
128	#if defined(__LIBUSB__) || defined(_WIN32)
129		g_DeviceMap[i] = new CWII_IPC_HLE_Device_hid(i, "/dev/usb/hid"); i++;
130	#else
131		g_DeviceMap[i] = new CWII_IPC_HLE_Device_stub(i, "/dev/usb/hid"); i++;
132	#endif
133	g_DeviceMap[i] = new CWII_IPC_HLE_Device_stub(i, "/dev/usb/oh1"); i++;
134	g_DeviceMap[i] = new IWII_IPC_HLE_Device(i, "_Unimplemented_Device_"); i++;
135
136	enque_reply = CoreTiming::RegisterEvent("IPCReply", EnqueReplyCallback);
137}
138
139void Reset(bool _bHard)
140{
141	CoreTiming::RemoveAllEvents(enque_reply);
142
143	for (IWII_IPC_HLE_Device*& dev : g_FdMap)
144	{
145		if (dev != nullptr && !dev->IsHardware())
146		{
147			// close all files and delete their resources
148			dev->Close(0, true);
149			delete dev;
150		}
151
152		dev = nullptr;
153	}
154
155	for (bool& in_use : es_inuse)
156	{
157		in_use = false;
158	}
159
160	for (const auto& entry : g_DeviceMap)
161	{
162		if (entry.second)
163		{
164			// Force close
165			entry.second->Close(0, true);
166
167			// Hardware should not be deleted unless it is a hard reset
168			if (_bHard)
169				delete entry.second;
170		}
171	}
172
173	if (_bHard)
174	{
175		g_DeviceMap.erase(g_DeviceMap.begin(), g_DeviceMap.end());
176	}
177	request_queue.clear();
178
179	// lock due to using reply_queue
180	{
181		std::lock_guard<std::mutex> lk(s_reply_queue);
182		reply_queue.clear();
183	}
184	last_reply_time = 0;
185}
186
187void Shutdown()
188{
189	Reset(true);
190}
191
192void SetDefaultContentFile(const std::string& _rFilename)
193{
194	for (const auto& entry : g_DeviceMap)
195	{
196		if (entry.second && entry.second->GetDeviceName().find("/dev/es") == 0)
197		{
198			((CWII_IPC_HLE_Device_es*)entry.second)->LoadWAD(_rFilename);
199		}
200	}
201}
202
203void ES_DIVerify(u8 *_pTMD, u32 _sz)
204{
205	CWII_IPC_HLE_Device_es::ES_DIVerify(_pTMD, _sz);
206}
207
208void SDIO_EventNotify()
209{
210	CWII_IPC_HLE_Device_sdio_slot0 *pDevice =
211		(CWII_IPC_HLE_Device_sdio_slot0*)GetDeviceByName("/dev/sdio/slot0");
212	if (pDevice)
213		pDevice->EventNotify();
214}
215
216int getFreeDeviceId()
217{
218	for (u32 i=0; i<IPC_MAX_FDS; i++)
219	{
220		if (g_FdMap[i] == nullptr)
221		{
222			return i;
223		}
224	}
225
226	return -1;
227}
228
229IWII_IPC_HLE_Device* GetDeviceByName(const std::string& _rDeviceName)
230{
231	for (const auto& entry : g_DeviceMap)
232	{
233		if (entry.second && entry.second->GetDeviceName() == _rDeviceName)
234		{
235			return entry.second;
236		}
237	}
238
239	return nullptr;
240}
241
242IWII_IPC_HLE_Device* AccessDeviceByID(u32 _ID)
243{
244	if (g_DeviceMap.find(_ID) != g_DeviceMap.end())
245	{
246		return g_DeviceMap[_ID];
247	}
248
249	return nullptr;
250}
251
252// This is called from ExecuteCommand() COMMAND_OPEN_DEVICE
253IWII_IPC_HLE_Device* CreateFileIO(u32 _DeviceID, const std::string& _rDeviceName)
254{
255	// scan device name and create the right one
256	IWII_IPC_HLE_Device* pDevice = nullptr;
257
258	INFO_LOG(WII_IPC_FILEIO, "IOP: Create FileIO %s", _rDeviceName.c_str());
259	pDevice = new CWII_IPC_HLE_Device_FileIO(_DeviceID, _rDeviceName);
260
261	return pDevice;
262}
263
264
265void DoState(PointerWrap &p)
266{
267	std::lock_guard<std::mutex> lk(s_reply_queue);
268
269	p.Do(request_queue);
270	p.Do(reply_queue);
271	p.Do(last_reply_time);
272
273	for (const auto& entry : g_DeviceMap)
274	{
275		if (entry.second->IsHardware())
276		{
277			entry.second->DoState(p);
278		}
279	}
280
281	if (p.GetMode() == PointerWrap::MODE_READ)
282	{
283		for (u32 i=0; i<IPC_MAX_FDS; i++)
284		{
285			u32 exists = 0;
286			p.Do(exists);
287			if (exists)
288			{
289				u32 isHw = 0;
290				p.Do(isHw);
291				if (isHw)
292				{
293					u32 hwId = 0;
294					p.Do(hwId);
295					g_FdMap[i] = AccessDeviceByID(hwId);
296				}
297				else
298				{
299					g_FdMap[i] = new CWII_IPC_HLE_Device_FileIO(i, "");
300					g_FdMap[i]->DoState(p);
301				}
302			}
303			else
304			{
305				g_FdMap[i] = nullptr;
306			}
307		}
308
309		for (u32 i=0; i<ES_MAX_COUNT; i++)
310		{
311			p.Do(es_inuse[i]);
312			u32 handleID = es_handles[i]->GetDeviceID();
313			p.Do(handleID);
314
315			es_handles[i] = AccessDeviceByID(handleID);
316		}
317	}
318	else
319	{
320		for (IWII_IPC_HLE_Device*& dev : g_FdMap)
321		{
322			u32 exists = dev ? 1 : 0;
323			p.Do(exists);
324			if (exists)
325			{
326				u32 isHw = dev->IsHardware() ? 1 : 0;
327				p.Do(isHw);
328				if (isHw)
329				{
330					u32 hwId = dev->GetDeviceID();
331					p.Do(hwId);
332				}
333				else
334				{
335					dev->DoState(p);
336				}
337			}
338		}
339
340		for (u32 i=0; i<ES_MAX_COUNT; i++)
341		{
342			p.Do(es_inuse[i]);
343			u32 handleID = es_handles[i]->GetDeviceID();
344			p.Do(handleID);
345		}
346	}
347}
348
349void ExecuteCommand(u32 _Address)
350{
351	bool CmdSuccess = false;
352
353	IPCCommandType Command = static_cast<IPCCommandType>(Memory::Read_U32(_Address));
354	volatile s32 DeviceID = Memory::Read_U32(_Address + 8);
355
356	IWII_IPC_HLE_Device* pDevice = (DeviceID >= 0 && DeviceID < IPC_MAX_FDS) ? g_FdMap[DeviceID] : nullptr;
357
358	INFO_LOG(WII_IPC_HLE, "-->> Execute Command Address: 0x%08x (code: %x, device: %x) %p", _Address, Command, DeviceID, pDevice);
359
360	switch (Command)
361	{
362	case IPC_CMD_OPEN:
363	{
364		u32 Mode = Memory::Read_U32(_Address + 0x10);
365		DeviceID = getFreeDeviceId();
366
367		std::string DeviceName;
368		Memory::GetString(DeviceName, Memory::Read_U32(_Address + 0xC));
369
370		WARN_LOG(WII_IPC_HLE, "Trying to open %s as %d", DeviceName.c_str(), DeviceID);
371		if (DeviceID >= 0)
372		{
373			if (DeviceName.find("/dev/es") == 0)
374			{
375				u32 j;
376				for (j=0; j<ES_MAX_COUNT; j++)
377				{
378					if (!es_inuse[j])
379					{
380						es_inuse[j] = true;
381						g_FdMap[DeviceID] = es_handles[j];
382						CmdSuccess = es_handles[j]->Open(_Address, Mode);
383						Memory::Write_U32(DeviceID, _Address+4);
384						break;
385					}
386				}
387
388				if (j == ES_MAX_COUNT)
389				{
390					Memory::Write_U32(FS_EESEXHAUSTED, _Address + 4);
391					CmdSuccess = true;
392				}
393			}
394			else if (DeviceName.find("/dev/") == 0)
395			{
396				pDevice = GetDeviceByName(DeviceName);
397				if (pDevice)
398				{
399					g_FdMap[DeviceID] = pDevice;
400					CmdSuccess = pDevice->Open(_Address, Mode);
401					INFO_LOG(WII_IPC_FILEIO, "IOP: ReOpen (Device=%s, DeviceID=%08x, Mode=%i)",
402						pDevice->GetDeviceName().c_str(), DeviceID, Mode);
403					Memory::Write_U32(DeviceID, _Address+4);
404				}
405				else
406				{
407					WARN_LOG(WII_IPC_HLE, "Unimplemented device: %s", DeviceName.c_str());
408					Memory::Write_U32(FS_ENOENT, _Address+4);
409					CmdSuccess = true;
410				}
411			}
412			else
413			{
414				pDevice = CreateFileIO(DeviceID, DeviceName);
415				CmdSuccess = pDevice->Open(_Address, Mode);
416
417				INFO_LOG(WII_IPC_FILEIO, "IOP: Open File (Device=%s, ID=%08x, Mode=%i)",
418						pDevice->GetDeviceName().c_str(), DeviceID, Mode);
419				if (Memory::Read_U32(_Address + 4) == (u32)DeviceID)
420				{
421					g_FdMap[DeviceID] = pDevice;
422				}
423				else
424				{
425					delete pDevice;
426					pDevice = nullptr;
427				}
428			}
429		}
430		else
431		{
432			Memory::Write_U32(FS_EFDEXHAUSTED, _Address + 4);
433			CmdSuccess = true;
434		}
435		break;
436	}
437	case IPC_CMD_CLOSE:
438	{
439		if (pDevice)
440		{
441			CmdSuccess = pDevice->Close(_Address);
442
443			for (u32 j=0; j<ES_MAX_COUNT; j++)
444			{
445				if (es_handles[j] == g_FdMap[DeviceID])
446				{
447					es_inuse[j] = false;
448				}
449			}
450
451			g_FdMap[DeviceID] = nullptr;
452
453			// Don't delete hardware
454			if (!pDevice->IsHardware())
455			{
456				delete pDevice;
457				pDevice = nullptr;
458			}
459		}
460		else
461		{
462			Memory::Write_U32(FS_EINVAL, _Address + 4);
463			CmdSuccess = true;
464		}
465		break;
466	}
467	case IPC_CMD_READ:
468	{
469		if (pDevice)
470		{
471			CmdSuccess = pDevice->Read(_Address);
472		}
473		else
474		{
475			Memory::Write_U32(FS_EINVAL, _Address + 4);
476			CmdSuccess = true;
477		}
478		break;
479	}
480	case IPC_CMD_WRITE:
481	{
482		if (pDevice)
483		{
484			CmdSuccess = pDevice->Write(_Address);
485		}
486		else
487		{
488			Memory::Write_U32(FS_EINVAL, _Address + 4);
489			CmdSuccess = true;
490		}
491		break;
492	}
493	case IPC_CMD_SEEK:
494	{
495		if (pDevice)
496		{
497			CmdSuccess = pDevice->Seek(_Address);
498		}
499		else
500		{
501			Memory::Write_U32(FS_EINVAL, _Address + 4);
502			CmdSuccess = true;
503		}
504		break;
505	}
506	case IPC_CMD_IOCTL:
507	{
508		if (pDevice)
509		{
510			CmdSuccess = pDevice->IOCtl(_Address);
511		}
512		break;
513	}
514	case IPC_CMD_IOCTLV:
515	{
516		if (pDevice)
517		{
518			CmdSuccess = pDevice->IOCtlV(_Address);
519		}
520		break;
521	}
522	default:
523	{
524		_dbg_assert_msg_(WII_IPC_HLE, 0, "Unknown IPC Command %i (0x%08x)", Command, _Address);
525		break;
526	}
527	}
528
529
530	if (CmdSuccess)
531	{
532		// The original hardware overwrites the command type with the async reply type.
533		Memory::Write_U32(IPC_REP_ASYNC, _Address);
534		// IOS also seems to write back the command that was responded to in the FD field.
535		Memory::Write_U32(Command, _Address + 8);
536
537		// Ensure replies happen in order, fairly ugly
538		// Without this, tons of games fail now that DI commands have different reply delays
539		int reply_delay = pDevice ? pDevice->GetCmdDelay(_Address) : 0;
540
541		const s64 ticks_til_last_reply = last_reply_time - CoreTiming::GetTicks();
542
543		if (ticks_til_last_reply > 0)
544		{
545			reply_delay = (int)ticks_til_last_reply;
546		}
547
548		last_reply_time = CoreTiming::GetTicks() + reply_delay;
549
550		// Generate a reply to the IPC command
551		EnqReply(_Address, reply_delay);
552	}
553}
554
555// Happens AS SOON AS IPC gets a new pointer!
556void EnqRequest(u32 _Address)
557{
558	request_queue.push_back(_Address);
559}
560
561// Called when IOS module has some reply
562// NOTE: Only call this if you have correctly handled
563//       CommandAddress+0 and CommandAddress+8.
564//       Please search for examples of this being called elsewhere.
565void EnqReply(u32 _Address, int cycles_in_future)
566{
567	CoreTiming::ScheduleEvent(cycles_in_future, enque_reply, _Address);
568}
569
570// This is called every IPC_HLE_PERIOD from SystemTimers.cpp
571// Takes care of routing ipc <-> ipc HLE
572void Update()
573{
574	if (!WII_IPCInterface::IsReady())
575		return;
576
577	UpdateDevices();
578
579	if (request_queue.size())
580	{
581		WII_IPCInterface::GenerateAck(request_queue.front());
582		INFO_LOG(WII_IPC_HLE, "||-- Acknowledge IPC Request @ 0x%08x", request_queue.front());
583		u32 command = request_queue.front();
584		request_queue.pop_front();
585		ExecuteCommand(command);
586
587#if MAX_LOGLEVEL >= DEBUG_LEVEL
588		Dolphin_Debugger::PrintCallstack(LogTypes::WII_IPC_HLE, LogTypes::LDEBUG);
589#endif
590	}
591
592	// lock due to using reply_queue
593	{
594		std::lock_guard<std::mutex> lk(s_reply_queue);
595		if (reply_queue.size())
596		{
597			WII_IPCInterface::GenerateReply(reply_queue.front());
598			INFO_LOG(WII_IPC_HLE, "<<-- Reply to IPC Request @ 0x%08x", reply_queue.front());
599			reply_queue.pop_front();
600		}
601	}
602}
603
604void UpdateDevices()
605{
606	// Check if a hardware device must be updated
607	for (const auto& entry : g_DeviceMap)
608	{
609		if (entry.second->IsOpened() && entry.second->Update())
610		{
611			break;
612		}
613	}
614}
615
616
617} // end of namespace WII_IPC_HLE_Interface
618
619// TODO: create WII_IPC_HLE_Device.cpp ?
620void IWII_IPC_HLE_Device::DoStateShared(PointerWrap& p)
621{
622	p.Do(m_Name);
623	p.Do(m_DeviceID);
624	p.Do(m_Hardware);
625	p.Do(m_Active);
626}