diff -Nur a/core/hw/maple/maple_cfg.cpp b/core/hw/maple/maple_cfg.cpp
--- a/core/hw/maple/maple_cfg.cpp	2016-03-18 23:11:34.099340091 -0300
+++ b/core/hw/maple/maple_cfg.cpp	2016-03-18 23:12:24.396511206 -0300
@@ -21,6 +21,7 @@
 		ImageUpdate(data);
 */
 void UpdateInputState(u32 port);
+void UpdateVibration(u32 port, u32 value);
 
 extern u16 kcode[4];
 extern u32 vks[4];
@@ -41,6 +42,11 @@
 		this->dev=dev;
 	}
 
+	void SetVibration(u32 value)
+	{
+		UpdateVibration(dev->bus_id, value);
+	}
+
 	void GetInput(PlainJoystickState* pjs)
 	{
 		UpdateInputState(dev->bus_id);
diff -Nur a/core/hw/maple/maple_cfg.h b/core/hw/maple/maple_cfg.h
--- a/core/hw/maple/maple_cfg.h	2016-03-18 23:11:34.099340091 -0300
+++ b/core/hw/maple/maple_cfg.h	2016-03-18 23:12:24.396511206 -0300
@@ -57,6 +57,7 @@
 
 struct IMapleConfigMap
 {
+	virtual void SetVibration(u32 value) = 0;
 	virtual void GetInput(PlainJoystickState* pjs)=0;
 	virtual void SetImage(void* img)=0;
 	virtual ~IMapleConfigMap() {}
diff -Nur a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp
--- a/core/hw/maple/maple_devs.cpp	2016-03-18 23:11:34.099340091 -0300
+++ b/core/hw/maple/maple_devs.cpp	2016-03-18 23:12:24.396511206 -0300
@@ -22,6 +22,7 @@
 const char* maple_sega_dreameye_name_1 = "Dreamcast Camera Flash  Devic";
 const char* maple_sega_dreameye_name_2 = "Dreamcast Camera Flash LDevic";
 const char* maple_sega_mic_name = "MicDevice for Dreameye";
+const char* maple_sega_purupuru_name = "Puru Puru Pack";
 
 const char* maple_sega_brand = "Produced By or Under License From SEGA ENTERPRISES,LTD.";
 
@@ -844,6 +845,98 @@
 	}	
 };
 
+
+struct maple_sega_purupuru : maple_base
+{
+	u16 AST, AST_ms;
+	u32 VIBSET;
+
+	virtual u32 dma(u32 cmd)
+	{
+		switch (cmd)
+		{
+		case MDC_DeviceRequest:
+			//caps
+			//4
+			w32(MFID_8_Vibration);
+
+			//struct data
+			//3*4
+			w32(0x00000101);
+			w32(0);
+			w32(0);
+
+			//1	area code
+			w8(0xFF);
+
+			//1	direction
+			w8(0);
+
+			//30
+			wstr(maple_sega_purupuru_name, 30);
+
+			//60
+			wstr(maple_sega_brand, 60);
+
+			//2
+			w16(0x00C8);
+
+			//2
+			w16(0x0640);
+
+			return MDRS_DeviceStatus;
+
+			//get last vibration
+		case MDCF_GetCondition:
+			
+			w32(MFID_8_Vibration);
+
+			w32(VIBSET);
+
+			return MDRS_DataTransfer;
+
+		case MDCF_GetMediaInfo:
+
+			w32(MFID_8_Vibration);
+
+			// PuruPuru vib specs
+			w32(0x3B07E010); 
+
+			return MDRS_DataTransfer;
+
+		case MDCF_BlockRead:
+
+			w32(MFID_8_Vibration);
+			w32(0);
+
+			w16(2);
+			w16(AST);
+
+			return MDRS_DataTransfer;
+
+		case MDCF_BlockWrite:
+			
+			//Auto-stop time
+			AST = dma_buffer_in[10];
+			AST_ms = AST * 250 + 250;
+
+			return MDRS_DeviceReply;
+
+		case MDCF_SetCondition:
+			
+			VIBSET = *(u32*)&dma_buffer_in[4];
+			//Do the rumble thing!
+			config->SetVibration(VIBSET);
+
+			return MDRS_DeviceReply;
+
+		default:
+			//printf("UNKOWN MAPLE COMMAND %d\n",cmd);
+			return MDRE_UnknownFunction;
+		}
+	}
+};
+
 char EEPROM[0x100];
 bool EEPROM_loaded = false;
 
