mirror of
https://github.com/Alkaid-Benetnash/EmuBTHID.git
synced 2026-03-18 11:04:39 +01:00
Merge 6f5da9c6ac into e430dbfe67
This commit is contained in:
commit
feb823f0d8
|
|
@ -1,9 +1,19 @@
|
|||
import bluetooth # requires pybluez package
|
||||
import dbus
|
||||
import dbus.service
|
||||
import os
|
||||
import socket
|
||||
|
||||
|
||||
# see section "2.5 PSMs and SPSMs": https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Assigned_Numbers/out/en/Assigned_Numbers.pdf
|
||||
BLUETOOTH_PORT_HID_CONTROL = 0x0011
|
||||
BLUETOOTH_PORT_HID_INTERRUPT = 0x0013
|
||||
|
||||
# see section "3.3 SDP Service Class and Profile Identifiers" of https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Assigned_Numbers/out/en/Assigned_Numbers.pdf
|
||||
# and Vol 3, Part B, section 2.5.1 of https://www.bluetooth.com/specifications/specs/core-specification-5-3/
|
||||
BLUETOOT_PROFILE_HID = "00001124-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
|
||||
class BluetoothHIDProfile(dbus.service.Object):
|
||||
def __init__(self, bus, path):
|
||||
super(BluetoothHIDProfile, self).__init__(bus, path)
|
||||
|
|
@ -42,40 +52,54 @@ def error_handler(e):
|
|||
|
||||
|
||||
class BluetoothHIDService(object):
|
||||
PROFILE_PATH = "/org/bluez/bthid_profile"
|
||||
PROFILE_PATH = "/org/bluez/emubthid_profile"
|
||||
|
||||
HOST = 0
|
||||
PORT = 1
|
||||
|
||||
def __init__(self, service_record, MAC):
|
||||
self.P_CTRL = 0x0011
|
||||
self.P_INTR = 0x0013
|
||||
if not bluetooth.is_valid_address(MAC):
|
||||
raise ValueError(f"'{MAC}' is not a valid MAC address")
|
||||
|
||||
self.SELFMAC = MAC
|
||||
|
||||
bus = dbus.SystemBus()
|
||||
bluez_obj = bus.get_object("org.bluez", "/org/bluez")
|
||||
manager = dbus.Interface(bluez_obj, "org.bluez.ProfileManager1")
|
||||
|
||||
BluetoothHIDProfile(bus, self.PROFILE_PATH)
|
||||
opts = {
|
||||
"ServiceRecord": service_record,
|
||||
"Name": "BTKeyboardProfile",
|
||||
"RequireAuthentication": False,
|
||||
"RequireAuthorization": False,
|
||||
"Service": "MY BTKBD",
|
||||
"Role": "server"
|
||||
}
|
||||
|
||||
sock_control = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_L2CAP)
|
||||
sock_control.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock_inter = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_L2CAP)
|
||||
sock_inter.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock_control.bind((self.SELFMAC, self.P_CTRL))
|
||||
sock_inter.bind((self.SELFMAC, self.P_INTR))
|
||||
manager.RegisterProfile(self.PROFILE_PATH, "00001124-0000-1000-8000-00805f9b34fb", opts)
|
||||
print("Registered")
|
||||
sock_control = L2CAPSocket(True)
|
||||
sock_inter = L2CAPSocket(True)
|
||||
|
||||
# The PSM parameter sets the bluetooth "port" for L2CAP connections
|
||||
# "Valid values in the first range are assigned by the Bluetooth SIG and indicate protocols."
|
||||
# https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host/logical-link-control-and-adaptation-protocol-specification.html
|
||||
sock_control.bind((self.SELFMAC, BLUETOOTH_PORT_HID_CONTROL))
|
||||
sock_inter.bind((self.SELFMAC, BLUETOOTH_PORT_HID_INTERRUPT))
|
||||
|
||||
# IDEs dont see this function because bluetooth.BluetoothSocket imports them dynamically
|
||||
sock_control.listen(1)
|
||||
sock_inter.listen(1)
|
||||
print(f"waiting for connection at controller {MAC}, please double check with the MAC in bluetoothctl")
|
||||
|
||||
manager.RegisterProfile(self.PROFILE_PATH, BLUETOOT_PROFILE_HID, {
|
||||
"Name": "Emulated Bluetooth keyboard",
|
||||
# "Service": "MY BTKBD", # this refers to the bluetooth spec service class uuid
|
||||
"Role": "server",
|
||||
"RequireAuthentication": True,
|
||||
"RequireAuthorization": True,
|
||||
"ServiceRecord": service_record,
|
||||
# note: it seems as if socket creation could be replaced with working through DBus (see PSM option and NewConnection function).
|
||||
# maybe currently the conenction is somehow handled at 2 places at the same time?
|
||||
#
|
||||
# https://www.bluez.org/bluez-5-api-introduction-and-porting-guide/
|
||||
# "The new Profile1 interface (and removal of org.bluez.Service)"
|
||||
})
|
||||
|
||||
print("Registered Bluetooth HID profile in Bluez")
|
||||
|
||||
print(f"Waiting for connection at controller {MAC}, please double check with the MAC in bluetoothctl")
|
||||
|
||||
self.ccontrol, cinfo = sock_control.accept()
|
||||
print("Control channel connected to " + cinfo[self.HOST])
|
||||
self.cinter, cinfo = sock_inter.accept()
|
||||
|
|
@ -83,3 +107,13 @@ class BluetoothHIDService(object):
|
|||
|
||||
def send(self, bytes_buf):
|
||||
self.cinter.send(bytes_buf)
|
||||
|
||||
|
||||
# https://github.com/karulis/pybluez/blob/master/examples/simple/l2capserver.py
|
||||
class L2CAPSocket(bluetooth.BluetoothSocket):
|
||||
|
||||
def __init__(self, reuseaddr: False):
|
||||
super().__init__(bluetooth.L2CAP)
|
||||
|
||||
if reuseaddr:
|
||||
self.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
|
|
@ -33,10 +33,6 @@ The client who uses the emulated HID device is an android 9 phone.
|
|||
|
||||
## How to Use
|
||||
|
||||
### (IMPORTANT) Update the bluetooth controller MAC in `main.py`
|
||||
|
||||
Edit `main.py` and change the `CONTROLLER_MAC` variable in the beginning to your own MAC. You can find the MAC of the bluetooth controller in `bluetoothctl`. E.g. the "5C:87:9C:96:BE:5E" shown in the screenshot below is the MAC.
|
||||
|
||||
### Enable bluetooth
|
||||
|
||||
1. make sure that bluetoothd has the plugin `input` disabled (i.e. "-P input").
|
||||
|
|
@ -67,7 +63,7 @@ Edit `main.py` and change the `CONTROLLER_MAC` variable in the beginning to your
|
|||
|
||||
5. Run `xhost +` to enable root user also draw something on a non-root user's X session. (see [this stackoverflow](https://stackoverflow.com/questions/31902846/how-to-fix-error-xlib-error-displayconnectionerror-cant-connect-to-display-0))
|
||||
|
||||
6. Run `sudo python3 main.py`
|
||||
6. Run `sudo python3 main.py <BT controller MAC address>`
|
||||
|
||||
In bluetoothctl, it should look like this, where a lot of UUIDs are registered
|
||||
|
||||
|
|
|
|||
18
main.py
18
main.py
|
|
@ -1,19 +1,12 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
from BluetoothHID import BluetoothHIDService
|
||||
from evdev_xkb_map import evdev_xkb_map, modkeys
|
||||
import keymap
|
||||
from Xlib import X, display, Xutil
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
|
||||
"""
|
||||
Change this CONTROLLER_MAC to the mac of your own device
|
||||
"""
|
||||
CONTROLLER_MAC = "5C:87:9C:96:BE:5E"
|
||||
|
||||
usbhid_map = {}
|
||||
with open("keycode.txt") as f:
|
||||
for line in f.read().splitlines():
|
||||
|
|
@ -222,13 +215,22 @@ class Window(object):
|
|||
else:
|
||||
prev_y = e.event_y
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
bt_controller_mac = sys.argv[1]
|
||||
except IndexError:
|
||||
print("You need to pass the MAC address of your Bluetooth controller in the first argument.")
|
||||
print("You can find the MAC of the bluetooth controller in `bluetoothctl`. In its format it is similar to \"5C:87:9C:96:BE:5E\".")
|
||||
exit(1)
|
||||
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
service_record = open("sdp_record_kbd.xml").read()
|
||||
d = display.Display()
|
||||
d.change_keyboard_control(auto_repeat_mode=X.AutoRepeatModeOff)
|
||||
|
||||
try:
|
||||
bthid_srv = BluetoothHIDService(service_record, CONTROLLER_MAC)
|
||||
bthid_srv = BluetoothHIDService(service_record, bt_controller_mac)
|
||||
Window(d).loop(bthid_srv.send)
|
||||
#Window(d).loop(print)
|
||||
finally:
|
||||
|
|
|
|||
Loading…
Reference in a new issue