|  |  |  | @@ -35,6 +35,15 @@ static u32 next_gyroscope_index; | 
		
	
		
			
				|  |  |  |  | static int enable_accelerometer_count = 0; // positive means enabled | 
		
	
		
			
				|  |  |  |  | static int enable_gyroscope_count = 0;     // positive means enabled | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | static int pad_update_event; | 
		
	
		
			
				|  |  |  |  | static int accelerometer_update_event; | 
		
	
		
			
				|  |  |  |  | static int gyroscope_update_event; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // Updating period for each HID device. These empirical values are measured from a 11.2 3DS. | 
		
	
		
			
				|  |  |  |  | constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234; | 
		
	
		
			
				|  |  |  |  | constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; | 
		
	
		
			
				|  |  |  |  | constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { | 
		
	
		
			
				|  |  |  |  |     // 30 degree and 60 degree are angular thresholds for directions | 
		
	
		
			
				|  |  |  |  |     constexpr float TAN30 = 0.577350269f; | 
		
	
	
		
			
				
					
					|  |  |  | @@ -65,14 +74,9 @@ static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { | 
		
	
		
			
				|  |  |  |  |     return state; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | void Update() { | 
		
	
		
			
				|  |  |  |  | static void UpdatePadCallback(u64 userdata, int cycles_late) { | 
		
	
		
			
				|  |  |  |  |     SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     if (mem == nullptr) { | 
		
	
		
			
				|  |  |  |  |         LOG_DEBUG(Service_HID, "Cannot update HID prior to mapping shared memory!"); | 
		
	
		
			
				|  |  |  |  |         return; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     PadState state = VideoCore::g_emu_window->GetPadState(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Get current circle pad position and update circle pad direction | 
		
	
	
		
			
				
					
					|  |  |  | @@ -131,59 +135,68 @@ void Update() { | 
		
	
		
			
				|  |  |  |  |     event_pad_or_touch_1->Signal(); | 
		
	
		
			
				|  |  |  |  |     event_pad_or_touch_2->Signal(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Update accelerometer | 
		
	
		
			
				|  |  |  |  |     if (enable_accelerometer_count > 0) { | 
		
	
		
			
				|  |  |  |  |         mem->accelerometer.index = next_accelerometer_index; | 
		
	
		
			
				|  |  |  |  |         next_accelerometer_index = | 
		
	
		
			
				|  |  |  |  |             (next_accelerometer_index + 1) % mem->accelerometer.entries.size(); | 
		
	
		
			
				|  |  |  |  |     // Reschedule recurrent event | 
		
	
		
			
				|  |  |  |  |     CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         AccelerometerDataEntry& accelerometer_entry = | 
		
	
		
			
				|  |  |  |  |             mem->accelerometer.entries[mem->accelerometer.index]; | 
		
	
		
			
				|  |  |  |  |         std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z) = | 
		
	
		
			
				|  |  |  |  |             VideoCore::g_emu_window->GetAccelerometerState(); | 
		
	
		
			
				|  |  |  |  | static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) { | 
		
	
		
			
				|  |  |  |  |     SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // Make up "raw" entry | 
		
	
		
			
				|  |  |  |  |         // TODO(wwylele): | 
		
	
		
			
				|  |  |  |  |         // From hardware testing, the raw_entry values are approximately, | 
		
	
		
			
				|  |  |  |  |         // but not exactly, as twice as corresponding entries (or with a minus sign). | 
		
	
		
			
				|  |  |  |  |         // It may caused by system calibration to the accelerometer. | 
		
	
		
			
				|  |  |  |  |         // Figure out how it works, or, if no game reads raw_entry, | 
		
	
		
			
				|  |  |  |  |         // the following three lines can be removed and leave raw_entry unimplemented. | 
		
	
		
			
				|  |  |  |  |         mem->accelerometer.raw_entry.x = -2 * accelerometer_entry.x; | 
		
	
		
			
				|  |  |  |  |         mem->accelerometer.raw_entry.z = 2 * accelerometer_entry.y; | 
		
	
		
			
				|  |  |  |  |         mem->accelerometer.raw_entry.y = -2 * accelerometer_entry.z; | 
		
	
		
			
				|  |  |  |  |     mem->accelerometer.index = next_accelerometer_index; | 
		
	
		
			
				|  |  |  |  |     next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // If we just updated index 0, provide a new timestamp | 
		
	
		
			
				|  |  |  |  |         if (mem->accelerometer.index == 0) { | 
		
	
		
			
				|  |  |  |  |             mem->accelerometer.index_reset_ticks_previous = mem->accelerometer.index_reset_ticks; | 
		
	
		
			
				|  |  |  |  |             mem->accelerometer.index_reset_ticks = (s64)CoreTiming::GetTicks(); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     AccelerometerDataEntry& accelerometer_entry = | 
		
	
		
			
				|  |  |  |  |         mem->accelerometer.entries[mem->accelerometer.index]; | 
		
	
		
			
				|  |  |  |  |     std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z) = | 
		
	
		
			
				|  |  |  |  |         VideoCore::g_emu_window->GetAccelerometerState(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         event_accelerometer->Signal(); | 
		
	
		
			
				|  |  |  |  |     // Make up "raw" entry | 
		
	
		
			
				|  |  |  |  |     // TODO(wwylele): | 
		
	
		
			
				|  |  |  |  |     // From hardware testing, the raw_entry values are approximately, but not exactly, as twice as | 
		
	
		
			
				|  |  |  |  |     // corresponding entries (or with a minus sign). It may caused by system calibration to the | 
		
	
		
			
				|  |  |  |  |     // accelerometer. Figure out how it works, or, if no game reads raw_entry, the following three | 
		
	
		
			
				|  |  |  |  |     // lines can be removed and leave raw_entry unimplemented. | 
		
	
		
			
				|  |  |  |  |     mem->accelerometer.raw_entry.x = -2 * accelerometer_entry.x; | 
		
	
		
			
				|  |  |  |  |     mem->accelerometer.raw_entry.z = 2 * accelerometer_entry.y; | 
		
	
		
			
				|  |  |  |  |     mem->accelerometer.raw_entry.y = -2 * accelerometer_entry.z; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // If we just updated index 0, provide a new timestamp | 
		
	
		
			
				|  |  |  |  |     if (mem->accelerometer.index == 0) { | 
		
	
		
			
				|  |  |  |  |         mem->accelerometer.index_reset_ticks_previous = mem->accelerometer.index_reset_ticks; | 
		
	
		
			
				|  |  |  |  |         mem->accelerometer.index_reset_ticks = (s64)CoreTiming::GetTicks(); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Update gyroscope | 
		
	
		
			
				|  |  |  |  |     if (enable_gyroscope_count > 0) { | 
		
	
		
			
				|  |  |  |  |         mem->gyroscope.index = next_gyroscope_index; | 
		
	
		
			
				|  |  |  |  |         next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size(); | 
		
	
		
			
				|  |  |  |  |     event_accelerometer->Signal(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index]; | 
		
	
		
			
				|  |  |  |  |         std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z) = | 
		
	
		
			
				|  |  |  |  |             VideoCore::g_emu_window->GetGyroscopeState(); | 
		
	
		
			
				|  |  |  |  |     // Reschedule recurrent event | 
		
	
		
			
				|  |  |  |  |     CoreTiming::ScheduleEvent(accelerometer_update_ticks - cycles_late, accelerometer_update_event); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // Make up "raw" entry | 
		
	
		
			
				|  |  |  |  |         mem->gyroscope.raw_entry.x = gyroscope_entry.x; | 
		
	
		
			
				|  |  |  |  |         mem->gyroscope.raw_entry.z = -gyroscope_entry.y; | 
		
	
		
			
				|  |  |  |  |         mem->gyroscope.raw_entry.y = gyroscope_entry.z; | 
		
	
		
			
				|  |  |  |  | static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) { | 
		
	
		
			
				|  |  |  |  |     SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         // If we just updated index 0, provide a new timestamp | 
		
	
		
			
				|  |  |  |  |         if (mem->gyroscope.index == 0) { | 
		
	
		
			
				|  |  |  |  |             mem->gyroscope.index_reset_ticks_previous = mem->gyroscope.index_reset_ticks; | 
		
	
		
			
				|  |  |  |  |             mem->gyroscope.index_reset_ticks = (s64)CoreTiming::GetTicks(); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     mem->gyroscope.index = next_gyroscope_index; | 
		
	
		
			
				|  |  |  |  |     next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         event_gyroscope->Signal(); | 
		
	
		
			
				|  |  |  |  |     GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index]; | 
		
	
		
			
				|  |  |  |  |     std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z) = | 
		
	
		
			
				|  |  |  |  |         VideoCore::g_emu_window->GetGyroscopeState(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Make up "raw" entry | 
		
	
		
			
				|  |  |  |  |     mem->gyroscope.raw_entry.x = gyroscope_entry.x; | 
		
	
		
			
				|  |  |  |  |     mem->gyroscope.raw_entry.z = -gyroscope_entry.y; | 
		
	
		
			
				|  |  |  |  |     mem->gyroscope.raw_entry.y = gyroscope_entry.z; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // If we just updated index 0, provide a new timestamp | 
		
	
		
			
				|  |  |  |  |     if (mem->gyroscope.index == 0) { | 
		
	
		
			
				|  |  |  |  |         mem->gyroscope.index_reset_ticks_previous = mem->gyroscope.index_reset_ticks; | 
		
	
		
			
				|  |  |  |  |         mem->gyroscope.index_reset_ticks = (s64)CoreTiming::GetTicks(); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     event_gyroscope->Signal(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Reschedule recurrent event | 
		
	
		
			
				|  |  |  |  |     CoreTiming::ScheduleEvent(gyroscope_update_ticks - cycles_late, gyroscope_update_event); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | void GetIPCHandles(Service::Interface* self) { | 
		
	
	
		
			
				
					
					|  |  |  | @@ -204,7 +217,11 @@ void EnableAccelerometer(Service::Interface* self) { | 
		
	
		
			
				|  |  |  |  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     ++enable_accelerometer_count; | 
		
	
		
			
				|  |  |  |  |     event_accelerometer->Signal(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Schedules the accelerometer update event if the accelerometer was just enabled | 
		
	
		
			
				|  |  |  |  |     if (enable_accelerometer_count == 1) { | 
		
	
		
			
				|  |  |  |  |         CoreTiming::ScheduleEvent(accelerometer_update_ticks, accelerometer_update_event); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     cmd_buff[1] = RESULT_SUCCESS.raw; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
	
		
			
				
					
					|  |  |  | @@ -215,7 +232,11 @@ void DisableAccelerometer(Service::Interface* self) { | 
		
	
		
			
				|  |  |  |  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     --enable_accelerometer_count; | 
		
	
		
			
				|  |  |  |  |     event_accelerometer->Signal(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Unschedules the accelerometer update event if the accelerometer was just disabled | 
		
	
		
			
				|  |  |  |  |     if (enable_accelerometer_count == 0) { | 
		
	
		
			
				|  |  |  |  |         CoreTiming::UnscheduleEvent(accelerometer_update_event, 0); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     cmd_buff[1] = RESULT_SUCCESS.raw; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
	
		
			
				
					
					|  |  |  | @@ -226,7 +247,11 @@ void EnableGyroscopeLow(Service::Interface* self) { | 
		
	
		
			
				|  |  |  |  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     ++enable_gyroscope_count; | 
		
	
		
			
				|  |  |  |  |     event_gyroscope->Signal(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Schedules the gyroscope update event if the gyroscope was just enabled | 
		
	
		
			
				|  |  |  |  |     if (enable_gyroscope_count == 1) { | 
		
	
		
			
				|  |  |  |  |         CoreTiming::ScheduleEvent(gyroscope_update_ticks, gyroscope_update_event); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     cmd_buff[1] = RESULT_SUCCESS.raw; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
	
		
			
				
					
					|  |  |  | @@ -237,7 +262,11 @@ void DisableGyroscopeLow(Service::Interface* self) { | 
		
	
		
			
				|  |  |  |  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     --enable_gyroscope_count; | 
		
	
		
			
				|  |  |  |  |     event_gyroscope->Signal(); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Unschedules the gyroscope update event if the gyroscope was just disabled | 
		
	
		
			
				|  |  |  |  |     if (enable_gyroscope_count == 0) { | 
		
	
		
			
				|  |  |  |  |         CoreTiming::UnscheduleEvent(gyroscope_update_event, 0); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     cmd_buff[1] = RESULT_SUCCESS.raw; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
	
		
			
				
					
					|  |  |  | @@ -298,6 +327,15 @@ void Init() { | 
		
	
		
			
				|  |  |  |  |     event_accelerometer = Event::Create(ResetType::OneShot, "HID:EventAccelerometer"); | 
		
	
		
			
				|  |  |  |  |     event_gyroscope = Event::Create(ResetType::OneShot, "HID:EventGyroscope"); | 
		
	
		
			
				|  |  |  |  |     event_debug_pad = Event::Create(ResetType::OneShot, "HID:EventDebugPad"); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // Register update callbacks | 
		
	
		
			
				|  |  |  |  |     pad_update_event = CoreTiming::RegisterEvent("HID::UpdatePadCallback", UpdatePadCallback); | 
		
	
		
			
				|  |  |  |  |     accelerometer_update_event = | 
		
	
		
			
				|  |  |  |  |         CoreTiming::RegisterEvent("HID::UpdateAccelerometerCallback", UpdateAccelerometerCallback); | 
		
	
		
			
				|  |  |  |  |     gyroscope_update_event = | 
		
	
		
			
				|  |  |  |  |         CoreTiming::RegisterEvent("HID::UpdateGyroscopeCallback", UpdateGyroscopeCallback); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | void Shutdown() { | 
		
	
	
		
			
				
					
					|  |  |  |   |