@@ -1300,6 +1393,11 @@
 		rv = new maple_sega_vmu();
 		break;
 
+	case MDT_PurupuruPack:
+		rv = new maple_sega_purupuru();
+		break;
+
+
 
 	case MDT_NaomiJamma:
 		rv = new maple_naomi_jamma();
diff -Nur a/core/hw/maple/maple_devs.h b/core/hw/maple/maple_devs.h
--- a/core/hw/maple/maple_devs.h	2016-03-18 23:11:34.099340091 -0300
+++ b/core/hw/maple/maple_devs.h	2016-03-18 23:12:24.396511206 -0300
@@ -6,6 +6,7 @@
 	MDT_SegaController,
 	MDT_SegaVMU,
 	MDT_Microphone,
+	MDT_PurupuruPack,
 
 	MDT_NaomiJamma,
 
diff -Nur a/core/linux-dist/evdev.cpp b/core/linux-dist/evdev.cpp
--- a/core/linux-dist/evdev.cpp	2016-03-18 23:11:34.113340136 -0300
+++ b/core/linux-dist/evdev.cpp	2016-03-18 23:12:24.397511209 -0300
@@ -102,6 +102,7 @@
 		this->data_y.init(this->fd, this->mapping->Axis_Analog_Y, this->mapping->Axis_Analog_Y_Inverted);
 		this->data_trigger_left.init(this->fd, this->mapping->Axis_Trigger_Left, this->mapping->Axis_Trigger_Left_Inverted);
 		this->data_trigger_right.init(this->fd, this->mapping->Axis_Trigger_Right, this->mapping->Axis_Trigger_Right_Inverted);
+		this->rumble_effect_id = -1;
 	}
 
 	std::map<std::string, EvdevControllerMapping> loaded_mappings;
@@ -202,7 +203,7 @@
 
 		printf("evdev: Trying to open device at '%s'\n", device);
 
-		int fd = open(device, O_RDONLY);
+		int fd = open(device, O_RDWR);
 
 		if (fd >= 0)
 		{
@@ -446,5 +447,43 @@
 			}
 		}
 	}
+
+	void input_evdev_rumble(EvdevController* controller, u16 pow_strong, u16 pow_weak)
+	{
+		if (controller->fd < 0 || controller->rumble_effect_id == -2)
+		{
+			// Either the controller is not used or previous rumble effect failed
+			printf("RUMBLE: %s\n", "Skipped!");
+			return;
+		}
+		printf("RUMBLE: %u / %u (%d)\n", pow_strong, pow_weak, controller->rumble_effect_id);
+		struct ff_effect effect;
+		effect.type = FF_RUMBLE;
+		effect.id = controller->rumble_effect_id;
+		effect.u.rumble.strong_magnitude = pow_strong;
+		effect.u.rumble.weak_magnitude = pow_weak;
+		effect.replay.length = 0;
+		effect.replay.delay = 0;
+		if (ioctl(controller->fd, EVIOCSFF, &effect) == -1)
+		{
+			perror("evdev: Force feedback error");
+			controller->rumble_effect_id = -2;
+		}
+		else
+		{
+			controller->rumble_effect_id = effect.id;
+
+			// Let's play the effect
+			input_event play;
+			play.type = EV_FF;
+			play.code = effect.id;
+			play.value = 1;
+			if (write(controller->fd, (const void*) &play, sizeof(play)) == -1)
+			{
+				perror("evdev: Force feedback error");
+				controller->rumble_effect_id = -2;
+			}
+		}
+	}
 #endif
 
diff -Nur a/core/linux-dist/evdev.h b/core/linux-dist/evdev.h
--- a/core/linux-dist/evdev.h	2016-03-18 23:11:34.113340136 -0300
+++ b/core/linux-dist/evdev.h	2016-03-18 23:12:24.397511209 -0300
@@ -54,6 +54,7 @@
 	EvdevAxisData data_y;
 	EvdevAxisData data_trigger_left;
 	EvdevAxisData data_trigger_right;
