Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions scapy/contrib/bluetooth_vsc_realtek.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# SPDX-License-Identifier: GPL-2.0-only
# This file is part of Scapy
# See https://scapy.net/ for more information
#
# scapy.contrib.description = Realtek Bluetooth HCI Vendor-Specific Commands
# scapy.contrib.status = loads

from scapy.packet import Packet, bind_layers
from scapy.fields import (
BitEnumField,
BitField,
ByteField,
LEShortField,
LEIntField,
MultipleTypeField,
XLEIntField,
XStrLenField,
)

from scapy.layers.bluetooth import (
HCI_Command_Hdr,
HCI_Event_Command_Complete,
)

# Address space selector:
# 0, 1 - direct native CPU load/store RAM/ROM.
# 2 - indirect reg routed to the port for the RF/analog-front-end/baseband/modem.
# 3 - invalid, rejected by the firmware with status 0x12.
_rtk_mem_space = {0: "direct", 1: "direct_alt", 2: "indirect_reg", 3: "invalid"}

# Access width selector for memory read/write operations.
_rtk_mem_width = {0: "byte", 1: "halfword", 2: "word", 3: "word"}


class HCI_Cmd_VSC_Realtek_Read_Mem(Packet):
"""
Realtek Read Controller Memory (OCF 0x061, opcode 0xFC61).

Reads ``1 << width`` bytes (1/2/4) from ``address``.
The ``space`` field selects direct CPU memory (0/1) or the indirect
RF/PHY register port (2). For ``space=indirect_reg`` only the low 16
bits of ``address`` are used, as the register offset.
"""
name = "Realtek Read Controller Memory"
fields_desc = [
BitField("reserved1", 0, 2), # bits 7-6
BitEnumField("width", 2, 2, _rtk_mem_width), # bits 5-4 (default word)
BitField("reserved2", 0, 2), # bits 3-2
BitEnumField("space", 0, 2, _rtk_mem_space), # bits 1-0 (default direct)
XLEIntField("address", 0x80000000),
]


class HCI_Cmd_VSC_Realtek_Write_Mem(Packet):
"""
Realtek Write Controller Memory (OCF 0x062, opcode 0xFC62).

Writes ``value`` (sized by ``width``: byte/halfword/word) to ``address``.
``value`` is a little-endian integer whose width follows the ``width``
field. ``space`` selects direct CPU memory (0/1) or the indirect RF/PHY
register port (2); for ``space=indirect_reg`` only the low 16 bits of
``address`` are used, as the register offset.
"""
name = "Realtek Write Controller Memory"
fields_desc = [
BitField("reserved1", 0, 2),
BitEnumField("width", 2, 2, _rtk_mem_width),
BitField("reserved2", 0, 2),
BitEnumField("space", 0, 2, _rtk_mem_space),
XLEIntField("address", 0x80000000),
MultipleTypeField(
[
(ByteField("value", 0), lambda pkt: pkt.width == 0),
(LEShortField("value", 0), lambda pkt: pkt.width == 1),
],
LEIntField("value", 0), # default: word
),
]


class HCI_Cmd_Complete_VSC_Realtek_Read_Mem(Packet):
"""Read Controller Memory (0xFC61) command complete"""
name = "Realtek Read Controller Memory complete"
fields_desc = [
XStrLenField("data", b"", lambda pkt: pkt.underlayer.underlayer.len - 4)
]


bind_layers(HCI_Command_Hdr, HCI_Cmd_VSC_Realtek_Read_Mem, ogf=0x3F, ocf=0x061)
bind_layers(HCI_Command_Hdr, HCI_Cmd_VSC_Realtek_Write_Mem, ogf=0x3F, ocf=0x062)

bind_layers(HCI_Event_Command_Complete, HCI_Cmd_Complete_VSC_Realtek_Read_Mem,
opcode=0xFC61)
81 changes: 81 additions & 0 deletions test/contrib/bluetooth_vsc_realtek.uts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
% Realtek Bluetooth Vendor-Specific Command (VSC) tests
# test/run_tests -P "load_contrib('bluetooth_vsc_realtek')" -t test/contrib/bluetooth_vsc_realtek.uts

+ Load the Realtek VSC contrib module

= Load the contrib module
from scapy.layers.bluetooth import *
load_contrib("bluetooth_vsc_realtek")
from scapy.contrib.bluetooth_vsc_realtek import *


