1 // Copyright (C) 2003 Dolphin Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official SVN repository and contact information can be found at
16 // http://code.google.com/p/dolphin-emu/
23 #include "StringUtil.h"
25 #include "pluginspecs_wiimote.h"
28 #include "WiimoteReal.h"
30 #include "../WiimoteEmu/WiimoteHid.h"
32 unsigned int g_wiimote_sources
[MAX_WIIMOTES
];
37 bool g_real_wiimotes_initialized
= false;
38 wiimote_t
** g_wiimotes_from_wiiuse
= NULL
;
39 unsigned int g_wiimotes_found
= 0;
41 volatile bool g_run_wiimote_thread
= false;
42 Common::Thread
*g_wiimote_thread
= NULL
;
43 Common::CriticalSection g_wiimote_critsec
;
45 THREAD_RETURN
WiimoteThreadFunc(void* arg
);
47 // silly, copying data n stuff, o well, don't use this too often
48 void SendPacket(wiimote_t
* const wm
, const u8 rpt_id
, const void* const data
, const unsigned int size
)
50 u8
* const rpt
= new u8
[size
+ 2];
53 memcpy(rpt
+ 2, data
, size
);
54 wiiuse_io_write(wm
, (byte
*)rpt
, size
+ 2);
61 Wiimote(wiimote_t
* const wm
, const unsigned int index
);
64 void ControlChannel(const u16 channel
, const void* const data
, const u32 size
);
65 void InterruptChannel(const u16 channel
, const void* const data
, const u32 size
);
70 void DisableDataReporting();
75 wiimote_t
* const m_wiimote
;
76 const unsigned int m_index
;
79 u8 m_last_data_report
[MAX_PAYLOAD
];
80 bool m_last_data_report_valid
;
82 std::queue
<u8
*> m_reports
;
85 Wiimote::Wiimote(wiimote_t
* const wm
, const unsigned int index
)
89 , m_last_data_report_valid(false)
92 DisableDataReporting();
94 // clear all msgs, silly maybe
95 // - cleared on first InterruptChannel call
96 //while (wiiuse_io_read(m_wiimote));
100 //wm_leds rpt = wm_leds();
102 //SendPacket(g_wiimotes_from_wiiuse[i], WM_LEDS, &rpt, sizeof(rpt));
106 wiiuse_rumble(m_wiimote
, 1);
108 wiiuse_rumble(m_wiimote
, 0);
111 wiiuse_set_leds(m_wiimote
, WIIMOTE_LED_1
<< m_index
);
113 // TODO: make Dolphin connect wiimote, maybe
120 // disable reporting / wiiuse might do this on shutdown anyway, o well, don't know for sure
121 DisableDataReporting();
123 // send disconnect message to wii, maybe, i hope, naw shit messes up on emu-stop
124 //if (g_WiimoteInitialize.pWiimoteInterruptChannel)
126 // //u8* const rpt = new u8[2];
127 // //rpt[0] = 0XA1; rpt[1] = 0x15;
128 // //m_reports.push(rpt);
131 // const u8 rpt[] = { 0xA1, 0x15 };
132 // g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, m_channel, rpt, sizeof(rpt));
136 void Wiimote::DisableDataReporting()
138 wm_report_mode rpt
= wm_report_mode();
139 rpt
.mode
= WM_REPORT_CORE
;
140 SendPacket(m_wiimote
, WM_REPORT_MODE
, &rpt
, sizeof(rpt
));
143 void Wiimote::ClearReports()
145 m_last_data_report_valid
= false;
146 while (m_reports
.size())
148 delete[] m_reports
.front();
153 void Wiimote::ControlChannel(const u16 channel
, const void* const data
, const u32 size
)
155 // Check for custom communication
159 InterruptChannel(channel
, data
, size
);
162 void Wiimote::InterruptChannel(const u16 channel
, const void* const data
, const u32 size
)
164 if (0 == m_channel
) // first interrupt/control channel sent
166 // clear all msgs, silly maybe
167 while (wiiuse_io_read(m_wiimote
));
170 wm_request_status rpt
= wm_request_status();
171 SendPacket(m_wiimote
, WM_REQUEST_STATUS
, &rpt
, sizeof(rpt
));
175 wiiuse_io_write(m_wiimote
, (byte
*)data
, size
);
180 // if not connected to Dolphin, don't do anything, to keep sanchez happy :p
184 if (wiiuse_io_read(m_wiimote
))
186 // a data report, save it
187 if (m_wiimote
->event_buf
[1] >= 0x30)
189 memcpy(m_last_data_report
, m_wiimote
->event_buf
, MAX_PAYLOAD
);
190 m_last_data_report_valid
= true;
194 // some other report, add it to queue
195 u8
* const rpt
= new u8
[MAX_PAYLOAD
];
196 memcpy(rpt
, m_wiimote
->event_buf
, MAX_PAYLOAD
);
202 void Wiimote::Update()
204 // do we have some queued reports
205 if (m_reports
.size())
207 u8
* const rpt
= m_reports
.front();
209 g_WiimoteInitialize
.pWiimoteInterruptChannel(m_index
, m_channel
, rpt
, MAX_PAYLOAD
);
212 else if (m_last_data_report_valid
)
214 // otherwise send the last data report, if there is one
215 g_WiimoteInitialize
.pWiimoteInterruptChannel(m_index
, m_channel
, m_last_data_report
, MAX_PAYLOAD
);
219 void Wiimote::Disconnect()
224 DisableDataReporting();
229 // clear out wiiuse queue, or maybe not, silly? idk
230 while (wiiuse_io_read(m_wiimote
));
233 Wiimote
* g_wiimotes
[4];
237 std::string ini_filename
= (std::string(File::GetUserPath(D_CONFIG_IDX
)) + g_plugin
.ini_name
+ ".ini" );
240 inifile
.Load(ini_filename
);
242 for (unsigned int i
=0; i
<MAX_WIIMOTES
; ++i
)
244 std::string
secname("Wiimote");
245 secname
+= (char)('1' + i
);
246 IniFile::Section
& sec
= *inifile
.GetOrCreateSection(secname
.c_str());
248 sec
.Get("Source", &g_wiimote_sources
[i
], WIIMOTE_SRC_EMU
);
252 unsigned int Initialize()
254 // return if already initialized
255 if (g_real_wiimotes_initialized
)
256 return g_wiimotes_found
;
258 memset(g_wiimotes
, 0, sizeof(g_wiimotes
));
260 // only call wiiuse_find with the number of slots configured for real wiimotes
261 unsigned int wanted_wiimotes
= 0;
262 for (unsigned int i
= 0; i
< MAX_WIIMOTES
; ++i
)
263 if (WIIMOTE_SRC_REAL
== g_wiimote_sources
[i
])
266 // don't bother initializing wiiuse if we don't want any real wiimotes
267 if (0 == wanted_wiimotes
)
269 g_wiimotes_found
= 0;
274 g_real_wiimotes_initialized
= true;
277 g_wiimotes_from_wiiuse
= wiiuse_init(MAX_WIIMOTES
);
278 g_wiimotes_found
= wiiuse_find(g_wiimotes_from_wiiuse
, wanted_wiimotes
, 5);
280 DEBUG_LOG(WIIMOTE
, "Found %i Real Wiimotes, %i wanted", g_wiimotes_found
, wanted_wiimotes
);
283 wiiuse_connect(g_wiimotes_from_wiiuse
, g_wiimotes_found
);
285 DEBUG_LOG(WIIMOTE
, "Connected to %i Real Wiimotes", g_wiimotes_found
);
287 g_wiimote_critsec
.Enter(); // enter
289 // create real wiimote class instances, assign wiimotes
291 for (unsigned int i
= 0, w
= 0; i
<MAX_WIIMOTES
&& w
<g_wiimotes_found
; ++i
)
293 if (WIIMOTE_SRC_REAL
!= g_wiimote_sources
[i
])
296 // create/assign wiimote
297 g_wiimotes
[i
] = new Wiimote(g_wiimotes_from_wiiuse
[w
++], i
);
300 g_wiimote_critsec
.Leave(); // leave
302 // start wiimote thread
303 g_run_wiimote_thread
= true;
304 g_wiimote_thread
= new Common::Thread(WiimoteThreadFunc
, 0);
306 return g_wiimotes_found
;
311 if (false == g_real_wiimotes_initialized
)
315 g_real_wiimotes_initialized
= false;
317 // stop wiimote thread
318 if (g_wiimote_thread
)
320 g_run_wiimote_thread
= false;
321 g_wiimote_thread
->WaitForDeath();
322 delete g_wiimote_thread
;
323 g_wiimote_thread
= NULL
;
326 g_wiimote_critsec
.Enter(); // enter
329 for (unsigned int i
=0; i
<MAX_WIIMOTES
; ++i
)
332 delete g_wiimotes
[i
];
333 g_wiimotes
[i
] = NULL
;
336 g_wiimote_critsec
.Leave(); // leave
338 // set all LEDs on, idk
339 //for (unsigned int i=0; i<g_wiimotes_found; ++i)
341 // wiiuse_set_leds(g_wiimotes_from_wiiuse[i], 0xF0);
345 wiiuse_cleanup(g_wiimotes_from_wiiuse
, MAX_WIIMOTES
);
351 // find the number of slots configured for real wiimotes
352 unsigned int wanted_wiimotes
= 0;
353 for (unsigned int i
= 0; i
< MAX_WIIMOTES
; ++i
)
354 if (g_wiimote_sources
[i
] == WIIMOTE_SRC_REAL
)
357 // don't scan for wiimotes if we don't want any more
358 if (wanted_wiimotes
<= g_wiimotes_found
)
362 unsigned int num_wiimotes
= wiiuse_find_more(g_wiimotes_from_wiiuse
, wanted_wiimotes
, 5);
364 DEBUG_LOG(WIIMOTE
, "Found %i Real Wiimotes, %i wanted", num_wiimotes
, wanted_wiimotes
);
366 int num_new_wiimotes
= wiiuse_connect(g_wiimotes_from_wiiuse
, num_wiimotes
);
368 DEBUG_LOG(WIIMOTE
, "Connected to %i additional Real Wiimotes", num_new_wiimotes
);
370 g_wiimote_critsec
.Enter(); // enter
372 // create real wiimote class instances, and assign wiimotes for the new wiimotes
373 for (unsigned int i
= g_wiimotes_found
, w
= g_wiimotes_found
;
374 i
< MAX_WIIMOTES
&& w
< num_wiimotes
; ++i
)
376 if (g_wiimote_sources
[i
] != WIIMOTE_SRC_REAL
|| g_wiimotes
[i
] != NULL
)
379 // create/assign wiimote
380 g_wiimotes
[i
] = new Wiimote(g_wiimotes_from_wiiuse
[w
++], i
);
382 g_wiimotes_found
= num_wiimotes
;
384 g_wiimote_critsec
.Leave(); // leave
391 // should be fine i think
397 void InterruptChannel(int _WiimoteNumber
, u16 _channelID
, const void* _pData
, u32 _Size
)
399 g_wiimote_critsec
.Enter(); // enter
401 u8
* data
= (u8
*)_pData
;
403 // some hax, since we just send the last data report to Dolphin on each Update() call
404 // , make the wiimote only send updated data reports when data changes
405 // == less bt traffic, eliminates some unneeded packets
406 if (WM_REPORT_MODE
== ((u8
*)_pData
)[1])
408 // I dont wanna write on the const *_pData
409 data
= new u8
[_Size
];
410 memcpy(data
, _pData
, _Size
);
412 // nice var names :p, this seems to be this one
413 ((wm_report_mode
*)(data
+ 2))->all_the_time
= false;
414 //((wm_report_mode*)(data + 2))->continuous = false;
417 if (g_wiimotes
[_WiimoteNumber
])
418 g_wiimotes
[_WiimoteNumber
]->InterruptChannel(_channelID
, data
, _Size
);
420 g_wiimote_critsec
.Leave(); // leave
426 void ControlChannel(int _WiimoteNumber
, u16 _channelID
, const void* _pData
, u32 _Size
)
428 g_wiimote_critsec
.Enter(); // enter
430 if (g_wiimotes
[_WiimoteNumber
])
431 g_wiimotes
[_WiimoteNumber
]->ControlChannel(_channelID
, _pData
, _Size
);
433 g_wiimote_critsec
.Leave(); // leave
437 // Read the Wiimote once
438 void Update(int _WiimoteNumber
)
440 g_wiimote_critsec
.Enter(); // enter
442 if (g_wiimotes
[_WiimoteNumber
])
443 g_wiimotes
[_WiimoteNumber
]->Update();
445 g_wiimote_critsec
.Leave(); // leave
448 void StateChange(PLUGIN_EMUSTATE newState
)
450 g_wiimote_critsec
.Enter(); // enter
452 // TODO: disable/enable auto reporting, maybe
454 g_wiimote_critsec
.Leave(); // leave
457 THREAD_RETURN
WiimoteThreadFunc(void* arg
)
459 Common::SetCurrentThreadName("Wiimote Read");
461 while (g_run_wiimote_thread
)
463 g_wiimote_critsec
.Enter(); // enter
465 for (unsigned int i
=0; i
<MAX_WIIMOTES
; ++i
)
467 g_wiimotes
[i
]->Read();
469 g_wiimote_critsec
.Leave(); // leave
471 // hmmm, i get occasional lockups without this :/
472 Common::SleepCurrentThread(1);
478 }; // end of namespace