+	int rumble_effect_id;
 	void init();
 };
 
@@ -72,3 +73,4 @@
 
 extern int input_evdev_init(EvdevController* controller, const char* device, const char* mapping_fname);
 extern bool input_evdev_handle(EvdevController* controller, u32 port);
+extern void input_evdev_rumble(EvdevController* controller, u16 pow_strong, u16 pow_weak);
diff -Nur a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp
--- a/core/linux-dist/main.cpp	2016-03-18 23:11:34.113340136 -0300
+++ b/core/linux-dist/main.cpp	2016-03-18 23:12:24.397511209 -0300
@@ -191,6 +191,30 @@
 	#endif
 }
 
+void UpdateVibration(u32 port, u32 value)
+{
+	#if defined(USE_EVDEV)
+		const u8 freq_l = 0x16;
+		//const u8 freq_h = 0x31;
+
+		u8 POW_POS = (value >> 8) & 0x3;
+		u8 POW_NEG = (value >> 12) & 0x3;
+		u8 FREQ = (value >> 16) & 0xFF;
+
+		double pow = (POW_POS + POW_NEG) / 7.0;
+		double pow_l = pow * (0x3B - FREQ) / 17.0;
+		double pow_r = pow * (FREQ / (double)freq_l);
+
+		if (pow_l > 1.0) pow_l = 1.0;
+		if (pow_r > 1.0) pow_r = 1.0;
+
+		u16 pow_strong = (u16)(65535 * pow_l);
+		u16 pow_weak = (u16)(65535 * pow_r);
+
+		input_evdev_rumble(&evdev_controllers[port], pow_strong, pow_weak);
+	#endif
+}
+
 void os_DoEvents()
 {
 	#if defined(SUPPORT_X11)
diff -Nur a/core/nacl/nacl.cpp b/core/nacl/nacl.cpp
--- a/core/nacl/nacl.cpp	2016-03-18 23:11:34.114340140 -0300
+++ b/core/nacl/nacl.cpp	2016-03-18 23:12:24.397511209 -0300
@@ -257,6 +257,10 @@
 
 }
 
+void UpdateVibration(u32 port, u32 value) {
+
+}
+
 void os_CreateWindow() {
 
 }
diff -Nur a/core/windows/winmain.cpp b/core/windows/winmain.cpp
--- a/core/windows/winmain.cpp	2016-03-18 23:11:34.119340156 -0300
+++ b/core/windows/winmain.cpp	2016-03-18 23:12:24.399511217 -0300
@@ -5,6 +5,8 @@
 #define _WIN32_WINNT 0x0500 
 #include <windows.h>
 
