summaryrefslogtreecommitdiffstats
path: root/host/lib/device.cpp
blob: 431595c4fd3b06e4b9c7a836f70c4b0eee7c9905 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//
// Copyright 2010 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// asize_t with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include <uhd/device.hpp>
#include <uhd/types/dict.hpp>
#include <uhd/utils/assert.hpp>
#include <uhd/utils/static.hpp>
#include <uhd/utils/algorithm.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/functional/hash.hpp>
#include <boost/tuple/tuple.hpp>
#include <stdexcept>

using namespace uhd;

/***********************************************************************
 * Helper Functions
 **********************************************************************/
/*!
 * Make a device hash that maps 1 to 1 with a device address.
 * The hash will be used to identify created devices.
 * \param dev_addr the device address
 * \return the hash number
 */
static size_t hash_device_addr(
    const device_addr_t &dev_addr
){
    //combine the hashes of sorted keys/value pairs
    size_t hash = 0;
    BOOST_FOREACH(const std::string &key, std::sorted(dev_addr.keys())){
        boost::hash_combine(hash, key);
        boost::hash_combine(hash, dev_addr[key]);
    }
    return hash;
}

/***********************************************************************
 * Registration
 **********************************************************************/
typedef boost::tuple<device::find_t, device::make_t> dev_fcn_reg_t;

// instantiate the device function registry container
UHD_SINGLETON_FCN(std::vector<dev_fcn_reg_t>, get_dev_fcn_regs)

void device::register_device(
    const find_t &find,
    const make_t &make
){
    //std::cout << "registering device" << std::endl;
    get_dev_fcn_regs().push_back(dev_fcn_reg_t(find, make));
}

/***********************************************************************
 * Discover
 **********************************************************************/
device_addrs_t device::find(const device_addr_t &hint){
    device_addrs_t device_addrs;

    BOOST_FOREACH(const dev_fcn_reg_t &fcn, get_dev_fcn_regs()){
        device_addrs_t discovered_addrs = fcn.get<0>()(hint);
        device_addrs.insert(
            device_addrs.begin(),
            discovered_addrs.begin(),
            discovered_addrs.end()
        );
    }

    return device_addrs;
}

/***********************************************************************
 * Make
 **********************************************************************/
device::sptr device::make(const device_addr_t &hint, size_t which){
    typedef boost::tuple<device_addr_t, make_t> dev_addr_make_t;
    std::vector<dev_addr_make_t> dev_addr_makers;

    BOOST_FOREACH(const dev_fcn_reg_t &fcn, get_dev_fcn_regs()){
        BOOST_FOREACH(device_addr_t dev_addr, fcn.get<0>()(hint)){
            //append the discovered address and its factory function
            dev_addr_makers.push_back(dev_addr_make_t(dev_addr, fcn.get<1>()));
        }
    }

    //check that we found any devices
    if (dev_addr_makers.size() == 0){
        throw std::runtime_error(str(
            boost::format("No devices found for ----->\n%s") % hint.to_pp_string()
        ));
    }

    //check that the which index is valid
    if (dev_addr_makers.size() <= which){
        throw std::runtime_error(str(
            boost::format("No device at index %d for ----->\n%s") % which % hint.to_pp_string()
        ));
    }

    //create a unique hash for the device address
    device_addr_t dev_addr; make_t maker;
    boost::tie(dev_addr, maker) = dev_addr_makers.at(which);
    size_t dev_hash = hash_device_addr(dev_addr);
    //std::cout << boost::format("Hash: %u") % dev_hash << std::endl;

    //copy keys that were in hint but not in dev_addr
    //this way, we can pass additional transport arguments
    BOOST_FOREACH(const std::string &key, hint.keys()){
        if (not dev_addr.has_key(key)) dev_addr[key] = hint[key];
    }

    //map device address hash to created devices
    static uhd::dict<size_t, boost::weak_ptr<device> > hash_to_device;

    //try to find an existing device
    try{
        UHD_ASSERT_THROW(hash_to_device.has_key(dev_hash));
        UHD_ASSERT_THROW(not hash_to_device[dev_hash].expired());
        return hash_to_device[dev_hash].lock();
    }
    //create and register a new device
    catch(const uhd::assert_error &){
        device::sptr dev = maker(dev_addr);
        hash_to_device[dev_hash] = dev;
        return dev;
    }
}