BLE建立L2CAP Socket连接remote提示connect refused

最近在全志r329 上面编写和某个remote设备建立BLE连接的程序,发现创建socket以后,和remote设备连接时候,提示connect refused。

最后分析原因是:没有设置LE_HOST_SUPPORTED。 

在此记录问题原因和解决方法:

int __host_create_gatt_socket( char *slave_macaddr ) //XX:XX:XX:XX:XX:XX
{
    int sk = 0;
    struct sockaddr_l2 master_addr;
    struct sockaddr_l2 slave_addr;
    struct bt_security btsec;

    //创建gatt层的socket
    sk = socket( AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP );
//    sk = socket( AF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK, BTPROTO_L2CAP );
    printf( "sock = %d
",sk );
       if ( sk<0 )
       {
        pt_error( "Can't create seqpacket socket: %s
", strerror(errno) );
        return -1;
    }

    
    //绑定socket
    memset( &master_addr,0,sizeof(master_addr) );
    master_addr.l2_family = AF_BLUETOOTH;
    master_addr.l2_cid = 0x0004;
    master_addr.l2_bdaddr_type=0x01;
    
    if( bind(sk, (struct sockaddr *)&master_addr, sizeof(master_addr))<0 )
    {
        pt_error( "[con] bind error: %s
", strerror(errno) );
        close( sk );
        return -1;
    }
    printf( "bind success
" );


    
    //slave 的mac 地址
    memset( &slave_addr, 0, sizeof(slave_addr) );
    slave_addr.l2_family = AF_BLUETOOTH;
    slave_addr.l2_cid = 0x0004;
    slave_addr.l2_bdaddr_type = 0x01;
    str2ba( slave_macaddr, (bdaddr_t*)&slave_addr.l2_bdaddr );



    if( connect( sk, (struct sockaddr *)&slave_addr, sizeof(slave_addr))<0 )
    {
        pt_error( "[con] connect error: %s-%d", strerror(errno),errno );
        close( sk );
        return -1;
    }

    printf( "connect to slave success
" );
    return sk;
}

创建L2CAP socket以后,调用connect()接口,提示connect refused。

添加如下接口:

int hci_read_le_host_supported(int dd,int *le,int *simul, int to);
int hci_write_le_host_supported(int dd, uint8_t leEnable,uint8_t simulEnable , int to)
 
int hci_read_le_host_supported(int dd,int *le,int *simul, int to)
{
    read_le_host_supported_rp rp;
    struct hci_request rq;

    memset(&rq, 0, sizeof(rq));
    rq.ogf    = OGF_HOST_CTL;
    rq.ocf    = OCF_READ_LE_HOST_SUPPORTED;
    rq.rparam = &rp;
    rq.rlen   = READ_LE_HOST_SUPPORTED_RP_SIZE;

    if (hci_send_req(dd, &rq, to) < 0)
        return -1;

    if (rp.status) {
        errno = EIO;
        return -1;
    }

    if(le)
        *le = rp.le;
    if(simul)
        *simul = rp.simul;

    return 0;
}

int hci_write_le_host_supported(int dd, uint8_t leEnable,uint8_t simulEnable , int to)
{
    write_le_host_supported_cp cp;
    struct hci_request rq;

    memset(&cp, 0, sizeof(cp));
    cp.le = leEnable;
    cp.simul = simulEnable;

    memset(&rq, 0, sizeof(rq));
    rq.ogf    = OGF_HOST_CTL;
    rq.ocf    = OCF_WRITE_LE_HOST_SUPPORTED;
    rq.cparam = &cp;
    rq.clen   = WRITE_LE_HOST_SUPPORTED_CP_SIZE;

    if (hci_send_req(dd, &rq, to) < 0)
        return -1;

    return 0;
}
ble_host_scan( int enable )
{
   int m_advsock ;
int le=0,simul=0;
if( enable ) { m_advsock = hci_open_dev(0) ; //hci0 if( m_advsock>0 ) { hci_read_le_host_supported(m_ble_advsock,&le,&simul,5000); printf( "le=%d simul=%d... ",le, simul ); hci_write_le_host_supported(m_ble_advsock, 1,simul , 5000); hci_read_le_host_supported(m_ble_advsock,&le,&simul,5000); printf( " le=%d simul=%d... ",le, simul );

//设置LE扫描参数,使能扫描
       ble_hci_lescan( m_advsock, enable ); } }
else
{
    ble_hci_lescan( m_advsock, enable );

}

}
int ble_hci_lescan( int s, int enable )
{
    printf( "ble_hci_lescan=%d %d
", s, enable );
    if( s >= 0 )
    {
        int err=-1;
        m_is_enable = enable;
        if( m_is_enable==0 )
        {
            uint8_t filter_dup = 0x01;
            setsockopt( s, SOL_HCI, HCI_FILTER, &m_orig_filter, sizeof(m_orig_filter) );
            err = hci_le_set_scan_enable( s, 0x00, filter_dup, 10000 );
            if (err < 0)
            {
                pt_error("Disable scan failed ->%s", strerror(errno) );
                return -1;
            }
            printf( "hci le set scan disable
" );
        }
        else
        {

            uint8_t own_type = LE_PUBLIC_ADDRESS;
            uint8_t scan_type = 0x00;
            uint8_t filter_policy = 0x00;
            uint16_t interval = htobs(0x0050);
            uint16_t window = htobs(0x0020);
            uint8_t filter_dup = 0x00;//过滤掉相同的 1

    
//            uint16_t interval = htobs(ble_scan_paramter_interval);
//            uint16_t window = htobs(ble_scan_paramter_windows);

            printf( "interval = %d, window = %d
", interval, window );

            err = hci_le_set_scan_parameters( s, scan_type, interval, window, own_type, filter_policy, 10000 );
            if (err < 0)
            {
                pt_error("Set scan parameters failed ->%s", strerror(errno) );
                return -1;
            }
            printf( "set scan prameter succ:%d %d
",interval, window );
            err = hci_le_set_scan_enable(s, 0x01, filter_dup, 10000);
            if (err < 0)
            {
                pt_error("Enable scan failed ->%s", strerror(errno) );
                return -1;
            }
struct hci_filter nf;
            socklen_t olen;

            olen = sizeof(m_orig_filter);
            if (getsockopt(s, SOL_HCI, HCI_FILTER, &m_orig_filter, &olen) < 0)
            {
                pt_error("Could not get socket options
");
                return -1;
            }

            hci_filter_clear(&nf);
            hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
            hci_filter_set_event(EVT_LE_META_EVENT, &nf);
            //try to get disconnect event!!!
            hci_filter_set_event(EVT_DISCONN_COMPLETE, &nf);
            if (setsockopt(s, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0)
            {
                pt_error("Could not set socket options
");
                setsockopt(s, SOL_HCI, HCI_FILTER, &m_orig_filter, sizeof(m_orig_filter));
                return -1;
            }
        }
        return 0;
    }
    return -1;
}

设置了LE_HOST_SUPPORT后,即可正常连接remote 设备。

注:

1. 在测试中发现,如果启动bluetoothd,不需要设置LE_HOST_SUPPORT,也可以连接成功。怀疑是Bluetoothd启动中,设置了LE_HOST_SUPPORT。 

2. LE_HOST_SUPPORT feature 开发板每次启动都需要设置。该feature应该是设置到了内核中。

3. 跟踪到了内核代码中,初步发现在mgmt.c中的set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)接口中有设置LE_HOST_SUPPORT