+#include <Xinput.h>
+#pragma comment(lib, "XInput9_1_0.lib")
 
 PCHAR*
 	CommandLineToArgvA(
@@ -105,7 +107,7 @@
 bool VramLockedWrite(u8* address);
 bool ngen_Rewrite(unat& addr,unat retadr,unat acc);
 bool BM_LockedWrite(u8* address);
-
+void UpdateController(u32 port);
 
 LONG ExeptionHandler(EXCEPTION_POINTERS *ExceptionInfo)
 {
@@ -235,6 +237,8 @@
 		if (GetAsyncKeyState(VK_RIGHT))
 			kcode[port]&=~key_CONT_DPAD_RIGHT;
 
+		UpdateController(port);
+
 		if (GetAsyncKeyState(VK_F1))
 			settings.pvr.ta_skip = 100;
 
@@ -245,7 +249,66 @@
 			DiscSwap();
 	}
 
+void UpdateController(u32 port)
+	{
+		XINPUT_STATE state;
+		
+		if (XInputGetState(port, &state) == 0)
+		{
+			WORD xbutton = state.Gamepad.wButtons;
+
+			if (xbutton & XINPUT_GAMEPAD_A) 
+				kcode[port] &= ~key_CONT_A;
+			if (xbutton & XINPUT_GAMEPAD_B)
+				kcode[port] &= ~key_CONT_B;
+			if (xbutton & XINPUT_GAMEPAD_Y)
+				kcode[port] &= ~key_CONT_Y;
+			if (xbutton & XINPUT_GAMEPAD_X)
+				kcode[port] &= ~key_CONT_X;
+
+			if (xbutton & XINPUT_GAMEPAD_START)
+				kcode[port] &= ~key_CONT_START;
+
+			if (xbutton & XINPUT_GAMEPAD_DPAD_UP)
+				kcode[port] &= ~key_CONT_DPAD_UP;
+			if (xbutton & XINPUT_GAMEPAD_DPAD_DOWN)
+				kcode[port] &= ~key_CONT_DPAD_DOWN;
+			if (xbutton & XINPUT_GAMEPAD_DPAD_LEFT)
+				kcode[port] &= ~key_CONT_DPAD_LEFT;
+			if (xbutton & XINPUT_GAMEPAD_DPAD_RIGHT)
+				kcode[port] &= ~key_CONT_DPAD_RIGHT;
+
+			lt[port] |= state.Gamepad.bLeftTrigger;
+			rt[port] |= state.Gamepad.bRightTrigger;
+
+			joyx[port] |=  state.Gamepad.sThumbLX / 257;
+			joyy[port] |= -state.Gamepad.sThumbLY / 257;
+		}
+	}
 
+void UpdateVibration(u32 port, u32 value)
+{
+		const u8 freq_l = 0x16;
+		//const u8 freq_h = 0x31;
+
+		u8 POW_POS = (value >> 8) & 0x3;
+		u8 POW_NEG = (value >> 12) & 0x3;
+		u8 FREQ = (value >> 16) & 0xFF;
+
+		XINPUT_VIBRATION vib;
+
+		double pow = (POW_POS + POW_NEG) / 7.0;
+		double pow_l = pow * (0x3B - FREQ) / 17.0;
+		double pow_r = pow * (FREQ - 0x07) / 15.0;
+
+		if (pow_l > 1.0) pow_l = 1.0;
+		if (pow_r > 1.0) pow_r = 1.0;
+
+		vib.wLeftMotorSpeed = (u16)(65535 * pow_l);
+		vib.wRightMotorSpeed = (u16)(65535 * pow_r);
+
+		XInputSetState(port, &vib);
+}
 
 LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
diff -Nur a/shell/android/jni/src/Android.cpp b/shell/android/jni/src/Android.cpp
--- a/shell/android/jni/src/Android.cpp	2016-03-18 23:11:34.145340240 -0300
+++ b/shell/android/jni/src/Android.cpp	2016-03-18 23:12:24.399511217 -0300
@@ -231,6 +231,11 @@
   // @@@ Nothing here yet
 }
 
+void UpdateVibration(u32 port, u32 value)
+{
+	
+}
+
 void *libPvr_GetRenderTarget() 
 {
   // No X11 window in Android 
diff -Nur a/shell/apple/emulator-ios/emulator/ios_main.mm b/shell/apple/emulator-ios/emulator/ios_main.mm
--- a/shell/apple/emulator-ios/emulator/ios_main.mm	2016-03-18 23:11:34.192340393 -0300
+++ b/shell/apple/emulator-ios/emulator/ios_main.mm	2016-03-18 23:12:24.400511220 -0300
@@ -106,6 +106,10 @@
     
 }
 
+void UpdateVibration(u32 port, u32 value) {
+
+}
+
 void get_mic_data(u8* ) {
 
 }
diff -Nur a/shell/apple/emulator-osx/emulator-osx/osx-main.mm b/shell/apple/emulator-osx/emulator-osx/osx-main.mm
--- a/shell/apple/emulator-osx/emulator-osx/osx-main.mm	2016-03-18 23:11:34.194340399 -0300
+++ b/shell/apple/emulator-osx/emulator-osx/osx-main.mm	2016-03-18 23:12:24.401511224 -0300
@@ -59,6 +59,10 @@
     
 }
 
+void UpdateVibration(u32 port, u32 value) {
+
+}
+
 void os_CreateWindow() {
     
 }