| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- // Package ble provides BLE scanning via raw HCI sockets
- package ble
- import (
- "encoding/binary"
- "fmt"
- "syscall"
- "unsafe"
- )
- // HCI constants
- const (
- AF_BLUETOOTH = 31
- BTPROTO_HCI = 1
- SOL_HCI = 0
- HCI_FILTER = 2
- // HCI packet types
- HCI_COMMAND_PKT = 0x01
- HCI_EVENT_PKT = 0x04
- // HCI events
- EVT_LE_META = 0x3E
- EVT_CMD_COMPLETE = 0x0E
- // LE Meta sub-events
- EVT_LE_ADVERTISING_REPORT = 0x02
- // OGF (Opcode Group Field)
- OGF_LE_CTL = 0x08
- // OCF (Opcode Command Field)
- OCF_LE_SET_SCAN_PARAMETERS = 0x000B
- OCF_LE_SET_SCAN_ENABLE = 0x000C
- // Scan types
- SCAN_TYPE_PASSIVE = 0x00
- SCAN_TYPE_ACTIVE = 0x01
- )
- // sockaddrHCI is the HCI socket address
- type sockaddrHCI struct {
- Family uint16
- Dev uint16
- Channel uint16
- }
- // hciFilter is the HCI event filter
- type hciFilter struct {
- TypeMask uint32
- EventMask [2]uint32
- Opcode uint16
- }
- // HCISocket represents a raw HCI socket
- type HCISocket struct {
- fd int
- devID int
- }
- // NewHCISocket creates a new HCI socket for the given device
- func NewHCISocket(devID int) (*HCISocket, error) {
- fd, err := syscall.Socket(AF_BLUETOOTH, syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, BTPROTO_HCI)
- if err != nil {
- return nil, fmt.Errorf("socket: %w", err)
- }
- // Bind to device
- addr := sockaddrHCI{
- Family: AF_BLUETOOTH,
- Dev: uint16(devID),
- Channel: 0, // HCI_CHANNEL_RAW
- }
- _, _, errno := syscall.Syscall(
- syscall.SYS_BIND,
- uintptr(fd),
- uintptr(unsafe.Pointer(&addr)),
- unsafe.Sizeof(addr),
- )
- if errno != 0 {
- syscall.Close(fd)
- return nil, fmt.Errorf("bind: %v", errno)
- }
- return &HCISocket{fd: fd, devID: devID}, nil
- }
- // SetEventFilter sets the HCI event filter to receive LE Meta events
- func (h *HCISocket) SetEventFilter() error {
- var filter hciFilter
- // Enable HCI_EVENT_PKT
- filter.TypeMask = 1 << HCI_EVENT_PKT
- // Enable EVT_LE_META (0x3E = 62) and EVT_CMD_COMPLETE (0x0E = 14) events
- // EventMask is split: [0] = events 0-31, [1] = events 32-63
- filter.EventMask[0] = 1 << EVT_CMD_COMPLETE // bit 14
- filter.EventMask[1] = 1 << (EVT_LE_META - 32) // bit 30 in second word (62-32=30)
- _, _, errno := syscall.Syscall6(
- syscall.SYS_SETSOCKOPT,
- uintptr(h.fd),
- SOL_HCI,
- HCI_FILTER,
- uintptr(unsafe.Pointer(&filter)),
- unsafe.Sizeof(filter),
- 0,
- )
- if errno != 0 {
- return fmt.Errorf("setsockopt HCI_FILTER: %v", errno)
- }
- return nil
- }
- // SendCommand sends an HCI command
- func (h *HCISocket) SendCommand(ogf, ocf uint16, params []byte) error {
- opcode := uint16(ocf) | (uint16(ogf) << 10)
- buf := make([]byte, 4+len(params))
- buf[0] = HCI_COMMAND_PKT
- binary.LittleEndian.PutUint16(buf[1:3], opcode)
- buf[3] = byte(len(params))
- copy(buf[4:], params)
- _, err := syscall.Write(h.fd, buf)
- return err
- }
- // SetScanParameters sets LE scan parameters
- func (h *HCISocket) SetScanParameters(scanType byte, interval, window uint16) error {
- params := make([]byte, 7)
- params[0] = scanType
- binary.LittleEndian.PutUint16(params[1:3], interval) // interval (units of 0.625ms)
- binary.LittleEndian.PutUint16(params[3:5], window) // window (units of 0.625ms)
- params[5] = 0x00 // own address type: public
- params[6] = 0x00 // filter policy: accept all
- return h.SendCommand(OGF_LE_CTL, OCF_LE_SET_SCAN_PARAMETERS, params)
- }
- // SetScanEnable enables or disables LE scanning
- func (h *HCISocket) SetScanEnable(enable bool, filterDuplicates bool) error {
- params := make([]byte, 2)
- if enable {
- params[0] = 0x01
- }
- if filterDuplicates {
- params[1] = 0x01
- }
- return h.SendCommand(OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE, params)
- }
- // Read reads an HCI event packet
- func (h *HCISocket) Read(buf []byte) (int, error) {
- return syscall.Read(h.fd, buf)
- }
- // Close closes the HCI socket
- func (h *HCISocket) Close() error {
- return syscall.Close(h.fd)
- }
- // Fd returns the file descriptor
- func (h *HCISocket) Fd() int {
- return h.fd
- }
|