+ Realtek Read Controller RAM (OCF 0x061 / opcode 0xFC61)

= Read a word (default width) build + dissect
cmd = HCI_Command_Hdr() / HCI_Cmd_VSC_Realtek_Read_Mem(address=0x80000000)
assert cmd.ogf == 0x3f
assert cmd.ocf == 0x061
assert cmd.opcode == 0xfc61
assert cmd.width == 2 # word
assert cmd.space == 0 # direct
r = raw(cmd)
assert r == b'\x61\xfc\x05\x20\x00\x00\x00\x80'
p = HCI_Command_Hdr(r)
assert HCI_Cmd_VSC_Realtek_Read_Mem in p
assert p[HCI_Cmd_VSC_Realtek_Read_Mem].address == 0x80000000
assert p[HCI_Cmd_VSC_Realtek_Read_Mem].width == 2

= Read a single byte (control byte 0x00)
cmd = HCI_Command_Hdr() / HCI_Cmd_VSC_Realtek_Read_Mem(width="byte", address=0x12345678)
r = raw(cmd)
assert r == b'\x61\xfc\x05\x00\x78\x56\x34\x12'
p = HCI_Command_Hdr(r)
assert p[HCI_Cmd_VSC_Realtek_Read_Mem].width == 0
assert p[HCI_Cmd_VSC_Realtek_Read_Mem].address == 0x12345678

= Indirect register read (space = indirect_reg = 2)
cmd = HCI_Command_Hdr() / HCI_Cmd_VSC_Realtek_Read_Mem(width="halfword", space="indirect_reg", address=0x40)
assert cmd.space == 2
r = raw(cmd)
# ctrl byte: width=1 (bits5-4=01), space=2 (bits1-0=10) -> 0b00010010 = 0x12
assert r == b'\x61\xfc\x05\x12\x40\x00\x00\x00'


+ Realtek Write Controller RAM (OCF 0x062 / opcode 0xFC62)

= Write a word value
cmd = HCI_Command_Hdr() / HCI_Cmd_VSC_Realtek_Write_Mem(address=0x80000000, value=0x12345678)
assert cmd.opcode == 0xfc62
r = raw(cmd)
assert r == b'\x62\xfc\x09\x20\x00\x00\x00\x80\x78\x56\x34\x12'
p = HCI_Command_Hdr(r)
assert p[HCI_Cmd_VSC_Realtek_Write_Mem].address == 0x80000000
assert p[HCI_Cmd_VSC_Realtek_Write_Mem].value == 0x12345678

= Write a single byte value (width-sized value field)
cmd = HCI_Command_Hdr() / HCI_Cmd_VSC_Realtek_Write_Mem(width="byte", address=0x80000000, value=0xAA)
r = raw(cmd)
assert r == b'\x62\xfc\x06\x00\x00\x00\x00\x80\xaa'
p = HCI_Command_Hdr(r)
assert p[HCI_Cmd_VSC_Realtek_Write_Mem].width == 0
assert p[HCI_Cmd_VSC_Realtek_Write_Mem].value == 0xAA

= Write a halfword value
cmd = HCI_Command_Hdr() / HCI_Cmd_VSC_Realtek_Write_Mem(width="halfword", address=0x80000000, value=0xBEEF)
r = raw(cmd)
assert r == b'\x62\xfc\x07\x10\x00\x00\x00\x80\xef\xbe'


+ Realtek Read Controller RAM command complete (opcode 0xFC61)

= Dissect a Command Complete carrying 4 data bytes
# 04(evt) 0e(cmd complete) len=08 num=01 op=61fc status=00 data=00b0083c
evt = HCI_Hdr(bytes.fromhex("040e080161fc0000b0083c"))
assert HCI_Cmd_Complete_VSC_Realtek_Read_Mem in evt
assert evt[HCI_Event_Command_Complete].status == 0
assert evt[HCI_Cmd_Complete_VSC_Realtek_Read_Mem].data == bytes.fromhex("00b0083c")

= Dissect a single-byte read result
# event param len = 5 -> 1 data byte
evt = HCI_Hdr(bytes.fromhex("040e050161fc00ab"))
assert evt[HCI_Cmd_Complete_VSC_Realtek_Read_Mem].data == b"\xab"
Loading