mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-02 21:23:16 +00:00
Compare commits
142 Commits
fairwaves/
...
1.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
309ad4d901 | ||
|
|
3b8f7c4d97 | ||
|
|
48cad832ea | ||
|
|
56237bce95 | ||
|
|
5738940535 | ||
|
|
cec9eda227 | ||
|
|
ad202d72e1 | ||
|
|
0f4d5791df | ||
|
|
2a637a5c9c | ||
|
|
819cad1776 | ||
|
|
54a98b5b52 | ||
|
|
fca503d0b4 | ||
|
|
e8edd1fcae | ||
|
|
7e27deb8cb | ||
|
|
a9512d963a | ||
|
|
c6220741b1 | ||
|
|
4a4e607a19 | ||
|
|
1587307a99 | ||
|
|
7e83f18bba | ||
|
|
57db77f185 | ||
|
|
94def47fdf | ||
|
|
3fb4d31ecb | ||
|
|
744e44eaa1 | ||
|
|
faacb1987e | ||
|
|
c0e7ce922a | ||
|
|
ab6e7f35ab | ||
|
|
5d6504c45a | ||
|
|
c620ced36d | ||
|
|
ef79fd9b95 | ||
|
|
e0bdb6b47b | ||
|
|
948b4e4096 | ||
|
|
c7a750d428 | ||
|
|
0ff9c9fca3 | ||
|
|
24cb0c9948 | ||
|
|
6be2d15541 | ||
|
|
bc5263cee1 | ||
|
|
6b4acc12f7 | ||
|
|
d1ca287d83 | ||
|
|
a0d862ba1d | ||
|
|
c5989fe180 | ||
|
|
08970c562f | ||
|
|
e91544d740 | ||
|
|
93fee1f163 | ||
|
|
e69a56cec5 | ||
|
|
76795401fb | ||
|
|
30863e8720 | ||
|
|
0fbdfefebc | ||
|
|
b7c6f1e83f | ||
|
|
4d43684194 | ||
|
|
54bde5a8ba | ||
|
|
fd88564acb | ||
|
|
82c72218fd | ||
|
|
1f37e4dd74 | ||
|
|
7d8676a144 | ||
|
|
8808fa86f0 | ||
|
|
2a0fb962c7 | ||
|
|
03334967c9 | ||
|
|
e30e0ad9be | ||
|
|
7a52e42ee0 | ||
|
|
4080eb76f8 | ||
|
|
1e17c4fb0a | ||
|
|
f9a2f44272 | ||
|
|
3bf2c5de8d | ||
|
|
553a25033e | ||
|
|
1d165a043e | ||
|
|
199a306d27 | ||
|
|
c249ce2a58 | ||
|
|
761da1a08a | ||
|
|
9032c87d80 | ||
|
|
df675784a7 | ||
|
|
99330740dd | ||
|
|
b70686c13d | ||
|
|
1d0c6fe752 | ||
|
|
8b0c5368f5 | ||
|
|
c0d6fd27ff | ||
|
|
032c1d8da9 | ||
|
|
c62a05140c | ||
|
|
a71c5d073f | ||
|
|
92ba59dacf | ||
|
|
6a3a2b8647 | ||
|
|
fb96767ac5 | ||
|
|
394053e599 | ||
|
|
68d8db4d8c | ||
|
|
58d80a014e | ||
|
|
5bd3d4263b | ||
|
|
f68f19b110 | ||
|
|
8ac169f7ed | ||
|
|
405f17a98c | ||
|
|
b536ab9bdf | ||
|
|
174fb03b8e | ||
|
|
b899c19f1f | ||
|
|
992c9bd1ce | ||
|
|
056ce136e6 | ||
|
|
0e09e7c98a | ||
|
|
1b3a8881eb | ||
|
|
fdefbfac39 | ||
|
|
5e16f79f0f | ||
|
|
f3155e33b9 | ||
|
|
7bbe19ee9d | ||
|
|
f669bf43e7 | ||
|
|
4a575b02b5 | ||
|
|
cf35c37c94 | ||
|
|
6e369348b0 | ||
|
|
546516d79c | ||
|
|
6879bb0db9 | ||
|
|
a7143bf7a1 | ||
|
|
4ffdca10d4 | ||
|
|
1a26b4f085 | ||
|
|
c5d5586fbd | ||
|
|
cc971aa1a6 | ||
|
|
748d8edbf8 | ||
|
|
dfc6e5ffc7 | ||
|
|
f8c0c464b8 | ||
|
|
93707d0227 | ||
|
|
5291e8a654 | ||
|
|
7a07de1efd | ||
|
|
fd67262df8 | ||
|
|
0569845a08 | ||
|
|
c69b87f9bd | ||
|
|
a7bf6cd8a4 | ||
|
|
e7f6a27ab6 | ||
|
|
a979f5f32b | ||
|
|
b0e54265ad | ||
|
|
62c9280590 | ||
|
|
1421adbc61 | ||
|
|
dccc82491c | ||
|
|
bf58370675 | ||
|
|
7c84925ea4 | ||
|
|
fd99c6ce05 | ||
|
|
e947db8d98 | ||
|
|
9279e0e123 | ||
|
|
84231bd8b7 | ||
|
|
aebbfe0ee7 | ||
|
|
da7fee8ea8 | ||
|
|
9f2baf3e04 | ||
|
|
0a2a40f43c | ||
|
|
501d053b70 | ||
|
|
0fafe03199 | ||
|
|
9a87d90c1e | ||
|
|
066fd04f47 | ||
|
|
aeaba02e02 | ||
|
|
b7e992703c |
521
.clang-format
Normal file
521
.clang-format
Normal file
@@ -0,0 +1,521 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# clang-format configuration file. Intended for clang-format >= 4.
|
||||
#
|
||||
# For more information, see:
|
||||
#
|
||||
# Documentation/process/clang-format.rst
|
||||
# https://clang.llvm.org/docs/ClangFormat.html
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
#
|
||||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
#AfterExternBlock: false # Unknown to clang-format-5.0
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
#SplitEmptyFunction: true # Unknown to clang-format-4.0
|
||||
#SplitEmptyRecord: true # Unknown to clang-format-4.0
|
||||
#SplitEmptyNamespace: true # Unknown to clang-format-4.0
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 120
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
#CompactNamespaces: false # Unknown to clang-format-4.0
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
ContinuationIndentWidth: 8
|
||||
Cpp11BracedListStyle: false
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
#FixNamespaceComments: false # Unknown to clang-format-4.0
|
||||
|
||||
# Taken from:
|
||||
# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \
|
||||
# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
|
||||
# | sort | uniq
|
||||
ForEachMacros:
|
||||
- 'apei_estatus_for_each_section'
|
||||
- 'ata_for_each_dev'
|
||||
- 'ata_for_each_link'
|
||||
- '__ata_qc_for_each'
|
||||
- 'ata_qc_for_each'
|
||||
- 'ata_qc_for_each_raw'
|
||||
- 'ata_qc_for_each_with_internal'
|
||||
- 'ax25_for_each'
|
||||
- 'ax25_uid_for_each'
|
||||
- '__bio_for_each_bvec'
|
||||
- 'bio_for_each_bvec'
|
||||
- 'bio_for_each_integrity_vec'
|
||||
- '__bio_for_each_segment'
|
||||
- 'bio_for_each_segment'
|
||||
- 'bio_for_each_segment_all'
|
||||
- 'bio_list_for_each'
|
||||
- 'bip_for_each_vec'
|
||||
- 'bitmap_for_each_clear_region'
|
||||
- 'bitmap_for_each_set_region'
|
||||
- 'blkg_for_each_descendant_post'
|
||||
- 'blkg_for_each_descendant_pre'
|
||||
- 'blk_queue_for_each_rl'
|
||||
- 'bond_for_each_slave'
|
||||
- 'bond_for_each_slave_rcu'
|
||||
- 'bpf_for_each_spilled_reg'
|
||||
- 'btree_for_each_safe128'
|
||||
- 'btree_for_each_safe32'
|
||||
- 'btree_for_each_safe64'
|
||||
- 'btree_for_each_safel'
|
||||
- 'card_for_each_dev'
|
||||
- 'cgroup_taskset_for_each'
|
||||
- 'cgroup_taskset_for_each_leader'
|
||||
- 'cpufreq_for_each_entry'
|
||||
- 'cpufreq_for_each_entry_idx'
|
||||
- 'cpufreq_for_each_valid_entry'
|
||||
- 'cpufreq_for_each_valid_entry_idx'
|
||||
- 'css_for_each_child'
|
||||
- 'css_for_each_descendant_post'
|
||||
- 'css_for_each_descendant_pre'
|
||||
- 'device_for_each_child_node'
|
||||
- 'dma_fence_chain_for_each'
|
||||
- 'drm_atomic_crtc_for_each_plane'
|
||||
- 'drm_atomic_crtc_state_for_each_plane'
|
||||
- 'drm_atomic_crtc_state_for_each_plane_state'
|
||||
- 'drm_atomic_for_each_plane_damage'
|
||||
- 'drm_client_for_each_connector_iter'
|
||||
- 'drm_client_for_each_modeset'
|
||||
- 'drm_connector_for_each_possible_encoder'
|
||||
- 'drm_for_each_bridge_in_chain'
|
||||
- 'drm_for_each_connector_iter'
|
||||
- 'drm_for_each_crtc'
|
||||
- 'drm_for_each_encoder'
|
||||
- 'drm_for_each_encoder_mask'
|
||||
- 'drm_for_each_fb'
|
||||
- 'drm_for_each_legacy_plane'
|
||||
- 'drm_for_each_plane'
|
||||
- 'drm_for_each_plane_mask'
|
||||
- 'drm_for_each_privobj'
|
||||
- 'drm_mm_for_each_hole'
|
||||
- 'drm_mm_for_each_node'
|
||||
- 'drm_mm_for_each_node_in_range'
|
||||
- 'drm_mm_for_each_node_safe'
|
||||
- 'flow_action_for_each'
|
||||
- 'for_each_active_dev_scope'
|
||||
- 'for_each_active_drhd_unit'
|
||||
- 'for_each_active_iommu'
|
||||
- 'for_each_available_child_of_node'
|
||||
- 'for_each_bio'
|
||||
- 'for_each_board_func_rsrc'
|
||||
- 'for_each_bvec'
|
||||
- 'for_each_card_auxs'
|
||||
- 'for_each_card_auxs_safe'
|
||||
- 'for_each_card_components'
|
||||
- 'for_each_card_pre_auxs'
|
||||
- 'for_each_card_prelinks'
|
||||
- 'for_each_card_rtds'
|
||||
- 'for_each_card_rtds_safe'
|
||||
- 'for_each_cgroup_storage_type'
|
||||
- 'for_each_child_of_node'
|
||||
- 'for_each_clear_bit'
|
||||
- 'for_each_clear_bit_from'
|
||||
- 'for_each_cmsghdr'
|
||||
- 'for_each_compatible_node'
|
||||
- 'for_each_component_dais'
|
||||
- 'for_each_component_dais_safe'
|
||||
- 'for_each_comp_order'
|
||||
- 'for_each_console'
|
||||
- 'for_each_cpu'
|
||||
- 'for_each_cpu_and'
|
||||
- 'for_each_cpu_not'
|
||||
- 'for_each_cpu_wrap'
|
||||
- 'for_each_dev_addr'
|
||||
- 'for_each_dev_scope'
|
||||
- 'for_each_displayid_db'
|
||||
- 'for_each_dma_cap_mask'
|
||||
- 'for_each_dpcm_be'
|
||||
- 'for_each_dpcm_be_rollback'
|
||||
- 'for_each_dpcm_be_safe'
|
||||
- 'for_each_dpcm_fe'
|
||||
- 'for_each_drhd_unit'
|
||||
- 'for_each_dss_dev'
|
||||
- 'for_each_efi_handle'
|
||||
- 'for_each_efi_memory_desc'
|
||||
- 'for_each_efi_memory_desc_in_map'
|
||||
- 'for_each_element'
|
||||
- 'for_each_element_extid'
|
||||
- 'for_each_element_id'
|
||||
- 'for_each_endpoint_of_node'
|
||||
- 'for_each_evictable_lru'
|
||||
- 'for_each_fib6_node_rt_rcu'
|
||||
- 'for_each_fib6_walker_rt'
|
||||
- 'for_each_free_mem_pfn_range_in_zone'
|
||||
- 'for_each_free_mem_pfn_range_in_zone_from'
|
||||
- 'for_each_free_mem_range'
|
||||
- 'for_each_free_mem_range_reverse'
|
||||
- 'for_each_func_rsrc'
|
||||
- 'for_each_hstate'
|
||||
- 'for_each_if'
|
||||
- 'for_each_iommu'
|
||||
- 'for_each_ip_tunnel_rcu'
|
||||
- 'for_each_irq_nr'
|
||||
- 'for_each_link_codecs'
|
||||
- 'for_each_link_platforms'
|
||||
- 'for_each_lru'
|
||||
- 'for_each_matching_node'
|
||||
- 'for_each_matching_node_and_match'
|
||||
- 'for_each_member'
|
||||
- 'for_each_memblock'
|
||||
- 'for_each_memblock_type'
|
||||
- 'for_each_memcg_cache_index'
|
||||
- 'for_each_mem_pfn_range'
|
||||
- 'for_each_mem_range'
|
||||
- 'for_each_mem_range_rev'
|
||||
- 'for_each_migratetype_order'
|
||||
- 'for_each_msi_entry'
|
||||
- 'for_each_msi_entry_safe'
|
||||
- 'for_each_net'
|
||||
- 'for_each_net_continue_reverse'
|
||||
- 'for_each_netdev'
|
||||
- 'for_each_netdev_continue'
|
||||
- 'for_each_netdev_continue_rcu'
|
||||
- 'for_each_netdev_continue_reverse'
|
||||
- 'for_each_netdev_feature'
|
||||
- 'for_each_netdev_in_bond_rcu'
|
||||
- 'for_each_netdev_rcu'
|
||||
- 'for_each_netdev_reverse'
|
||||
- 'for_each_netdev_safe'
|
||||
- 'for_each_net_rcu'
|
||||
- 'for_each_new_connector_in_state'
|
||||
- 'for_each_new_crtc_in_state'
|
||||
- 'for_each_new_mst_mgr_in_state'
|
||||
- 'for_each_new_plane_in_state'
|
||||
- 'for_each_new_private_obj_in_state'
|
||||
- 'for_each_node'
|
||||
- 'for_each_node_by_name'
|
||||
- 'for_each_node_by_type'
|
||||
- 'for_each_node_mask'
|
||||
- 'for_each_node_state'
|
||||
- 'for_each_node_with_cpus'
|
||||
- 'for_each_node_with_property'
|
||||
- 'for_each_of_allnodes'
|
||||
- 'for_each_of_allnodes_from'
|
||||
- 'for_each_of_cpu_node'
|
||||
- 'for_each_of_pci_range'
|
||||
- 'for_each_old_connector_in_state'
|
||||
- 'for_each_old_crtc_in_state'
|
||||
- 'for_each_old_mst_mgr_in_state'
|
||||
- 'for_each_oldnew_connector_in_state'
|
||||
- 'for_each_oldnew_crtc_in_state'
|
||||
- 'for_each_oldnew_mst_mgr_in_state'
|
||||
- 'for_each_oldnew_plane_in_state'
|
||||
- 'for_each_oldnew_plane_in_state_reverse'
|
||||
- 'for_each_oldnew_private_obj_in_state'
|
||||
- 'for_each_old_plane_in_state'
|
||||
- 'for_each_old_private_obj_in_state'
|
||||
- 'for_each_online_cpu'
|
||||
- 'for_each_online_node'
|
||||
- 'for_each_online_pgdat'
|
||||
- 'for_each_pci_bridge'
|
||||
- 'for_each_pci_dev'
|
||||
- 'for_each_pci_msi_entry'
|
||||
- 'for_each_populated_zone'
|
||||
- 'for_each_possible_cpu'
|
||||
- 'for_each_present_cpu'
|
||||
- 'for_each_prime_number'
|
||||
- 'for_each_prime_number_from'
|
||||
- 'for_each_process'
|
||||
- 'for_each_process_thread'
|
||||
- 'for_each_property_of_node'
|
||||
- 'for_each_registered_fb'
|
||||
- 'for_each_reserved_mem_region'
|
||||
- 'for_each_rtd_codec_dai'
|
||||
- 'for_each_rtd_codec_dai_rollback'
|
||||
- 'for_each_rtd_components'
|
||||
- 'for_each_set_bit'
|
||||
- 'for_each_set_bit_from'
|
||||
- 'for_each_set_clump8'
|
||||
- 'for_each_sg'
|
||||
- 'for_each_sg_dma_page'
|
||||
- 'for_each_sg_page'
|
||||
- 'for_each_sibling_event'
|
||||
- 'for_each_subelement'
|
||||
- 'for_each_subelement_extid'
|
||||
- 'for_each_subelement_id'
|
||||
- '__for_each_thread'
|
||||
- 'for_each_thread'
|
||||
- 'for_each_wakeup_source'
|
||||
- 'for_each_zone'
|
||||
- 'for_each_zone_zonelist'
|
||||
- 'for_each_zone_zonelist_nodemask'
|
||||
- 'fwnode_for_each_available_child_node'
|
||||
- 'fwnode_for_each_child_node'
|
||||
- 'fwnode_graph_for_each_endpoint'
|
||||
- 'gadget_for_each_ep'
|
||||
- 'genradix_for_each'
|
||||
- 'genradix_for_each_from'
|
||||
- 'hash_for_each'
|
||||
- 'hash_for_each_possible'
|
||||
- 'hash_for_each_possible_rcu'
|
||||
- 'hash_for_each_possible_rcu_notrace'
|
||||
- 'hash_for_each_possible_safe'
|
||||
- 'hash_for_each_rcu'
|
||||
- 'hash_for_each_safe'
|
||||
- 'hctx_for_each_ctx'
|
||||
- 'hlist_bl_for_each_entry'
|
||||
- 'hlist_bl_for_each_entry_rcu'
|
||||
- 'hlist_bl_for_each_entry_safe'
|
||||
- 'hlist_for_each'
|
||||
- 'hlist_for_each_entry'
|
||||
- 'hlist_for_each_entry_continue'
|
||||
- 'hlist_for_each_entry_continue_rcu'
|
||||
- 'hlist_for_each_entry_continue_rcu_bh'
|
||||
- 'hlist_for_each_entry_from'
|
||||
- 'hlist_for_each_entry_from_rcu'
|
||||
- 'hlist_for_each_entry_rcu'
|
||||
- 'hlist_for_each_entry_rcu_bh'
|
||||
- 'hlist_for_each_entry_rcu_notrace'
|
||||
- 'hlist_for_each_entry_safe'
|
||||
- '__hlist_for_each_rcu'
|
||||
- 'hlist_for_each_safe'
|
||||
- 'hlist_nulls_for_each_entry'
|
||||
- 'hlist_nulls_for_each_entry_from'
|
||||
- 'hlist_nulls_for_each_entry_rcu'
|
||||
- 'hlist_nulls_for_each_entry_safe'
|
||||
- 'i3c_bus_for_each_i2cdev'
|
||||
- 'i3c_bus_for_each_i3cdev'
|
||||
- 'ide_host_for_each_port'
|
||||
- 'ide_port_for_each_dev'
|
||||
- 'ide_port_for_each_present_dev'
|
||||
- 'idr_for_each_entry'
|
||||
- 'idr_for_each_entry_continue'
|
||||
- 'idr_for_each_entry_continue_ul'
|
||||
- 'idr_for_each_entry_ul'
|
||||
- 'in_dev_for_each_ifa_rcu'
|
||||
- 'in_dev_for_each_ifa_rtnl'
|
||||
- 'inet_bind_bucket_for_each'
|
||||
- 'inet_lhash2_for_each_icsk_rcu'
|
||||
- 'key_for_each'
|
||||
- 'key_for_each_safe'
|
||||
- 'klp_for_each_func'
|
||||
- 'klp_for_each_func_safe'
|
||||
- 'klp_for_each_func_static'
|
||||
- 'klp_for_each_object'
|
||||
- 'klp_for_each_object_safe'
|
||||
- 'klp_for_each_object_static'
|
||||
- 'kvm_for_each_memslot'
|
||||
- 'kvm_for_each_vcpu'
|
||||
- 'list_for_each'
|
||||
- 'list_for_each_codec'
|
||||
- 'list_for_each_codec_safe'
|
||||
- 'list_for_each_continue'
|
||||
- 'list_for_each_entry'
|
||||
- 'list_for_each_entry_continue'
|
||||
- 'list_for_each_entry_continue_rcu'
|
||||
- 'list_for_each_entry_continue_reverse'
|
||||
- 'list_for_each_entry_from'
|
||||
- 'list_for_each_entry_from_rcu'
|
||||
- 'list_for_each_entry_from_reverse'
|
||||
- 'list_for_each_entry_lockless'
|
||||
- 'list_for_each_entry_rcu'
|
||||
- 'list_for_each_entry_reverse'
|
||||
- 'list_for_each_entry_safe'
|
||||
- 'list_for_each_entry_safe_continue'
|
||||
- 'list_for_each_entry_safe_from'
|
||||
- 'list_for_each_entry_safe_reverse'
|
||||
- 'list_for_each_prev'
|
||||
- 'list_for_each_prev_safe'
|
||||
- 'list_for_each_safe'
|
||||
- 'llist_for_each'
|
||||
- 'llist_for_each_entry'
|
||||
- 'llist_for_each_entry_safe'
|
||||
- 'llist_for_each_safe'
|
||||
- 'mci_for_each_dimm'
|
||||
- 'media_device_for_each_entity'
|
||||
- 'media_device_for_each_intf'
|
||||
- 'media_device_for_each_link'
|
||||
- 'media_device_for_each_pad'
|
||||
- 'nanddev_io_for_each_page'
|
||||
- 'netdev_for_each_lower_dev'
|
||||
- 'netdev_for_each_lower_private'
|
||||
- 'netdev_for_each_lower_private_rcu'
|
||||
- 'netdev_for_each_mc_addr'
|
||||
- 'netdev_for_each_uc_addr'
|
||||
- 'netdev_for_each_upper_dev_rcu'
|
||||
- 'netdev_hw_addr_list_for_each'
|
||||
- 'nft_rule_for_each_expr'
|
||||
- 'nla_for_each_attr'
|
||||
- 'nla_for_each_nested'
|
||||
- 'nlmsg_for_each_attr'
|
||||
- 'nlmsg_for_each_msg'
|
||||
- 'nr_neigh_for_each'
|
||||
- 'nr_neigh_for_each_safe'
|
||||
- 'nr_node_for_each'
|
||||
- 'nr_node_for_each_safe'
|
||||
- 'of_for_each_phandle'
|
||||
- 'of_property_for_each_string'
|
||||
- 'of_property_for_each_u32'
|
||||
- 'pci_bus_for_each_resource'
|
||||
- 'ping_portaddr_for_each_entry'
|
||||
- 'plist_for_each'
|
||||
- 'plist_for_each_continue'
|
||||
- 'plist_for_each_entry'
|
||||
- 'plist_for_each_entry_continue'
|
||||
- 'plist_for_each_entry_safe'
|
||||
- 'plist_for_each_safe'
|
||||
- 'pnp_for_each_card'
|
||||
- 'pnp_for_each_dev'
|
||||
- 'protocol_for_each_card'
|
||||
- 'protocol_for_each_dev'
|
||||
- 'queue_for_each_hw_ctx'
|
||||
- 'radix_tree_for_each_slot'
|
||||
- 'radix_tree_for_each_tagged'
|
||||
- 'rbtree_postorder_for_each_entry_safe'
|
||||
- 'rdma_for_each_block'
|
||||
- 'rdma_for_each_port'
|
||||
- 'resource_list_for_each_entry'
|
||||
- 'resource_list_for_each_entry_safe'
|
||||
- 'rhl_for_each_entry_rcu'
|
||||
- 'rhl_for_each_rcu'
|
||||
- 'rht_for_each'
|
||||
- 'rht_for_each_entry'
|
||||
- 'rht_for_each_entry_from'
|
||||
- 'rht_for_each_entry_rcu'
|
||||
- 'rht_for_each_entry_rcu_from'
|
||||
- 'rht_for_each_entry_safe'
|
||||
- 'rht_for_each_from'
|
||||
- 'rht_for_each_rcu'
|
||||
- 'rht_for_each_rcu_from'
|
||||
- '__rq_for_each_bio'
|
||||
- 'rq_for_each_bvec'
|
||||
- 'rq_for_each_segment'
|
||||
- 'scsi_for_each_prot_sg'
|
||||
- 'scsi_for_each_sg'
|
||||
- 'sctp_for_each_hentry'
|
||||
- 'sctp_skb_for_each'
|
||||
- 'shdma_for_each_chan'
|
||||
- '__shost_for_each_device'
|
||||
- 'shost_for_each_device'
|
||||
- 'sk_for_each'
|
||||
- 'sk_for_each_bound'
|
||||
- 'sk_for_each_entry_offset_rcu'
|
||||
- 'sk_for_each_from'
|
||||
- 'sk_for_each_rcu'
|
||||
- 'sk_for_each_safe'
|
||||
- 'sk_nulls_for_each'
|
||||
- 'sk_nulls_for_each_from'
|
||||
- 'sk_nulls_for_each_rcu'
|
||||
- 'snd_array_for_each'
|
||||
- 'snd_pcm_group_for_each_entry'
|
||||
- 'snd_soc_dapm_widget_for_each_path'
|
||||
- 'snd_soc_dapm_widget_for_each_path_safe'
|
||||
- 'snd_soc_dapm_widget_for_each_sink_path'
|
||||
- 'snd_soc_dapm_widget_for_each_source_path'
|
||||
- 'tb_property_for_each'
|
||||
- 'tcf_exts_for_each_action'
|
||||
- 'udp_portaddr_for_each_entry'
|
||||
- 'udp_portaddr_for_each_entry_rcu'
|
||||
- 'usb_hub_for_each_child'
|
||||
- 'v4l2_device_for_each_subdev'
|
||||
- 'v4l2_m2m_for_each_dst_buf'
|
||||
- 'v4l2_m2m_for_each_dst_buf_safe'
|
||||
- 'v4l2_m2m_for_each_src_buf'
|
||||
- 'v4l2_m2m_for_each_src_buf_safe'
|
||||
- 'virtio_device_for_each_vq'
|
||||
- 'xa_for_each'
|
||||
- 'xa_for_each_marked'
|
||||
- 'xa_for_each_range'
|
||||
- 'xa_for_each_start'
|
||||
- 'xas_for_each'
|
||||
- 'xas_for_each_conflict'
|
||||
- 'xas_for_each_marked'
|
||||
- 'xbc_array_for_each_value'
|
||||
- 'xbc_for_each_key_value'
|
||||
- 'xbc_node_for_each_array_value'
|
||||
- 'xbc_node_for_each_child'
|
||||
- 'xbc_node_for_each_key_value'
|
||||
- 'zorro_for_each_dev'
|
||||
|
||||
#IncludeBlocks: Preserve # Unknown to clang-format-5.0
|
||||
IncludeCategories:
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
#IndentPPDirectives: None # Unknown to clang-format-5.0
|
||||
IndentWidth: 8
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: Inner
|
||||
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
|
||||
ObjCBlockIndentWidth: 8
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
|
||||
# Taken from git's rules
|
||||
#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
|
||||
PenaltyBreakBeforeFirstCallParameter: 30
|
||||
PenaltyBreakComment: 10
|
||||
PenaltyBreakFirstLessLess: 0
|
||||
PenaltyBreakString: 10
|
||||
PenaltyExcessCharacter: 100
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
#SortUsingDeclarations: false # Unknown to clang-format-4.0
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
|
||||
#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
|
||||
SpaceBeforeParens: ControlStatements
|
||||
#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp03
|
||||
TabWidth: 8
|
||||
UseTab: Always
|
||||
...
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -5,6 +5,9 @@
|
||||
Transceiver52M/osmo-trx-uhd
|
||||
Transceiver52M/osmo-trx-usrp1
|
||||
Transceiver52M/osmo-trx-lms
|
||||
Transceiver52M/osmo-trx-ipc
|
||||
|
||||
.clang-format
|
||||
|
||||
# tests
|
||||
tests/CommonLibs/BitVectorTest
|
||||
@@ -19,6 +22,7 @@ tests/CommonLibs/VectorTest
|
||||
tests/CommonLibs/PRBSTest
|
||||
tests/Transceiver52M/convolve_test
|
||||
tests/Transceiver52M/LMSDeviceTest
|
||||
Transceiver52M/device/ipc/ipc-driver-test
|
||||
|
||||
# automake/autoconf
|
||||
*.in
|
||||
@@ -60,6 +64,14 @@ doc/manuals/*.pdf
|
||||
doc/manuals/*__*.png
|
||||
doc/manuals/*.check
|
||||
doc/manuals/generated/
|
||||
doc/manuals/vty/osmotrx-*-vty-reference.xml
|
||||
doc/manuals/vty/osmotrx-*-vty-reference.xml.inc.gen
|
||||
doc/manuals/vty/osmotrx-*-vty-reference.xml.inc.merged
|
||||
doc/manuals/osmomsc-usermanual.xml
|
||||
doc/manuals/common
|
||||
doc/manuals/build
|
||||
|
||||
contrib/osmo-trx.spec
|
||||
!contrib/osmo-trx.spec.in
|
||||
|
||||
utils/osmo-prbs-tool
|
||||
|
||||
2
COPYING
2
COPYING
@@ -666,7 +666,7 @@ For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
=========================================================================
|
||||
|
||||
This marks the end of the AGPLv3 text. The following text is appended to the
|
||||
same file for convience but constituting a distinct document, not part of the
|
||||
same file for convenience but constituting a distinct document, not part of the
|
||||
actual AGPL text and not part of an attempt to create a deriviative work based
|
||||
on the AGPLv3 text.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ using namespace std;
|
||||
|
||||
|
||||
/**
|
||||
Apply a Galois polymonial to a binary seqeunce.
|
||||
Apply a Galois polymonial to a binary sequence.
|
||||
@param val The input sequence.
|
||||
@param poly The polynomial.
|
||||
@param order The order of the polynomial.
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
// (pat) The elements in the queue are type T*, and
|
||||
// the Fifo class implements the underlying queue.
|
||||
// The default is class PointerFIFO, which does not place any restrictions on the type of T,
|
||||
// and is implemented by allocating auxilliary structures for the queue,
|
||||
// and is implemented by allocating auxiliary structures for the queue,
|
||||
// or SingleLinkedList, which implements the queue using an internal pointer in type T,
|
||||
// which must implement the functional interface of class SingleLinkListNode,
|
||||
// namely: functions T*next() and void setNext(T*).
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in
|
||||
* the main directory for licensing information for this specific distribuion.
|
||||
* the main directory for licensing information for this specific distribution.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
|
||||
@@ -48,9 +48,11 @@ Log::~Log()
|
||||
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
||||
const char *fmt = neednl ? "%s\n" : "%s";
|
||||
|
||||
log_mutex_lock_canceldisable(&old_state);
|
||||
/* print related function called inside a C++ destructor, use pthread_setcancelstate() APIs.
|
||||
See osmo-trx commit 86be40b4eb762d5c12e8e3f7388ca9f254e77b36 for more information */
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
|
||||
LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str());
|
||||
log_mutex_unlock_canceldisable(old_state);
|
||||
pthread_setcancelstate(old_state, NULL);
|
||||
}
|
||||
|
||||
ostringstream& Log::get()
|
||||
|
||||
@@ -58,6 +58,9 @@ extern "C" {
|
||||
#define LOGLV(category, level) \
|
||||
Log(category, level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
|
||||
|
||||
#define LOGSRC(category, level, file, line) \
|
||||
Log(category, level, file, line).get() << "[tid=" << pthread_self() << "] "
|
||||
|
||||
#define LOGCHAN(chan, category, level) \
|
||||
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "][chan=" << chan << "] "
|
||||
|
||||
|
||||
@@ -32,11 +32,7 @@
|
||||
#include "Timeval.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#ifndef gettid
|
||||
#include <sys/syscall.h>
|
||||
#define gettid() syscall(SYS_gettid)
|
||||
#endif
|
||||
|
||||
#include <osmocom/core/thread.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -114,7 +110,7 @@ void Signal::wait(Mutex& wMutex, unsigned timeout) const
|
||||
void set_selfthread_name(const char *name)
|
||||
{
|
||||
pthread_t selfid = pthread_self();
|
||||
pid_t tid = gettid();
|
||||
pid_t tid = osmo_gettid();
|
||||
if (pthread_setname_np(selfid, name) == 0) {
|
||||
LOG(INFO) << "Thread "<< selfid << " (task " << tid << ") set name: " << name;
|
||||
} else {
|
||||
|
||||
@@ -186,7 +186,7 @@ class Thread {
|
||||
}
|
||||
}
|
||||
|
||||
/** Send cancelation to thread */
|
||||
/** Send cancellation to thread */
|
||||
void cancel() { pthread_cancel(mThread); }
|
||||
};
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ class Timeval {
|
||||
uint32_t usec() const { return mTimespec.tv_nsec / 1000; }
|
||||
uint32_t nsec() const { return mTimespec.tv_nsec; }
|
||||
|
||||
/** Return differnce from other (other-self), in ms. */
|
||||
/** Return difference from other (other-self), in ms. */
|
||||
long delta(const Timeval& other) const;
|
||||
|
||||
/** Elapsed time in ms. */
|
||||
|
||||
@@ -36,7 +36,12 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// We cant use Logger.h in this file...
|
||||
#ifndef __OPTIMIZE__
|
||||
#define assert_no_opt(x) assert(x)
|
||||
#else
|
||||
#define assert_no_opt(x)
|
||||
#endif
|
||||
// We can't use Logger.h in this file...
|
||||
extern int gVectorDebug;
|
||||
#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
|
||||
|
||||
@@ -81,8 +86,8 @@ template <class T> class Vector {
|
||||
/** Return the size of the Vector. */
|
||||
size_t size() const
|
||||
{
|
||||
assert(mStart>=mData);
|
||||
assert(mEnd>=mStart);
|
||||
assert_no_opt(mStart>=mData);
|
||||
assert_no_opt(mEnd>=mStart);
|
||||
return mEnd - mStart;
|
||||
}
|
||||
|
||||
@@ -112,7 +117,7 @@ template <class T> class Vector {
|
||||
/** Reduce addressable size of the Vector, keeping content. */
|
||||
void shrink(size_t newSize)
|
||||
{
|
||||
assert(newSize <= mEnd - mStart);
|
||||
assert_no_opt(newSize <= mEnd - mStart);
|
||||
mEnd = mStart + newSize;
|
||||
}
|
||||
|
||||
@@ -199,7 +204,7 @@ template <class T> class Vector {
|
||||
{
|
||||
T* wStart = mStart + start;
|
||||
T* wEnd = wStart + span;
|
||||
assert(wEnd<=mEnd);
|
||||
assert_no_opt(wEnd<=mEnd);
|
||||
return Vector<T>(NULL,wStart,wEnd);
|
||||
}
|
||||
|
||||
@@ -208,7 +213,7 @@ template <class T> class Vector {
|
||||
{
|
||||
T* wStart = mStart + start;
|
||||
T* wEnd = wStart + span;
|
||||
assert(wEnd<=mEnd);
|
||||
assert_no_opt(wEnd<=mEnd);
|
||||
return Vector<T>(NULL,wStart,wEnd);
|
||||
}
|
||||
|
||||
@@ -228,11 +233,11 @@ template <class T> class Vector {
|
||||
unsigned int i;
|
||||
T* dst = other.mStart + start;
|
||||
T* src = mStart;
|
||||
assert(dst+span<=other.mEnd);
|
||||
assert(mStart+span<=mEnd);
|
||||
assert_no_opt(dst+span<=other.mEnd);
|
||||
assert_no_opt(mStart+span<=mEnd);
|
||||
for (i = 0; i < span; i++, src++, dst++)
|
||||
*dst = *src;
|
||||
/*TODO if not non-trivially copyable type class, optimize:
|
||||
/*TODO if not non-trivially copiable type class, optimize:
|
||||
memcpy(dst,mStart,span*sizeof(T)); */
|
||||
}
|
||||
|
||||
@@ -250,8 +255,8 @@ template <class T> class Vector {
|
||||
void segmentCopyTo(Vector<T>& other, size_t start, size_t span) const
|
||||
{
|
||||
const T* base = mStart + start;
|
||||
assert(base+span<=mEnd);
|
||||
assert(other.mStart+span<=other.mEnd);
|
||||
assert_no_opt(base+span<=mEnd);
|
||||
assert_no_opt(other.mStart+span<=other.mEnd);
|
||||
memcpy(other.mStart,base,span*sizeof(T));
|
||||
}
|
||||
|
||||
@@ -265,8 +270,8 @@ template <class T> class Vector {
|
||||
{
|
||||
const T* baseFrom = mStart + from;
|
||||
T* baseTo = mStart + to;
|
||||
assert(baseFrom+span<=mEnd);
|
||||
assert(baseTo+span<=mEnd);
|
||||
assert_no_opt(baseFrom+span<=mEnd);
|
||||
assert_no_opt(baseTo+span<=mEnd);
|
||||
memmove(baseTo,baseFrom,span*sizeof(T));
|
||||
}
|
||||
|
||||
@@ -280,7 +285,7 @@ template <class T> class Vector {
|
||||
{
|
||||
T* dp=mStart+start;
|
||||
T* end=dp+length;
|
||||
assert(end<=mEnd);
|
||||
assert_no_opt(end<=mEnd);
|
||||
while (dp<end) *dp++=val;
|
||||
}
|
||||
|
||||
@@ -292,13 +297,13 @@ template <class T> class Vector {
|
||||
|
||||
T& operator[](size_t index)
|
||||
{
|
||||
assert(mStart+index<mEnd);
|
||||
assert_no_opt(mStart+index<mEnd);
|
||||
return mStart[index];
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const
|
||||
{
|
||||
assert(mStart+index<mEnd);
|
||||
assert_no_opt(mStart+index<mEnd);
|
||||
return mStart[index];
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
* osmo-trx (CXX, dir Transceiver52)
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
enum FillerType {
|
||||
FILLER_DUMMY,
|
||||
FILLER_ZERO,
|
||||
@@ -18,3 +20,40 @@ enum ReferenceType {
|
||||
REF_EXTERNAL,
|
||||
REF_GPS,
|
||||
};
|
||||
|
||||
/* Maximum number of physical RF channels */
|
||||
#define TRX_CHAN_MAX 8
|
||||
|
||||
struct trx_ctx;
|
||||
|
||||
struct trx_chan {
|
||||
struct trx_ctx *trx; /* backpointer */
|
||||
unsigned int idx; /* channel index */
|
||||
char *rx_path;
|
||||
char *tx_path;
|
||||
};
|
||||
|
||||
struct trx_cfg {
|
||||
char *bind_addr;
|
||||
char *remote_addr;
|
||||
char *dev_args;
|
||||
unsigned int base_port;
|
||||
unsigned int tx_sps;
|
||||
unsigned int rx_sps;
|
||||
unsigned int rtsc;
|
||||
unsigned int rach_delay;
|
||||
enum ReferenceType clock_ref;
|
||||
enum FillerType filler;
|
||||
bool multi_arfcn;
|
||||
double offset;
|
||||
double freq_offset_khz;
|
||||
double rssi_offset;
|
||||
bool force_rssi_offset; /* Force value set in VTY? */
|
||||
bool swap_channels;
|
||||
bool ext_rach;
|
||||
bool egprs;
|
||||
unsigned int sched_rr;
|
||||
unsigned int stack_size;
|
||||
unsigned int num_chans;
|
||||
struct trx_chan chans[TRX_CHAN_MAX];
|
||||
};
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include "debug.h"
|
||||
@@ -35,21 +33,45 @@ static const struct log_info_cat default_categories[] = {
|
||||
.color = NULL,
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DTRXCLK] = {
|
||||
.name = "DTRXCLK",
|
||||
.description = "TRX Master Clock",
|
||||
.color = NULL,
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DTRXCTRL] = {
|
||||
.name = "DTRXCTRL",
|
||||
.description = "TRX CTRL interface",
|
||||
.color = "\033[1;33m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DTRXDDL] = {
|
||||
.name = "DTRXDDL",
|
||||
.description = "TRX Data interface Downlink",
|
||||
.color = NULL,
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DTRXDUL] = {
|
||||
.name = "DTRXDUL",
|
||||
.description = "TRX CTRL interface Uplink",
|
||||
.color = NULL,
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DDEV] = {
|
||||
.name = "DDEV",
|
||||
.description = "Device/Driver specific code",
|
||||
.color = NULL,
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DLMS] = {
|
||||
.name = "DLMS",
|
||||
.description = "Logging from within LimeSuite itself",
|
||||
[DDEVDRV] = {
|
||||
.name = "DDEVDRV",
|
||||
.description = "Logging from external device driver library implementing lower level specifics",
|
||||
.color = NULL,
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DCTR] = {
|
||||
.name = "DCTR",
|
||||
.description = "Rate counter related logging",
|
||||
.color = NULL,
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
@@ -59,49 +81,3 @@ const struct log_info log_info = {
|
||||
.cat = default_categories,
|
||||
.num_cat = ARRAY_SIZE(default_categories),
|
||||
};
|
||||
|
||||
pthread_mutex_t log_mutex;
|
||||
|
||||
bool log_mutex_init() {
|
||||
int rc;
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
if ((rc = pthread_mutexattr_init(&attr))) {
|
||||
fprintf(stderr, "pthread_mutexattr_init() failed: %d\n", rc);
|
||||
return false;
|
||||
}
|
||||
if ((rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE))) {
|
||||
fprintf(stderr, "pthread_mutexattr_settype() failed: %d\n", rc);
|
||||
return false;
|
||||
}
|
||||
if ((rc = pthread_mutex_init(&log_mutex, &attr))) {
|
||||
fprintf(stderr, "pthread_mutex_init() failed: %d\n", rc);
|
||||
return false;
|
||||
}
|
||||
if ((rc = pthread_mutexattr_destroy(&attr))) {
|
||||
fprintf(stderr, "pthread_mutexattr_destroy() failed: %d\n", rc);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
/* FIXME: do we need to call pthread_mutex_destroy() during process exit? */
|
||||
}
|
||||
|
||||
/* If called inside a C++ destructor, use log_mutex_(un)lock_canceldisable() APIs instead.
|
||||
See osmo-trx commit 86be40b4eb762d5c12e8e3f7388ca9f254e77b36 for more information */
|
||||
void log_mutex_lock() {
|
||||
OSMO_ASSERT(!pthread_mutex_lock(&log_mutex));
|
||||
}
|
||||
|
||||
void log_mutex_unlock() {
|
||||
OSMO_ASSERT(!pthread_mutex_unlock(&log_mutex));
|
||||
}
|
||||
|
||||
void log_mutex_lock_canceldisable(int *st) {
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, st);
|
||||
log_mutex_lock();
|
||||
}
|
||||
|
||||
void log_mutex_unlock_canceldisable(int st) {
|
||||
log_mutex_unlock();
|
||||
pthread_setcancelstate(st, NULL);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/thread.h>
|
||||
|
||||
extern const struct log_info log_info;
|
||||
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
DMAIN,
|
||||
DTRXCLK,
|
||||
DTRXCTRL,
|
||||
DTRXDDL,
|
||||
DTRXDUL,
|
||||
DDEV,
|
||||
DLMS,
|
||||
DDEVDRV,
|
||||
DCTR,
|
||||
};
|
||||
|
||||
|
||||
bool log_mutex_init();
|
||||
void log_mutex_lock();
|
||||
void log_mutex_unlock();
|
||||
void log_mutex_lock_canceldisable(int *st);
|
||||
void log_mutex_unlock_canceldisable(int st);
|
||||
|
||||
#define CLOGC(category, level, fmt, args...) do { \
|
||||
log_mutex_lock(); \
|
||||
LOGP(category, level, "[tid=%lu] " fmt, pthread_self(), ##args); \
|
||||
log_mutex_unlock(); \
|
||||
LOGP(category, level, "[tid=%ld] " fmt, (long int) osmo_gettid(), ##args); \
|
||||
} while(0)
|
||||
|
||||
#define CLOGCHAN(chan, category, level, fmt, args...) do { \
|
||||
log_mutex_lock(); \
|
||||
LOGP(category, level, "[tid=%lu][chan=%lu] " fmt, pthread_self(), chan, ##args); \
|
||||
log_mutex_unlock(); \
|
||||
LOGP(category, level, "[tid=%ld][chan=%zu] " fmt, (long int) osmo_gettid(), chan, ##args); \
|
||||
} while(0)
|
||||
|
||||
@@ -43,6 +43,7 @@ enum SS_DEVICE {
|
||||
(struct device_counters). Must be sent with PTHREAD_CANCEL_DISABLE
|
||||
to avoid deadlocks in case osmo-trx process is asked to exit. */
|
||||
S_DEVICE_COUNTER_CHANGE,
|
||||
S_TRX_COUNTER_CHANGE, /* same, but for Transceiver class */
|
||||
};
|
||||
|
||||
/* signal cb for signal <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> */
|
||||
@@ -55,3 +56,16 @@ struct device_counters {
|
||||
unsigned int tx_dropped_events;
|
||||
unsigned int tx_dropped_samples;
|
||||
};
|
||||
|
||||
/* signal cb for signal <SS_DEVICE,S_TRX_COUNTER_CHANGE> */
|
||||
struct trx_counters {
|
||||
size_t chan;
|
||||
unsigned int tx_stale_bursts;
|
||||
unsigned int tx_unavailable_bursts;
|
||||
unsigned int tx_trxd_fn_repeated;
|
||||
unsigned int tx_trxd_fn_outoforder;
|
||||
unsigned int tx_trxd_fn_skipped;
|
||||
unsigned int rx_empty_burst;
|
||||
unsigned int rx_clipping;
|
||||
unsigned int rx_no_burst_detected;
|
||||
};
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
* That signal is processed here in device_sig_cb, where a copy of the "struct
|
||||
* device_counters" structure is held and the main thread is instructed through
|
||||
* a timerfd to update rate_ctr APIs against this copy. All this is done inside
|
||||
* a mutex to avoid different race conditons (between Rx andTx threads, and
|
||||
* a mutex to avoid different race conditions (between Rx andTx threads, and
|
||||
* between Rx/Tx and main thread). For the same reason, callers of signal
|
||||
* <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> (device_sig_cb), that is Rx/Tx threads,
|
||||
* must do so with PTHREAD_CANCEL_DISABLE, in order to avoid possible deadlocks
|
||||
@@ -67,21 +67,24 @@ extern "C" {
|
||||
#include "Threads.h"
|
||||
#include "Logger.h"
|
||||
|
||||
/* Used in ctrs_pending, when set it means that channel slot contains unused
|
||||
/* Used in dev_ctrs_pending, when set it means that channel slot contains unused
|
||||
(non-pending) counter data */
|
||||
#define PENDING_CHAN_NONE SIZE_MAX
|
||||
|
||||
static void *trx_rate_ctr_ctx;
|
||||
|
||||
static struct rate_ctr_group** rate_ctrs;
|
||||
static struct device_counters* ctrs_pending;
|
||||
static struct device_counters* dev_ctrs_pending;
|
||||
static struct trx_counters* trx_ctrs_pending;
|
||||
static size_t chan_len;
|
||||
static struct osmo_fd rate_ctr_timerfd;
|
||||
static Mutex rate_ctr_mutex;
|
||||
static struct osmo_fd dev_rate_ctr_timerfd;
|
||||
static struct osmo_fd trx_rate_ctr_timerfd;
|
||||
static Mutex dev_rate_ctr_mutex;
|
||||
static Mutex trx_rate_ctr_mutex;
|
||||
|
||||
struct osmo_timer_list threshold_timer;
|
||||
static LLIST_HEAD(threshold_list);
|
||||
static int threshold_timer_sched_secs;
|
||||
static unsigned int threshold_timer_sched_secs;
|
||||
static bool threshold_initied;
|
||||
|
||||
const struct value_string rate_ctr_intv[] = {
|
||||
@@ -93,22 +96,38 @@ const struct value_string rate_ctr_intv[] = {
|
||||
};
|
||||
|
||||
const struct value_string trx_chan_ctr_names[] = {
|
||||
{ TRX_CTR_RX_OVERRUNS, "rx_overruns" },
|
||||
{ TRX_CTR_TX_UNDERRUNS, "tx_underruns" },
|
||||
{ TRX_CTR_RX_DROP_EV, "rx_drop_events" },
|
||||
{ TRX_CTR_RX_DROP_SMPL, "rx_drop_samples" },
|
||||
{ TRX_CTR_TX_DROP_EV, "tx_drop_events" },
|
||||
{ TRX_CTR_TX_DROP_SMPL, "tx_drop_samples" },
|
||||
{ TRX_CTR_DEV_RX_OVERRUNS, "rx_overruns" },
|
||||
{ TRX_CTR_DEV_TX_UNDERRUNS, "tx_underruns" },
|
||||
{ TRX_CTR_DEV_RX_DROP_EV, "rx_drop_events" },
|
||||
{ TRX_CTR_DEV_RX_DROP_SMPL, "rx_drop_samples" },
|
||||
{ TRX_CTR_DEV_TX_DROP_EV, "tx_drop_events" },
|
||||
{ TRX_CTR_DEV_TX_DROP_SMPL, "tx_drop_samples" },
|
||||
{ TRX_CTR_TRX_TX_STALE_BURSTS, "tx_stale_bursts" },
|
||||
{ TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS, "tx_unavailable_bursts" },
|
||||
{ TRX_CTR_TRX_TRXD_FN_REPEATED, "tx_trxd_fn_repeated" },
|
||||
{ TRX_CTR_TRX_TRXD_FN_OUTOFORDER, "tx_trxd_fn_outoforder" },
|
||||
{ TRX_CTR_TRX_TRXD_FN_SKIPPED, "tx_trxd_fn_skipped" },
|
||||
{ TRX_CTR_TRX_RX_EMPTY_BURST, "rx_empty_burst" },
|
||||
{ TRX_CTR_TRX_RX_CLIPPING, "rx_clipping" },
|
||||
{ TRX_CTR_TRX_RX_NO_BURST_DETECTED, "rx_no_burst_detected" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc trx_chan_ctr_desc[] = {
|
||||
[TRX_CTR_RX_OVERRUNS] = { "device:rx_overruns", "Number of Rx overruns in FIFO queue" },
|
||||
[TRX_CTR_TX_UNDERRUNS] = { "device:tx_underruns", "Number of Tx underruns in FIFO queue" },
|
||||
[TRX_CTR_RX_DROP_EV] = { "device:rx_drop_events", "Number of times Rx samples were dropped by HW" },
|
||||
[TRX_CTR_RX_DROP_SMPL] = { "device:rx_drop_samples", "Number of Rx samples dropped by HW" },
|
||||
[TRX_CTR_TX_DROP_EV] = { "device:tx_drop_events", "Number of times Tx samples were dropped by HW" },
|
||||
[TRX_CTR_TX_DROP_SMPL] = { "device:tx_drop_samples", "Number of Tx samples dropped by HW" }
|
||||
[TRX_CTR_DEV_RX_OVERRUNS] = { "device:rx_overruns", "Number of Rx overruns in FIFO queue" },
|
||||
[TRX_CTR_DEV_TX_UNDERRUNS] = { "device:tx_underruns", "Number of Tx underruns in FIFO queue" },
|
||||
[TRX_CTR_DEV_RX_DROP_EV] = { "device:rx_drop_events", "Number of times Rx samples were dropped by HW" },
|
||||
[TRX_CTR_DEV_RX_DROP_SMPL] = { "device:rx_drop_samples", "Number of Rx samples dropped by HW" },
|
||||
[TRX_CTR_DEV_TX_DROP_EV] = { "device:tx_drop_events", "Number of times Tx samples were dropped by HW" },
|
||||
[TRX_CTR_DEV_TX_DROP_SMPL] = { "device:tx_drop_samples", "Number of Tx samples dropped by HW" },
|
||||
[TRX_CTR_TRX_TX_STALE_BURSTS] = { "trx:tx_stale_bursts", "Number of Tx burts dropped by TRX due to arriving too late" },
|
||||
[TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS] = { "trx:tx_unavailable_bursts","Number of Tx burts unavailable (not enqueued) at the time they should be transmitted" },
|
||||
[TRX_CTR_TRX_TRXD_FN_REPEATED] = { "trx:tx_trxd_fn_repeated", "Number of Tx burts received from TRXD with repeated FN" },
|
||||
[TRX_CTR_TRX_TRXD_FN_OUTOFORDER] = { "trx:tx_trxd_fn_outoforder","Number of Tx burts received from TRXD with a past FN" },
|
||||
[TRX_CTR_TRX_TRXD_FN_SKIPPED] = { "trx:tx_trxd_fn_skipped", "Number of Tx burts potentially skipped due to FN jumps" },
|
||||
[TRX_CTR_TRX_RX_EMPTY_BURST] = { "trx:rx_empty_burst", "Number of Rx bursts empty" },
|
||||
[TRX_CTR_TRX_RX_CLIPPING] = { "trx:rx_clipping", "Number of Rx bursts discarded due to clipping" },
|
||||
[TRX_CTR_TRX_RX_NO_BURST_DETECTED] = { "trx:rx_no_burst_detected", "Number of Rx burts discarded due to burst detection error" },
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc trx_chan_ctr_group_desc = {
|
||||
@@ -119,34 +138,68 @@ static const struct rate_ctr_group_desc trx_chan_ctr_group_desc = {
|
||||
.ctr_desc = trx_chan_ctr_desc,
|
||||
};
|
||||
|
||||
static int rate_ctr_timerfd_cb(struct osmo_fd *ofd, unsigned int what) {
|
||||
static int dev_rate_ctr_timerfd_cb(struct osmo_fd *ofd, unsigned int what) {
|
||||
size_t chan;
|
||||
struct rate_ctr *ctr;
|
||||
LOGC(DMAIN, NOTICE) << "Main thread is updating counters";
|
||||
rate_ctr_mutex.lock();
|
||||
LOGC(DCTR, INFO) << "Main thread is updating Device counters";
|
||||
dev_rate_ctr_mutex.lock();
|
||||
for (chan = 0; chan < chan_len; chan++) {
|
||||
if (ctrs_pending[chan].chan == PENDING_CHAN_NONE)
|
||||
if (dev_ctrs_pending[chan].chan == PENDING_CHAN_NONE)
|
||||
continue;
|
||||
LOGCHAN(chan, DMAIN, INFO) << "rate_ctr update";
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_RX_OVERRUNS];
|
||||
rate_ctr_add(ctr, ctrs_pending[chan].rx_overruns - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TX_UNDERRUNS];
|
||||
rate_ctr_add(ctr, ctrs_pending[chan].tx_underruns - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_RX_DROP_EV];
|
||||
rate_ctr_add(ctr, ctrs_pending[chan].rx_dropped_events - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_RX_DROP_SMPL];
|
||||
rate_ctr_add(ctr, ctrs_pending[chan].rx_dropped_samples - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TX_DROP_EV];
|
||||
rate_ctr_add(ctr, ctrs_pending[chan].tx_dropped_events - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TX_DROP_SMPL];
|
||||
rate_ctr_add(ctr, ctrs_pending[chan].tx_dropped_samples - ctr->current);
|
||||
LOGCHAN(chan, DCTR, DEBUG) << "rate_ctr update";
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_RX_OVERRUNS];
|
||||
rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_overruns - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_TX_UNDERRUNS];
|
||||
rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_underruns - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_RX_DROP_EV];
|
||||
rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_dropped_events - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_RX_DROP_SMPL];
|
||||
rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_dropped_samples - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_TX_DROP_EV];
|
||||
rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_dropped_events - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_TX_DROP_SMPL];
|
||||
rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_dropped_samples - ctr->current);
|
||||
|
||||
/* Mark as done */
|
||||
ctrs_pending[chan].chan = PENDING_CHAN_NONE;
|
||||
dev_ctrs_pending[chan].chan = PENDING_CHAN_NONE;
|
||||
}
|
||||
if (osmo_timerfd_disable(&rate_ctr_timerfd) < 0)
|
||||
LOGC(DMAIN, ERROR) << "Failed to disable timerfd";
|
||||
rate_ctr_mutex.unlock();
|
||||
if (osmo_timerfd_disable(&dev_rate_ctr_timerfd) < 0)
|
||||
LOGC(DCTR, ERROR) << "Failed to disable timerfd";
|
||||
dev_rate_ctr_mutex.unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trx_rate_ctr_timerfd_cb(struct osmo_fd *ofd, unsigned int what) {
|
||||
size_t chan;
|
||||
struct rate_ctr *ctr;
|
||||
LOGC(DCTR, INFO) << "Main thread is updating Transceiver counters";
|
||||
trx_rate_ctr_mutex.lock();
|
||||
for (chan = 0; chan < chan_len; chan++) {
|
||||
if (trx_ctrs_pending[chan].chan == PENDING_CHAN_NONE)
|
||||
continue;
|
||||
LOGCHAN(chan, DCTR, DEBUG) << "rate_ctr update";
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TX_STALE_BURSTS];
|
||||
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_stale_bursts - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS];
|
||||
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_unavailable_bursts - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TRXD_FN_REPEATED];
|
||||
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_repeated - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TRXD_FN_OUTOFORDER];
|
||||
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_outoforder - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TRXD_FN_SKIPPED];
|
||||
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_skipped - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_RX_EMPTY_BURST];
|
||||
rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_empty_burst - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_RX_CLIPPING];
|
||||
rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_clipping - ctr->current);
|
||||
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_RX_NO_BURST_DETECTED];
|
||||
rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_no_burst_detected - ctr->current);
|
||||
/* Mark as done */
|
||||
trx_ctrs_pending[chan].chan = PENDING_CHAN_NONE;
|
||||
}
|
||||
if (osmo_timerfd_disable(&trx_rate_ctr_timerfd) < 0)
|
||||
LOGC(DCTR, ERROR) << "Failed to disable timerfd";
|
||||
trx_rate_ctr_mutex.unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -154,7 +207,8 @@ static int rate_ctr_timerfd_cb(struct osmo_fd *ofd, unsigned int what) {
|
||||
static int device_sig_cb(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data)
|
||||
{
|
||||
struct device_counters *ctr;
|
||||
struct device_counters *dev_ctr;
|
||||
struct trx_counters *trx_ctr;
|
||||
/* Delay sched around 20 ms, in case we receive several calls from several
|
||||
* channels batched */
|
||||
struct timespec next_sched = {.tv_sec = 0, .tv_nsec = 20*1000*1000};
|
||||
@@ -163,14 +217,24 @@ static int device_sig_cb(unsigned int subsys, unsigned int signal,
|
||||
|
||||
switch (signal) {
|
||||
case S_DEVICE_COUNTER_CHANGE:
|
||||
ctr = (struct device_counters *)signal_data;
|
||||
LOGCHAN(ctr->chan, DMAIN, NOTICE) << "Received counter change from radioDevice";
|
||||
rate_ctr_mutex.lock();
|
||||
ctrs_pending[ctr->chan] = *ctr;
|
||||
if (osmo_timerfd_schedule(&rate_ctr_timerfd, &next_sched, &intv_sched) < 0) {
|
||||
LOGC(DMAIN, ERROR) << "Failed to schedule timerfd: " << errno << " = "<< strerror(errno);
|
||||
dev_ctr = (struct device_counters *)signal_data;
|
||||
LOGCHAN(dev_ctr->chan, DCTR, INFO) << "Received counter change from radioDevice";
|
||||
dev_rate_ctr_mutex.lock();
|
||||
dev_ctrs_pending[dev_ctr->chan] = *dev_ctr;
|
||||
if (osmo_timerfd_schedule(&dev_rate_ctr_timerfd, &next_sched, &intv_sched) < 0) {
|
||||
LOGC(DCTR, ERROR) << "Failed to schedule timerfd: " << errno << " = "<< strerror(errno);
|
||||
}
|
||||
rate_ctr_mutex.unlock();
|
||||
dev_rate_ctr_mutex.unlock();
|
||||
break;
|
||||
case S_TRX_COUNTER_CHANGE:
|
||||
trx_ctr = (struct trx_counters *)signal_data;
|
||||
LOGCHAN(trx_ctr->chan, DCTR, INFO) << "Received counter change from Transceiver";
|
||||
trx_rate_ctr_mutex.lock();
|
||||
trx_ctrs_pending[trx_ctr->chan] = *trx_ctr;
|
||||
if (osmo_timerfd_schedule(&trx_rate_ctr_timerfd, &next_sched, &intv_sched) < 0) {
|
||||
LOGC(DCTR, ERROR) << "Failed to schedule timerfd: " << errno << " = "<< strerror(errno);
|
||||
}
|
||||
trx_rate_ctr_mutex.unlock();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -195,15 +259,15 @@ static void threshold_timer_cb(void *data)
|
||||
struct ctr_threshold *ctr_thr;
|
||||
struct rate_ctr *rate_ctr;
|
||||
size_t chan;
|
||||
LOGC(DMAIN, DEBUG) << "threshold_timer_cb fired!";
|
||||
LOGC(DCTR, DEBUG) << "threshold_timer_cb fired!";
|
||||
|
||||
llist_for_each_entry(ctr_thr, &threshold_list, list) {
|
||||
for (chan = 0; chan < chan_len; chan++) {
|
||||
rate_ctr = &rate_ctrs[chan]->ctr[ctr_thr->ctr_id];
|
||||
LOGCHAN(chan, DMAIN, INFO) << "checking threshold: " << ctr_threshold_2_vty_str(ctr_thr)
|
||||
LOGCHAN(chan, DCTR, INFO) << "checking threshold: " << ctr_threshold_2_vty_str(ctr_thr)
|
||||
<< " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")";
|
||||
if (rate_ctr->intv[ctr_thr->intv].rate >= ctr_thr->val) {
|
||||
LOGCHAN(chan, DMAIN, FATAL) << "threshold reached, stopping! " << ctr_threshold_2_vty_str(ctr_thr)
|
||||
LOGCHAN(chan, DCTR, FATAL) << "threshold reached, stopping! " << ctr_threshold_2_vty_str(ctr_thr)
|
||||
<< " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")";
|
||||
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
|
||||
return;
|
||||
@@ -255,13 +319,13 @@ static void threshold_timer_update_intv() {
|
||||
|
||||
llist_for_each_entry(ctr, &threshold_list, list) {
|
||||
secs = ctr_threshold_2_seconds(ctr);
|
||||
if( min_secs > secs)
|
||||
if (min_secs > secs)
|
||||
min_secs = secs;
|
||||
}
|
||||
|
||||
|
||||
threshold_timer_sched_secs = OSMO_MAX(min_secs / 2 - 1, 1);
|
||||
LOGC(DMAIN, INFO) << "New ctr-error-threshold check interval: "
|
||||
threshold_timer_sched_secs = OSMO_MAX((int)(min_secs / 2 - 1), 1);
|
||||
LOGC(DCTR, INFO) << "New ctr-error-threshold check interval: "
|
||||
<< threshold_timer_sched_secs << " seconds";
|
||||
osmo_timer_schedule(&threshold_timer, threshold_timer_sched_secs, 0);
|
||||
}
|
||||
@@ -272,20 +336,27 @@ void trx_rate_ctr_init(void *ctx, struct trx_ctx* trx_ctx)
|
||||
size_t i;
|
||||
trx_rate_ctr_ctx = ctx;
|
||||
chan_len = trx_ctx->cfg.num_chans;
|
||||
ctrs_pending = (struct device_counters*) talloc_zero_size(ctx, chan_len * sizeof(struct device_counters));
|
||||
dev_ctrs_pending = (struct device_counters*) talloc_zero_size(ctx, chan_len * sizeof(struct device_counters));
|
||||
trx_ctrs_pending = (struct trx_counters*) talloc_zero_size(ctx, chan_len * sizeof(struct trx_counters));
|
||||
rate_ctrs = (struct rate_ctr_group**) talloc_zero_size(ctx, chan_len * sizeof(struct rate_ctr_group*));
|
||||
|
||||
for (i = 0; i < chan_len; i++) {
|
||||
ctrs_pending[i].chan = PENDING_CHAN_NONE;
|
||||
dev_ctrs_pending[i].chan = PENDING_CHAN_NONE;
|
||||
trx_ctrs_pending[i].chan = PENDING_CHAN_NONE;
|
||||
rate_ctrs[i] = rate_ctr_group_alloc(ctx, &trx_chan_ctr_group_desc, i);
|
||||
if (!rate_ctrs[i]) {
|
||||
LOGCHAN(i, DMAIN, ERROR) << "Failed to allocate rate ctr";
|
||||
LOGCHAN(i, DCTR, ERROR) << "Failed to allocate rate ctr";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
rate_ctr_timerfd.fd = -1;
|
||||
if (osmo_timerfd_setup(&rate_ctr_timerfd, rate_ctr_timerfd_cb, NULL) < 0) {
|
||||
LOGC(DMAIN, ERROR) << "Failed to setup timerfd";
|
||||
dev_rate_ctr_timerfd.fd = -1;
|
||||
if (osmo_timerfd_setup(&dev_rate_ctr_timerfd, dev_rate_ctr_timerfd_cb, NULL) < 0) {
|
||||
LOGC(DCTR, ERROR) << "Failed to setup timerfd";
|
||||
exit(1);
|
||||
}
|
||||
trx_rate_ctr_timerfd.fd = -1;
|
||||
if (osmo_timerfd_setup(&trx_rate_ctr_timerfd, trx_rate_ctr_timerfd_cb, NULL) < 0) {
|
||||
LOGC(DCTR, ERROR) << "Failed to setup timerfd";
|
||||
exit(1);
|
||||
}
|
||||
osmo_signal_register_handler(SS_DEVICE, device_sig_cb, NULL);
|
||||
@@ -302,7 +373,7 @@ void trx_rate_ctr_threshold_add(struct ctr_threshold *ctr)
|
||||
|
||||
new_ctr = talloc_zero(trx_rate_ctr_ctx, struct ctr_threshold);
|
||||
*new_ctr = *ctr;
|
||||
LOGC(DMAIN, NOTICE) << "Adding new threshold check: " << ctr_threshold_2_vty_str(new_ctr);
|
||||
LOGC(DCTR, NOTICE) << "Adding new threshold check: " << ctr_threshold_2_vty_str(new_ctr);
|
||||
llist_add(&new_ctr->list, &threshold_list);
|
||||
threshold_timer_update_intv();
|
||||
}
|
||||
@@ -317,7 +388,7 @@ int trx_rate_ctr_threshold_del(struct ctr_threshold *del_ctr)
|
||||
ctr->val != del_ctr->val)
|
||||
continue;
|
||||
|
||||
LOGC(DMAIN, NOTICE) << "Deleting threshold check: " << ctr_threshold_2_vty_str(del_ctr);
|
||||
LOGC(DCTR, NOTICE) << "Deleting threshold check: " << ctr_threshold_2_vty_str(del_ctr);
|
||||
llist_del(&ctr->list);
|
||||
talloc_free(ctr);
|
||||
threshold_timer_update_intv();
|
||||
|
||||
@@ -4,12 +4,20 @@
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
enum TrxCtr {
|
||||
TRX_CTR_RX_OVERRUNS,
|
||||
TRX_CTR_TX_UNDERRUNS,
|
||||
TRX_CTR_RX_DROP_EV,
|
||||
TRX_CTR_RX_DROP_SMPL,
|
||||
TRX_CTR_TX_DROP_EV,
|
||||
TRX_CTR_TX_DROP_SMPL,
|
||||
TRX_CTR_DEV_RX_OVERRUNS,
|
||||
TRX_CTR_DEV_TX_UNDERRUNS,
|
||||
TRX_CTR_DEV_RX_DROP_EV,
|
||||
TRX_CTR_DEV_RX_DROP_SMPL,
|
||||
TRX_CTR_DEV_TX_DROP_EV,
|
||||
TRX_CTR_DEV_TX_DROP_SMPL,
|
||||
TRX_CTR_TRX_TX_STALE_BURSTS,
|
||||
TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS,
|
||||
TRX_CTR_TRX_TRXD_FN_REPEATED,
|
||||
TRX_CTR_TRX_TRXD_FN_OUTOFORDER,
|
||||
TRX_CTR_TRX_TRXD_FN_SKIPPED,
|
||||
TRX_CTR_TRX_RX_EMPTY_BURST,
|
||||
TRX_CTR_TRX_RX_CLIPPING,
|
||||
TRX_CTR_TRX_RX_NO_BURST_DETECTED,
|
||||
};
|
||||
|
||||
struct ctr_threshold {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
|
||||
@@ -41,22 +42,41 @@
|
||||
|
||||
static struct trx_ctx* g_trx_ctx;
|
||||
|
||||
static const struct value_string clock_ref_names[] = {
|
||||
const struct value_string clock_ref_names[] = {
|
||||
{ REF_INTERNAL, "internal" },
|
||||
{ REF_EXTERNAL, "external" },
|
||||
{ REF_GPS, "gpsdo" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const struct value_string filler_names[] = {
|
||||
{ FILLER_DUMMY, "Dummy bursts" },
|
||||
{ FILLER_ZERO, "Disabled" },
|
||||
{ FILLER_NORM_RAND, "Normal bursts with random payload" },
|
||||
{ FILLER_EDGE_RAND, "EDGE bursts with random payload" },
|
||||
{ FILLER_ACCESS_RAND, "Access bursts with random payload" },
|
||||
const struct value_string filler_names[] = {
|
||||
{ FILLER_DUMMY, "Dummy bursts (C0 only)" },
|
||||
{ FILLER_ZERO, "Empty bursts" },
|
||||
{ FILLER_NORM_RAND, "GMSK Normal Bursts with random payload" },
|
||||
{ FILLER_EDGE_RAND, "8-PSK Normal Bursts with random payload" },
|
||||
{ FILLER_ACCESS_RAND, "Access Bursts with random payload" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const struct value_string filler_types[] = {
|
||||
{ FILLER_DUMMY, "dummy" },
|
||||
{ FILLER_ZERO, "zero" },
|
||||
{ FILLER_NORM_RAND, "random-nb-gmsk" },
|
||||
{ FILLER_EDGE_RAND, "random-nb-8psk" },
|
||||
{ FILLER_ACCESS_RAND, "random-ab" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const struct value_string filler_docs[] = {
|
||||
{ FILLER_DUMMY, "Send a Dummy Burst on C0 (TRX0) and empty burst on other channels" },
|
||||
{ FILLER_ZERO, "Send an empty burst (default)" },
|
||||
{ FILLER_NORM_RAND, "Send a GMSK modulated Normal Burst with random bits (spectrum mask testing)" },
|
||||
{ FILLER_EDGE_RAND, "Send an 8-PSK modulated Normal Burst with random bits (spectrum mask testing)" },
|
||||
{ FILLER_ACCESS_RAND, "Send an Access Burst with random bits (Rx/Tx alignment testing)" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
struct trx_ctx *trx_from_vty(struct vty *v)
|
||||
{
|
||||
/* It can't hurt to force callers to continue to pass the vty instance
|
||||
@@ -101,7 +121,7 @@ DEFUN(cfg_trx, cfg_trx_cmd,
|
||||
}
|
||||
|
||||
DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
|
||||
"bind-ip A.B.C.D",
|
||||
"bind-ip " VTY_IPV4_CMD,
|
||||
"Set the IP address for the local bind\n"
|
||||
"IPv4 Address\n")
|
||||
{
|
||||
@@ -113,7 +133,7 @@ DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
|
||||
}
|
||||
|
||||
DEFUN(cfg_remote_ip, cfg_remote_ip_cmd,
|
||||
"remote-ip A.B.C.D",
|
||||
"remote-ip " VTY_IPV4_CMD,
|
||||
"Set the IP address for the remote BTS\n"
|
||||
"IPv4 Address\n")
|
||||
{
|
||||
@@ -151,7 +171,9 @@ DEFUN(cfg_dev_args, cfg_dev_args_cmd,
|
||||
DEFUN(cfg_tx_sps, cfg_tx_sps_cmd,
|
||||
"tx-sps (1|4)",
|
||||
"Set the Tx Samples-per-Symbol\n"
|
||||
"Tx Samples-per-Symbol\n")
|
||||
"Tx Samples-per-Symbol\n"
|
||||
"1 Sample-per-Symbol\n"
|
||||
"4 Samples-per-Symbol\n")
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
@@ -163,7 +185,9 @@ DEFUN(cfg_tx_sps, cfg_tx_sps_cmd,
|
||||
DEFUN(cfg_rx_sps, cfg_rx_sps_cmd,
|
||||
"rx-sps (1|4)",
|
||||
"Set the Rx Samples-per-Symbol\n"
|
||||
"Rx Samples-per-Symbol\n")
|
||||
"Rx Samples-per-Symbol\n"
|
||||
"1 Sample-per-Symbol\n"
|
||||
"4 Samples-per-Symbol\n")
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
@@ -172,57 +196,10 @@ DEFUN(cfg_rx_sps, cfg_rx_sps_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_test_rtsc, cfg_test_rtsc_cmd,
|
||||
"test rtsc <0-7>",
|
||||
"Set the Random Normal Burst test mode with TSC\n"
|
||||
"TSC\n")
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
if (trx->cfg.rach_delay_set) {
|
||||
vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
trx->cfg.rtsc_set = true;
|
||||
trx->cfg.rtsc = atoi(argv[0]);
|
||||
if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
|
||||
trx->cfg.filler = FILLER_NORM_RAND;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_test_rach_delay, cfg_test_rach_delay_cmd,
|
||||
"test rach-delay <0-68>",
|
||||
"Set the Random Access Burst test mode with delay\n"
|
||||
"RACH delay\n")
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
if (trx->cfg.rtsc_set) {
|
||||
vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (trx->cfg.egprs) {
|
||||
vty_out(vty, "rach-delay and egprs options are mutual-exclusive%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
trx->cfg.rach_delay_set = true;
|
||||
trx->cfg.rach_delay = atoi(argv[0]);
|
||||
trx->cfg.filler = FILLER_ACCESS_RAND;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_clock_ref, cfg_clock_ref_cmd,
|
||||
"clock-ref (internal|external|gpsdo)",
|
||||
"Set the Reference Clock\n"
|
||||
"Enable internal referece (default)\n"
|
||||
"Enable internal reference (default)\n"
|
||||
"Enable external 10 MHz reference\n"
|
||||
"Enable GPSDO reference\n")
|
||||
{
|
||||
@@ -235,7 +212,8 @@ DEFUN(cfg_clock_ref, cfg_clock_ref_cmd,
|
||||
|
||||
DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd,
|
||||
"multi-arfcn (disable|enable)",
|
||||
"Enable multi-ARFCN transceiver (default=disable)\n")
|
||||
"Multi-ARFCN transceiver mode (default=disable)\n"
|
||||
"Enable multi-ARFCN mode\n" "Disable multi-ARFCN mode\n")
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
@@ -266,21 +244,38 @@ DEFUN(cfg_offset, cfg_offset_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_ATTR(cfg_freq_offset, cfg_freq_offset_cmd,
|
||||
"freq-offset FLOAT",
|
||||
"Apply an artificial offset to Rx/Tx carrier frequency\n"
|
||||
"Frequency offset in kHz (e.g. -145300)\n",
|
||||
CMD_ATTR_HIDDEN)
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
trx->cfg.freq_offset_khz = atof(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_rssi_offset, cfg_rssi_offset_cmd,
|
||||
"rssi-offset FLOAT",
|
||||
"rssi-offset FLOAT [relative]",
|
||||
"Set the RSSI to dBm offset in dB (default=0)\n"
|
||||
"RSSI to dBm offset in dB\n")
|
||||
"RSSI to dBm offset in dB\n"
|
||||
"Add to the default rssi-offset value instead of completely replacing it\n")
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
trx->cfg.rssi_offset = atof(argv[0]);
|
||||
trx->cfg.force_rssi_offset = (argc == 1);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
|
||||
"swap-channels (disable|enable)",
|
||||
"Swap channels (default=disable)\n")
|
||||
"Swap primary and secondary channels of the PHY (if any)\n"
|
||||
"Do not swap primary and secondary channels (default)\n"
|
||||
"Swap primary and secondary channels\n")
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
@@ -297,7 +292,9 @@ DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
|
||||
|
||||
DEFUN(cfg_egprs, cfg_egprs_cmd,
|
||||
"egprs (disable|enable)",
|
||||
"Enable EDGE receiver (default=disable)\n")
|
||||
"EGPRS (8-PSK demodulation) support (default=disable)\n"
|
||||
"Disable EGPRS (8-PSK demodulation) support\n"
|
||||
"Enable EGPRS (8-PSK demodulation) support\n")
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
@@ -305,7 +302,6 @@ DEFUN(cfg_egprs, cfg_egprs_cmd,
|
||||
trx->cfg.egprs = false;
|
||||
} else if (strcmp("enable", argv[0]) == 0) {
|
||||
trx->cfg.egprs = true;
|
||||
trx->cfg.filler = FILLER_EDGE_RAND;
|
||||
} else {
|
||||
return CMD_WARNING;
|
||||
}
|
||||
@@ -315,7 +311,9 @@ DEFUN(cfg_egprs, cfg_egprs_cmd,
|
||||
|
||||
DEFUN(cfg_ext_rach, cfg_ext_rach_cmd,
|
||||
"ext-rach (disable|enable)",
|
||||
"Enable extended (11-bit) RACH (default=disable)\n")
|
||||
"11-bit Access Burst correlation support (default=disable)\n"
|
||||
"Disable 11-bit Access Burst (TS1 & TS2) correlation\n"
|
||||
"Enable 11-bit Access Burst (TS1 & TS2) correlation\n")
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
@@ -328,7 +326,7 @@ DEFUN(cfg_ext_rach, cfg_ext_rach_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_rt_prio, cfg_rt_prio_cmd,
|
||||
DEFUN_DEPRECATED(cfg_rt_prio, cfg_rt_prio_cmd,
|
||||
"rt-prio <1-32>",
|
||||
"Set the SCHED_RR real-time priority\n"
|
||||
"Real time priority\n")
|
||||
@@ -336,6 +334,8 @@ DEFUN(cfg_rt_prio, cfg_rt_prio_cmd,
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
trx->cfg.sched_rr = atoi(argv[0]);
|
||||
vty_out (vty, "%% 'rt-prio %u' is deprecated, use 'policy rr %u' under 'sched' node instead%s",
|
||||
trx->cfg.sched_rr, trx->cfg.sched_rr, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -352,14 +352,48 @@ DEFUN(cfg_stack_size, cfg_stack_size_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_filler, cfg_filler_cmd,
|
||||
"filler dummy",
|
||||
"Enable C0 filler table\n"
|
||||
"Dummy method\n")
|
||||
#define CFG_FILLER_DOC_STR \
|
||||
"Filler burst settings\n"
|
||||
|
||||
DEFUN(cfg_filler, cfg_filler_type_cmd,
|
||||
"AUTO-GENERATED", "AUTO-GENERATED")
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
// trx->cfg.filler is unsigned, so we need an interim int var to detect errors
|
||||
int type = get_string_value(filler_types, argv[0]);
|
||||
|
||||
if (type < 0) {
|
||||
trx->cfg.filler = FILLER_ZERO;
|
||||
return CMD_WARNING;
|
||||
}
|
||||
trx->cfg.filler = type;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_test_rtsc, cfg_filler_tsc_cmd,
|
||||
"filler tsc <0-7>",
|
||||
CFG_FILLER_DOC_STR
|
||||
"Set the TSC for GMSK/8-PSK Normal Burst random fillers. Used only with 'random-nb-gmsk' and"
|
||||
" 'random-nb-8psk' filler types. (default=0)\n"
|
||||
"TSC\n")
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
trx->cfg.filler = FILLER_DUMMY;
|
||||
trx->cfg.rtsc = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_test_rach_delay, cfg_filler_rach_delay_cmd,
|
||||
"filler access-burst-delay <0-68>",
|
||||
CFG_FILLER_DOC_STR
|
||||
"Set the delay for Access Burst random fillers. Used only with 'random-ab' filler type. (default=0)\n"
|
||||
"RACH delay in symbols\n")
|
||||
{
|
||||
struct trx_ctx *trx = trx_from_vty(vty);
|
||||
|
||||
trx->cfg.rach_delay = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -384,7 +418,7 @@ static int vty_intv_name_2_id(const char* str) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define THRESHOLD_ARGS "(rx_overruns|tx_underruns|rx_drop_events|rx_drop_samples|tx_drop_events|tx_drop_samples)"
|
||||
#define THRESHOLD_ARGS "(rx_overruns|tx_underruns|rx_drop_events|rx_drop_samples|tx_drop_events|tx_drop_samples|tx_stale_bursts|tx_unavailable_bursts|tx_trxd_fn_repeated|tx_trxd_fn_outoforder|tx_trxd_fn_skipped)"
|
||||
#define THRESHOLD_STR_VAL(s) "Set threshold value for rate_ctr device:" OSMO_STRINGIFY_VAL(s) "\n"
|
||||
#define THRESHOLD_STRS \
|
||||
THRESHOLD_STR_VAL(rx_overruns) \
|
||||
@@ -392,7 +426,13 @@ static int vty_intv_name_2_id(const char* str) {
|
||||
THRESHOLD_STR_VAL(rx_drop_events) \
|
||||
THRESHOLD_STR_VAL(rx_drop_samples) \
|
||||
THRESHOLD_STR_VAL(tx_drop_events) \
|
||||
THRESHOLD_STR_VAL(tx_drop_samples)
|
||||
THRESHOLD_STR_VAL(tx_drop_samples) \
|
||||
THRESHOLD_STR_VAL(tx_stale_bursts) \
|
||||
THRESHOLD_STR_VAL(tx_unavailable_bursts) \
|
||||
THRESHOLD_STR_VAL(tx_trxd_fn_repeated) \
|
||||
THRESHOLD_STR_VAL(tx_trxd_fn_outoforder) \
|
||||
THRESHOLD_STR_VAL(tx_trxd_fn_skipped) \
|
||||
""
|
||||
#define INTV_ARGS "(per-second|per-minute|per-hour|per-day)"
|
||||
#define INTV_STR_VAL(s) "Threshold value sampled " OSMO_STRINGIFY_VAL(s) "\n"
|
||||
#define INTV_STRS \
|
||||
@@ -401,12 +441,13 @@ static int vty_intv_name_2_id(const char* str) {
|
||||
INTV_STR_VAL(per-hour) \
|
||||
INTV_STR_VAL(per-day)
|
||||
|
||||
DEFUN(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
|
||||
"ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,
|
||||
"Threshold rate for error counter\n"
|
||||
THRESHOLD_STRS
|
||||
"Value to set for threshold\n"
|
||||
INTV_STRS)
|
||||
DEFUN_ATTR(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
|
||||
"ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,
|
||||
"Threshold rate for error counter\n"
|
||||
THRESHOLD_STRS
|
||||
"Value to set for threshold\n"
|
||||
INTV_STRS,
|
||||
CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
int rc;
|
||||
struct ctr_threshold ctr;
|
||||
@@ -432,12 +473,13 @@ DEFUN(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,
|
||||
"no ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,
|
||||
NO_STR "Threshold rate for error counter\n"
|
||||
THRESHOLD_STRS
|
||||
"Value to set for threshold\n"
|
||||
INTV_STRS)
|
||||
DEFUN_ATTR(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,
|
||||
"no ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,
|
||||
NO_STR "Threshold rate for error counter\n"
|
||||
THRESHOLD_STRS
|
||||
"Value to set for threshold\n"
|
||||
INTV_STRS,
|
||||
CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
int rc;
|
||||
struct ctr_threshold ctr;
|
||||
@@ -506,6 +548,12 @@ DEFUN(cfg_chan_rx_path, cfg_chan_rx_path_cmd,
|
||||
{
|
||||
struct trx_chan *chan = vty->index;
|
||||
|
||||
if (chan->trx->cfg.multi_arfcn && chan->idx > 0) {
|
||||
vty_out(vty, "%% Setting 'rx-path' for chan %u in multi-ARFCN mode "
|
||||
"does not make sense, because only chan 0 is used%s",
|
||||
chan->idx, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
osmo_talloc_replace_string(chan->trx, &chan->rx_path, argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
@@ -518,6 +566,12 @@ DEFUN(cfg_chan_tx_path, cfg_chan_tx_path_cmd,
|
||||
{
|
||||
struct trx_chan *chan = vty->index;
|
||||
|
||||
if (chan->trx->cfg.multi_arfcn && chan->idx > 0) {
|
||||
vty_out(vty, "%% Setting 'tx-path' for chan %u in multi-ARFCN mode "
|
||||
"does not make sense, because only chan 0 is used%s",
|
||||
chan->idx, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
osmo_talloc_replace_string(chan->trx, &chan->tx_path, argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
@@ -547,22 +601,27 @@ static int config_write_trx(struct vty *vty)
|
||||
vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
|
||||
if (trx->cfg.rx_sps != DEFAULT_RX_SPS)
|
||||
vty_out(vty, " rx-sps %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
|
||||
if (trx->cfg.rtsc_set)
|
||||
vty_out(vty, " test rtsc %u%s", trx->cfg.rtsc, VTY_NEWLINE);
|
||||
if (trx->cfg.rach_delay_set)
|
||||
vty_out(vty, " test rach-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
|
||||
if (trx->cfg.clock_ref != REF_INTERNAL)
|
||||
vty_out(vty, " clock-ref %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
|
||||
vty_out(vty, " multi-arfcn %s%s", trx->cfg.multi_arfcn ? "enable" : "disable", VTY_NEWLINE);
|
||||
if (trx->cfg.offset != 0)
|
||||
vty_out(vty, " offset %f%s", trx->cfg.offset, VTY_NEWLINE);
|
||||
if (trx->cfg.rssi_offset != 0)
|
||||
vty_out(vty, " rssi-offset %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
|
||||
if (trx->cfg.freq_offset_khz != 0)
|
||||
vty_out(vty, " freq-offset %f%s", trx->cfg.freq_offset_khz, VTY_NEWLINE);
|
||||
if (!(trx->cfg.rssi_offset == 0 && !trx->cfg.force_rssi_offset))
|
||||
vty_out(vty, " rssi-offset %f%s%s", trx->cfg.rssi_offset,
|
||||
trx->cfg.force_rssi_offset ? " relative": "", VTY_NEWLINE);
|
||||
vty_out(vty, " swap-channels %s%s", trx->cfg.swap_channels ? "enable" : "disable", VTY_NEWLINE);
|
||||
vty_out(vty, " egprs %s%s", trx->cfg.egprs ? "enable" : "disable", VTY_NEWLINE);
|
||||
vty_out(vty, " ext-rach %s%s", trx->cfg.ext_rach ? "enable" : "disable", VTY_NEWLINE);
|
||||
if (trx->cfg.sched_rr != 0)
|
||||
vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE);
|
||||
if (trx->cfg.filler != FILLER_ZERO)
|
||||
vty_out(vty, " filler type %s%s", get_value_string(filler_types, trx->cfg.filler), VTY_NEWLINE);
|
||||
if (trx->cfg.rtsc > 0)
|
||||
vty_out(vty, " filler tsc %u%s", trx->cfg.rtsc, VTY_NEWLINE);
|
||||
if (trx->cfg.rach_delay > 0)
|
||||
vty_out(vty, " filler access-burst-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
|
||||
if (trx->cfg.stack_size != 0)
|
||||
vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE);
|
||||
trx_rate_ctr_threshold_write_config(vty, " ");
|
||||
@@ -590,11 +649,9 @@ static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx)
|
||||
vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE);
|
||||
vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
|
||||
vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
|
||||
vty_out(vty, " Test Mode: TSC: %u (%s)%s", trx->cfg.rtsc,
|
||||
trx->cfg.rtsc_set ? "Enabled" : "Disabled", VTY_NEWLINE);
|
||||
vty_out(vty, " Test Mode: RACH Delay: %u (%s)%s", trx->cfg.rach_delay,
|
||||
trx->cfg.rach_delay_set ? "Enabled" : "Disabled", VTY_NEWLINE);
|
||||
vty_out(vty, " C0 Filler Table: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE);
|
||||
vty_out(vty, " Filler Burst Type: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE);
|
||||
vty_out(vty, " Filler Burst TSC: %u%s", trx->cfg.rtsc, VTY_NEWLINE);
|
||||
vty_out(vty, " Filler Burst RACH Delay: %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
|
||||
vty_out(vty, " Clock Reference: %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
|
||||
vty_out(vty, " Multi-Carrier: %s%s", trx->cfg.multi_arfcn ? "Enabled" : "Disabled", VTY_NEWLINE);
|
||||
vty_out(vty, " Tuning offset: %f%s", trx->cfg.offset, VTY_NEWLINE);
|
||||
@@ -663,6 +720,7 @@ static int trx_vty_go_parent(struct vty *vty)
|
||||
static const char trx_copyright[] =
|
||||
"Copyright (C) 2007-2014 Free Software Foundation, Inc.\r\n"
|
||||
"Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>\r\n"
|
||||
"Copyright (C) 2013-2019 Fairwaves, Inc.\r\n"
|
||||
"Copyright (C) 2015 Ettus Research LLC\r\n"
|
||||
"Copyright (C) 2017-2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
|
||||
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
||||
@@ -687,12 +745,19 @@ struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
|
||||
trx->cfg.tx_sps = DEFAULT_TX_SPS;
|
||||
trx->cfg.rx_sps = DEFAULT_RX_SPS;
|
||||
trx->cfg.filler = FILLER_ZERO;
|
||||
trx->cfg.rssi_offset = 0.0f;
|
||||
|
||||
return trx;
|
||||
}
|
||||
|
||||
int trx_vty_init(struct trx_ctx* trx)
|
||||
{
|
||||
cfg_filler_type_cmd.string = vty_cmd_string_from_valstr(trx, filler_types,
|
||||
"filler type (", "|", ")", 0);
|
||||
cfg_filler_type_cmd.doc = vty_cmd_string_from_valstr(trx, filler_docs,
|
||||
CFG_FILLER_DOC_STR "What to do when there is nothing to send "
|
||||
"(filler type, default=zero)\n", "\n", "", 0);
|
||||
|
||||
g_trx_ctx = trx;
|
||||
install_element_ve(&show_trx_cmd);
|
||||
|
||||
@@ -705,17 +770,18 @@ int trx_vty_init(struct trx_ctx* trx)
|
||||
install_element(TRX_NODE, &cfg_dev_args_cmd);
|
||||
install_element(TRX_NODE, &cfg_tx_sps_cmd);
|
||||
install_element(TRX_NODE, &cfg_rx_sps_cmd);
|
||||
install_element(TRX_NODE, &cfg_test_rtsc_cmd);
|
||||
install_element(TRX_NODE, &cfg_test_rach_delay_cmd);
|
||||
install_element(TRX_NODE, &cfg_clock_ref_cmd);
|
||||
install_element(TRX_NODE, &cfg_multi_arfcn_cmd);
|
||||
install_element(TRX_NODE, &cfg_offset_cmd);
|
||||
install_element(TRX_NODE, &cfg_freq_offset_cmd);
|
||||
install_element(TRX_NODE, &cfg_rssi_offset_cmd);
|
||||
install_element(TRX_NODE, &cfg_swap_channels_cmd);
|
||||
install_element(TRX_NODE, &cfg_egprs_cmd);
|
||||
install_element(TRX_NODE, &cfg_ext_rach_cmd);
|
||||
install_element(TRX_NODE, &cfg_rt_prio_cmd);
|
||||
install_element(TRX_NODE, &cfg_filler_cmd);
|
||||
install_element(TRX_NODE, &cfg_filler_type_cmd);
|
||||
install_element(TRX_NODE, &cfg_filler_tsc_cmd);
|
||||
install_element(TRX_NODE, &cfg_filler_rach_delay_cmd);
|
||||
install_element(TRX_NODE, &cfg_ctr_error_threshold_cmd);
|
||||
install_element(TRX_NODE, &cfg_no_ctr_error_threshold_cmd);
|
||||
install_element(TRX_NODE, &cfg_stack_size_cmd);
|
||||
@@ -725,5 +791,7 @@ int trx_vty_init(struct trx_ctx* trx)
|
||||
install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
|
||||
install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
|
||||
|
||||
logging_vty_add_deprecated_subsys(g_trx_ctx, "lms");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include "config_defs.h"
|
||||
|
||||
extern struct vty_app_info g_vty_info;
|
||||
extern const struct value_string clock_ref_names[];
|
||||
extern const struct value_string filler_names[];
|
||||
|
||||
/* Maximum number of physical RF channels */
|
||||
#define TRX_CHAN_MAX 8
|
||||
/* Maximum number of carriers in multi-ARFCN mode */
|
||||
#define TRX_MCHAN_MAX 3
|
||||
|
||||
@@ -33,40 +33,8 @@ extern struct vty_app_info g_vty_info;
|
||||
#define DEFAULT_TRX_IP "127.0.0.1"
|
||||
#define DEFAULT_CHANS 1
|
||||
|
||||
struct trx_ctx;
|
||||
|
||||
struct trx_chan {
|
||||
struct trx_ctx *trx; /* backpointer */
|
||||
unsigned int idx; /* channel index */
|
||||
char *rx_path;
|
||||
char *tx_path;
|
||||
};
|
||||
|
||||
struct trx_ctx {
|
||||
struct {
|
||||
char *bind_addr;
|
||||
char *remote_addr;
|
||||
char *dev_args;
|
||||
unsigned int base_port;
|
||||
unsigned int tx_sps;
|
||||
unsigned int rx_sps;
|
||||
unsigned int rtsc;
|
||||
bool rtsc_set;
|
||||
unsigned int rach_delay;
|
||||
bool rach_delay_set;
|
||||
enum ReferenceType clock_ref;
|
||||
enum FillerType filler;
|
||||
bool multi_arfcn;
|
||||
double offset;
|
||||
double rssi_offset;
|
||||
bool swap_channels;
|
||||
bool ext_rach;
|
||||
bool egprs;
|
||||
unsigned int sched_rr;
|
||||
unsigned int stack_size;
|
||||
unsigned int num_chans;
|
||||
struct trx_chan chans[TRX_CHAN_MAX];
|
||||
} cfg;
|
||||
struct trx_cfg cfg;
|
||||
};
|
||||
|
||||
int trx_vty_init(struct trx_ctx* trx);
|
||||
|
||||
11
Makefile.am
11
Makefile.am
@@ -28,17 +28,22 @@ AM_CXXFLAGS = -Wall -pthread
|
||||
|
||||
# Order must be preserved
|
||||
SUBDIRS = \
|
||||
doc \
|
||||
CommonLibs \
|
||||
GSM \
|
||||
Transceiver52M \
|
||||
contrib \
|
||||
tests
|
||||
tests \
|
||||
utils \
|
||||
doc \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
LEGAL \
|
||||
COPYING \
|
||||
README.md
|
||||
README.md \
|
||||
contrib/osmo-trx.spec.in \
|
||||
debian \
|
||||
$(NULL)
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
18
README.md
18
README.md
@@ -1,5 +1,5 @@
|
||||
About OsmTRX
|
||||
============
|
||||
About OsmoTRX
|
||||
=============
|
||||
|
||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
||||
physical layer of a BTS comprising the following 3GPP specifications:
|
||||
@@ -9,14 +9,12 @@ physical layer of a BTS comprising the following 3GPP specifications:
|
||||
* TS 05.04 "Modulation"
|
||||
* TS 05.10 "Radio subsystem synchronization"
|
||||
|
||||
OsmoTRX is based on the transceiver code from the
|
||||
OsmoTRX is originally based on the transceiver code from the
|
||||
[OpenBTS](https://osmocom.org/projects/osmobts/wiki/OpenBTS) project, but setup
|
||||
to operate independently with the purpose of using with non-OpenBTS software and
|
||||
projects, while still maintaining backwards compatibility with OpenBTS when
|
||||
possible. Currently there are numerous features contained in OsmoTRX that extend
|
||||
the functionality of the OpenBTS transceiver. These features include enhanced
|
||||
support for various embedded platforms - notably ARM - and dual channel
|
||||
diversity support for the Fairwaves umtrx.
|
||||
projects, specifically within the Osmocom stack. Used together with
|
||||
[OsmoBTS](https://osmocom.org/projects/osmobts/wiki) you can get a pretty
|
||||
standard GSM BTS with Abis interface as per the relevant 3GPP specifications.
|
||||
|
||||
Homepage
|
||||
--------
|
||||
@@ -31,7 +29,7 @@ You can clone from the official osmo-trx.git repository using
|
||||
|
||||
git clone git://git.osmocom.org/osmo-trx.git
|
||||
|
||||
There is a cgit interface at <http://git.osmocom.org/osmo-trx/>
|
||||
There is a cgit interface at <https://git.osmocom.org/osmo-trx/>
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
@@ -39,7 +37,7 @@ Documentation
|
||||
Doxygen-generated API documentation is generated during the build process, but
|
||||
also available online for each of the sub-libraries at User Manual for OsmoTRX
|
||||
can be generated during the build process, and is also available online at
|
||||
<http://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>.
|
||||
<https://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>.
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
0
TODO-RELEASE
Normal file
0
TODO-RELEASE
Normal file
@@ -98,7 +98,7 @@ bool Channelizer::rotate(const float *in, size_t len)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Setup channelizer paramaters */
|
||||
/* Setup channelizer parameters */
|
||||
Channelizer::Channelizer(size_t m, size_t blockLen, size_t hLen)
|
||||
: ChannelizerBase(m, blockLen, hLen)
|
||||
{
|
||||
|
||||
@@ -225,7 +225,7 @@ bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen)
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup channelizer paramaters
|
||||
* Setup channelizer parameters
|
||||
*/
|
||||
ChannelizerBase::ChannelizerBase(size_t m, size_t blockLen, size_t hLen)
|
||||
: subFilters(NULL), hInputs(NULL), hOutputs(NULL), hist(NULL),
|
||||
@@ -244,6 +244,7 @@ ChannelizerBase::~ChannelizerBase()
|
||||
free(subFilters[i]);
|
||||
delete[] hist[i];
|
||||
}
|
||||
free(subFilters);
|
||||
|
||||
fft_free(fftInput);
|
||||
fft_free(fftOutput);
|
||||
|
||||
@@ -32,7 +32,7 @@ protected:
|
||||
/* Buffer length validity checking */
|
||||
bool checkLen(size_t innerLen, size_t outerLen);
|
||||
public:
|
||||
/* Initilize channelizer/synthesis filter internals */
|
||||
/* Initialize channelizer/synthesis filter internals */
|
||||
bool init();
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ unlike the built-in complex<> templates, these inline most operations for speed
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
@@ -104,3 +104,13 @@ osmo_trx_lms_LDADD = \
|
||||
$(LMS_LIBS)
|
||||
osmo_trx_lms_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
|
||||
endif
|
||||
|
||||
if DEVICE_IPC
|
||||
bin_PROGRAMS += osmo-trx-ipc
|
||||
osmo_trx_ipc_SOURCES = osmo-trx.cpp
|
||||
osmo_trx_ipc_LDADD = \
|
||||
$(builddir)/device/ipc/libdevice.la \
|
||||
$(COMMON_LDADD)
|
||||
osmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS)
|
||||
endif
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@ void Resampler::initFilters(float bw)
|
||||
reverse(&part[0], &part[filt_len]);
|
||||
}
|
||||
|
||||
#ifndef __OPTIMIZE__
|
||||
static bool check_vec_len(int in_len, int out_len, int p, int q)
|
||||
{
|
||||
if (in_len % q) {
|
||||
@@ -129,14 +130,15 @@ static bool check_vec_len(int in_len, int out_len, int p, int q)
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
|
||||
{
|
||||
int n, path;
|
||||
|
||||
#ifndef __OPTIMIZE__
|
||||
if (!check_vec_len(in_len, out_len, p, q))
|
||||
return -1;
|
||||
|
||||
#endif
|
||||
/* Generate output from precomputed input/output paths */
|
||||
for (size_t i = 0; i < out_len; i++) {
|
||||
n = in_index[i];
|
||||
|
||||
@@ -35,12 +35,12 @@ public:
|
||||
Resampler(size_t p, size_t q, size_t filt_len = 16);
|
||||
~Resampler();
|
||||
|
||||
/* Initilize resampler filterbank.
|
||||
/* Initialize resampler filterbank.
|
||||
* @param bw bandwidth factor on filter generation (pre-window)
|
||||
* @return false on error, zero otherwise
|
||||
*
|
||||
* Automatic setting is to compute the filter to prevent aliasing with
|
||||
* a Blackman-Harris window. Adjustment is made through a bandwith
|
||||
* a Blackman-Harris window. Adjustment is made through a bandwidth
|
||||
* factor to shift the cutoff and/or the constituent filter lengths.
|
||||
* Calculation of specific rolloff factors or 3-dB cutoff points is
|
||||
* left as an excersize for the reader.
|
||||
|
||||
@@ -37,6 +37,7 @@ extern "C" {
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/vty/cpu_sched_vty.h>
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
@@ -45,13 +46,24 @@ extern "C" {
|
||||
|
||||
using namespace GSM;
|
||||
|
||||
Transceiver *transceiver;
|
||||
|
||||
#define USB_LATENCY_INTRVL 10,0
|
||||
|
||||
/* Number of running values use in noise average */
|
||||
#define NOISE_CNT 20
|
||||
|
||||
|
||||
static void dispatch_trx_rate_ctr_change(TransceiverState *state, unsigned int chan) {
|
||||
thread_enable_cancel(false);
|
||||
state->ctrs.chan = chan;
|
||||
osmo_signal_dispatch(SS_DEVICE, S_TRX_COUNTER_CHANGE, &state->ctrs);
|
||||
thread_enable_cancel(true);
|
||||
}
|
||||
|
||||
TransceiverState::TransceiverState()
|
||||
: mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
|
||||
: mFiller(FILLER_ZERO), mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT),
|
||||
mPower(0.0), mMuted(false), first_dl_fn_rcv()
|
||||
{
|
||||
for (int i = 0; i < 8; i++) {
|
||||
chanType[i] = Transceiver::NONE;
|
||||
@@ -63,6 +75,7 @@ TransceiverState::TransceiverState()
|
||||
for (int n = 0; n < 102; n++)
|
||||
fillerTable[n][i] = NULL;
|
||||
}
|
||||
memset(&ctrs, 0, sizeof(struct trx_counters));
|
||||
}
|
||||
|
||||
TransceiverState::~TransceiverState()
|
||||
@@ -84,6 +97,8 @@ bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t r
|
||||
if ((sps != 1) && (sps != 4))
|
||||
return false;
|
||||
|
||||
mFiller = filler;
|
||||
|
||||
for (size_t n = 0; n < 8; n++) {
|
||||
for (size_t i = 0; i < 102; i++) {
|
||||
switch (filler) {
|
||||
@@ -117,26 +132,21 @@ bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t r
|
||||
return false;
|
||||
}
|
||||
|
||||
Transceiver::Transceiver(int wBasePort,
|
||||
const char *TRXAddress,
|
||||
const char *GSMcoreAddress,
|
||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||
Transceiver::Transceiver(const struct trx_cfg *cfg,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface,
|
||||
double wRssiOffset, int wStackSize)
|
||||
: mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
|
||||
mClockSocket(-1), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
||||
rssiOffset(wRssiOffset), stackSize(wStackSize),
|
||||
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mExtRACH(false), mEdge(false),
|
||||
mOn(false), mForceClockInterface(false),
|
||||
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
|
||||
mWriteBurstToDiskMask(0)
|
||||
RadioInterface *wRadioInterface)
|
||||
: cfg(cfg), mClockSocket(-1),
|
||||
mRxLowerLoopThread(nullptr), mTxLowerLoopThread(nullptr),
|
||||
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
||||
mChans(cfg->num_chans), mOn(false), mForceClockInterface(false),
|
||||
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0),
|
||||
mMaxExpectedDelayNB(0), mWriteBurstToDiskMask(0)
|
||||
{
|
||||
txFullScale = mRadioInterface->fullScaleInputValue();
|
||||
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int j = 0; j < 8; j++)
|
||||
for (size_t i = 0; i < ARRAY_SIZE(mHandover); i++) {
|
||||
for (size_t j = 0; j < ARRAY_SIZE(mHandover[i]); j++)
|
||||
mHandover[i][j] = false;
|
||||
}
|
||||
}
|
||||
@@ -151,20 +161,30 @@ Transceiver::~Transceiver()
|
||||
close(mClockSocket);
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
if (mControlServiceLoopThreads[i]) {
|
||||
mControlServiceLoopThreads[i]->cancel();
|
||||
mControlServiceLoopThreads[i]->join();
|
||||
delete mControlServiceLoopThreads[i];
|
||||
}
|
||||
|
||||
mTxPriorityQueues[i].clear();
|
||||
if (mCtrlSockets[i] >= 0)
|
||||
close(mCtrlSockets[i]);
|
||||
if (mDataSockets[i] >= 0)
|
||||
close(mDataSockets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int Transceiver::ctrl_sock_cb(struct osmo_fd *bfd, unsigned int flags)
|
||||
{
|
||||
int rc = 0;
|
||||
int chan = static_cast<int>(reinterpret_cast<uintptr_t>(bfd->data));
|
||||
|
||||
if (flags & OSMO_FD_READ)
|
||||
rc = transceiver->ctrl_sock_handle_rx(chan);
|
||||
if (rc < 0)
|
||||
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
|
||||
|
||||
if (flags & OSMO_FD_WRITE)
|
||||
rc = transceiver->ctrl_sock_write(chan);
|
||||
if (rc < 0)
|
||||
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize transceiver
|
||||
*
|
||||
@@ -174,27 +194,21 @@ Transceiver::~Transceiver()
|
||||
* are still expected to report clock indications through control channel
|
||||
* activity.
|
||||
*/
|
||||
bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
|
||||
bool edge, bool ext_rach)
|
||||
bool Transceiver::init()
|
||||
{
|
||||
int d_srcport, d_dstport, c_srcport, c_dstport;
|
||||
|
||||
if (!mChans) {
|
||||
LOG(ALERT) << "No channels assigned";
|
||||
LOG(FATAL) << "No channels assigned";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sigProcLibSetup()) {
|
||||
LOG(ALERT) << "Failed to initialize signal processing library";
|
||||
LOG(FATAL) << "Failed to initialize signal processing library";
|
||||
return false;
|
||||
}
|
||||
|
||||
mExtRACH = ext_rach;
|
||||
mEdge = edge;
|
||||
|
||||
mDataSockets.resize(mChans, -1);
|
||||
mCtrlSockets.resize(mChans, -1);
|
||||
mControlServiceLoopThreads.resize(mChans);
|
||||
mCtrlSockets.resize(mChans);
|
||||
mTxPriorityQueueServiceLoopThreads.resize(mChans);
|
||||
mRxServiceLoopThreads.resize(mChans);
|
||||
|
||||
@@ -204,36 +218,47 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
|
||||
mVersionTRXD.resize(mChans);
|
||||
|
||||
/* Filler table retransmissions - support only on channel 0 */
|
||||
if (filler == FILLER_DUMMY)
|
||||
if (cfg->filler == FILLER_DUMMY)
|
||||
mStates[0].mRetrans = true;
|
||||
|
||||
/* Setup sockets */
|
||||
mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
||||
mLocalAddr.c_str(), mBasePort,
|
||||
mRemoteAddr.c_str(), mBasePort + 100,
|
||||
cfg->bind_addr, cfg->base_port,
|
||||
cfg->remote_addr, cfg->base_port + 100,
|
||||
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
||||
if (mClockSocket < 0)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
c_srcport = mBasePort + 2 * i + 1;
|
||||
c_dstport = mBasePort + 2 * i + 101;
|
||||
d_srcport = mBasePort + 2 * i + 2;
|
||||
d_dstport = mBasePort + 2 * i + 102;
|
||||
int rv;
|
||||
FillerType filler = cfg->filler;
|
||||
c_srcport = cfg->base_port + 2 * i + 1;
|
||||
c_dstport = cfg->base_port + 2 * i + 101;
|
||||
d_srcport = cfg->base_port + 2 * i + 2;
|
||||
d_dstport = cfg->base_port + 2 * i + 102;
|
||||
|
||||
mCtrlSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
||||
mLocalAddr.c_str(), c_srcport,
|
||||
mRemoteAddr.c_str(), c_dstport,
|
||||
rv = osmo_sock_init2_ofd(&mCtrlSockets[i].conn_bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
||||
cfg->bind_addr, c_srcport,
|
||||
cfg->remote_addr, c_dstport,
|
||||
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
||||
if (mCtrlSockets[i] < 0)
|
||||
if (rv < 0)
|
||||
return false;
|
||||
|
||||
mCtrlSockets[i].conn_bfd.cb = ctrl_sock_cb;
|
||||
mCtrlSockets[i].conn_bfd.data = reinterpret_cast<void*>(i);
|
||||
|
||||
|
||||
mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
||||
mLocalAddr.c_str(), d_srcport,
|
||||
mRemoteAddr.c_str(), d_dstport,
|
||||
cfg->bind_addr, d_srcport,
|
||||
cfg->remote_addr, d_dstport,
|
||||
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
||||
if (mCtrlSockets[i] < 0)
|
||||
if (mDataSockets[i] < 0)
|
||||
return false;
|
||||
|
||||
if (i && filler == FILLER_DUMMY)
|
||||
filler = FILLER_ZERO;
|
||||
|
||||
mStates[i].init(filler, cfg->tx_sps, txFullScale, cfg->rtsc, cfg->rach_delay);
|
||||
}
|
||||
|
||||
/* Randomize the central clock */
|
||||
@@ -243,21 +268,6 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
|
||||
mLastClockUpdateTime = startTime;
|
||||
mLatencyUpdateTime = startTime;
|
||||
|
||||
/* Start control threads */
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
|
||||
params->trx = this;
|
||||
params->num = i;
|
||||
mControlServiceLoopThreads[i] = new Thread(stackSize);
|
||||
mControlServiceLoopThreads[i]->start((void * (*)(void*))
|
||||
ControlServiceLoopAdapter, (void*) params);
|
||||
|
||||
if (i && filler == FILLER_DUMMY)
|
||||
filler = FILLER_ZERO;
|
||||
|
||||
mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -285,13 +295,13 @@ bool Transceiver::start()
|
||||
mLatencyUpdateTime = time;
|
||||
|
||||
if (!mRadioInterface->start()) {
|
||||
LOG(ALERT) << "Device failed to start";
|
||||
LOG(FATAL) << "Device failed to start";
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Device is running - launch I/O threads */
|
||||
mRxLowerLoopThread = new Thread(stackSize);
|
||||
mTxLowerLoopThread = new Thread(stackSize);
|
||||
mRxLowerLoopThread = new Thread(cfg->stack_size);
|
||||
mTxLowerLoopThread = new Thread(cfg->stack_size);
|
||||
mTxLowerLoopThread->start((void * (*)(void*))
|
||||
TxLowerLoopAdapter,(void*) this);
|
||||
mRxLowerLoopThread->start((void * (*)(void*))
|
||||
@@ -302,14 +312,14 @@ bool Transceiver::start()
|
||||
TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
|
||||
params->trx = this;
|
||||
params->num = i;
|
||||
mRxServiceLoopThreads[i] = new Thread(stackSize);
|
||||
mRxServiceLoopThreads[i] = new Thread(cfg->stack_size);
|
||||
mRxServiceLoopThreads[i]->start((void * (*)(void*))
|
||||
RxUpperLoopAdapter, (void*) params);
|
||||
|
||||
params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
|
||||
params->trx = this;
|
||||
params->num = i;
|
||||
mTxPriorityQueueServiceLoopThreads[i] = new Thread(stackSize);
|
||||
mTxPriorityQueueServiceLoopThreads[i] = new Thread(cfg->stack_size);
|
||||
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
|
||||
TxUpperLoopAdapter, (void*) params);
|
||||
}
|
||||
@@ -371,22 +381,22 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
|
||||
radioVector *radio_burst;
|
||||
|
||||
if (chan >= mTxPriorityQueues.size()) {
|
||||
LOG(ALERT) << "Invalid channel " << chan;
|
||||
LOGCHAN(chan, DTRXDDL, FATAL) << "Invalid channel";
|
||||
return;
|
||||
}
|
||||
|
||||
if (wTime.TN() > 7) {
|
||||
LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
|
||||
LOGCHAN(chan, DTRXDDL, FATAL) << "Received burst with invalid slot " << wTime.TN();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Use the number of bits as the EDGE burst indicator */
|
||||
if (bits.size() == EDGE_BURST_NBITS)
|
||||
burst = modulateEdgeBurst(bits, mSPSTx);
|
||||
burst = modulateEdgeBurst(bits, cfg->tx_sps);
|
||||
else
|
||||
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
|
||||
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), cfg->tx_sps);
|
||||
|
||||
scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
|
||||
scaleVector(*burst, txFullScale * pow(10, (double) -RSSI / 20));
|
||||
|
||||
radio_burst = new radioVector(wTime, burst);
|
||||
|
||||
@@ -414,24 +424,29 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
|
||||
std::vector<signalVector *> bursts(mChans);
|
||||
std::vector<bool> zeros(mChans);
|
||||
std::vector<bool> filler(mChans, true);
|
||||
bool ratectr_changed;
|
||||
|
||||
TN = nowTime.TN();
|
||||
|
||||
for (size_t i = 0; i < mChans; i ++) {
|
||||
state = &mStates[i];
|
||||
ratectr_changed = false;
|
||||
|
||||
zeros[i] = state->chanType[TN] == NONE || state->mMuted;
|
||||
|
||||
Mutex *mtx = mTxPriorityQueues[i].getMutex();
|
||||
mtx->lock();
|
||||
|
||||
while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
|
||||
LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
|
||||
LOGCHAN(i, DTRXDDL, INFO) << "dumping STALE burst in TRX->SDR interface ("
|
||||
<< burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
|
||||
state->ctrs.tx_stale_bursts++;
|
||||
ratectr_changed = true;
|
||||
if (state->mRetrans)
|
||||
updateFillerTable(i, burst);
|
||||
delete burst;
|
||||
}
|
||||
|
||||
TN = nowTime.TN();
|
||||
modFN = nowTime.FN() % state->fillerModulus[TN];
|
||||
|
||||
bursts[i] = state->fillerTable[modFN][TN];
|
||||
zeros[i] = state->chanType[TN] == NONE;
|
||||
|
||||
if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
|
||||
bursts[i] = burst->getVector();
|
||||
|
||||
@@ -443,7 +458,21 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
|
||||
}
|
||||
|
||||
delete burst;
|
||||
} else {
|
||||
modFN = nowTime.FN() % state->fillerModulus[TN];
|
||||
bursts[i] = state->fillerTable[modFN][TN];
|
||||
if (i == 0 && state->mFiller == FILLER_ZERO) {
|
||||
LOGCHAN(i, DTRXDDL, INFO) << "No Tx burst available for " << nowTime
|
||||
<< ", retrans=" << state->mRetrans;
|
||||
state->ctrs.tx_unavailable_bursts++;
|
||||
ratectr_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
mtx->unlock();
|
||||
|
||||
if (ratectr_changed)
|
||||
dispatch_trx_rate_ctr_change(state, i);
|
||||
}
|
||||
|
||||
mRadioInterface->driveTransmitRadio(bursts, zeros);
|
||||
@@ -528,16 +557,16 @@ CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
||||
break;
|
||||
case IV:
|
||||
case VI:
|
||||
return mExtRACH ? EXT_RACH : RACH;
|
||||
return cfg->ext_rach ? EXT_RACH : RACH;
|
||||
break;
|
||||
case V: {
|
||||
int mod51 = burstFN % 51;
|
||||
if ((mod51 <= 36) && (mod51 >= 14))
|
||||
return mExtRACH ? EXT_RACH : RACH;
|
||||
return cfg->ext_rach ? EXT_RACH : RACH;
|
||||
else if ((mod51 == 4) || (mod51 == 5))
|
||||
return mExtRACH ? EXT_RACH : RACH;
|
||||
return cfg->ext_rach ? EXT_RACH : RACH;
|
||||
else if ((mod51 == 45) || (mod51 == 46))
|
||||
return mExtRACH ? EXT_RACH : RACH;
|
||||
return cfg->ext_rach ? EXT_RACH : RACH;
|
||||
else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
|
||||
return RACH;
|
||||
else
|
||||
@@ -555,11 +584,11 @@ CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
||||
case XIII: {
|
||||
int mod52 = burstFN % 52;
|
||||
if ((mod52 == 12) || (mod52 == 38))
|
||||
return mExtRACH ? EXT_RACH : RACH;
|
||||
return cfg->ext_rach ? EXT_RACH : RACH;
|
||||
else if ((mod52 == 25) || (mod52 == 51))
|
||||
return IDLE;
|
||||
else /* Enable 8-PSK burst detection if EDGE is enabled */
|
||||
return mEdge ? EDGE : TSC;
|
||||
return cfg->egprs ? EDGE : TSC;
|
||||
break;
|
||||
}
|
||||
case LOOPBACK:
|
||||
@@ -584,6 +613,13 @@ void writeToFile(radioVector *radio_burst, size_t chan)
|
||||
outfile.close();
|
||||
}
|
||||
|
||||
double Transceiver::rssiOffset(size_t chan)
|
||||
{
|
||||
if (cfg->force_rssi_offset)
|
||||
return cfg->rssi_offset;
|
||||
return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pull bursts from the FIFO and handle according to the slot
|
||||
* and burst correlation type. Equalzation is currently disabled.
|
||||
@@ -602,11 +638,13 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
|
||||
GSM::Time burstTime;
|
||||
SoftVector *rxBurst;
|
||||
TransceiverState *state = &mStates[chan];
|
||||
bool ctr_changed = false;
|
||||
double rssi_offset;
|
||||
|
||||
/* Blocking FIFO read */
|
||||
radioVector *radio_burst = mReceiveFIFO[chan]->read();
|
||||
if (!radio_burst) {
|
||||
LOGCHAN(chan, DMAIN, ERROR) << "ReceiveFIFO->read() returned no burst";
|
||||
LOGCHAN(chan, DTRXDUL, ERROR) << "ReceiveFIFO->read() returned no burst";
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -640,9 +678,13 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* If TRX RF is locked/muted by BTS, send idle burst indications */
|
||||
if (state->mMuted)
|
||||
goto ret_idle;
|
||||
|
||||
/* Select the diversity channel with highest energy */
|
||||
for (size_t i = 0; i < radio_burst->chans(); i++) {
|
||||
float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
|
||||
float pow = energyDetect(*radio_burst->getVector(i), 20 * cfg->rx_sps);
|
||||
if (pow > max) {
|
||||
max = pow;
|
||||
max_i = i;
|
||||
@@ -651,7 +693,9 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
|
||||
}
|
||||
|
||||
if (max_i < 0) {
|
||||
LOG(ALERT) << "Received empty burst";
|
||||
LOGCHAN(chan, DTRXDUL, INFO) << "Received empty burst";
|
||||
state->ctrs.rx_empty_burst++;
|
||||
ctr_changed = true;
|
||||
goto ret_idle;
|
||||
}
|
||||
|
||||
@@ -665,8 +709,9 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
|
||||
state->mNoiseLev = state->mNoises.avg();
|
||||
}
|
||||
|
||||
bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
|
||||
bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
|
||||
rssi_offset = rssiOffset(chan);
|
||||
bi->rssi = 20.0 * log10(rxFullScale / avg) + rssi_offset;
|
||||
bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssi_offset;
|
||||
|
||||
if (type == IDLE)
|
||||
goto ret_idle;
|
||||
@@ -675,20 +720,24 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
|
||||
mMaxExpectedDelayAB : mMaxExpectedDelayNB;
|
||||
|
||||
/* Detect normal or RACH bursts */
|
||||
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
|
||||
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
|
||||
if (rc <= 0) {
|
||||
if (rc == -SIGERR_CLIP)
|
||||
LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
|
||||
else if (rc != SIGERR_NONE)
|
||||
LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
|
||||
if (rc == -SIGERR_CLIP) {
|
||||
LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
|
||||
state->ctrs.rx_clipping++;
|
||||
ctr_changed = true;
|
||||
} else if (rc != SIGERR_NONE) {
|
||||
LOGCHAN(chan, DTRXDUL, INFO) << "Unhandled RACH or Normal Burst detection error";
|
||||
state->ctrs.rx_no_burst_detected++;
|
||||
ctr_changed = true;
|
||||
}
|
||||
goto ret_idle;
|
||||
}
|
||||
|
||||
type = (CorrType) rc;
|
||||
rxBurst = demodAnyBurst(*burst, (CorrType) rc, cfg->rx_sps, &ebp);
|
||||
bi->toa = ebp.toa;
|
||||
bi->tsc = ebp.tsc;
|
||||
bi->ci = ebp.ci;
|
||||
rxBurst = demodAnyBurst(*burst, mSPSRx, ebp.amp, ebp.toa, type);
|
||||
|
||||
/* EDGE demodulator returns 444 (gSlotLen * 3) bits */
|
||||
if (rxBurst->size() == EDGE_BURST_NBITS) {
|
||||
@@ -707,6 +756,8 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
|
||||
return 0;
|
||||
|
||||
ret_idle:
|
||||
if (ctr_changed)
|
||||
dispatch_trx_rate_ctr_change(state, chan);
|
||||
bi->idle = true;
|
||||
delete radio_burst;
|
||||
return 0;
|
||||
@@ -719,8 +770,6 @@ void Transceiver::reset()
|
||||
}
|
||||
|
||||
|
||||
#define MAX_PACKET_LENGTH 100
|
||||
|
||||
/**
|
||||
* Matches a buffer with a command.
|
||||
* @param buf a buffer to look command in
|
||||
@@ -750,27 +799,77 @@ static bool match_cmd(char *buf,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Transceiver::driveControl(size_t chan)
|
||||
void Transceiver::ctrl_sock_send(ctrl_msg& m, int chan)
|
||||
{
|
||||
char buffer[MAX_PACKET_LENGTH + 1];
|
||||
char response[MAX_PACKET_LENGTH + 1];
|
||||
ctrl_sock_state& s = mCtrlSockets[chan];
|
||||
struct osmo_fd *conn_bfd = &s.conn_bfd;
|
||||
|
||||
s.txmsgqueue.push_back(m);
|
||||
osmo_fd_write_enable(conn_bfd);
|
||||
}
|
||||
|
||||
int Transceiver::ctrl_sock_write(int chan)
|
||||
{
|
||||
int rc;
|
||||
ctrl_sock_state& s = mCtrlSockets[chan];
|
||||
|
||||
if (s.conn_bfd.fd < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
while (s.txmsgqueue.size()) {
|
||||
const ctrl_msg m = s.txmsgqueue.front();
|
||||
|
||||
osmo_fd_write_disable(&s.conn_bfd);
|
||||
|
||||
/* try to send it over the socket */
|
||||
rc = write(s.conn_bfd.fd, m.data, strlen(m.data) + 1);
|
||||
if (rc == 0)
|
||||
goto close;
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
osmo_fd_write_enable(&s.conn_bfd);
|
||||
break;
|
||||
}
|
||||
goto close;
|
||||
}
|
||||
|
||||
s.txmsgqueue.pop_front();
|
||||
}
|
||||
return 0;
|
||||
|
||||
close:
|
||||
LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets write(" << s.conn_bfd.fd << ") failed: " << rc;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Transceiver::ctrl_sock_handle_rx(int chan)
|
||||
{
|
||||
ctrl_msg cmd_received;
|
||||
ctrl_msg cmd_to_send;
|
||||
char *buffer = cmd_received.data;
|
||||
char *response = cmd_to_send.data;
|
||||
char *command, *params;
|
||||
int msgLen;
|
||||
ctrl_sock_state& s = mCtrlSockets[chan];
|
||||
|
||||
/* Attempt to read from control socket */
|
||||
msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
|
||||
msgLen = read(s.conn_bfd.fd, buffer, sizeof(cmd_received.data)-1);
|
||||
if (msgLen < 0 && errno == EAGAIN)
|
||||
return 0; /* Try again later */
|
||||
if (msgLen <= 0) {
|
||||
LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
|
||||
return false;
|
||||
LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets read(" << s.conn_bfd.fd << ") failed: " << msgLen;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
/* Zero-terminate received string */
|
||||
buffer[msgLen] = '\0';
|
||||
|
||||
/* Verify a command signature */
|
||||
if (strncmp(buffer, "CMD ", 4)) {
|
||||
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
|
||||
return false;
|
||||
LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Set command pointer */
|
||||
@@ -794,7 +893,7 @@ bool Transceiver::driveControl(size_t chan)
|
||||
unsigned ts = 0, ss = 0;
|
||||
sscanf(params, "%u %u", &ts, &ss);
|
||||
if (ts > 7 || ss > 7) {
|
||||
sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
|
||||
sprintf(response, "RSP HANDOVER 1 %u %u", ts, ss);
|
||||
} else {
|
||||
mHandover[ts][ss] = true;
|
||||
sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
|
||||
@@ -833,7 +932,7 @@ bool Transceiver::driveControl(size_t chan)
|
||||
(int) round(20.0 * log10(rxFullScale / lev)));
|
||||
}
|
||||
else {
|
||||
sprintf(response,"RSP NOISELEV 1 0");
|
||||
sprintf(response,"RSP NOISELEV 1 0");
|
||||
}
|
||||
} else if (match_cmd(command, "SETPOWER", ¶ms)) {
|
||||
int power;
|
||||
@@ -848,13 +947,16 @@ bool Transceiver::driveControl(size_t chan)
|
||||
power = mRadioInterface->setPowerAttenuation(power, chan);
|
||||
mStates[chan].mPower = power;
|
||||
sprintf(response, "RSP ADJPOWER 0 %d", power);
|
||||
} else if (match_cmd(command, "NOMTXPOWER", NULL)) {
|
||||
int power = mRadioInterface->getNominalTxPower(chan);
|
||||
sprintf(response, "RSP NOMTXPOWER 0 %d", power);
|
||||
} else if (match_cmd(command, "RXTUNE", ¶ms)) {
|
||||
// tune receiver
|
||||
int freqKhz;
|
||||
sscanf(params, "%d", &freqKhz);
|
||||
mRxFreq = freqKhz * 1e3;
|
||||
mRxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3;
|
||||
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
|
||||
LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
|
||||
LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune";
|
||||
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
|
||||
}
|
||||
else
|
||||
@@ -863,9 +965,9 @@ bool Transceiver::driveControl(size_t chan)
|
||||
// tune txmtr
|
||||
int freqKhz;
|
||||
sscanf(params, "%d", &freqKhz);
|
||||
mTxFreq = freqKhz * 1e3;
|
||||
mTxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3;
|
||||
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
|
||||
LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
|
||||
LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune";
|
||||
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
|
||||
}
|
||||
else
|
||||
@@ -887,9 +989,9 @@ bool Transceiver::driveControl(size_t chan)
|
||||
int timeslot;
|
||||
sscanf(params, "%d %d", ×lot, &corrCode);
|
||||
if ((timeslot < 0) || (timeslot > 7)) {
|
||||
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
|
||||
LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
|
||||
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
|
||||
setModulus(timeslot, chan);
|
||||
@@ -908,25 +1010,27 @@ bool Transceiver::driveControl(size_t chan)
|
||||
mVersionTRXD[chan] = version_recv;
|
||||
sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
|
||||
}
|
||||
} else if (match_cmd(command, "RFMUTE", ¶ms)) {
|
||||
// (Un)mute RF TX and RX
|
||||
unsigned mute;
|
||||
sscanf(params, "%u", &mute);
|
||||
mStates[chan].mMuted = mute ? true : false;
|
||||
sprintf(response, "RSP RFMUTE 0 %u", mute);
|
||||
} else if (match_cmd(command, "_SETBURSTTODISKMASK", ¶ms)) {
|
||||
// debug command! may change or disapear without notice
|
||||
// debug command! may change or disappear without notice
|
||||
// set a mask which bursts to dump to disk
|
||||
int mask;
|
||||
sscanf(params, "%d", &mask);
|
||||
mWriteBurstToDiskMask = mask;
|
||||
sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
|
||||
} else {
|
||||
LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
|
||||
LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus command " << command << " on control interface.";
|
||||
sprintf(response,"RSP ERR 1");
|
||||
}
|
||||
|
||||
LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
|
||||
msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
|
||||
if (msgLen <= 0) {
|
||||
LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
transceiver->ctrl_sock_send(cmd_to_send, chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||
@@ -936,11 +1040,12 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||
struct trxd_hdr_v01_dl *dl;
|
||||
char buffer[sizeof(*dl) + EDGE_BURST_NBITS];
|
||||
uint32_t fn;
|
||||
uint8_t tn;
|
||||
|
||||
// check data socket
|
||||
msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
|
||||
if (msgLen <= 0) {
|
||||
LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
|
||||
LOGCHAN(chan, DTRXDDL, NOTICE) << "mDataSockets read(" << mDataSockets[chan] << ") failed: " << msgLen;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -949,14 +1054,14 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||
burstLen = gSlotLen;
|
||||
break;
|
||||
case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */
|
||||
if (mSPSTx != 4) {
|
||||
LOG(ERR) << "EDGE burst received but SPS is set to " << mSPSTx;
|
||||
if (cfg->tx_sps != 4) {
|
||||
LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << cfg->tx_sps;
|
||||
return false;
|
||||
}
|
||||
burstLen = EDGE_BURST_NBITS;
|
||||
break;
|
||||
default:
|
||||
LOG(ERR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
|
||||
LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -964,6 +1069,7 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||
|
||||
/* Convert TDMA FN to the host endianness */
|
||||
fn = osmo_load32be(&dl->common.fn);
|
||||
tn = dl->common.tn;
|
||||
|
||||
/* Make sure we support the received header format */
|
||||
switch (dl->common.version) {
|
||||
@@ -972,13 +1078,49 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
LOG(ERR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
|
||||
LOGCHAN(chan, DTRXDDL, ERROR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version) << "): "
|
||||
<< "fn=" << fn << ", tn=" << unsigned(dl->common.tn) << ", "
|
||||
<< "burst_len=" << burstLen;
|
||||
LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version)
|
||||
<< "): fn=" << fn << ", tn=" << unsigned(tn) << ", burst_len=" << burstLen;
|
||||
|
||||
TransceiverState *state = &mStates[chan];
|
||||
GSM::Time currTime = GSM::Time(fn, tn);
|
||||
|
||||
/* Verify proper FN order in DL stream */
|
||||
if (state->first_dl_fn_rcv[tn]) {
|
||||
int32_t delta = GSM::FNDelta(currTime.FN(), state->last_dl_time_rcv[tn].FN());
|
||||
if (delta == 1) {
|
||||
/* usual expected scenario, continue code flow */
|
||||
} else if (delta == 0) {
|
||||
LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with repeated FN " << currTime;
|
||||
state->ctrs.tx_trxd_fn_repeated++;
|
||||
dispatch_trx_rate_ctr_change(state, chan);
|
||||
return true;
|
||||
} else if (delta < 0) {
|
||||
LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with previous FN " << currTime
|
||||
<< " vs last " << state->last_dl_time_rcv[tn];
|
||||
state->ctrs.tx_trxd_fn_outoforder++;
|
||||
dispatch_trx_rate_ctr_change(state, chan);
|
||||
/* Allow adding radio vector below, since it gets sorted in the queue */
|
||||
} else if (chan == 0 && state->mFiller == FILLER_ZERO) {
|
||||
/* delta > 1. Some FN was lost in the middle. We can only easily rely
|
||||
* on consecutive FNs in TRX0 since it must transmit continuously in all
|
||||
* setups. Also, osmo-trx supports optionally filling empty bursts on
|
||||
* its own. In that case bts-trx is not obliged to submit all bursts. */
|
||||
LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with future FN " << currTime
|
||||
<< " vs last " << state->last_dl_time_rcv[tn]
|
||||
<< ", " << delta - 1 << " FN lost";
|
||||
state->ctrs.tx_trxd_fn_skipped += delta - 1;
|
||||
dispatch_trx_rate_ctr_change(state, chan);
|
||||
}
|
||||
if (delta > 0)
|
||||
state->last_dl_time_rcv[tn] = currTime;
|
||||
} else { /* Initial check, simply store state */
|
||||
state->first_dl_fn_rcv[tn] = true;
|
||||
state->last_dl_time_rcv[tn] = currTime;
|
||||
}
|
||||
|
||||
BitVector newBurst(burstLen);
|
||||
BitVector::iterator itr = newBurst.begin();
|
||||
@@ -986,8 +1128,6 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||
while (itr < newBurst.end())
|
||||
*itr++ = *bufferItr++;
|
||||
|
||||
GSM::Time currTime = GSM::Time(fn, dl->common.tn);
|
||||
|
||||
addRadioVector(chan, newBurst, dl->tx_att, currTime);
|
||||
|
||||
return true;
|
||||
@@ -1004,6 +1144,8 @@ bool Transceiver::driveReceiveRadio()
|
||||
return false;
|
||||
|
||||
if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
|
||||
if (mForceClockInterface)
|
||||
LOGC(DTRXCLK, NOTICE) << "Sending CLOCK indications";
|
||||
mForceClockInterface = false;
|
||||
return writeClockInterface();
|
||||
}
|
||||
@@ -1020,11 +1162,13 @@ void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
|
||||
else os << "-";
|
||||
}
|
||||
|
||||
LOGCHAN(chan, DMAIN, DEBUG) << std::fixed << std::right
|
||||
double rssi_offset = rssiOffset(chan);
|
||||
|
||||
LOGCHAN(chan, DTRXDUL, DEBUG) << std::fixed << std::right
|
||||
<< " time: " << unsigned(bi->tn) << ":" << bi->fn
|
||||
<< " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
|
||||
<< " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssi_offset)
|
||||
<< "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
|
||||
<< " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
|
||||
<< " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssi_offset)
|
||||
<< "dBFS/" << std::setw(6) << -bi->noise << "dBm"
|
||||
<< " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
|
||||
<< " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
|
||||
@@ -1038,13 +1182,13 @@ bool Transceiver::driveReceiveFIFO(size_t chan)
|
||||
|
||||
if ((rc = pullRadioVector(chan, &bi)) < 0) {
|
||||
if (rc == -ENOENT) { /* timeslot off, continue processing */
|
||||
LOGCHAN(chan, DMAIN, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
|
||||
LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
|
||||
return true;
|
||||
}
|
||||
return false; /* other errors: we want to stop the process */
|
||||
}
|
||||
|
||||
if (!bi.idle)
|
||||
if (!bi.idle && log_check_level(DTRXDUL, LOGL_DEBUG))
|
||||
logRxBurst(chan, &bi);
|
||||
|
||||
switch (mVersionTRXD[chan]) {
|
||||
@@ -1075,7 +1219,7 @@ void Transceiver::driveTxFIFO()
|
||||
|
||||
if (mOn) {
|
||||
//radioClock->wait(); // wait until clock updates
|
||||
LOG(DEBUG) << "radio clock " << radioClock->get();
|
||||
LOGC(DTRXCLK, DEBUG) << "radio clock " << radioClock->get();
|
||||
while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
|
||||
// if underrun, then we're not providing bursts to radio/USRP fast
|
||||
// enough. Need to increase latency by one GSM frame.
|
||||
@@ -1084,8 +1228,9 @@ void Transceiver::driveTxFIFO()
|
||||
// only update latency at the defined frame interval
|
||||
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
|
||||
mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
|
||||
LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
|
||||
<< radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
|
||||
LOGC(DTRXCLK, INFO) << "new latency: " << mTransmitLatency << " (underrun "
|
||||
<< radioClock->get() << " vs "
|
||||
<< mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
|
||||
mLatencyUpdateTime = radioClock->get();
|
||||
}
|
||||
}
|
||||
@@ -1095,7 +1240,7 @@ void Transceiver::driveTxFIFO()
|
||||
if (mTransmitLatency > mRadioInterface->minLatency()) {
|
||||
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
|
||||
mTransmitLatency.decTN();
|
||||
LOG(INFO) << "reduced latency: " << mTransmitLatency;
|
||||
LOGC(DTRXCLK, INFO) << "reduced latency: " << mTransmitLatency;
|
||||
mLatencyUpdateTime = radioClock->get();
|
||||
}
|
||||
}
|
||||
@@ -1119,11 +1264,11 @@ bool Transceiver::writeClockInterface()
|
||||
// FIXME -- This should be adaptive.
|
||||
sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
|
||||
|
||||
LOG(INFO) << "ClockInterface: sending " << command;
|
||||
LOGC(DTRXCLK, INFO) << "sending " << command;
|
||||
|
||||
msgLen = write(mClockSocket, command, strlen(command) + 1);
|
||||
if (msgLen <= 0) {
|
||||
LOG(ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
|
||||
LOGC(DTRXCLK, ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1141,10 +1286,11 @@ void *RxUpperLoopAdapter(TrxChanThParams *params)
|
||||
|
||||
snprintf(thread_name, 16, "RxUpper%zu", num);
|
||||
set_selfthread_name(thread_name);
|
||||
OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
|
||||
|
||||
while (1) {
|
||||
if (!trx->driveReceiveFIFO(num)) {
|
||||
LOGCHAN(num, DMAIN, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
|
||||
LOGCHAN(num, DTRXDUL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
|
||||
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
|
||||
break;
|
||||
}
|
||||
@@ -1156,10 +1302,11 @@ void *RxUpperLoopAdapter(TrxChanThParams *params)
|
||||
void *RxLowerLoopAdapter(Transceiver *transceiver)
|
||||
{
|
||||
set_selfthread_name("RxLower");
|
||||
OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
|
||||
|
||||
while (1) {
|
||||
if (!transceiver->driveReceiveRadio()) {
|
||||
LOG(FATAL) << "Something went wrong in thread RxLower, requesting stop";
|
||||
LOGC(DTRXDUL, FATAL) << "Something went wrong in thread RxLower, requesting stop";
|
||||
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
|
||||
break;
|
||||
}
|
||||
@@ -1171,6 +1318,7 @@ void *RxLowerLoopAdapter(Transceiver *transceiver)
|
||||
void *TxLowerLoopAdapter(Transceiver *transceiver)
|
||||
{
|
||||
set_selfthread_name("TxLower");
|
||||
OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
|
||||
|
||||
while (1) {
|
||||
transceiver->driveTxFIFO();
|
||||
@@ -1179,28 +1327,6 @@ void *TxLowerLoopAdapter(Transceiver *transceiver)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ControlServiceLoopAdapter(TrxChanThParams *params)
|
||||
{
|
||||
char thread_name[16];
|
||||
Transceiver *trx = params->trx;
|
||||
size_t num = params->num;
|
||||
|
||||
free(params);
|
||||
|
||||
snprintf(thread_name, 16, "CtrlService%zu", num);
|
||||
set_selfthread_name(thread_name);
|
||||
|
||||
while (1) {
|
||||
if (!trx->driveControl(num)) {
|
||||
LOGCHAN(num, DTRXCTRL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
|
||||
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
|
||||
break;
|
||||
}
|
||||
pthread_testcancel();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *TxUpperLoopAdapter(TrxChanThParams *params)
|
||||
{
|
||||
char thread_name[16];
|
||||
@@ -1211,10 +1337,11 @@ void *TxUpperLoopAdapter(TrxChanThParams *params)
|
||||
|
||||
snprintf(thread_name, 16, "TxUpper%zu", num);
|
||||
set_selfthread_name(thread_name);
|
||||
OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
|
||||
|
||||
while (1) {
|
||||
if (!trx->driveTxPriorityQueue(num)) {
|
||||
LOGCHAN(num, DMAIN, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
|
||||
LOGCHAN(num, DTRXDDL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
|
||||
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -33,11 +33,14 @@
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/signal.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include "config_defs.h"
|
||||
}
|
||||
|
||||
class Transceiver;
|
||||
|
||||
extern Transceiver *transceiver;
|
||||
|
||||
/** Channel descriptor for transceiver object and channel number pair */
|
||||
struct TrxChanThParams {
|
||||
Transceiver *trx;
|
||||
@@ -60,6 +63,7 @@ struct TransceiverState {
|
||||
/* The filler table */
|
||||
signalVector *fillerTable[102][8];
|
||||
int fillerModulus[8];
|
||||
FillerType mFiller;
|
||||
bool mRetrans;
|
||||
|
||||
/* Most recent channel estimate of all timeslots */
|
||||
@@ -80,33 +84,35 @@ struct TransceiverState {
|
||||
|
||||
/* Shadowed downlink attenuation */
|
||||
int mPower;
|
||||
|
||||
/* RF emission and reception disabled, as per NM Administrative State Locked */
|
||||
bool mMuted;
|
||||
|
||||
/* counters */
|
||||
struct trx_counters ctrs;
|
||||
|
||||
/* Used to keep track of lost and out of order frames */
|
||||
bool first_dl_fn_rcv[8];
|
||||
GSM::Time last_dl_time_rcv[8];
|
||||
};
|
||||
|
||||
/** The Transceiver class, responsible for physical layer of basestation */
|
||||
class Transceiver {
|
||||
public:
|
||||
/** Transceiver constructor
|
||||
@param wBasePort base port number of UDP sockets
|
||||
@param TRXAddress IP address of the TRX, as a string
|
||||
@param GSMcoreAddress IP address of the GSM core, as a string
|
||||
@param wSPS number of samples per GSM symbol
|
||||
@param cfg VTY populated config
|
||||
@param wTransmitLatency initial setting of transmit latency
|
||||
@param radioInterface associated radioInterface object
|
||||
*/
|
||||
Transceiver(int wBasePort,
|
||||
const char *TRXAddress,
|
||||
const char *GSMcoreAddress,
|
||||
size_t tx_sps, size_t rx_sps, size_t chans,
|
||||
Transceiver(const struct trx_cfg *cfg,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface,
|
||||
double wRssiOffset, int stackSize);
|
||||
RadioInterface *wRadioInterface);
|
||||
|
||||
/** Destructor */
|
||||
~Transceiver();
|
||||
|
||||
/** Start the control loop */
|
||||
bool init(FillerType filler, size_t rtsc, unsigned rach_delay,
|
||||
bool edge, bool ext_rach);
|
||||
bool init(void);
|
||||
|
||||
/** attach the radioInterface receive FIFO */
|
||||
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
||||
@@ -119,7 +125,7 @@ public:
|
||||
}
|
||||
|
||||
/** accessor for number of channels */
|
||||
size_t numChans() const { return mChans; };
|
||||
size_t numChans() const { return cfg->num_chans; };
|
||||
|
||||
/** Codes for channel combinations */
|
||||
typedef enum {
|
||||
@@ -142,12 +148,29 @@ public:
|
||||
} ChannelCombination;
|
||||
|
||||
private:
|
||||
int mBasePort;
|
||||
std::string mLocalAddr;
|
||||
std::string mRemoteAddr;
|
||||
struct ctrl_msg {
|
||||
char data[101];
|
||||
ctrl_msg() {};
|
||||
};
|
||||
|
||||
struct ctrl_sock_state {
|
||||
osmo_fd conn_bfd;
|
||||
std::deque<ctrl_msg> txmsgqueue;
|
||||
ctrl_sock_state() {
|
||||
conn_bfd.fd = -1;
|
||||
}
|
||||
~ctrl_sock_state() {
|
||||
if(conn_bfd.fd >= 0) {
|
||||
close(conn_bfd.fd);
|
||||
conn_bfd.fd = -1;
|
||||
osmo_fd_unregister(&conn_bfd);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const struct trx_cfg *cfg; ///< VTY populated config
|
||||
std::vector<int> mDataSockets; ///< socket for writing to/reading from GSM core
|
||||
std::vector<int> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||
std::vector<ctrl_sock_state> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||
int mClockSocket; ///< socket for writing clock updates to GSM core
|
||||
|
||||
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
||||
@@ -156,7 +179,6 @@ private:
|
||||
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
|
||||
Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
|
||||
Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
|
||||
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
||||
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
||||
|
||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||
@@ -168,9 +190,6 @@ private:
|
||||
double txFullScale; ///< full scale input to radio
|
||||
double rxFullScale; ///< full scale output to radio
|
||||
|
||||
double rssiOffset; ///< RSSI to dBm conversion offset
|
||||
int stackSize; ///< stack size for threads, 0 = OS default
|
||||
|
||||
/** modulate and add a burst to the transmit queue */
|
||||
void addRadioVector(size_t chan, BitVector &bits,
|
||||
int RSSI, GSM::Time &wTime);
|
||||
@@ -193,12 +212,13 @@ private:
|
||||
/** send messages over the clock socket */
|
||||
bool writeClockInterface(void);
|
||||
|
||||
int mSPSTx; ///< number of samples per Tx symbol
|
||||
int mSPSRx; ///< number of samples per Rx symbol
|
||||
size_t mChans;
|
||||
static int ctrl_sock_cb(struct osmo_fd *bfd, unsigned int flags);
|
||||
int ctrl_sock_write(int chan);
|
||||
void ctrl_sock_send(ctrl_msg& m, int chan);
|
||||
/** drive handling of control messages from GSM core */
|
||||
int ctrl_sock_handle_rx(int chan);
|
||||
|
||||
bool mExtRACH;
|
||||
bool mEdge;
|
||||
size_t mChans;
|
||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||
bool mForceClockInterface; ///< flag to indicate whether IND CLOCK shall be sent unconditionally after transceiver is started
|
||||
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
|
||||
@@ -216,7 +236,7 @@ private:
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
/** Protect destructor accessable stop call */
|
||||
/** Protect destructor accessible stop call */
|
||||
Mutex mLock;
|
||||
|
||||
protected:
|
||||
@@ -229,9 +249,6 @@ protected:
|
||||
/** drive transmission of GSM bursts */
|
||||
void driveTxFIFO();
|
||||
|
||||
/** drive handling of control messages from GSM core */
|
||||
bool driveControl(size_t chan);
|
||||
|
||||
/**
|
||||
drive modulation and sorting of GSM bursts from GSM core
|
||||
@return true if a burst was transferred successfully
|
||||
@@ -242,9 +259,8 @@ protected:
|
||||
friend void *TxUpperLoopAdapter(TrxChanThParams *params);
|
||||
friend void *RxLowerLoopAdapter(Transceiver *transceiver);
|
||||
friend void *TxLowerLoopAdapter(Transceiver *transceiver);
|
||||
friend void *ControlServiceLoopAdapter(TrxChanThParams *params);
|
||||
|
||||
|
||||
double rssiOffset(size_t chan);
|
||||
void reset();
|
||||
|
||||
void logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi);
|
||||
@@ -256,8 +272,5 @@ void *RxUpperLoopAdapter(TrxChanThParams *params);
|
||||
void *RxLowerLoopAdapter(Transceiver *transceiver);
|
||||
void *TxLowerLoopAdapter(Transceiver *transceiver);
|
||||
|
||||
/** control message handler thread loop */
|
||||
void *ControlServiceLoopAdapter(TrxChanThParams *params);
|
||||
|
||||
/** transmit queueing thread loop */
|
||||
void *TxUpperLoopAdapter(TrxChanThParams *params);
|
||||
|
||||
@@ -58,7 +58,7 @@ static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* API: Initalize convolve module */
|
||||
/* API: Initialize convolve module */
|
||||
void convolve_init(void)
|
||||
{
|
||||
/* Stub */
|
||||
|
||||
@@ -103,7 +103,7 @@ void free_fft(struct fft_hdl *hdl)
|
||||
}
|
||||
|
||||
/*! \brief Run multiple DFT operations with the initialized plan
|
||||
* \param[in] hdl handle to an intitialized fft struct
|
||||
* \param[in] hdl handle to an initialized fft struct
|
||||
*
|
||||
* Input and output buffers are configured with init_fft().
|
||||
*/
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/* Architecture dependant function pointers */
|
||||
/* Architecture dependent function pointers */
|
||||
struct convert_cpu_context {
|
||||
void (*convert_si16_ps_16n) (float *, const short *, int);
|
||||
void (*convert_si16_ps) (float *, const short *, int);
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/* Architecture dependant function pointers */
|
||||
/* Architecture dependent function pointers */
|
||||
struct convolve_cpu_context {
|
||||
void (*conv_cmplx_4n) (const float *, int, const float *, int, float *,
|
||||
int, int, int);
|
||||
@@ -66,7 +66,7 @@ int _base_convolve_complex(const float *x, int x_len,
|
||||
int bounds_check(int x_len, int h_len, int y_len,
|
||||
int start, int len);
|
||||
|
||||
/* API: Initalize convolve module */
|
||||
/* API: Initialize convolve module */
|
||||
void convolve_init(void)
|
||||
{
|
||||
c.conv_cmplx_4n = (void *)_base_convolve_complex;
|
||||
@@ -99,9 +99,10 @@ int convolve_real(const float *x, int x_len,
|
||||
const float *h, int h_len,
|
||||
float *y, int y_len, int start, int len)
|
||||
{
|
||||
#ifndef __OPTIMIZE__
|
||||
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
||||
return -1;
|
||||
|
||||
#endif
|
||||
memset(y, 0, len * 2 * sizeof(float));
|
||||
|
||||
switch (h_len) {
|
||||
@@ -138,9 +139,10 @@ int convolve_complex(const float *x, int x_len,
|
||||
float *y, int y_len,
|
||||
int start, int len)
|
||||
{
|
||||
#ifndef __OPTIMIZE__
|
||||
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
|
||||
return -1;
|
||||
|
||||
#endif
|
||||
memset(y, 0, len * 2 * sizeof(float));
|
||||
|
||||
if (!(h_len % 8))
|
||||
|
||||
@@ -2,6 +2,10 @@ include $(top_srcdir)/Makefile.common
|
||||
|
||||
SUBDIRS = common
|
||||
|
||||
if DEVICE_IPC
|
||||
SUBDIRS += ipc
|
||||
endif
|
||||
|
||||
if DEVICE_USRP1
|
||||
SUBDIRS += usrp1
|
||||
endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
|
||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
|
||||
|
||||
|
||||
noinst_HEADERS = radioDevice.h smpl_buf.h
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
@@ -77,23 +77,20 @@ class RadioDevice {
|
||||
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
|
||||
@param timestamp The timestamp of the first samples to be read
|
||||
@param underrun Set if radio does not have data to transmit, e.g. data not being sent fast enough
|
||||
@param RSSI The received signal strength of the read result
|
||||
@return The number of samples actually read
|
||||
*/
|
||||
virtual int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0,
|
||||
unsigned *RSSI = 0) = 0;
|
||||
TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0) = 0;
|
||||
/**
|
||||
Write samples to the radio.
|
||||
@param buf Contains the data to be written.
|
||||
@param len number of samples to write.
|
||||
@param underrun Set if radio does not have data to transmit, e.g. data not being sent fast enough
|
||||
@param timestamp The timestamp of the first sample of the data buffer.
|
||||
@param isControl Set if data is a control packet, e.g. a ping command
|
||||
@return The number of samples actually written
|
||||
*/
|
||||
virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
TIMESTAMP timestamp, bool isControl = false) = 0;
|
||||
TIMESTAMP timestamp) = 0;
|
||||
|
||||
/** Update the alignment between the read and write timestamps */
|
||||
virtual bool updateAlignment(TIMESTAMP timestamp)=0;
|
||||
@@ -128,17 +125,11 @@ class RadioDevice {
|
||||
/** return minimum Rx Gain **/
|
||||
virtual double minRxGain(void) = 0;
|
||||
|
||||
/** sets the transmit chan gain, returns the gain setting **/
|
||||
virtual double setTxGain(double dB, size_t chan = 0) = 0;
|
||||
/** return base RSSI offset to apply for received samples **/
|
||||
virtual double rssiOffset(size_t chan) = 0;
|
||||
|
||||
/** get transmit gain */
|
||||
virtual double getTxGain(size_t chan = 0) = 0;
|
||||
|
||||
/** return maximum Tx Gain **/
|
||||
virtual double maxTxGain(void) = 0;
|
||||
|
||||
/** return minimum Tx Gain **/
|
||||
virtual double minTxGain(void) = 0;
|
||||
/** returns the Nominal transmit output power of the transceiver in dBm, negative on error **/
|
||||
virtual int getNominalTxPower(size_t chan = 0) = 0;
|
||||
|
||||
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||
virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) = 0;
|
||||
@@ -163,6 +154,9 @@ class RadioDevice {
|
||||
virtual double getRxFreq(size_t chan = 0) = 0;
|
||||
virtual double getSampleRate()=0;
|
||||
|
||||
virtual double setPowerAttenuation(int atten, size_t chan) = 0;
|
||||
virtual double getPowerAttenuation(size_t chan=0) = 0;
|
||||
|
||||
protected:
|
||||
size_t tx_sps, rx_sps;
|
||||
InterfaceType iface;
|
||||
@@ -195,6 +189,13 @@ class RadioDevice {
|
||||
for (i = 0; i < tx_paths.size(); i++) {
|
||||
if (tx_paths[i] == "")
|
||||
continue;
|
||||
if (iface == MULTI_ARFCN && i > 0) {
|
||||
LOGCHAN(i, DDEV, NOTICE) << "Not setting Tx antenna "
|
||||
<< tx_paths[i]
|
||||
<< " for a logical channel";
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGCHAN(i, DDEV, DEBUG) << "Configuring Tx antenna " << tx_paths[i];
|
||||
if (!setTxAntenna(tx_paths[i], i)) {
|
||||
LOGCHAN(i, DDEV, ALERT) << "Failed configuring Tx antenna " << tx_paths[i];
|
||||
@@ -205,6 +206,13 @@ class RadioDevice {
|
||||
for (i = 0; i < rx_paths.size(); i++) {
|
||||
if (rx_paths[i] == "")
|
||||
continue;
|
||||
if (iface == MULTI_ARFCN && i > 0) {
|
||||
LOGCHAN(i, DDEV, NOTICE) << "Not setting Rx antenna "
|
||||
<< rx_paths[i]
|
||||
<< " for a logical channel";
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGCHAN(i, DDEV, DEBUG) << "Configuring Rx antenna " << rx_paths[i];
|
||||
if (!setRxAntenna(rx_paths[i], i)) {
|
||||
LOGCHAN(i, DDEV, ALERT) << "Failed configuring Rx antenna " << rx_paths[i];
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
smpl_buf::smpl_buf(size_t len)
|
||||
: buf_len(len), time_start(0), time_end(0),
|
||||
data_start(0), data_end(0)
|
||||
: buf_len(len)
|
||||
{
|
||||
reset();
|
||||
data = new uint32_t[len];
|
||||
}
|
||||
|
||||
@@ -39,6 +39,14 @@ smpl_buf::~smpl_buf()
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
void smpl_buf::reset()
|
||||
{
|
||||
time_start = 0;
|
||||
time_end = 0;
|
||||
data_start = 0;
|
||||
data_end = 0;
|
||||
}
|
||||
|
||||
ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
|
||||
{
|
||||
if (timestamp < time_start)
|
||||
@@ -154,7 +162,7 @@ std::string smpl_buf::str_status(TIMESTAMP timestamp) const
|
||||
return ost.str();
|
||||
}
|
||||
|
||||
std::string smpl_buf::str_code(ssize_t code)
|
||||
std::string smpl_buf::str_code(int code)
|
||||
{
|
||||
switch (code) {
|
||||
case ERROR_TIMESTAMP:
|
||||
@@ -166,6 +174,8 @@ std::string smpl_buf::str_code(ssize_t code)
|
||||
case ERROR_OVERFLOW:
|
||||
return "Sample buffer: Overrun";
|
||||
default:
|
||||
return "Sample buffer: Unknown error";
|
||||
std::stringstream ss;
|
||||
ss << "Sample buffer: Unknown error " << code;
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
/*
|
||||
Sample Buffer - Allows reading and writing of timed samples using osmo-trx
|
||||
timestamps. Time conversions are handled
|
||||
internally or accessable through the static convert calls.
|
||||
internally or accessible through the static convert calls.
|
||||
*/
|
||||
class smpl_buf {
|
||||
public:
|
||||
@@ -44,6 +44,10 @@ public:
|
||||
smpl_buf(size_t len);
|
||||
~smpl_buf();
|
||||
|
||||
/** Reset this buffer, keeps the size
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/** Query number of samples available for reading
|
||||
@param timestamp time of first sample
|
||||
@return number of available samples or error
|
||||
@@ -68,7 +72,7 @@ public:
|
||||
@param code an error code
|
||||
@return a formatted error string
|
||||
*/
|
||||
static std::string str_code(ssize_t code);
|
||||
static std::string str_code(int code);
|
||||
|
||||
enum err_code {
|
||||
ERROR_TIMESTAMP = -1,
|
||||
|
||||
1271
Transceiver52M/device/ipc/IPCDevice.cpp
Normal file
1271
Transceiver52M/device/ipc/IPCDevice.cpp
Normal file
File diff suppressed because it is too large
Load Diff
242
Transceiver52M/device/ipc/IPCDevice.h
Normal file
242
Transceiver52M/device/ipc/IPCDevice.h
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*/
|
||||
|
||||
#ifndef _IPC_DEVICE_H_
|
||||
#define _IPC_DEVICE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <climits>
|
||||
#include <string>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include "shm.h"
|
||||
}
|
||||
|
||||
#include "radioDevice.h"
|
||||
|
||||
class smpl_buf;
|
||||
|
||||
#define IPC_MAX_NUM_TRX 8
|
||||
|
||||
struct ipc_per_trx_sock_state {
|
||||
struct osmo_fd conn_bfd; /* fd for connection to the BTS */
|
||||
struct osmo_timer_list timer; /* socket connect retry timer */
|
||||
struct llist_head upqueue; /* queue for sending messages */
|
||||
uint32_t messages_processed_mask; // (=| IPC_IF_MSG_xxx-IPC_IF_CHAN_MSG_OFFSET) bitmask
|
||||
ipc_per_trx_sock_state() : conn_bfd(), timer(), upqueue(), messages_processed_mask()
|
||||
{
|
||||
conn_bfd.fd = -1;
|
||||
}
|
||||
};
|
||||
|
||||
class IPCDevice : public RadioDevice {
|
||||
protected:
|
||||
struct ipc_per_trx_sock_state master_sk_state;
|
||||
|
||||
std::vector<struct ipc_per_trx_sock_state> sk_chan_state;
|
||||
|
||||
uint32_t tx_attenuation[IPC_MAX_NUM_TRX];
|
||||
uint8_t tmp_state;
|
||||
char shm_name[SHM_NAME_MAX];
|
||||
int ipc_shm_connect(const char *shm_name);
|
||||
void *shm;
|
||||
struct ipc_shm_region *shm_dec;
|
||||
|
||||
std::vector<smpl_buf *> rx_buffers;
|
||||
double actualSampleRate;
|
||||
|
||||
bool started;
|
||||
|
||||
TIMESTAMP ts_initial, ts_offset;
|
||||
|
||||
std::vector<double> tx_gains, rx_gains;
|
||||
|
||||
struct ipc_sk_if_info_req current_info_req;
|
||||
struct ipc_sk_if_info_cnf current_info_cnf;
|
||||
struct ipc_sk_if_open_cnf current_open_cnf;
|
||||
|
||||
std::vector<struct ipc_shm_io *> shm_io_rx_streams;
|
||||
std::vector<struct ipc_shm_io *> shm_io_tx_streams;
|
||||
|
||||
bool flush_recv(size_t num_pkts);
|
||||
void update_stream_stats_rx(size_t chan, bool *overrun);
|
||||
void update_stream_stats_tx(size_t chan, bool *underrun);
|
||||
void manually_poll_sock_fds();
|
||||
|
||||
void ipc_sock_close(ipc_per_trx_sock_state *state);
|
||||
|
||||
int ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim);
|
||||
int ipc_rx_greeting_cnf(const struct ipc_sk_if_greeting *greeting_cnf);
|
||||
int ipc_rx_info_cnf(const struct ipc_sk_if_info_cnf *info_cnf);
|
||||
int ipc_rx_open_cnf(const struct ipc_sk_if_open_cnf *open_cnf);
|
||||
int ipc_tx_open_req(struct ipc_per_trx_sock_state *state, uint32_t num_chans, uint32_t ref);
|
||||
|
||||
int ipc_chan_rx(uint8_t msg_type, ipc_sk_chan_if *ipc_prim, uint8_t chan_nr);
|
||||
int ipc_rx_chan_start_cnf(ipc_sk_chan_if_op_rc *ret, uint8_t chan_nr);
|
||||
int ipc_rx_chan_stop_cnf(ipc_sk_chan_if_op_rc *ret, uint8_t chan_nr);
|
||||
int ipc_rx_chan_setgain_cnf(ipc_sk_chan_if_gain *ret, uint8_t chan_nr);
|
||||
int ipc_rx_chan_setfreq_cnf(ipc_sk_chan_if_freq_cnf *ret, uint8_t chan_nr);
|
||||
int ipc_rx_chan_notify_underflow(ipc_sk_chan_if_notfiy *ret, uint8_t chan_nr);
|
||||
int ipc_rx_chan_notify_overflow(ipc_sk_chan_if_notfiy *ret, uint8_t chan_nr);
|
||||
int ipc_rx_chan_settxattn_cnf(ipc_sk_chan_if_tx_attenuation *ret, uint8_t chan_nr);
|
||||
|
||||
bool send_chan_wait_rsp(uint32_t chan, struct msgb *msg_to_send, uint32_t expected_rsp_msg_id);
|
||||
bool send_all_chan_wait_rsp(uint32_t msgid_to_send, uint32_t msgid_to_expect);
|
||||
|
||||
public:
|
||||
int ipc_sock_read(struct osmo_fd *bfd);
|
||||
int ipc_sock_write(struct osmo_fd *bfd);
|
||||
int ipc_chan_sock_read(osmo_fd *bfd);
|
||||
int ipc_chan_sock_write(osmo_fd *bfd);
|
||||
|
||||
/** Object constructor */
|
||||
IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths);
|
||||
virtual ~IPCDevice() override;
|
||||
|
||||
/** Instantiate the IPC */
|
||||
virtual int open(const std::string &args, int ref, bool swap_channels) override;
|
||||
|
||||
/** Start the IPC */
|
||||
virtual bool start() override;
|
||||
|
||||
/** Stop the IPC */
|
||||
virtual bool stop() override;
|
||||
|
||||
/* FIXME: any != USRP1 will do for now... */
|
||||
enum TxWindowType getWindowType() override
|
||||
{
|
||||
return TX_WINDOW_LMS1;
|
||||
}
|
||||
|
||||
/**
|
||||
Read samples from the IPC.
|
||||
@param buf preallocated buf to contain read result
|
||||
@param len number of samples desired
|
||||
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
|
||||
@param timestamp The timestamp of the first samples to be read
|
||||
@param underrun Set if IPC does not have data to transmit, e.g. data not being sent fast enough
|
||||
@return The number of samples actually read
|
||||
*/
|
||||
virtual int readSamples(std::vector<short *> &buf, int len, bool *overrun, TIMESTAMP timestamp = 0xffffffff,
|
||||
bool *underrun = NULL) override;
|
||||
/**
|
||||
Write samples to the IPC.
|
||||
@param buf Contains the data to be written.
|
||||
@param len number of samples to write.
|
||||
@param underrun Set if IPC does not have data to transmit, e.g. data not being sent fast enough
|
||||
@param timestamp The timestamp of the first sample of the data buffer.
|
||||
@return The number of samples actually written
|
||||
*/
|
||||
virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
TIMESTAMP timestamp = 0xffffffff) override;
|
||||
|
||||
/** Update the alignment between the read and write timestamps */
|
||||
virtual bool updateAlignment(TIMESTAMP timestamp) override;
|
||||
|
||||
/** Set the transmitter frequency */
|
||||
virtual bool setTxFreq(double wFreq, size_t chan = 0) override;
|
||||
|
||||
/** Set the receiver frequency */
|
||||
virtual bool setRxFreq(double wFreq, size_t chan = 0) override;
|
||||
|
||||
/** Returns the starting write Timestamp*/
|
||||
virtual TIMESTAMP initialWriteTimestamp(void) override;
|
||||
|
||||
/** Returns the starting read Timestamp*/
|
||||
virtual TIMESTAMP initialReadTimestamp(void) override;
|
||||
|
||||
/** returns the full-scale transmit amplitude **/
|
||||
virtual double fullScaleInputValue() override
|
||||
{
|
||||
return (double)SHRT_MAX * current_info_cnf.iq_scaling_val_rx;
|
||||
}
|
||||
|
||||
/** returns the full-scale receive amplitude **/
|
||||
virtual double fullScaleOutputValue() override
|
||||
{
|
||||
return (double)SHRT_MAX * current_info_cnf.iq_scaling_val_tx;
|
||||
}
|
||||
|
||||
/** sets the receive chan gain, returns the gain setting **/
|
||||
virtual double setRxGain(double dB, size_t chan = 0) override;
|
||||
|
||||
/** get the current receive gain */
|
||||
virtual double getRxGain(size_t chan = 0) override
|
||||
{
|
||||
return rx_gains[chan];
|
||||
}
|
||||
|
||||
/** return maximum Rx Gain **/
|
||||
virtual double maxRxGain(void) override;
|
||||
|
||||
/** return minimum Rx Gain **/
|
||||
virtual double minRxGain(void) override;
|
||||
|
||||
/* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */
|
||||
double rssiOffset(size_t chan) { return 0.0f; };
|
||||
|
||||
double setPowerAttenuation(int atten, size_t chan) override;
|
||||
double getPowerAttenuation(size_t chan = 0) override;
|
||||
|
||||
virtual int getNominalTxPower(size_t chan = 0) override;
|
||||
|
||||
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||
virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) override;
|
||||
|
||||
/* return the used RX path */
|
||||
virtual std::string getRxAntenna(size_t chan = 0) override;
|
||||
|
||||
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||
virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) override;
|
||||
|
||||
/* return the used RX path */
|
||||
virtual std::string getTxAntenna(size_t chan = 0) override;
|
||||
|
||||
/** return whether user drives synchronization of Tx/Rx of USRP */
|
||||
virtual bool requiresRadioAlign() override;
|
||||
|
||||
/** return whether user drives synchronization of Tx/Rx of USRP */
|
||||
virtual GSM::Time minLatency() override;
|
||||
|
||||
/** Return internal status values */
|
||||
virtual inline double getTxFreq(size_t chan = 0) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual inline double getRxFreq(size_t chan = 0) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual inline double getSampleRate() override
|
||||
{
|
||||
return actualSampleRate;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _IPC_DEVICE_H_
|
||||
41
Transceiver52M/device/ipc/Makefile.am
Normal file
41
Transceiver52M/device/ipc/Makefile.am
Normal file
@@ -0,0 +1,41 @@
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
|
||||
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
|
||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
|
||||
AM_LDFLAGS = -lpthread -lrt
|
||||
|
||||
noinst_HEADERS = IPCDevice.h shm.h ipc_shm.h ipc_chan.h ipc_sock.h
|
||||
|
||||
if DEVICE_UHD
|
||||
noinst_HEADERS += ../uhd/UHDDevice.h uhdwrap.h ipc-driver-test.h
|
||||
endif
|
||||
|
||||
noinst_LTLIBRARIES = libdevice.la
|
||||
|
||||
libdevice_la_SOURCES = IPCDevice.cpp shm.c ipc_shm.c ipc_chan.c ipc_sock.c
|
||||
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
|
||||
libdevice_la_CXXFLAGS = $(AM_CXXFLAGS) -DIPCMAGIC
|
||||
|
||||
if DEVICE_UHD
|
||||
|
||||
#work around distclean issue on older autotools vers:
|
||||
#a direct build of ../uhd/UHDDevice.cpp tries to clean
|
||||
#../uhd/.dep/UHDDevice.Plo twice and fails
|
||||
uhddev_ipc.cpp:
|
||||
echo "#include \"../uhd/UHDDevice.cpp\"" >$@
|
||||
CLEANFILES= uhddev_ipc.cpp
|
||||
|
||||
bin_PROGRAMS = ipc-driver-test
|
||||
#ipc_driver_test_SHORTNAME = drvt
|
||||
ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ipc_shm.c ipc_chan.c ipc_sock.c uhddev_ipc.cpp
|
||||
ipc_driver_test_LDADD = \
|
||||
shm.lo \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
ipc_driver_test_CXXFLAGS = $(AM_CXXFLAGS) $(UHD_CFLAGS)
|
||||
ipc_driver_test_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
|
||||
ipc_driver_test_CFLAGS = $(AM_CFLAGS) $(UHD_CFLAGS)
|
||||
ipc_driver_test_LDFLAGS = $(AM_LDFLAGS) $(UHD_LIBS)
|
||||
ipc_driver_test_LDADD += $(top_builddir)/Transceiver52M/device/common/libdevice_common.la $(top_builddir)/CommonLibs/libcommon.la
|
||||
endif
|
||||
492
Transceiver52M/device/ipc/ipc-driver-test.c
Normal file
492
Transceiver52M/device/ipc/ipc-driver-test.c
Normal file
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
|
||||
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <pthread.h>
|
||||
|
||||
#include <debug.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h> /* For mode constants */
|
||||
#include <fcntl.h> /* For O_* constants */
|
||||
#include <getopt.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include "shm.h"
|
||||
#include "ipc_shm.h"
|
||||
#include "ipc_chan.h"
|
||||
#include "ipc_sock.h"
|
||||
|
||||
#define DEFAULT_SHM_NAME "/osmo-trx-ipc-driver-shm2"
|
||||
#define IPC_SOCK_PATH_PREFIX "/tmp"
|
||||
|
||||
static void *tall_ctx;
|
||||
struct ipc_sock_state *global_ipc_sock_state;
|
||||
|
||||
/* 8 channels are plenty */
|
||||
struct ipc_sock_state *global_ctrl_socks[8];
|
||||
struct ipc_shm_io *ios_tx_to_device[8];
|
||||
struct ipc_shm_io *ios_rx_from_device[8];
|
||||
|
||||
void *shm;
|
||||
void *global_dev;
|
||||
|
||||
static struct ipc_shm_region *decoded_region;
|
||||
|
||||
static struct {
|
||||
int msocknum;
|
||||
char *ud_prefix_dir;
|
||||
} cmdline_cfg;
|
||||
|
||||
static const struct log_info_cat default_categories[] = {
|
||||
[DMAIN] = {
|
||||
.name = "DMAIN",
|
||||
.color = NULL,
|
||||
.description = "Main generic category",
|
||||
.loglevel = LOGL_DEBUG,.enabled = 1,
|
||||
},
|
||||
[DDEV] = {
|
||||
.name = "DDEV",
|
||||
.description = "Device/Driver specific code",
|
||||
.color = NULL,
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info log_infox = {
|
||||
.cat = default_categories,
|
||||
.num_cat = ARRAY_SIZE(default_categories),
|
||||
};
|
||||
|
||||
#include "uhdwrap.h"
|
||||
|
||||
volatile int ipc_exit_requested = 0;
|
||||
|
||||
static int ipc_shm_setup(const char *shm_name, size_t shm_len)
|
||||
{
|
||||
int fd;
|
||||
int rc;
|
||||
|
||||
LOGP(DMAIN, LOGL_NOTICE, "Opening shm path %s\n", shm_name);
|
||||
if ((fd = shm_open(shm_name, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
|
||||
LOGP(DMAIN, LOGL_ERROR, "shm_open %d: %s\n", errno, strerror(errno));
|
||||
rc = -errno;
|
||||
goto err_shm_open;
|
||||
}
|
||||
|
||||
LOGP(DMAIN, LOGL_NOTICE, "Truncating %d to size %zu\n", fd, shm_len);
|
||||
if (ftruncate(fd, shm_len) < 0) {
|
||||
LOGP(DMAIN, LOGL_ERROR, "ftruncate %d: %s\n", errno, strerror(errno));
|
||||
rc = -errno;
|
||||
goto err_mmap;
|
||||
}
|
||||
|
||||
LOGP(DMAIN, LOGL_NOTICE, "mmaping shared memory fd %d\n", fd);
|
||||
if ((shm = mmap(NULL, shm_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
|
||||
LOGP(DMAIN, LOGL_ERROR, "mmap %d: %s\n", errno, strerror(errno));
|
||||
rc = -errno;
|
||||
goto err_mmap;
|
||||
}
|
||||
|
||||
LOGP(DMAIN, LOGL_NOTICE, "mmap'ed shared memory at addr %p\n", shm);
|
||||
/* After a call to mmap(2) the file descriptor may be closed without affecting the memory mapping. */
|
||||
close(fd);
|
||||
return 0;
|
||||
err_mmap:
|
||||
shm_unlink(shm_name);
|
||||
close(fd);
|
||||
err_shm_open:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct msgb *ipc_msgb_alloc(uint8_t msg_type)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct ipc_sk_if *ipc_prim;
|
||||
|
||||
msg = msgb_alloc(sizeof(struct ipc_sk_if) + 1000, "ipc_sock_tx");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
msgb_put(msg, sizeof(struct ipc_sk_if) + 1000);
|
||||
ipc_prim = (struct ipc_sk_if *)msg->data;
|
||||
ipc_prim->msg_type = msg_type;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static int ipc_tx_greeting_cnf(uint8_t req_version)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct ipc_sk_if *ipc_prim;
|
||||
|
||||
msg = ipc_msgb_alloc(IPC_IF_MSG_GREETING_CNF);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
ipc_prim = (struct ipc_sk_if *)msg->data;
|
||||
ipc_prim->u.greeting_cnf.req_version = req_version;
|
||||
|
||||
return ipc_sock_send(msg);
|
||||
}
|
||||
|
||||
static int ipc_tx_info_cnf()
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct ipc_sk_if *ipc_prim;
|
||||
|
||||
msg = ipc_msgb_alloc(IPC_IF_MSG_INFO_CNF);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
ipc_prim = (struct ipc_sk_if *)msg->data;
|
||||
|
||||
uhdwrap_fill_info_cnf(ipc_prim);
|
||||
|
||||
return ipc_sock_send(msg);
|
||||
}
|
||||
|
||||
static int ipc_tx_open_cnf(int rc, uint32_t num_chans, int32_t timingoffset)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct ipc_sk_if *ipc_prim;
|
||||
struct ipc_sk_if_open_cnf_chan *chan_info;
|
||||
unsigned int i;
|
||||
|
||||
msg = ipc_msgb_alloc(IPC_IF_MSG_OPEN_CNF);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
ipc_prim = (struct ipc_sk_if *)msg->data;
|
||||
ipc_prim->u.open_cnf.return_code = rc;
|
||||
ipc_prim->u.open_cnf.path_delay = timingoffset; // 6.18462e-5 * 1625e3 / 6;
|
||||
OSMO_STRLCPY_ARRAY(ipc_prim->u.open_cnf.shm_name, DEFAULT_SHM_NAME);
|
||||
|
||||
chan_info = ipc_prim->u.open_cnf.chan_info;
|
||||
for (i = 0; i < num_chans; i++) {
|
||||
snprintf(chan_info->chan_ipc_sk_path, sizeof(chan_info->chan_ipc_sk_path),"%s/ipc_sock%d_%d",
|
||||
cmdline_cfg.ud_prefix_dir, cmdline_cfg.msocknum, i);
|
||||
/* FIXME: dynamc chan limit, currently 8 */
|
||||
if (i < 8)
|
||||
ipc_sock_init(chan_info->chan_ipc_sk_path, &global_ctrl_socks[i], ipc_chan_sock_accept, i);
|
||||
chan_info++;
|
||||
}
|
||||
|
||||
return ipc_sock_send(msg);
|
||||
}
|
||||
|
||||
int ipc_rx_greeting_req(struct ipc_sk_if_greeting *greeting_req)
|
||||
{
|
||||
if (greeting_req->req_version == IPC_SOCK_API_VERSION)
|
||||
ipc_tx_greeting_cnf(IPC_SOCK_API_VERSION);
|
||||
else
|
||||
ipc_tx_greeting_cnf(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipc_rx_info_req(struct ipc_sk_if_info_req *info_req)
|
||||
{
|
||||
ipc_tx_info_cnf();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipc_rx_open_req(struct ipc_sk_if_open_req *open_req)
|
||||
{
|
||||
/* calculate size needed */
|
||||
unsigned int len;
|
||||
unsigned int i;
|
||||
|
||||
global_dev = uhdwrap_open(open_req);
|
||||
|
||||
/* b210 packet size is 2040, but our tx size is 2500, so just do *2 */
|
||||
int shmbuflen = uhdwrap_get_bufsizerx(global_dev) * 2;
|
||||
|
||||
len = ipc_shm_encode_region(NULL, open_req->num_chans, 4, shmbuflen);
|
||||
/* Here we verify num_chans, rx_path, tx_path, clockref, etc. */
|
||||
int rc = ipc_shm_setup(DEFAULT_SHM_NAME, len);
|
||||
len = ipc_shm_encode_region((struct ipc_shm_raw_region *)shm, open_req->num_chans, 4, shmbuflen);
|
||||
// LOGP(DMAIN, LOGL_NOTICE, "%s\n", osmo_hexdump((const unsigned char *)shm, 80));
|
||||
|
||||
/* set up our own copy of the decoded area, we have to do it here,
|
||||
* since the uhd wrapper does not allow starting single channels
|
||||
* additionally go for the producer init for both, so only we are responsible for the init, instead
|
||||
* of splitting it with the client and causing potential races if one side uses it too early */
|
||||
decoded_region = ipc_shm_decode_region(0, (struct ipc_shm_raw_region *)shm);
|
||||
for (i = 0; i < open_req->num_chans; i++) {
|
||||
// ios_tx_to_device[i] = ipc_shm_init_consumer(decoded_region->channels[i]->dl_stream);
|
||||
ios_tx_to_device[i] = ipc_shm_init_producer(decoded_region->channels[i]->dl_stream);
|
||||
ios_rx_from_device[i] = ipc_shm_init_producer(decoded_region->channels[i]->ul_stream);
|
||||
}
|
||||
|
||||
ipc_tx_open_cnf(-rc, open_req->num_chans, uhdwrap_get_timingoffset(global_dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
volatile bool ul_running = false;
|
||||
volatile bool dl_running = false;
|
||||
|
||||
void *uplink_thread(void *x_void_ptr)
|
||||
{
|
||||
uint32_t chann = decoded_region->num_chans;
|
||||
ul_running = true;
|
||||
pthread_setname_np(pthread_self(), "uplink_rx");
|
||||
|
||||
while (!ipc_exit_requested) {
|
||||
int32_t read = uhdwrap_read(global_dev, chann);
|
||||
if (read < 0)
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *downlink_thread(void *x_void_ptr)
|
||||
{
|
||||
int chann = decoded_region->num_chans;
|
||||
dl_running = true;
|
||||
pthread_setname_np(pthread_self(), "downlink_tx");
|
||||
|
||||
while (!ipc_exit_requested) {
|
||||
bool underrun;
|
||||
uhdwrap_write(global_dev, chann, &underrun);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipc_rx_chan_start_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct ipc_sk_chan_if *ipc_prim;
|
||||
int rc = 0;
|
||||
|
||||
rc = uhdwrap_start(global_dev, chan_nr);
|
||||
|
||||
/* no per-chan start/stop */
|
||||
if (!dl_running || !ul_running) {
|
||||
/* chan != first chan start will "fail", which is fine, usrp can't start/stop chans independently */
|
||||
if (rc) {
|
||||
LOGP(DMAIN, LOGL_INFO, "starting rx/tx threads.. req for chan:%d\n", chan_nr);
|
||||
pthread_t rx, tx;
|
||||
pthread_create(&rx, NULL, uplink_thread, 0);
|
||||
pthread_create(&tx, NULL, downlink_thread, 0);
|
||||
}
|
||||
} else
|
||||
LOGP(DMAIN, LOGL_INFO, "starting rx/tx threads request ignored.. req for chan:%d\n", chan_nr);
|
||||
|
||||
msg = ipc_msgb_alloc(IPC_IF_MSG_START_CNF);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
|
||||
ipc_prim->u.start_cnf.return_code = rc ? 0 : -1;
|
||||
|
||||
return ipc_chan_sock_send(msg, chan_nr);
|
||||
}
|
||||
int ipc_rx_chan_stop_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct ipc_sk_chan_if *ipc_prim;
|
||||
int rc;
|
||||
|
||||
/* no per-chan start/stop */
|
||||
rc = uhdwrap_stop(global_dev, chan_nr);
|
||||
|
||||
msg = ipc_msgb_alloc(IPC_IF_MSG_STOP_CNF);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
|
||||
ipc_prim->u.stop_cnf.return_code = rc ? 0 : -1;
|
||||
|
||||
return ipc_chan_sock_send(msg, chan_nr);
|
||||
}
|
||||
int ipc_rx_chan_setgain_req(struct ipc_sk_chan_if_gain *req, uint8_t chan_nr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct ipc_sk_chan_if *ipc_prim;
|
||||
double rv;
|
||||
|
||||
rv = uhdwrap_set_gain(global_dev, req->gain, chan_nr, req->is_tx);
|
||||
|
||||
msg = ipc_msgb_alloc(IPC_IF_MSG_SETGAIN_CNF);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
|
||||
ipc_prim->u.set_gain_cnf.is_tx = req->is_tx;
|
||||
ipc_prim->u.set_gain_cnf.gain = rv;
|
||||
|
||||
return ipc_chan_sock_send(msg, chan_nr);
|
||||
}
|
||||
|
||||
int ipc_rx_chan_setfreq_req(struct ipc_sk_chan_if_freq_req *req, uint8_t chan_nr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct ipc_sk_chan_if *ipc_prim;
|
||||
bool rv;
|
||||
|
||||
rv = uhdwrap_set_freq(global_dev, req->freq, chan_nr, req->is_tx);
|
||||
|
||||
msg = ipc_msgb_alloc(IPC_IF_MSG_SETFREQ_CNF);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
|
||||
ipc_prim->u.set_freq_cnf.return_code = rv ? 0 : 1;
|
||||
|
||||
return ipc_chan_sock_send(msg, chan_nr);
|
||||
}
|
||||
|
||||
int ipc_rx_chan_settxatten_req(struct ipc_sk_chan_if_tx_attenuation *req, uint8_t chan_nr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct ipc_sk_chan_if *ipc_prim;
|
||||
double rv;
|
||||
|
||||
rv = uhdwrap_set_txatt(global_dev, req->attenuation, chan_nr);
|
||||
|
||||
msg = ipc_msgb_alloc(IPC_IF_MSG_SETTXATTN_CNF);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
|
||||
ipc_prim->u.txatten_cnf.attenuation = rv;
|
||||
|
||||
return ipc_chan_sock_send(msg, chan_nr);
|
||||
}
|
||||
|
||||
int ipc_sock_init(const char *path, struct ipc_sock_state **global_state_var,
|
||||
int (*sock_callback_fn)(struct osmo_fd *fd, unsigned int what), int n)
|
||||
{
|
||||
struct ipc_sock_state *state;
|
||||
struct osmo_fd *bfd;
|
||||
int rc;
|
||||
|
||||
state = talloc_zero(NULL, struct ipc_sock_state);
|
||||
if (!state)
|
||||
return -ENOMEM;
|
||||
*global_state_var = state;
|
||||
|
||||
INIT_LLIST_HEAD(&state->upqueue);
|
||||
state->conn_bfd.fd = -1;
|
||||
|
||||
bfd = &state->listen_bfd;
|
||||
|
||||
bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path, OSMO_SOCK_F_BIND);
|
||||
if (bfd->fd < 0) {
|
||||
LOGP(DMAIN, LOGL_ERROR, "Could not create %s unix socket: %s\n", path, strerror(errno));
|
||||
talloc_free(state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
osmo_fd_setup(bfd, bfd->fd, OSMO_FD_READ, sock_callback_fn, state, n);
|
||||
|
||||
rc = osmo_fd_register(bfd);
|
||||
if (rc < 0) {
|
||||
LOGP(DMAIN, LOGL_ERROR, "Could not register listen fd: %d\n", rc);
|
||||
close(bfd->fd);
|
||||
talloc_free(state);
|
||||
return rc;
|
||||
}
|
||||
|
||||
LOGP(DMAIN, LOGL_INFO, "Started listening on IPC socket: %s\n", path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_help(void)
|
||||
{
|
||||
printf("ipc-driver-test Usage:\n"
|
||||
" -h --help This message\n"
|
||||
" -u --unix-sk-dir DIR Existing directory where to create the Master socket\n"
|
||||
" -n --sock-num NR Master socket suffix number NR\n");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
const struct option long_options[] = { { "help", 0, 0, 'h' },
|
||||
{ "unix-sk-dir", 1, 0, 'u' },
|
||||
{ "sock-num", 1, 0, 'n' },
|
||||
{ 0, 0, 0, 0 } };
|
||||
|
||||
c = getopt_long(argc, argv, "hu:n:", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_help();
|
||||
exit(0);
|
||||
break;
|
||||
case 'u':
|
||||
cmdline_cfg.ud_prefix_dir = talloc_strdup(tall_ctx, optarg);
|
||||
break;
|
||||
case 'n':
|
||||
cmdline_cfg.msocknum = atoi(optarg);
|
||||
break;
|
||||
|
||||
default:
|
||||
exit(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > optind) {
|
||||
fprintf(stderr, "Unsupported positional arguments on command line\n");
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char ipc_msock_path[128];
|
||||
tall_ctx = talloc_named_const(NULL, 0, "OsmoTRX");
|
||||
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||
osmo_init_logging2(tall_ctx, &log_infox);
|
||||
log_enable_multithread();
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
if (!cmdline_cfg.ud_prefix_dir)
|
||||
cmdline_cfg.ud_prefix_dir = talloc_strdup(tall_ctx, IPC_SOCK_PATH_PREFIX);
|
||||
|
||||
|
||||
snprintf(ipc_msock_path, sizeof(ipc_msock_path), "%s/ipc_sock%d", cmdline_cfg.ud_prefix_dir, cmdline_cfg.msocknum);
|
||||
|
||||
LOGP(DMAIN, LOGL_INFO, "Starting %s\n", argv[0]);
|
||||
ipc_sock_init(ipc_msock_path, &global_ipc_sock_state, ipc_sock_accept, 0);
|
||||
while (!ipc_exit_requested)
|
||||
osmo_select_main(0);
|
||||
|
||||
if (global_dev) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < decoded_region->num_chans; i++)
|
||||
uhdwrap_stop(global_dev, i);
|
||||
}
|
||||
|
||||
ipc_sock_close(global_ipc_sock_state);
|
||||
return 0;
|
||||
}
|
||||
45
Transceiver52M/device/ipc/ipc-driver-test.h
Normal file
45
Transceiver52M/device/ipc/ipc-driver-test.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
|
||||
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include "shm.h"
|
||||
|
||||
extern struct ipc_sock_state *global_ipc_sock_state;
|
||||
|
||||
/* 8 channels are plenty */
|
||||
extern struct ipc_sock_state *global_ctrl_socks[8];
|
||||
extern struct ipc_shm_io *ios_tx_to_device[8];
|
||||
extern struct ipc_shm_io *ios_rx_from_device[8];
|
||||
|
||||
struct ipc_sock_state {
|
||||
struct osmo_fd listen_bfd; /* fd for listen socket */
|
||||
struct osmo_fd conn_bfd; /* fd for connection */
|
||||
struct llist_head upqueue; /* queue for sending messages */
|
||||
};
|
||||
|
||||
int ipc_sock_init(const char *path, struct ipc_sock_state **global_state_var,
|
||||
int (*sock_callback_fn)(struct osmo_fd *fd, unsigned int what), int n);
|
||||
|
||||
int ipc_rx_greeting_req(struct ipc_sk_if_greeting *greeting_req);
|
||||
int ipc_rx_info_req(struct ipc_sk_if_info_req *info_req);
|
||||
int ipc_rx_open_req(struct ipc_sk_if_open_req *open_req);
|
||||
|
||||
int ipc_rx_chan_start_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr);
|
||||
int ipc_rx_chan_stop_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr);
|
||||
int ipc_rx_chan_setgain_req(struct ipc_sk_chan_if_gain *req, uint8_t chan_nr);
|
||||
int ipc_rx_chan_setfreq_req(struct ipc_sk_chan_if_freq_req *req, uint8_t chan_nr);
|
||||
int ipc_rx_chan_settxatten_req(struct ipc_sk_chan_if_tx_attenuation *req, uint8_t chan_nr);
|
||||
254
Transceiver52M/device/ipc/ipc_chan.c
Normal file
254
Transceiver52M/device/ipc/ipc_chan.c
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
|
||||
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h> /* For mode constants */
|
||||
#include <fcntl.h> /* For O_* constants */
|
||||
|
||||
#include <debug.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include "shm.h"
|
||||
#include "ipc-driver-test.h"
|
||||
#include "ipc_chan.h"
|
||||
#include "ipc_sock.h"
|
||||
|
||||
static int ipc_chan_rx(uint8_t msg_type, struct ipc_sk_chan_if *ipc_prim, uint8_t chan_nr)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (msg_type) {
|
||||
case IPC_IF_MSG_START_REQ:
|
||||
rc = ipc_rx_chan_start_req(&ipc_prim->u.start_req, chan_nr);
|
||||
break;
|
||||
case IPC_IF_MSG_STOP_REQ:
|
||||
rc = ipc_rx_chan_stop_req(&ipc_prim->u.stop_req, chan_nr);
|
||||
break;
|
||||
case IPC_IF_MSG_SETGAIN_REQ:
|
||||
rc = ipc_rx_chan_setgain_req(&ipc_prim->u.set_gain_req, chan_nr);
|
||||
break;
|
||||
case IPC_IF_MSG_SETFREQ_REQ:
|
||||
rc = ipc_rx_chan_setfreq_req(&ipc_prim->u.set_freq_req, chan_nr);
|
||||
break;
|
||||
case IPC_IF_MSG_SETTXATTN_REQ:
|
||||
rc = ipc_rx_chan_settxatten_req(&ipc_prim->u.txatten_req, chan_nr);
|
||||
break;
|
||||
default:
|
||||
LOGP(DDEV, LOGL_ERROR, "Received unknown IPC msg type 0x%02x on chan %d\n", msg_type, chan_nr);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ipc_chan_sock_read(struct osmo_fd *bfd)
|
||||
{
|
||||
struct ipc_sock_state *state = (struct ipc_sock_state *)bfd->data;
|
||||
struct ipc_sk_chan_if *ipc_prim;
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
msg = msgb_alloc(sizeof(*ipc_prim) + 1000, "ipc_chan_sock_rx");
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ipc_prim = (struct ipc_sk_chan_if *)msg->tail;
|
||||
|
||||
rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
|
||||
if (rc == 0)
|
||||
goto close;
|
||||
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
goto close;
|
||||
}
|
||||
|
||||
if (rc < (int)sizeof(*ipc_prim)) {
|
||||
LOGP(DDEV, LOGL_ERROR,
|
||||
"Received %d bytes on Unix Socket, but primitive size "
|
||||
"is %zu, discarding\n",
|
||||
rc, sizeof(*ipc_prim));
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = ipc_chan_rx(ipc_prim->msg_type, ipc_prim, bfd->priv_nr);
|
||||
|
||||
/* as we always synchronously process the message in IPC_rx() and
|
||||
* its callbacks, we can free the message here. */
|
||||
msgb_free(msg);
|
||||
|
||||
return rc;
|
||||
|
||||
close:
|
||||
msgb_free(msg);
|
||||
ipc_sock_close(state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ipc_chan_sock_send(struct msgb *msg, uint8_t chan_nr)
|
||||
{
|
||||
struct ipc_sock_state *state = global_ctrl_socks[chan_nr];
|
||||
struct osmo_fd *conn_bfd;
|
||||
|
||||
if (!state)
|
||||
return -EINVAL;
|
||||
|
||||
if (!state) {
|
||||
LOGP(DDEV, LOGL_INFO,
|
||||
"IPC socket not created, "
|
||||
"dropping message\n");
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
conn_bfd = &state->conn_bfd;
|
||||
if (conn_bfd->fd <= 0) {
|
||||
LOGP(DDEV, LOGL_NOTICE,
|
||||
"IPC socket not connected, "
|
||||
"dropping message\n");
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
msgb_enqueue(&state->upqueue, msg);
|
||||
osmo_fd_write_enable(conn_bfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipc_chan_sock_write(struct osmo_fd *bfd)
|
||||
{
|
||||
struct ipc_sock_state *state = (struct ipc_sock_state *)bfd->data;
|
||||
int rc;
|
||||
|
||||
while (!llist_empty(&state->upqueue)) {
|
||||
struct msgb *msg, *msg2;
|
||||
struct ipc_sk_chan_if *ipc_prim;
|
||||
|
||||
/* peek at the beginning of the queue */
|
||||
msg = llist_entry(state->upqueue.next, struct msgb, list);
|
||||
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
|
||||
|
||||
osmo_fd_write_disable(bfd);
|
||||
|
||||
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
|
||||
if (!msgb_length(msg)) {
|
||||
LOGP(DDEV, LOGL_ERROR,
|
||||
"message type (%d) with ZERO "
|
||||
"bytes!\n",
|
||||
ipc_prim->msg_type);
|
||||
goto dontsend;
|
||||
}
|
||||
|
||||
/* try to send it over the socket */
|
||||
rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
|
||||
if (rc == 0)
|
||||
goto close;
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
osmo_fd_write_enable(bfd);
|
||||
break;
|
||||
}
|
||||
goto close;
|
||||
}
|
||||
|
||||
dontsend:
|
||||
/* _after_ we send it, we can dequeue */
|
||||
msg2 = msgb_dequeue(&state->upqueue);
|
||||
assert(msg == msg2);
|
||||
msgb_free(msg);
|
||||
}
|
||||
return 0;
|
||||
|
||||
close:
|
||||
ipc_sock_close(state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (flags & OSMO_FD_READ)
|
||||
rc = ipc_chan_sock_read(bfd);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (flags & OSMO_FD_WRITE)
|
||||
rc = ipc_chan_sock_write(bfd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ipc_chan_sock_accept(struct osmo_fd *bfd, unsigned int flags)
|
||||
{
|
||||
struct ipc_sock_state *state = (struct ipc_sock_state *)bfd->data;
|
||||
struct osmo_fd *conn_bfd = &state->conn_bfd;
|
||||
struct sockaddr_un un_addr;
|
||||
socklen_t len;
|
||||
int rc;
|
||||
|
||||
len = sizeof(un_addr);
|
||||
rc = accept(bfd->fd, (struct sockaddr *)&un_addr, &len);
|
||||
if (rc < 0) {
|
||||
LOGP(DDEV, LOGL_ERROR, "Failed to accept a new connection\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conn_bfd->fd >= 0) {
|
||||
LOGP(DDEV, LOGL_NOTICE,
|
||||
"osmo-trx connects but we already have "
|
||||
"another active connection ?!?\n");
|
||||
/* We already have one IPC connected, this is all we support */
|
||||
osmo_fd_read_disable(&state->listen_bfd);
|
||||
close(rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* copy chan nr, required for proper bfd<->chan # mapping */
|
||||
osmo_fd_setup(conn_bfd, rc, OSMO_FD_READ, ipc_chan_sock_cb, state, bfd->priv_nr);
|
||||
|
||||
if (osmo_fd_register(conn_bfd) != 0) {
|
||||
LOGP(DDEV, LOGL_ERROR,
|
||||
"Failed to register new connection "
|
||||
"fd\n");
|
||||
close(conn_bfd->fd);
|
||||
conn_bfd->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DDEV, LOGL_NOTICE, "Unix socket connected to external osmo-trx\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
25
Transceiver52M/device/ipc/ipc_chan.h
Normal file
25
Transceiver52M/device/ipc/ipc_chan.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
|
||||
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef IPC_CHAN_H
|
||||
#define IPC_CHAN_H
|
||||
|
||||
#include "shm.h"
|
||||
#include "ipc-driver-test.h"
|
||||
|
||||
int ipc_chan_sock_send(struct msgb *msg, uint8_t chan_nr);
|
||||
int ipc_chan_sock_accept(struct osmo_fd *bfd, unsigned int flags);
|
||||
|
||||
#endif // IPC_CHAN_H
|
||||
200
Transceiver52M/device/ipc/ipc_shm.c
Normal file
200
Transceiver52M/device/ipc/ipc_shm.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Eric Wild <ewild@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
|
||||
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <shm.h>
|
||||
#include "ipc_shm.h"
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <osmocom/core/panic.h>
|
||||
|
||||
#include <debug.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SAMPLE_SIZE_BYTE (sizeof(uint16_t) * 2)
|
||||
|
||||
struct ipc_shm_io *ipc_shm_init_consumer(struct ipc_shm_stream *s)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
struct ipc_shm_io *r = (struct ipc_shm_io *)malloc(sizeof(struct ipc_shm_io));
|
||||
r->this_stream = s->raw;
|
||||
r->buf_ptrs =
|
||||
(volatile struct ipc_shm_raw_smpl_buf **)malloc(sizeof(struct ipc_shm_raw_smpl_buf *) * s->num_buffers);
|
||||
|
||||
/* save actual ptrs */
|
||||
for (i = 0; i < s->num_buffers; i++)
|
||||
r->buf_ptrs[i] = s->buffers[i];
|
||||
|
||||
r->partial_read_begin_ptr = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
struct ipc_shm_io *ipc_shm_init_producer(struct ipc_shm_stream *s)
|
||||
{
|
||||
int rv;
|
||||
pthread_mutexattr_t att;
|
||||
pthread_condattr_t t1, t2;
|
||||
struct ipc_shm_io *r = ipc_shm_init_consumer(s);
|
||||
rv = pthread_mutexattr_init(&att);
|
||||
if (rv != 0) {
|
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
|
||||
}
|
||||
|
||||
rv = pthread_mutexattr_setrobust(&att, PTHREAD_MUTEX_ROBUST);
|
||||
if (rv != 0) {
|
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
|
||||
}
|
||||
|
||||
rv = pthread_mutexattr_setpshared(&att, PTHREAD_PROCESS_SHARED);
|
||||
if (rv != 0) {
|
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
|
||||
}
|
||||
|
||||
rv = pthread_mutex_init((pthread_mutex_t *)&r->this_stream->lock, &att);
|
||||
if (rv != 0) {
|
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
|
||||
}
|
||||
|
||||
pthread_mutexattr_destroy(&att);
|
||||
|
||||
rv = pthread_condattr_setpshared(&t1, PTHREAD_PROCESS_SHARED);
|
||||
if (rv != 0) {
|
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
|
||||
}
|
||||
|
||||
rv = pthread_condattr_setpshared(&t2, PTHREAD_PROCESS_SHARED);
|
||||
if (rv != 0) {
|
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
|
||||
}
|
||||
|
||||
rv = pthread_cond_init((pthread_cond_t *)&r->this_stream->cf, &t1);
|
||||
if (rv != 0) {
|
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
|
||||
}
|
||||
|
||||
rv = pthread_cond_init((pthread_cond_t *)&r->this_stream->ce, &t2);
|
||||
if (rv != 0) {
|
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
|
||||
}
|
||||
|
||||
pthread_condattr_destroy(&t1);
|
||||
pthread_condattr_destroy(&t2);
|
||||
|
||||
r->this_stream->read_next = 0;
|
||||
r->this_stream->write_next = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
void ipc_shm_close(struct ipc_shm_io *r)
|
||||
{
|
||||
if (r) {
|
||||
free(r->buf_ptrs);
|
||||
free(r);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t ipc_shm_enqueue(struct ipc_shm_io *r, uint64_t timestamp, uint32_t len_in_sps, uint16_t *data)
|
||||
{
|
||||
volatile struct ipc_shm_raw_smpl_buf *buf;
|
||||
int32_t rv;
|
||||
struct timespec tv;
|
||||
clock_gettime(CLOCK_REALTIME, &tv);
|
||||
tv.tv_sec += 1;
|
||||
|
||||
rv = pthread_mutex_timedlock((pthread_mutex_t *)&r->this_stream->lock, &tv);
|
||||
if (rv != 0)
|
||||
return -rv;
|
||||
|
||||
while (((r->this_stream->write_next + 1) & (r->this_stream->num_buffers - 1)) == r->this_stream->read_next &&
|
||||
rv == 0)
|
||||
rv = pthread_cond_timedwait((pthread_cond_t *)&r->this_stream->ce,
|
||||
(pthread_mutex_t *)&r->this_stream->lock, &tv);
|
||||
if (rv != 0)
|
||||
return -rv;
|
||||
|
||||
buf = r->buf_ptrs[r->this_stream->write_next];
|
||||
buf->timestamp = timestamp;
|
||||
|
||||
rv = len_in_sps <= r->this_stream->buffer_size ? len_in_sps : r->this_stream->buffer_size;
|
||||
|
||||
memcpy((void *)buf->samples, data, SAMPLE_SIZE_BYTE * rv);
|
||||
buf->data_len = rv;
|
||||
|
||||
r->this_stream->write_next = (r->this_stream->write_next + 1) & (r->this_stream->num_buffers - 1);
|
||||
|
||||
pthread_cond_signal((pthread_cond_t *)&r->this_stream->cf);
|
||||
pthread_mutex_unlock((pthread_mutex_t *)&r->this_stream->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int32_t ipc_shm_read(struct ipc_shm_io *r, uint16_t *out_buf, uint32_t num_samples, uint64_t *timestamp,
|
||||
uint32_t timeout_seconds)
|
||||
{
|
||||
volatile struct ipc_shm_raw_smpl_buf *buf;
|
||||
int32_t rv;
|
||||
uint8_t freeflag = 0;
|
||||
struct timespec tv;
|
||||
clock_gettime(CLOCK_REALTIME, &tv);
|
||||
tv.tv_sec += timeout_seconds;
|
||||
|
||||
rv = pthread_mutex_timedlock((pthread_mutex_t *)&r->this_stream->lock, &tv);
|
||||
if (rv != 0)
|
||||
return -rv;
|
||||
|
||||
while (r->this_stream->write_next == r->this_stream->read_next && rv == 0)
|
||||
rv = pthread_cond_timedwait((pthread_cond_t *)&r->this_stream->cf,
|
||||
(pthread_mutex_t *)&r->this_stream->lock, &tv);
|
||||
if (rv != 0)
|
||||
return -rv;
|
||||
|
||||
buf = r->buf_ptrs[r->this_stream->read_next];
|
||||
if (buf->data_len <= num_samples) {
|
||||
memcpy(out_buf, (void *)&buf->samples[r->partial_read_begin_ptr * 2], SAMPLE_SIZE_BYTE * buf->data_len);
|
||||
r->partial_read_begin_ptr = 0;
|
||||
rv = buf->data_len;
|
||||
buf->data_len = 0;
|
||||
r->this_stream->read_next = (r->this_stream->read_next + 1) & (r->this_stream->num_buffers - 1);
|
||||
freeflag = 1;
|
||||
|
||||
} else /*if (buf->data_len > num_samples)*/ {
|
||||
memcpy(out_buf, (void *)&buf->samples[r->partial_read_begin_ptr * 2], SAMPLE_SIZE_BYTE * num_samples);
|
||||
r->partial_read_begin_ptr += num_samples;
|
||||
buf->data_len -= num_samples;
|
||||
rv = num_samples;
|
||||
}
|
||||
|
||||
*timestamp = buf->timestamp;
|
||||
buf->timestamp += rv;
|
||||
|
||||
if (freeflag)
|
||||
pthread_cond_signal((pthread_cond_t *)&r->this_stream->ce);
|
||||
|
||||
pthread_mutex_unlock((pthread_mutex_t *)&r->this_stream->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
45
Transceiver52M/device/ipc/ipc_shm.h
Normal file
45
Transceiver52M/device/ipc/ipc_shm.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Eric Wild <ewild@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
|
||||
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef IPC_SHM_H
|
||||
#define IPC_SHM_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <shm.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
struct ipc_shm_io {
|
||||
volatile struct ipc_shm_raw_stream *this_stream;
|
||||
volatile struct ipc_shm_raw_smpl_buf **volatile buf_ptrs;
|
||||
uint32_t partial_read_begin_ptr;
|
||||
};
|
||||
|
||||
struct ipc_shm_io *ipc_shm_init_consumer(struct ipc_shm_stream *s);
|
||||
struct ipc_shm_io *ipc_shm_init_producer(struct ipc_shm_stream *s);
|
||||
void ipc_shm_close(struct ipc_shm_io *r);
|
||||
int32_t ipc_shm_enqueue(struct ipc_shm_io *r, uint64_t timestamp, uint32_t len_in_sps, uint16_t *data);
|
||||
int32_t ipc_shm_tryenqueue(struct ipc_shm_io *r, uint64_t timestamp, uint32_t len_in_sps, uint16_t *data);
|
||||
volatile struct ipc_shm_raw_smpl_buf *ipc_shm_dequeue(struct ipc_shm_io *r);
|
||||
int32_t ipc_shm_read(struct ipc_shm_io *r, uint16_t *out_buf, uint32_t num_samples, uint64_t *timestamp,
|
||||
uint32_t timeout_seconds);
|
||||
|
||||
#endif // IPC_SHM_H
|
||||
266
Transceiver52M/device/ipc/ipc_sock.c
Normal file
266
Transceiver52M/device/ipc/ipc_sock.c
Normal file
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
|
||||
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h> /* For mode constants */
|
||||
#include <fcntl.h> /* For O_* constants */
|
||||
|
||||
#include <debug.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include "shm.h"
|
||||
#include "ipc-driver-test.h"
|
||||
|
||||
extern volatile int ipc_exit_requested;
|
||||
static int ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (msg_type) {
|
||||
case IPC_IF_MSG_GREETING_REQ:
|
||||
rc = ipc_rx_greeting_req(&ipc_prim->u.greeting_req);
|
||||
break;
|
||||
case IPC_IF_MSG_INFO_REQ:
|
||||
rc = ipc_rx_info_req(&ipc_prim->u.info_req);
|
||||
break;
|
||||
case IPC_IF_MSG_OPEN_REQ:
|
||||
rc = ipc_rx_open_req(&ipc_prim->u.open_req);
|
||||
break;
|
||||
default:
|
||||
LOGP(DDEV, LOGL_ERROR, "Received unknown IPC msg type 0x%02x\n", msg_type);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ipc_sock_send(struct msgb *msg)
|
||||
{
|
||||
struct ipc_sock_state *state = global_ipc_sock_state;
|
||||
struct osmo_fd *conn_bfd;
|
||||
|
||||
if (!state) {
|
||||
LOGP(DDEV, LOGL_INFO,
|
||||
"IPC socket not created, "
|
||||
"dropping message\n");
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
conn_bfd = &state->conn_bfd;
|
||||
if (conn_bfd->fd <= 0) {
|
||||
LOGP(DDEV, LOGL_NOTICE,
|
||||
"IPC socket not connected, "
|
||||
"dropping message\n");
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
msgb_enqueue(&state->upqueue, msg);
|
||||
osmo_fd_write_enable(conn_bfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ipc_sock_close(struct ipc_sock_state *state)
|
||||
{
|
||||
struct osmo_fd *bfd = &state->conn_bfd;
|
||||
|
||||
LOGP(DDEV, LOGL_NOTICE, "IPC socket has LOST connection\n");
|
||||
|
||||
ipc_exit_requested = 1;
|
||||
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
osmo_fd_unregister(bfd);
|
||||
|
||||
/* re-enable the generation of ACCEPT for new connections */
|
||||
osmo_fd_read_enable(&state->listen_bfd);
|
||||
|
||||
/* flush the queue */
|
||||
while (!llist_empty(&state->upqueue)) {
|
||||
struct msgb *msg = msgb_dequeue(&state->upqueue);
|
||||
msgb_free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
int ipc_sock_read(struct osmo_fd *bfd)
|
||||
{
|
||||
struct ipc_sock_state *state = (struct ipc_sock_state *)bfd->data;
|
||||
struct ipc_sk_if *ipc_prim;
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
msg = msgb_alloc(sizeof(*ipc_prim) + 1000, "ipc_sock_rx");
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ipc_prim = (struct ipc_sk_if *)msg->tail;
|
||||
|
||||
rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
|
||||
if (rc == 0)
|
||||
goto close;
|
||||
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
goto close;
|
||||
}
|
||||
|
||||
if (rc < (int)sizeof(*ipc_prim)) {
|
||||
LOGP(DDEV, LOGL_ERROR,
|
||||
"Received %d bytes on Unix Socket, but primitive size "
|
||||
"is %zu, discarding\n",
|
||||
rc, sizeof(*ipc_prim));
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = ipc_rx(ipc_prim->msg_type, ipc_prim);
|
||||
|
||||
/* as we always synchronously process the message in IPC_rx() and
|
||||
* its callbacks, we can free the message here. */
|
||||
msgb_free(msg);
|
||||
|
||||
return rc;
|
||||
|
||||
close:
|
||||
msgb_free(msg);
|
||||
ipc_sock_close(state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ipc_sock_write(struct osmo_fd *bfd)
|
||||
{
|
||||
struct ipc_sock_state *state = (struct ipc_sock_state *)bfd->data;
|
||||
int rc;
|
||||
|
||||
while (!llist_empty(&state->upqueue)) {
|
||||
struct msgb *msg, *msg2;
|
||||
struct ipc_sk_if *ipc_prim;
|
||||
|
||||
/* peek at the beginning of the queue */
|
||||
msg = llist_entry(state->upqueue.next, struct msgb, list);
|
||||
ipc_prim = (struct ipc_sk_if *)msg->data;
|
||||
|
||||
osmo_fd_write_disable(bfd);
|
||||
|
||||
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
|
||||
if (!msgb_length(msg)) {
|
||||
LOGP(DDEV, LOGL_ERROR,
|
||||
"message type (%d) with ZERO "
|
||||
"bytes!\n",
|
||||
ipc_prim->msg_type);
|
||||
goto dontsend;
|
||||
}
|
||||
|
||||
/* try to send it over the socket */
|
||||
rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
|
||||
if (rc == 0)
|
||||
goto close;
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
osmo_fd_write_enable(bfd);
|
||||
break;
|
||||
}
|
||||
goto close;
|
||||
}
|
||||
|
||||
dontsend:
|
||||
/* _after_ we send it, we can deueue */
|
||||
msg2 = msgb_dequeue(&state->upqueue);
|
||||
assert(msg == msg2);
|
||||
msgb_free(msg);
|
||||
}
|
||||
return 0;
|
||||
|
||||
close:
|
||||
ipc_sock_close(state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ipc_sock_cb(struct osmo_fd *bfd, unsigned int flags)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (flags & OSMO_FD_READ)
|
||||
rc = ipc_sock_read(bfd);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (flags & OSMO_FD_WRITE)
|
||||
rc = ipc_sock_write(bfd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* accept connection coming from IPC */
|
||||
int ipc_sock_accept(struct osmo_fd *bfd, unsigned int flags)
|
||||
{
|
||||
struct ipc_sock_state *state = (struct ipc_sock_state *)bfd->data;
|
||||
struct osmo_fd *conn_bfd = &state->conn_bfd;
|
||||
struct sockaddr_un un_addr;
|
||||
socklen_t len;
|
||||
int rc;
|
||||
|
||||
len = sizeof(un_addr);
|
||||
rc = accept(bfd->fd, (struct sockaddr *)&un_addr, &len);
|
||||
if (rc < 0) {
|
||||
LOGP(DDEV, LOGL_ERROR, "Failed to accept a new connection\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conn_bfd->fd >= 0) {
|
||||
LOGP(DDEV, LOGL_NOTICE,
|
||||
"ip clent connects but we already have "
|
||||
"another active connection ?!?\n");
|
||||
/* We already have one IPC connected, this is all we support */
|
||||
osmo_fd_read_disable(&state->listen_bfd);
|
||||
close(rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
osmo_fd_setup(conn_bfd, rc, OSMO_FD_READ, ipc_sock_cb, state, 0);
|
||||
|
||||
if (osmo_fd_register(conn_bfd) != 0) {
|
||||
LOGP(DDEV, LOGL_ERROR,
|
||||
"Failed to register new connection "
|
||||
"fd\n");
|
||||
close(conn_bfd->fd);
|
||||
conn_bfd->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DDEV, LOGL_NOTICE, "Unix socket connected to external osmo-trx\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
26
Transceiver52M/device/ipc/ipc_sock.h
Normal file
26
Transceiver52M/device/ipc/ipc_sock.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
|
||||
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef IPC_SOCK_H
|
||||
#define IPC_SOCK_H
|
||||
|
||||
#include "shm.h"
|
||||
#include "ipc-driver-test.h"
|
||||
|
||||
int ipc_sock_send(struct msgb *msg);
|
||||
int ipc_sock_accept(struct osmo_fd *bfd, unsigned int flags);
|
||||
void ipc_sock_close(struct ipc_sock_state *state);
|
||||
|
||||
#endif // IPC_SOCK_H
|
||||
149
Transceiver52M/device/ipc/shm.c
Normal file
149
Transceiver52M/device/ipc/shm.c
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
|
||||
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
#include "shm.h"
|
||||
|
||||
#define ENCDECDEBUG(...) //fprintf(stderr, __VA_ARGS__)
|
||||
|
||||
/* Convert offsets to pointers */
|
||||
struct ipc_shm_stream *ipc_shm_decode_stream(void *tall_ctx, struct ipc_shm_raw_region *root_raw,
|
||||
struct ipc_shm_raw_stream *stream_raw)
|
||||
{
|
||||
unsigned int i;
|
||||
struct ipc_shm_stream *stream;
|
||||
stream = talloc_zero(tall_ctx, struct ipc_shm_stream);
|
||||
stream = talloc_zero_size(tall_ctx, sizeof(struct ipc_shm_stream) +
|
||||
sizeof(struct ipc_shm_raw_smpl_buf *) * stream_raw->num_buffers);
|
||||
if (!stream)
|
||||
return NULL;
|
||||
stream->num_buffers = stream_raw->num_buffers;
|
||||
stream->buffer_size = stream_raw->buffer_size;
|
||||
stream->raw = stream_raw;
|
||||
for (i = 0; i < stream->num_buffers; i++) {
|
||||
ENCDECDEBUG("decode: smpl_buf %d at offset %u\n", i, stream_raw->buffer_offset[i]);
|
||||
stream->buffers[i] =
|
||||
(struct ipc_shm_raw_smpl_buf *)(((uint8_t *)root_raw) + stream_raw->buffer_offset[i]);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
struct ipc_shm_channel *ipc_shm_decode_channel(void *tall_ctx, struct ipc_shm_raw_region *root_raw,
|
||||
struct ipc_shm_raw_channel *chan_raw)
|
||||
{
|
||||
struct ipc_shm_channel *chan;
|
||||
chan = talloc_zero(tall_ctx, struct ipc_shm_channel);
|
||||
if (!chan)
|
||||
return NULL;
|
||||
ENCDECDEBUG("decode: streams at offset %u and %u\n", chan_raw->dl_buf_offset, chan_raw->ul_buf_offset);
|
||||
chan->dl_stream = ipc_shm_decode_stream(
|
||||
chan, root_raw, (struct ipc_shm_raw_stream *)(((uint8_t *)root_raw) + chan_raw->dl_buf_offset));
|
||||
chan->ul_stream = ipc_shm_decode_stream(
|
||||
chan, root_raw, (struct ipc_shm_raw_stream *)(((uint8_t *)root_raw) + chan_raw->ul_buf_offset));
|
||||
return chan;
|
||||
}
|
||||
struct ipc_shm_region *ipc_shm_decode_region(void *tall_ctx, struct ipc_shm_raw_region *root_raw)
|
||||
{
|
||||
unsigned int i;
|
||||
struct ipc_shm_region *root;
|
||||
root = talloc_zero_size(tall_ctx,
|
||||
sizeof(struct ipc_shm_region) + sizeof(struct ipc_shm_channel *) * root_raw->num_chans);
|
||||
if (!root)
|
||||
return NULL;
|
||||
|
||||
root->num_chans = root_raw->num_chans;
|
||||
for (i = 0; i < root->num_chans; i++) {
|
||||
ENCDECDEBUG("decode: channel %d at offset %u\n", i, root_raw->chan_offset[i]);
|
||||
root->channels[i] = ipc_shm_decode_channel(
|
||||
root, root_raw,
|
||||
(struct ipc_shm_raw_channel *)(((uint8_t *)root_raw) + root_raw->chan_offset[i]));
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
unsigned int ipc_shm_encode_smpl_buf(struct ipc_shm_raw_region *root_raw, struct ipc_shm_raw_smpl_buf *smpl_buf_raw,
|
||||
uint32_t buffer_size)
|
||||
{
|
||||
unsigned int offset = sizeof(struct ipc_shm_raw_smpl_buf);
|
||||
offset = (((uintptr_t)offset + 7) & ~0x07ULL);
|
||||
ENCDECDEBUG("encode: smpl_buf at offset %u\n", offset);
|
||||
offset += buffer_size * sizeof(uint16_t) * 2; /* samples */
|
||||
return offset;
|
||||
}
|
||||
|
||||
unsigned int ipc_shm_encode_stream(struct ipc_shm_raw_region *root_raw, struct ipc_shm_raw_stream *stream_raw,
|
||||
uint32_t num_buffers, uint32_t buffer_size)
|
||||
{
|
||||
unsigned int i;
|
||||
ptrdiff_t start = (ptrdiff_t)stream_raw;
|
||||
unsigned int offset = sizeof(struct ipc_shm_raw_stream) + sizeof(uint32_t) * num_buffers;
|
||||
offset = (((uintptr_t)offset + 7) & ~0x07ULL);
|
||||
ENCDECDEBUG("encode: stream at offset %lu\n", (start - (ptrdiff_t)root_raw));
|
||||
if (root_raw) {
|
||||
stream_raw->num_buffers = num_buffers;
|
||||
stream_raw->buffer_size = buffer_size;
|
||||
stream_raw->read_next = 0;
|
||||
stream_raw->write_next = 0;
|
||||
}
|
||||
for (i = 0; i < num_buffers; i++) {
|
||||
if (root_raw)
|
||||
stream_raw->buffer_offset[i] = (start + offset - (ptrdiff_t)root_raw);
|
||||
offset +=
|
||||
ipc_shm_encode_smpl_buf(root_raw, (struct ipc_shm_raw_smpl_buf *)(start + offset), buffer_size);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
unsigned int ipc_shm_encode_channel(struct ipc_shm_raw_region *root_raw, struct ipc_shm_raw_channel *chan_raw,
|
||||
uint32_t num_buffers, uint32_t buffer_size)
|
||||
{
|
||||
uint8_t *start = (uint8_t *)chan_raw;
|
||||
unsigned int offset = sizeof(struct ipc_shm_raw_channel);
|
||||
offset = (((uintptr_t)offset + 7) & ~0x07ULL);
|
||||
ENCDECDEBUG("encode: channel at offset %lu\n", (start - (uint8_t *)root_raw));
|
||||
if (root_raw)
|
||||
chan_raw->dl_buf_offset = (start + offset - (uint8_t *)root_raw);
|
||||
offset += ipc_shm_encode_stream(root_raw, (struct ipc_shm_raw_stream *)(start + offset), num_buffers,
|
||||
buffer_size);
|
||||
if (root_raw)
|
||||
chan_raw->ul_buf_offset = (start + offset - (uint8_t *)root_raw);
|
||||
offset += ipc_shm_encode_stream(root_raw, (struct ipc_shm_raw_stream *)(start + offset), num_buffers,
|
||||
buffer_size);
|
||||
return offset;
|
||||
}
|
||||
/* if root_raw is NULL, then do a dry run, aka only calculate final offset */
|
||||
unsigned int ipc_shm_encode_region(struct ipc_shm_raw_region *root_raw, uint32_t num_chans, uint32_t num_buffers,
|
||||
uint32_t buffer_size)
|
||||
{
|
||||
unsigned i;
|
||||
uintptr_t start = (uintptr_t)root_raw;
|
||||
unsigned int offset = sizeof(struct ipc_shm_raw_region) + sizeof(uint32_t) * num_chans;
|
||||
offset = (((uintptr_t)offset + 7) & ~0x07ULL);
|
||||
|
||||
if (root_raw)
|
||||
root_raw->num_chans = num_chans;
|
||||
for (i = 0; i < num_chans; i++) {
|
||||
if (root_raw)
|
||||
root_raw->chan_offset[i] = (start + offset - (uintptr_t)root_raw);
|
||||
ENCDECDEBUG("encode: channel %d chan_offset[i]=%lu\n", i, start + offset - (uintptr_t)root_raw);
|
||||
offset += ipc_shm_encode_channel(root_raw, (struct ipc_shm_raw_channel *)(start + offset), num_buffers,
|
||||
buffer_size);
|
||||
}
|
||||
//TODO: pass maximum size and verify we didn't go through
|
||||
return offset;
|
||||
}
|
||||
234
Transceiver52M/device/ipc/shm.h
Normal file
234
Transceiver52M/device/ipc/shm.h
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
|
||||
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
/* RAW structures */
|
||||
struct ipc_shm_raw_smpl_buf {
|
||||
uint64_t timestamp;
|
||||
uint32_t data_len; /* In samples */
|
||||
uint16_t samples[0];
|
||||
};
|
||||
|
||||
struct ipc_shm_raw_stream {
|
||||
pthread_mutex_t lock; /* protects this struct */
|
||||
pthread_cond_t cf; /* signals fill to reader */
|
||||
pthread_cond_t ce; /* signals empty nbuf to writer */
|
||||
uint32_t num_buffers;
|
||||
uint32_t buffer_size; /* In samples */
|
||||
uint32_t read_next;
|
||||
uint32_t write_next;
|
||||
uint32_t buffer_offset[0];
|
||||
//struct ipc_shm_smpl_buf buffers[0];
|
||||
};
|
||||
|
||||
struct ipc_shm_raw_channel {
|
||||
uint32_t dl_buf_offset;
|
||||
uint32_t ul_buf_offset;
|
||||
};
|
||||
|
||||
struct ipc_shm_raw_region {
|
||||
uint32_t num_chans;
|
||||
uint32_t chan_offset[0];
|
||||
};
|
||||
|
||||
/* non-raw, Pointer converted structures */
|
||||
struct ipc_shm_stream {
|
||||
uint32_t num_buffers;
|
||||
uint32_t buffer_size;
|
||||
volatile struct ipc_shm_raw_stream *raw;
|
||||
volatile struct ipc_shm_raw_smpl_buf *buffers[0];
|
||||
};
|
||||
|
||||
struct ipc_shm_channel {
|
||||
struct ipc_shm_stream *dl_stream;
|
||||
struct ipc_shm_stream *ul_stream;
|
||||
};
|
||||
|
||||
/* Pointer converted structures */
|
||||
struct ipc_shm_region {
|
||||
uint32_t num_chans;
|
||||
struct ipc_shm_channel *channels[0];
|
||||
};
|
||||
|
||||
unsigned int ipc_shm_encode_region(struct ipc_shm_raw_region *root_raw, uint32_t num_chans, uint32_t num_buffers,
|
||||
uint32_t buffer_size);
|
||||
struct ipc_shm_region *ipc_shm_decode_region(void *tall_ctx, struct ipc_shm_raw_region *root_raw);
|
||||
/****************************************/
|
||||
/* UNIX SOCKET API */
|
||||
/****************************************/
|
||||
|
||||
//////////////////
|
||||
// Master socket
|
||||
//////////////////
|
||||
#define IPC_SOCK_API_VERSION 1
|
||||
|
||||
/* msg_type */
|
||||
#define IPC_IF_MSG_GREETING_REQ 0x00
|
||||
#define IPC_IF_MSG_GREETING_CNF 0x01
|
||||
#define IPC_IF_MSG_INFO_REQ 0x02
|
||||
#define IPC_IF_MSG_INFO_CNF 0x03
|
||||
#define IPC_IF_MSG_OPEN_REQ 0x04
|
||||
#define IPC_IF_MSG_OPEN_CNF 0x05
|
||||
|
||||
#define MAX_NUM_CHANS 30
|
||||
#define RF_PATH_NAME_SIZE 25
|
||||
#define MAX_NUM_RF_PATHS 10
|
||||
#define SHM_NAME_MAX NAME_MAX /* 255 */
|
||||
|
||||
#define FEATURE_MASK_CLOCKREF_INTERNAL (0x1 << 0)
|
||||
#define FEATURE_MASK_CLOCKREF_EXTERNAL (0x1 << 1)
|
||||
struct ipc_sk_if_info_chan {
|
||||
char tx_path[MAX_NUM_RF_PATHS][RF_PATH_NAME_SIZE];
|
||||
char rx_path[MAX_NUM_RF_PATHS][RF_PATH_NAME_SIZE];
|
||||
double min_rx_gain;
|
||||
double max_rx_gain;
|
||||
double min_tx_gain;
|
||||
double max_tx_gain;
|
||||
double nominal_tx_power; /* dBm */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_if_open_req_chan {
|
||||
char tx_path[RF_PATH_NAME_SIZE];
|
||||
char rx_path[RF_PATH_NAME_SIZE];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_if_open_cnf_chan {
|
||||
char chan_ipc_sk_path[108];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_if_greeting {
|
||||
uint8_t req_version;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_if_info_req {
|
||||
uint8_t spare;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_if_info_cnf {
|
||||
uint32_t feature_mask;
|
||||
double iq_scaling_val_rx; /* for scaling, sample format is 16 bit, but adc/dac might be less */
|
||||
double iq_scaling_val_tx;
|
||||
uint32_t max_num_chans;
|
||||
char dev_desc[200];
|
||||
struct ipc_sk_if_info_chan chan_info[MAX_NUM_CHANS];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_if_open_req {
|
||||
uint32_t num_chans;
|
||||
uint32_t clockref; /* One of FEATUER_MASK_CLOCKREF_* */
|
||||
uint32_t rx_sample_freq_num;
|
||||
uint32_t rx_sample_freq_den;
|
||||
uint32_t tx_sample_freq_num;
|
||||
uint32_t tx_sample_freq_den;
|
||||
uint32_t bandwidth;
|
||||
struct ipc_sk_if_open_req_chan chan_info[MAX_NUM_CHANS];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_if_open_cnf {
|
||||
uint8_t return_code;
|
||||
uint32_t path_delay;
|
||||
char shm_name[SHM_NAME_MAX];
|
||||
struct ipc_sk_if_open_cnf_chan chan_info[MAX_NUM_CHANS];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_if {
|
||||
uint8_t msg_type; /* message type */
|
||||
uint8_t spare[2];
|
||||
|
||||
union {
|
||||
struct ipc_sk_if_greeting greeting_req;
|
||||
struct ipc_sk_if_greeting greeting_cnf;
|
||||
struct ipc_sk_if_info_req info_req;
|
||||
struct ipc_sk_if_info_cnf info_cnf;
|
||||
struct ipc_sk_if_open_req open_req;
|
||||
struct ipc_sk_if_open_cnf open_cnf;
|
||||
} u;
|
||||
} __attribute__((packed));
|
||||
|
||||
//////////////////
|
||||
// Channel socket
|
||||
//////////////////
|
||||
#define IPC_IF_CHAN_MSG_OFFSET 50
|
||||
#define IPC_IF_MSG_START_REQ IPC_IF_CHAN_MSG_OFFSET + 0
|
||||
#define IPC_IF_MSG_START_CNF IPC_IF_CHAN_MSG_OFFSET + 1
|
||||
#define IPC_IF_MSG_STOP_REQ IPC_IF_CHAN_MSG_OFFSET + 2
|
||||
#define IPC_IF_MSG_STOP_CNF IPC_IF_CHAN_MSG_OFFSET + 3
|
||||
#define IPC_IF_MSG_SETGAIN_REQ IPC_IF_CHAN_MSG_OFFSET + 4
|
||||
#define IPC_IF_MSG_SETGAIN_CNF IPC_IF_CHAN_MSG_OFFSET + 5
|
||||
#define IPC_IF_MSG_SETFREQ_REQ IPC_IF_CHAN_MSG_OFFSET + 6
|
||||
#define IPC_IF_MSG_SETFREQ_CNF IPC_IF_CHAN_MSG_OFFSET + 7
|
||||
|
||||
#define IPC_IF_NOTIFY_UNDERFLOW IPC_IF_CHAN_MSG_OFFSET + 8
|
||||
#define IPC_IF_NOTIFY_OVERFLOW IPC_IF_CHAN_MSG_OFFSET + 9
|
||||
|
||||
#define IPC_IF_MSG_SETTXATTN_REQ IPC_IF_CHAN_MSG_OFFSET + 10
|
||||
#define IPC_IF_MSG_SETTXATTN_CNF IPC_IF_CHAN_MSG_OFFSET + 11
|
||||
|
||||
struct ipc_sk_chan_if_op_void {
|
||||
// at least one dummy byte, to allow c/c++ compatibility
|
||||
uint8_t dummy;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_chan_if_op_rc {
|
||||
uint8_t return_code;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_chan_if_gain {
|
||||
double gain;
|
||||
uint8_t is_tx;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_chan_if_freq_req {
|
||||
double freq;
|
||||
uint8_t is_tx;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_chan_if_freq_cnf {
|
||||
uint8_t return_code;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_chan_if_notfiy {
|
||||
uint8_t dummy;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_chan_if_tx_attenuation {
|
||||
double attenuation;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipc_sk_chan_if {
|
||||
uint8_t msg_type; /* message type */
|
||||
uint8_t spare[2];
|
||||
|
||||
union {
|
||||
struct ipc_sk_chan_if_op_void start_req;
|
||||
struct ipc_sk_chan_if_op_rc start_cnf;
|
||||
struct ipc_sk_chan_if_op_void stop_req;
|
||||
struct ipc_sk_chan_if_op_rc stop_cnf;
|
||||
struct ipc_sk_chan_if_gain set_gain_req;
|
||||
struct ipc_sk_chan_if_gain set_gain_cnf;
|
||||
struct ipc_sk_chan_if_freq_req set_freq_req;
|
||||
struct ipc_sk_chan_if_freq_cnf set_freq_cnf;
|
||||
struct ipc_sk_chan_if_notfiy notify;
|
||||
struct ipc_sk_chan_if_tx_attenuation txatten_req;
|
||||
struct ipc_sk_chan_if_tx_attenuation txatten_cnf;
|
||||
} u;
|
||||
} __attribute__((packed));
|
||||
255
Transceiver52M/device/ipc/uhdwrap.cpp
Normal file
255
Transceiver52M/device/ipc/uhdwrap.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Eric Wild <ewild@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
|
||||
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
extern "C" {
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include "shm.h"
|
||||
#include "ipc_shm.h"
|
||||
#include "ipc-driver-test.h"
|
||||
}
|
||||
#include "../uhd/UHDDevice.h"
|
||||
#include "uhdwrap.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Threads.h"
|
||||
#include "Utils.h"
|
||||
|
||||
int uhd_wrap::open(const std::string &args, int ref, bool swap_channels)
|
||||
{
|
||||
int rv = uhd_device::open(args, ref, swap_channels);
|
||||
samps_per_buff_rx = rx_stream->get_max_num_samps();
|
||||
samps_per_buff_tx = tx_stream->get_max_num_samps();
|
||||
channel_count = usrp_dev->get_rx_num_channels();
|
||||
|
||||
wrap_rx_buffs = std::vector<std::vector<short> >(channel_count, std::vector<short>(2 * samps_per_buff_rx));
|
||||
for (size_t i = 0; i < wrap_rx_buffs.size(); i++)
|
||||
wrap_rx_buf_ptrs.push_back(&wrap_rx_buffs[i].front());
|
||||
|
||||
wrap_tx_buffs = std::vector<std::vector<short> >(channel_count, std::vector<short>(2 * 5000));
|
||||
for (size_t i = 0; i < wrap_tx_buffs.size(); i++)
|
||||
wrap_tx_buf_ptrs.push_back(&wrap_tx_buffs[i].front());
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
uhd_wrap::~uhd_wrap()
|
||||
{
|
||||
// drvtest::gshutdown = 1;
|
||||
//t->join();
|
||||
}
|
||||
|
||||
size_t uhd_wrap::bufsizerx()
|
||||
{
|
||||
return samps_per_buff_rx;
|
||||
}
|
||||
|
||||
size_t uhd_wrap::bufsizetx()
|
||||
{
|
||||
return samps_per_buff_tx;
|
||||
}
|
||||
|
||||
int uhd_wrap::chancount()
|
||||
{
|
||||
return channel_count;
|
||||
}
|
||||
|
||||
int uhd_wrap::wrap_read(TIMESTAMP *timestamp)
|
||||
{
|
||||
uhd::rx_metadata_t md;
|
||||
size_t num_rx_samps = rx_stream->recv(wrap_rx_buf_ptrs, samps_per_buff_rx, md, 0.1, true);
|
||||
*timestamp = md.time_spec.to_ticks(rx_rate);
|
||||
return num_rx_samps; //uhd_device::readSamples(bufs, len, overrun, timestamp, underrun);
|
||||
}
|
||||
|
||||
extern "C" void *uhdwrap_open(struct ipc_sk_if_open_req *open_req)
|
||||
{
|
||||
unsigned int rx_sps, tx_sps;
|
||||
|
||||
/* FIXME: dev arg string* */
|
||||
/* FIXME: rx frontend bw? */
|
||||
/* FIXME: tx frontend bw? */
|
||||
ReferenceType cref;
|
||||
switch (open_req->clockref) {
|
||||
case FEATURE_MASK_CLOCKREF_EXTERNAL:
|
||||
cref = ReferenceType::REF_EXTERNAL;
|
||||
break;
|
||||
case FEATURE_MASK_CLOCKREF_INTERNAL:
|
||||
default:
|
||||
cref = ReferenceType::REF_INTERNAL;
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<std::string> tx_paths;
|
||||
std::vector<std::string> rx_paths;
|
||||
for (unsigned int i = 0; i < open_req->num_chans; i++) {
|
||||
tx_paths.push_back(open_req->chan_info[i].tx_path);
|
||||
rx_paths.push_back(open_req->chan_info[i].rx_path);
|
||||
}
|
||||
|
||||
/* FIXME: this is actually the sps value, not the sample rate!
|
||||
* sample rate is looked up according to the sps rate by uhd backend */
|
||||
rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den;
|
||||
tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den;
|
||||
uhd_wrap *uhd_wrap_dev =
|
||||
new uhd_wrap(tx_sps, rx_sps, RadioDevice::NORMAL, open_req->num_chans, 0.0, tx_paths, rx_paths);
|
||||
uhd_wrap_dev->open("", cref, false);
|
||||
|
||||
return uhd_wrap_dev;
|
||||
}
|
||||
extern "C" int32_t uhdwrap_get_bufsizerx(void *dev)
|
||||
{
|
||||
uhd_wrap *d = (uhd_wrap *)dev;
|
||||
return d->bufsizerx();
|
||||
}
|
||||
|
||||
extern "C" int32_t uhdwrap_get_timingoffset(void *dev)
|
||||
{
|
||||
uhd_wrap *d = (uhd_wrap *)dev;
|
||||
return d->getTimingOffset();
|
||||
}
|
||||
|
||||
extern "C" int32_t uhdwrap_read(void *dev, uint32_t num_chans)
|
||||
{
|
||||
TIMESTAMP t;
|
||||
uhd_wrap *d = (uhd_wrap *)dev;
|
||||
|
||||
if (num_chans != d->wrap_rx_buf_ptrs.size()) {
|
||||
perror("omg chans?!");
|
||||
}
|
||||
|
||||
int32_t read = d->wrap_read(&t);
|
||||
|
||||
/* multi channel rx on b210 will return 0 due to alignment adventures, do not put 0 samples into a ipc buffer... */
|
||||
if (read <= 0)
|
||||
return read;
|
||||
|
||||
for (uint32_t i = 0; i < num_chans; i++) {
|
||||
ipc_shm_enqueue(ios_rx_from_device[i], t, read, (uint16_t *)&d->wrap_rx_buffs[i].front());
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
extern "C" int32_t uhdwrap_write(void *dev, uint32_t num_chans, bool *underrun)
|
||||
{
|
||||
uhd_wrap *d = (uhd_wrap *)dev;
|
||||
|
||||
uint64_t timestamp;
|
||||
int32_t len = -1;
|
||||
for (uint32_t i = 0; i < num_chans; i++) {
|
||||
len = ipc_shm_read(ios_tx_to_device[i], (uint16_t *)&d->wrap_tx_buffs[i].front(), 5000, ×tamp, 1);
|
||||
if (len < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return d->writeSamples(d->wrap_tx_buf_ptrs, len, underrun, timestamp);
|
||||
}
|
||||
|
||||
extern "C" double uhdwrap_set_freq(void *dev, double f, size_t chan, bool for_tx)
|
||||
{
|
||||
uhd_wrap *d = (uhd_wrap *)dev;
|
||||
if (for_tx)
|
||||
return d->setTxFreq(f, chan);
|
||||
else
|
||||
return d->setRxFreq(f, chan);
|
||||
}
|
||||
|
||||
extern "C" double uhdwrap_set_gain(void *dev, double f, size_t chan, bool for_tx)
|
||||
{
|
||||
uhd_wrap *d = (uhd_wrap *)dev;
|
||||
// if (for_tx)
|
||||
// return d->setTxGain(f, chan);
|
||||
// else
|
||||
return d->setRxGain(f, chan);
|
||||
}
|
||||
|
||||
extern "C" double uhdwrap_set_txatt(void *dev, double a, size_t chan)
|
||||
{
|
||||
uhd_wrap *d = (uhd_wrap *)dev;
|
||||
return d->setPowerAttenuation(a, chan);
|
||||
}
|
||||
|
||||
extern "C" int32_t uhdwrap_start(void *dev, int chan)
|
||||
{
|
||||
uhd_wrap *d = (uhd_wrap *)dev;
|
||||
return d->start();
|
||||
}
|
||||
|
||||
extern "C" int32_t uhdwrap_stop(void *dev, int chan)
|
||||
{
|
||||
uhd_wrap *d = (uhd_wrap *)dev;
|
||||
return d->stop();
|
||||
}
|
||||
|
||||
extern "C" void uhdwrap_fill_info_cnf(struct ipc_sk_if *ipc_prim)
|
||||
{
|
||||
struct ipc_sk_if_info_chan *chan_info;
|
||||
|
||||
uhd::device_addr_t args("");
|
||||
uhd::device_addrs_t devs_found = uhd::device::find(args);
|
||||
if (devs_found.size() < 1) {
|
||||
std::cout << "\n No device found!";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(devs_found[0]);
|
||||
auto rxchans = usrp->get_rx_num_channels();
|
||||
auto txchans = usrp->get_tx_num_channels();
|
||||
auto rx_range = usrp->get_rx_gain_range();
|
||||
auto tx_range = usrp->get_tx_gain_range();
|
||||
|
||||
//auto nboards = usrp->get_num_mboards();
|
||||
auto refs = usrp->get_clock_sources(0);
|
||||
auto devname = usrp->get_mboard_name(0);
|
||||
|
||||
ipc_prim->u.info_cnf.feature_mask = 0;
|
||||
if (std::find(refs.begin(), refs.end(), "internal") != refs.end())
|
||||
ipc_prim->u.info_cnf.feature_mask |= FEATURE_MASK_CLOCKREF_INTERNAL;
|
||||
if (std::find(refs.begin(), refs.end(), "external") != refs.end())
|
||||
ipc_prim->u.info_cnf.feature_mask |= FEATURE_MASK_CLOCKREF_EXTERNAL;
|
||||
|
||||
// at least one duplex channel
|
||||
auto num_chans = rxchans == txchans ? txchans : 1;
|
||||
|
||||
ipc_prim->u.info_cnf.iq_scaling_val_rx = 0.3;
|
||||
ipc_prim->u.info_cnf.iq_scaling_val_tx = 1;
|
||||
ipc_prim->u.info_cnf.max_num_chans = num_chans;
|
||||
OSMO_STRLCPY_ARRAY(ipc_prim->u.info_cnf.dev_desc, devname.c_str());
|
||||
chan_info = ipc_prim->u.info_cnf.chan_info;
|
||||
for (unsigned int i = 0; i < ipc_prim->u.info_cnf.max_num_chans; i++) {
|
||||
auto rxant = usrp->get_rx_antennas(i);
|
||||
auto txant = usrp->get_tx_antennas(i);
|
||||
for (unsigned int j = 0; j < txant.size(); j++) {
|
||||
OSMO_STRLCPY_ARRAY(chan_info->tx_path[j], txant[j].c_str());
|
||||
}
|
||||
for (unsigned int j = 0; j < rxant.size(); j++) {
|
||||
OSMO_STRLCPY_ARRAY(chan_info->rx_path[j], rxant[j].c_str());
|
||||
}
|
||||
chan_info->min_rx_gain = rx_range.start();
|
||||
chan_info->max_rx_gain = rx_range.stop();
|
||||
chan_info->min_tx_gain = tx_range.start();
|
||||
chan_info->max_tx_gain = tx_range.stop();
|
||||
chan_info->nominal_tx_power = 7.5; // FIXME: would require uhd dev + freq info
|
||||
chan_info++;
|
||||
}
|
||||
}
|
||||
83
Transceiver52M/device/ipc/uhdwrap.h
Normal file
83
Transceiver52M/device/ipc/uhdwrap.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Eric Wild <ewild@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
|
||||
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef IPC_UHDWRAP_H
|
||||
#define IPC_UHDWRAP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "../uhd/UHDDevice.h"
|
||||
|
||||
class uhd_wrap : public uhd_device {
|
||||
public:
|
||||
// std::thread *t;
|
||||
size_t samps_per_buff_rx;
|
||||
size_t samps_per_buff_tx;
|
||||
int channel_count;
|
||||
|
||||
std::vector<std::vector<short> > wrap_rx_buffs;
|
||||
std::vector<std::vector<short> > wrap_tx_buffs;
|
||||
std::vector<short *> wrap_rx_buf_ptrs;
|
||||
std::vector<short *> wrap_tx_buf_ptrs;
|
||||
|
||||
template <typename... Args> uhd_wrap(Args... args) : uhd_device(args...)
|
||||
{
|
||||
// t = new std::thread(magicthread);
|
||||
// give the thread some time to start and set up
|
||||
// std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
virtual ~uhd_wrap();
|
||||
|
||||
// void ipc_sock_close() override {};
|
||||
int wrap_read(TIMESTAMP *timestamp);
|
||||
virtual int open(const std::string &args, int ref, bool swap_channels) override;
|
||||
|
||||
// bool start() override;
|
||||
// bool stop() override;
|
||||
// virtual TIMESTAMP initialWriteTimestamp() override;
|
||||
// virtual TIMESTAMP initialReadTimestamp() override;
|
||||
|
||||
int getTimingOffset()
|
||||
{
|
||||
return ts_offset;
|
||||
}
|
||||
size_t bufsizerx();
|
||||
size_t bufsizetx();
|
||||
int chancount();
|
||||
};
|
||||
#else
|
||||
void *uhdwrap_open(struct ipc_sk_if_open_req *open_req);
|
||||
|
||||
int32_t uhdwrap_get_bufsizerx(void *dev);
|
||||
|
||||
int32_t uhdwrap_get_timingoffset(void *dev);
|
||||
|
||||
int32_t uhdwrap_read(void *dev, uint32_t num_chans);
|
||||
|
||||
int32_t uhdwrap_write(void *dev, uint32_t num_chans, bool *underrun);
|
||||
|
||||
double uhdwrap_set_freq(void *dev, double f, size_t chan, bool for_tx);
|
||||
|
||||
double uhdwrap_set_gain(void *dev, double f, size_t chan, bool for_tx);
|
||||
|
||||
int32_t uhdwrap_start(void *dev, int chan);
|
||||
|
||||
int32_t uhdwrap_stop(void *dev, int chan);
|
||||
|
||||
void uhdwrap_fill_info_cnf(struct ipc_sk_if *ipc_prim);
|
||||
|
||||
double uhdwrap_set_txatt(void *dev, double a, size_t chan);
|
||||
#endif
|
||||
|
||||
#endif // IPC_B210_H
|
||||
@@ -20,6 +20,9 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Threads.h"
|
||||
#include "LMSDevice.h"
|
||||
@@ -28,6 +31,7 @@
|
||||
#include <lime/LimeSuite.h>
|
||||
|
||||
extern "C" {
|
||||
#include "trx_vty.h"
|
||||
#include "osmo_signal.h"
|
||||
#include <osmocom/core/utils.h>
|
||||
}
|
||||
@@ -36,20 +40,102 @@ extern "C" {
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define MAX_ANTENNA_LIST_SIZE 10
|
||||
#define LMS_SAMPLE_RATE GSMRATE*32
|
||||
#define GSM_CARRIER_BW 270000.0 /* 270kHz */
|
||||
#define LMS_MIN_BW_SUPPORTED 2.5e6 /* 2.5mHz, minimum supported by LMS */
|
||||
#define LMS_CALIBRATE_BW_HZ OSMO_MAX(GSM_CARRIER_BW, LMS_MIN_BW_SUPPORTED)
|
||||
#define SAMPLE_BUF_SZ (1 << 20) /* Size of Rx timestamp based Ring buffer, in bytes */
|
||||
|
||||
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
|
||||
|
||||
/* Device Name Prefixes as presented by LimeSuite API LMS_GetDeviceInfo(): */
|
||||
#define LMS_DEV_SDR_USB_PREFIX_NAME "LimeSDR-USB"
|
||||
#define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini"
|
||||
#define LMS_DEV_NET_MICRO_PREFIX_NAME "LimeNET-Micro"
|
||||
|
||||
/* Device parameter descriptor */
|
||||
struct dev_desc {
|
||||
/* Does LimeSuite allow switching the clock source for this device?
|
||||
* LimeSDR-Mini does not have switches but needs soldering to select
|
||||
* external/internal clock. Any call to LMS_SetClockFreq() will fail.
|
||||
*/
|
||||
bool clock_src_switchable;
|
||||
/* Does LimeSuite allow using REF_INTERNAL for this device?
|
||||
* LimeNET-Micro does not like selecting internal clock
|
||||
*/
|
||||
bool clock_src_int_usable;
|
||||
/* Sample rate coef (without having TX/RX samples per symbol into account) */
|
||||
double rate;
|
||||
/* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
|
||||
double rate_multiarfcn;
|
||||
/* Coefficient multiplied by TX sample rate in order to shift Tx time */
|
||||
double ts_offset_coef;
|
||||
/* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
|
||||
double ts_offset_coef_multiarfcn;
|
||||
/* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
|
||||
std::string name_prefix;
|
||||
};
|
||||
|
||||
static const std::map<enum lms_dev_type, struct dev_desc> dev_param_map {
|
||||
{ LMS_DEV_SDR_USB, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_SDR_USB_PREFIX_NAME } },
|
||||
{ LMS_DEV_SDR_MINI, { false, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 8.2e-5, LMS_DEV_SDR_MINI_PREFIX_NAME } },
|
||||
{ LMS_DEV_NET_MICRO, { true, false, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_NET_MICRO_PREFIX_NAME } },
|
||||
{ LMS_DEV_UNKNOWN, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
|
||||
};
|
||||
|
||||
typedef std::tuple<lms_dev_type, enum gsm_band> dev_band_key;
|
||||
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
|
||||
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
|
||||
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_850), { 73.0, 11.2, -6.0 } },
|
||||
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_900), { 73.0, 10.8, -6.0 } },
|
||||
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1800), { 65.0, -3.5, -17.0 } }, /* FIXME: OS#4583: 1800Mhz is failing above TxGain=65, which is around -3.5dBm (already < 0 dBm) */
|
||||
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1900), { 73.0, 1.7, -17.0 } }, /* FIXME: OS#4583: 1900MHz is failing in all TxGain values */
|
||||
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_850), { 66.0, 3.1, -6.0 } }, /* FIXME: OS#4583: Ensure BAND2 is used at startup */
|
||||
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_900), { 66.0, 2.8, -6.0 } }, /* FIXME: OS#4583: Ensure BAND2 is used at startup */
|
||||
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_1800), { 66.0, -11.6, -17.0 } }, /* OS#4583: Any of BAND1 or BAND2 is fine */
|
||||
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_1900), { 66.0, -9.2, -17.0 } }, /* FIXME: OS#4583: Ensure BAND1 is used at startup */
|
||||
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_850), { 71.0, 6.8, -6.0 } },
|
||||
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_900), { 71.0, 6.8, -6.0 } },
|
||||
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_1800), { 65.0, -10.5, -17.0 } }, /* OS#4583: TxGain=71 (-4.4dBm) FAIL rms phase errors ~10° */
|
||||
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_1900), { 71.0, -6.3, -17.0 } }, /* FIXME: OS#4583: all FAIL, BAND1/BAND2 rms phase errors >23° */
|
||||
};
|
||||
|
||||
/* So far measurements done for B210 show really close to linear relationship
|
||||
* between gain and real output power, so we simply adjust the measured offset
|
||||
*/
|
||||
static double TxGain2TxPower(const dev_band_desc &desc, double tx_gain_db)
|
||||
{
|
||||
return desc.nom_out_tx_power - (desc.nom_lms_tx_gain - tx_gain_db);
|
||||
}
|
||||
static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
|
||||
{
|
||||
return desc.nom_lms_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
|
||||
}
|
||||
|
||||
static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
|
||||
{
|
||||
std::map<enum lms_dev_type, struct dev_desc>::const_iterator it = dev_param_map.begin();
|
||||
|
||||
const lms_dev_info_t* device_info = LMS_GetDeviceInfo(m_lms_dev);
|
||||
|
||||
while (it != dev_param_map.end())
|
||||
{
|
||||
enum lms_dev_type dev_type = it->first;
|
||||
struct dev_desc desc = it->second;
|
||||
|
||||
if (strncmp(device_info->deviceName, desc.name_prefix.c_str(), desc.name_prefix.length()) == 0) {
|
||||
LOGC(DDEV, INFO) << "Device identified as " << desc.name_prefix;
|
||||
return dev_type;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
return LMS_DEV_UNKNOWN;
|
||||
}
|
||||
|
||||
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
const std::vector<std::string>& rx_paths):
|
||||
RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths),
|
||||
m_lms_dev(NULL)
|
||||
RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
|
||||
m_lms_dev(NULL), started(false), band((enum gsm_band)0), m_dev_type(LMS_DEV_UNKNOWN)
|
||||
{
|
||||
LOGC(DDEV, INFO) << "creating LMS device...";
|
||||
|
||||
@@ -59,6 +145,11 @@ LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t c
|
||||
tx_gains.resize(chans);
|
||||
|
||||
rx_buffers.resize(chans);
|
||||
|
||||
/* Set up per-channel Rx timestamp based Ring buffers */
|
||||
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||
rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
|
||||
|
||||
}
|
||||
|
||||
LMSDevice::~LMSDevice()
|
||||
@@ -93,7 +184,7 @@ static void lms_log_callback(int lvl, const char *msg)
|
||||
if ((unsigned int) lvl >= ARRAY_SIZE(lvl_map))
|
||||
lvl = ARRAY_SIZE(lvl_map)-1;
|
||||
|
||||
LOGLV(DLMS, lvl_map[lvl]) << msg;
|
||||
LOGLV(DDEVDRV, lvl_map[lvl]) << msg;
|
||||
}
|
||||
|
||||
static void print_range(const char* name, lms_range_t *range)
|
||||
@@ -110,7 +201,7 @@ static void print_range(const char* name, lms_range_t *range)
|
||||
int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::string &args)
|
||||
{
|
||||
unsigned int i, j;
|
||||
vector<string> filters;
|
||||
std::vector<std::string> filters;
|
||||
|
||||
filters = comma_delimited_to_vector(args.c_str());
|
||||
|
||||
@@ -131,14 +222,52 @@ int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::str
|
||||
return -1;
|
||||
}
|
||||
|
||||
void LMSDevice::assign_band_desc(enum gsm_band req_band)
|
||||
{
|
||||
dev_band_map_it it;
|
||||
|
||||
it = dev_band_nom_power_param_map.find(dev_band_key(m_dev_type, req_band));
|
||||
if (it == dev_band_nom_power_param_map.end()) {
|
||||
dev_desc desc = dev_param_map.at(m_dev_type);
|
||||
LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device "
|
||||
<< desc.name_prefix << " on band " << gsm_band_name(req_band)
|
||||
<< ", using LimeSDR-USB ones as fallback";
|
||||
it = dev_band_nom_power_param_map.find(dev_band_key(LMS_DEV_SDR_USB, req_band));
|
||||
}
|
||||
OSMO_ASSERT(it != dev_band_nom_power_param_map.end());
|
||||
band_desc = it->second;
|
||||
}
|
||||
|
||||
bool LMSDevice::set_band(enum gsm_band req_band)
|
||||
{
|
||||
if (band != 0 && req_band != band) {
|
||||
LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
|
||||
<< " different from previous band " << gsm_band_name(band);
|
||||
return false;
|
||||
}
|
||||
|
||||
band = req_band;
|
||||
assign_band_desc(band);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LMSDevice::get_dev_band_desc(dev_band_desc& desc)
|
||||
{
|
||||
if (band == 0) {
|
||||
LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
|
||||
assign_band_desc(GSM_BAND_900);
|
||||
}
|
||||
desc = band_desc;
|
||||
}
|
||||
|
||||
int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
|
||||
{
|
||||
lms_info_str_t* info_list;
|
||||
const lms_dev_info_t* device_info;
|
||||
lms_range_t range_sr;
|
||||
float_type sr_host, sr_rf;
|
||||
unsigned int i, n;
|
||||
int rc, dev_id;
|
||||
struct dev_desc dev_desc;
|
||||
|
||||
LOGC(DDEV, INFO) << "Opening LMS device..";
|
||||
|
||||
@@ -175,19 +304,20 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
|
||||
|
||||
delete [] info_list;
|
||||
|
||||
device_info = LMS_GetDeviceInfo(m_lms_dev);
|
||||
m_dev_type = parse_dev_type(m_lms_dev);
|
||||
dev_desc = dev_param_map.at(m_dev_type);
|
||||
|
||||
if ((ref != REF_EXTERNAL) && (ref != REF_INTERNAL)){
|
||||
LOGC(DDEV, ERROR) << "Invalid reference type";
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/* if reference clock is external setup must happen _before_ calling LMS_Init */
|
||||
/* FIXME make external reference frequency configurable */
|
||||
/* if reference clock is external, setup must happen _before_ calling LMS_Init */
|
||||
if (ref == REF_EXTERNAL) {
|
||||
LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
|
||||
/* Assume an external 10 MHz reference clock */
|
||||
if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, 10000000.0) < 0)
|
||||
/* FIXME: Assume an external 10 MHz reference clock. make
|
||||
external reference frequency configurable */
|
||||
if (!do_clock_src_freq(REF_EXTERNAL, 10000000.0))
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
@@ -197,22 +327,13 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/* LimeSDR-Mini does not have switches but needs soldering to select external/internal clock */
|
||||
/* LimeNET-Micro also does not like selecting internal clock*/
|
||||
/* also set device specific maximum tx levels selected by phasenoise measurements*/
|
||||
if (strncmp(device_info->deviceName,"LimeSDR-USB",11) == 0){
|
||||
/* if reference clock is internal setup must happen _after_ calling LMS_Init */
|
||||
/* according to lms using LMS_CLOCK_EXTREF with a frequency <= 0 is the correct way to set clock to internal reference*/
|
||||
if (ref == REF_INTERNAL) {
|
||||
LOGC(DDEV, INFO) << "Setting Internal clock reference";
|
||||
if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, -1) < 0)
|
||||
goto out_close;
|
||||
}
|
||||
maxTxGainClamp = 73.0;
|
||||
} else if (strncmp(device_info->deviceName,"LimeSDR-Mini",12) == 0)
|
||||
maxTxGainClamp = 66.0;
|
||||
else
|
||||
maxTxGainClamp = 71.0; /* "LimeNET-Micro", etc FIXME pciE based LMS boards?*/
|
||||
/* if reference clock is internal, setup must happen _after_ calling LMS_Init */
|
||||
if (ref == REF_INTERNAL) {
|
||||
LOGC(DDEV, INFO) << "Setting Internal clock reference";
|
||||
/* Internal freq param is not used */
|
||||
if (!do_clock_src_freq(REF_INTERNAL, 0))
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/* enable all used channels */
|
||||
for (i=0; i<chans; i++) {
|
||||
@@ -227,16 +348,22 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
|
||||
goto out_close;
|
||||
print_range("Sample Rate", &range_sr);
|
||||
|
||||
LOGC(DDEV, INFO) << "Setting sample rate to " << GSMRATE*tx_sps << " " << tx_sps;
|
||||
if (LMS_SetSampleRate(m_lms_dev, GSMRATE*tx_sps, 32) < 0)
|
||||
if (iface == MULTI_ARFCN)
|
||||
sr_host = dev_desc.rate_multiarfcn * tx_sps;
|
||||
else
|
||||
sr_host = dev_desc.rate * tx_sps;
|
||||
LOGC(DDEV, INFO) << "Setting sample rate to " << sr_host << " " << tx_sps;
|
||||
if (LMS_SetSampleRate(m_lms_dev, sr_host, 32) < 0)
|
||||
goto out_close;
|
||||
|
||||
if (LMS_GetSampleRate(m_lms_dev, LMS_CH_RX, 0, &sr_host, &sr_rf))
|
||||
goto out_close;
|
||||
LOGC(DDEV, INFO) << "Sample Rate: Host=" << sr_host << " RF=" << sr_rf;
|
||||
|
||||
/* FIXME: make this device/model dependent, like UHDDevice:dev_param_map! */
|
||||
ts_offset = static_cast<TIMESTAMP>(8.9e-5 * GSMRATE * tx_sps); /* time * sample_rate */
|
||||
if (iface == MULTI_ARFCN)
|
||||
ts_offset = static_cast<TIMESTAMP>(dev_desc.ts_offset_coef_multiarfcn * sr_host);
|
||||
else
|
||||
ts_offset = static_cast<TIMESTAMP>(dev_desc.ts_offset_coef * sr_host);
|
||||
|
||||
/* configure antennas */
|
||||
if (!set_antennas()) {
|
||||
@@ -244,13 +371,7 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/* Set up per-channel Rx timestamp based Ring buffers */
|
||||
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||
rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
|
||||
|
||||
started = false;
|
||||
|
||||
return NORMAL;
|
||||
return iface == MULTI_ARFCN ? MULTI_ARFCN : NORMAL;
|
||||
|
||||
out_close:
|
||||
LOGC(DDEV, FATAL) << "Error in LMS open, closing: " << LMS_GetLastErrorMessage();
|
||||
@@ -264,17 +385,20 @@ bool LMSDevice::start()
|
||||
LOGC(DDEV, INFO) << "starting LMS...";
|
||||
|
||||
unsigned int i;
|
||||
dev_band_desc desc;
|
||||
|
||||
if (started) {
|
||||
LOGC(DDEV, ERR) << "Device already started";
|
||||
return false;
|
||||
}
|
||||
|
||||
get_dev_band_desc(desc);
|
||||
|
||||
/* configure the channels/streams */
|
||||
for (i=0; i<chans; i++) {
|
||||
/* Set gains for calibration/filter setup */
|
||||
/* TX gain to maximum */
|
||||
setTxGain(maxTxGain(), i);
|
||||
LMS_SetGaindB(m_lms_dev, LMS_CH_TX, i, TxPower2TxGain(desc, desc.nom_out_tx_power));
|
||||
/* RX gain to midpoint */
|
||||
setRxGain((minRxGain() + maxRxGain()) / 2, i);
|
||||
|
||||
@@ -344,6 +468,43 @@ bool LMSDevice::stop()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
|
||||
{
|
||||
struct dev_desc dev_desc = dev_param_map.at(m_dev_type);
|
||||
size_t lms_clk_id;
|
||||
|
||||
switch (ref) {
|
||||
case REF_EXTERNAL:
|
||||
lms_clk_id = LMS_CLOCK_EXTREF;
|
||||
break;
|
||||
case REF_INTERNAL:
|
||||
if (!dev_desc.clock_src_int_usable) {
|
||||
LOGC(DDEV, ERROR) << "Device type " << dev_desc.name_prefix
|
||||
<< " doesn't support internal reference clock";
|
||||
return false;
|
||||
}
|
||||
/* According to lms using LMS_CLOCK_EXTREF with a
|
||||
frequency <= 0 is the correct way to set clock to
|
||||
internal reference */
|
||||
lms_clk_id = LMS_CLOCK_EXTREF;
|
||||
freq = -1;
|
||||
break;
|
||||
default:
|
||||
LOGC(DDEV, ERROR) << "Invalid reference type " << get_value_string(clock_ref_names, ref);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dev_desc.clock_src_switchable) {
|
||||
if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0)
|
||||
return false;
|
||||
} else {
|
||||
LOGC(DDEV, INFO) << "Device type " << dev_desc.name_prefix
|
||||
<< " doesn't support switching clock source through SW";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* do rx/tx calibration - depends on gain, freq and bw */
|
||||
bool LMSDevice::do_calib(size_t chan)
|
||||
{
|
||||
@@ -382,17 +543,6 @@ bool LMSDevice::do_filters(size_t chan)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
double LMSDevice::maxTxGain()
|
||||
{
|
||||
return maxTxGainClamp;
|
||||
}
|
||||
|
||||
double LMSDevice::minTxGain()
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double LMSDevice::maxRxGain()
|
||||
{
|
||||
return 73.0;
|
||||
@@ -403,22 +553,6 @@ double LMSDevice::minRxGain()
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double LMSDevice::setTxGain(double dB, size_t chan)
|
||||
{
|
||||
if (dB > maxTxGain())
|
||||
dB = maxTxGain();
|
||||
if (dB < minTxGain())
|
||||
dB = minTxGain();
|
||||
|
||||
LOGCHAN(chan, DDEV, NOTICE) << "Setting TX gain to " << dB << " dB";
|
||||
|
||||
if (LMS_SetGaindB(m_lms_dev, LMS_CH_TX, chan, dB) < 0)
|
||||
LOGCHAN(chan, DDEV, ERR) << "Error setting TX gain to " << dB << " dB";
|
||||
else
|
||||
tx_gains[chan] = dB;
|
||||
return tx_gains[chan];
|
||||
}
|
||||
|
||||
double LMSDevice::setRxGain(double dB, size_t chan)
|
||||
{
|
||||
if (dB > maxRxGain())
|
||||
@@ -435,6 +569,63 @@ double LMSDevice::setRxGain(double dB, size_t chan)
|
||||
return rx_gains[chan];
|
||||
}
|
||||
|
||||
double LMSDevice::rssiOffset(size_t chan)
|
||||
{
|
||||
double rssiOffset;
|
||||
dev_band_desc desc;
|
||||
|
||||
if (chan >= rx_gains.size()) {
|
||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
get_dev_band_desc(desc);
|
||||
rssiOffset = rx_gains[chan] + desc.rxgain2rssioffset_rel;
|
||||
return rssiOffset;
|
||||
}
|
||||
|
||||
double LMSDevice::setPowerAttenuation(int atten, size_t chan)
|
||||
{
|
||||
double tx_power, dB;
|
||||
dev_band_desc desc;
|
||||
|
||||
if (chan >= tx_gains.size()) {
|
||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
get_dev_band_desc(desc);
|
||||
tx_power = desc.nom_out_tx_power - atten;
|
||||
dB = TxPower2TxGain(desc, tx_power);
|
||||
|
||||
LOGCHAN(chan, DDEV, NOTICE) << "Setting TX gain to " << dB << " dB (~" << tx_power << " dBm)";
|
||||
|
||||
if (LMS_SetGaindB(m_lms_dev, LMS_CH_TX, chan, dB) < 0)
|
||||
LOGCHAN(chan, DDEV, ERR) << "Error setting TX gain to " << dB << " dB (~" << tx_power << " dBm)";
|
||||
else
|
||||
tx_gains[chan] = dB;
|
||||
return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
|
||||
}
|
||||
|
||||
double LMSDevice::getPowerAttenuation(size_t chan) {
|
||||
dev_band_desc desc;
|
||||
if (chan >= tx_gains.size()) {
|
||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
get_dev_band_desc(desc);
|
||||
return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
|
||||
}
|
||||
|
||||
int LMSDevice::getNominalTxPower(size_t chan)
|
||||
{
|
||||
dev_band_desc desc;
|
||||
get_dev_band_desc(desc);
|
||||
|
||||
return desc.nom_out_tx_power;
|
||||
}
|
||||
|
||||
void LMSDevice::log_ant_list(bool dir_tx, size_t chan, std::ostringstream& os)
|
||||
{
|
||||
lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */
|
||||
@@ -624,7 +815,7 @@ void LMSDevice::update_stream_stats_rx(size_t chan, bool *overrun)
|
||||
m_ctr[chan].rx_overruns += status.overrun;
|
||||
|
||||
/* Dropped packets in Rx are counted when gaps in Rx timestamps are
|
||||
detected (likely because buffer oveflow in hardware). Value count
|
||||
detected (likely because buffer overflow in hardware). Value count
|
||||
since the last call to LMS_GetStreamStatus(stream). */
|
||||
if (status.droppedPackets) {
|
||||
changed = true;
|
||||
@@ -644,7 +835,7 @@ void LMSDevice::update_stream_stats_rx(size_t chan, bool *overrun)
|
||||
|
||||
// NOTE: Assumes sequential reads
|
||||
int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
|
||||
TIMESTAMP timestamp, bool * underrun, unsigned *RSSI)
|
||||
TIMESTAMP timestamp, bool * underrun)
|
||||
{
|
||||
int rc, num_smpls, expect_smpls;
|
||||
ssize_t avail_smpls;
|
||||
@@ -711,8 +902,9 @@ int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
|
||||
for (size_t i = 0; i < rx_buffers.size(); i++) {
|
||||
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
|
||||
if ((rc < 0) || (rc != len)) {
|
||||
LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
|
||||
LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
|
||||
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc) << ". "
|
||||
<< rx_buffers[i]->str_status(timestamp)
|
||||
<< ", (len=" << len << ")";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -762,8 +954,7 @@ void LMSDevice::update_stream_stats_tx(size_t chan, bool *underrun)
|
||||
}
|
||||
|
||||
int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
|
||||
bool * underrun, unsigned long long timestamp,
|
||||
bool isControl)
|
||||
bool * underrun, unsigned long long timestamp)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned int i;
|
||||
@@ -772,11 +963,6 @@ int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
|
||||
tx_metadata.waitForTimestamp = true;
|
||||
tx_metadata.timestamp = timestamp - ts_offset; /* Shift Tx time by offset */
|
||||
|
||||
if (isControl) {
|
||||
LOGC(DDEV, ERROR) << "Control packets not supported";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bufs.size() != chans) {
|
||||
LOGC(DDEV, ERROR) << "Invalid channel combination " << bufs.size();
|
||||
return -1;
|
||||
@@ -806,13 +992,39 @@ bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
|
||||
|
||||
bool LMSDevice::setTxFreq(double wFreq, size_t chan)
|
||||
{
|
||||
uint16_t req_arfcn;
|
||||
enum gsm_band req_band;
|
||||
|
||||
if (chan >= chans) {
|
||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
|
||||
|
||||
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
|
||||
if (req_arfcn == 0xffff) {
|
||||
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
|
||||
return false;
|
||||
}
|
||||
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
|
||||
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
|
||||
<< " Hz (ARFCN " << req_arfcn << " )";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (band != 0 && req_band != band) {
|
||||
LOGCHAN(chan, DDEV, ALERT) << "Requesting Tx Frequency " << wFreq
|
||||
<< " Hz different from previous band " << gsm_band_name(band);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
|
||||
LOGCHAN(chan, DDEV, ERROR) << "Error setting Tx Freq to " << wFreq << " Hz";
|
||||
return false;
|
||||
}
|
||||
|
||||
band = req_band;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in
|
||||
* the main directory for licensing information for this specific distribuion.
|
||||
* the main directory for licensing information for this specific distribution.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
@@ -32,6 +32,10 @@
|
||||
#include <iostream>
|
||||
#include <lime/LimeSuite.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
}
|
||||
|
||||
/* Definition of LIMESDR_TX_AMPL limits maximum amplitude of I and Q
|
||||
* channels separately. Hence LIMESDR_TX_AMPL value must be 1/sqrt(2) =
|
||||
* 0.7071.... to get an amplitude of 1 of the complex signal:
|
||||
@@ -41,6 +45,30 @@
|
||||
* A^2 = 1 */
|
||||
#define LIMESDR_TX_AMPL 0.707
|
||||
|
||||
enum lms_dev_type {
|
||||
LMS_DEV_SDR_USB, /* LimeSDR-USB */
|
||||
LMS_DEV_SDR_MINI, /* LimeSDR-Mini */
|
||||
LMS_DEV_NET_MICRO, /* LimeNet-micro */
|
||||
LMS_DEV_UNKNOWN,
|
||||
};
|
||||
|
||||
struct dev_band_desc {
|
||||
/* Maximum LimeSuite Tx Gain which can be set/used without distorting
|
||||
the output * signal, and the resulting real output power measured
|
||||
when that gain is used.
|
||||
*/
|
||||
double nom_lms_tx_gain; /* dB */
|
||||
double nom_out_tx_power; /* dBm */
|
||||
/* Factor used to infer base real RSSI offset on the Rx path based on current
|
||||
configured RxGain. The resulting rssiOffset is added to the per burst
|
||||
calculated energy in upper layers. These values were empirically
|
||||
found and may change based on multiple factors, see OS#4468.
|
||||
Correct measured values only provided for LimeSDR-USB so far.
|
||||
rssiOffset = rxGain + rxgain2rssioffset_rel;
|
||||
*/
|
||||
double rxgain2rssioffset_rel; /* dB */
|
||||
};
|
||||
|
||||
/** A class to handle a LimeSuite supported device */
|
||||
class LMSDevice:public RadioDevice {
|
||||
|
||||
@@ -59,7 +87,10 @@ private:
|
||||
TIMESTAMP ts_initial, ts_offset;
|
||||
|
||||
std::vector<double> tx_gains, rx_gains;
|
||||
double maxTxGainClamp;
|
||||
enum gsm_band band;
|
||||
struct dev_band_desc band_desc;
|
||||
|
||||
enum lms_dev_type m_dev_type;
|
||||
|
||||
bool do_calib(size_t chan);
|
||||
bool do_filters(size_t chan);
|
||||
@@ -68,11 +99,14 @@ private:
|
||||
bool flush_recv(size_t num_pkts);
|
||||
void update_stream_stats_rx(size_t chan, bool *overrun);
|
||||
void update_stream_stats_tx(size_t chan, bool *underrun);
|
||||
|
||||
bool do_clock_src_freq(enum ReferenceType ref, double freq);
|
||||
void get_dev_band_desc(dev_band_desc& desc);
|
||||
bool set_band(enum gsm_band req_band);
|
||||
void assign_band_desc(enum gsm_band req_band);
|
||||
public:
|
||||
|
||||
/** Object constructor */
|
||||
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
|
||||
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
const std::vector<std::string>& rx_paths);
|
||||
~LMSDevice();
|
||||
@@ -97,24 +131,21 @@ public:
|
||||
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
|
||||
@param timestamp The timestamp of the first samples to be read
|
||||
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
|
||||
@param RSSI The received signal strength of the read result
|
||||
@return The number of samples actually read
|
||||
*/
|
||||
int readSamples(std::vector < short *>&buf, int len, bool * overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool * underrun =
|
||||
NULL, unsigned *RSSI = NULL);
|
||||
NULL);
|
||||
/**
|
||||
Write samples to the LMS.
|
||||
@param buf Contains the data to be written.
|
||||
@param len number of samples to write.
|
||||
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
|
||||
@param timestamp The timestamp of the first sample of the data buffer.
|
||||
@param isControl Set if data is a control packet, e.g. a ping command
|
||||
@return The number of samples actually written
|
||||
*/
|
||||
int writeSamples(std::vector < short *>&bufs, int len, bool * underrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool isControl =
|
||||
false);
|
||||
TIMESTAMP timestamp = 0xffffffff);
|
||||
|
||||
/** Update the alignment between the read and write timestamps */
|
||||
bool updateAlignment(TIMESTAMP timestamp);
|
||||
@@ -159,19 +190,12 @@ public:
|
||||
/** return minimum Rx Gain **/
|
||||
double minRxGain(void);
|
||||
|
||||
/** sets the transmit chan gain, returns the gain setting **/
|
||||
double setTxGain(double dB, size_t chan = 0);
|
||||
double rssiOffset(size_t chan);
|
||||
|
||||
/** get transmit gain */
|
||||
double getTxGain(size_t chan = 0) {
|
||||
return tx_gains[chan];
|
||||
}
|
||||
double setPowerAttenuation(int atten, size_t chan);
|
||||
double getPowerAttenuation(size_t chan = 0);
|
||||
|
||||
/** return maximum Tx Gain **/
|
||||
double maxTxGain(void);
|
||||
|
||||
/** return minimum Rx Gain **/
|
||||
double minTxGain(void);
|
||||
int getNominalTxPower(size_t chan = 0);
|
||||
|
||||
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||
bool setRxAntenna(const std::string & ant, size_t chan = 0);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
|
||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
|
||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LMS_CFLAGS)
|
||||
|
||||
noinst_HEADERS = LMSDevice.h
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
|
||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
|
||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
|
||||
|
||||
noinst_HEADERS = UHDDevice.h
|
||||
|
||||
|
||||
@@ -33,11 +33,18 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef USE_UHD_3_11
|
||||
extern "C" {
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/vty/cpu_sched_vty.h>
|
||||
}
|
||||
|
||||
#ifdef USE_UHD_3_11
|
||||
#include <uhd/utils/log_add.hpp>
|
||||
#include <uhd/utils/thread.hpp>
|
||||
#else
|
||||
#include <uhd/utils/msg.hpp>
|
||||
#include <uhd/utils/thread_priority.hpp>
|
||||
#else
|
||||
#include <uhd/utils/thread.hpp>
|
||||
#endif
|
||||
|
||||
#define USRP_TX_AMPL 0.3
|
||||
@@ -122,9 +129,23 @@ static const std::map<dev_key, dev_desc> dev_param_map {
|
||||
{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
|
||||
};
|
||||
|
||||
typedef std::tuple<uhd_dev_type, enum gsm_band> dev_band_key;
|
||||
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
|
||||
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
|
||||
{ std::make_tuple(B200, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
|
||||
{ std::make_tuple(B200, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
|
||||
{ std::make_tuple(B200, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
|
||||
{ std::make_tuple(B200, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
|
||||
{ std::make_tuple(B210, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
|
||||
{ std::make_tuple(B210, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
|
||||
{ std::make_tuple(B210, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
|
||||
{ std::make_tuple(B210, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
|
||||
};
|
||||
|
||||
void *async_event_loop(uhd_device *dev)
|
||||
{
|
||||
set_selfthread_name("UHDAsyncEvent");
|
||||
osmo_cpu_sched_vty_apply_localthread();
|
||||
|
||||
while (1) {
|
||||
dev->recv_async_msg();
|
||||
@@ -134,23 +155,52 @@ void *async_event_loop(uhd_device *dev)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef USE_UHD_3_11
|
||||
#ifdef USE_UHD_3_11
|
||||
static void uhd_log_handler(const uhd::log::logging_info &info)
|
||||
{
|
||||
int level;
|
||||
|
||||
switch (info.verbosity)
|
||||
{
|
||||
case uhd::log::trace:
|
||||
case uhd::log::debug:
|
||||
level = LOGL_DEBUG;
|
||||
break;
|
||||
case uhd::log::info:
|
||||
level = LOGL_INFO;
|
||||
break;
|
||||
case uhd::log::warning:
|
||||
level = LOGL_NOTICE;
|
||||
break;
|
||||
case uhd::log::error:
|
||||
level = LOGL_ERROR;
|
||||
break;
|
||||
case uhd::log::fatal:
|
||||
level = LOGL_FATAL;
|
||||
break;
|
||||
default:
|
||||
level = LOGL_NOTICE;
|
||||
}
|
||||
|
||||
LOGSRC(DDEVDRV, level, info.file.c_str(), info.line) << "[" << info.component << "] " << info.message;
|
||||
}
|
||||
#else
|
||||
/*
|
||||
Catch and drop underrun 'U' and overrun 'O' messages from stdout
|
||||
since we already report using the logging facility. Direct
|
||||
everything else appropriately.
|
||||
*/
|
||||
void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
|
||||
static void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
|
||||
{
|
||||
switch (type) {
|
||||
case uhd::msg::status:
|
||||
LOGC(DDEV, INFO) << msg;
|
||||
LOGC(DDEVDRV, INFO) << msg;
|
||||
break;
|
||||
case uhd::msg::warning:
|
||||
LOGC(DDEV, WARNING) << msg;
|
||||
LOGC(DDEVDRV, NOTICE) << msg;
|
||||
break;
|
||||
case uhd::msg::error:
|
||||
LOGC(DDEV, ERROR) << msg;
|
||||
LOGC(DDEVDRV, ERROR) << msg;
|
||||
break;
|
||||
case uhd::msg::fastpath:
|
||||
break;
|
||||
@@ -158,14 +208,25 @@ void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* So far measurements done for B210 show really close to linear relationship
|
||||
* between gain and real output power, so we simply adjust the measured offset
|
||||
*/
|
||||
static double TxGain2TxPower(const dev_band_desc &desc, double tx_gain_db)
|
||||
{
|
||||
return desc.nom_out_tx_power - (desc.nom_uhd_tx_gain - tx_gain_db);
|
||||
}
|
||||
static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
|
||||
{
|
||||
return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
|
||||
}
|
||||
|
||||
uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
|
||||
InterfaceType iface, size_t chans, double lo_offset,
|
||||
InterfaceType iface, size_t chan_num, double lo_offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
const std::vector<std::string>& rx_paths)
|
||||
: RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths),
|
||||
tx_gain_min(0.0), tx_gain_max(0.0),
|
||||
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
|
||||
rx_gain_min(0.0), rx_gain_max(0.0),
|
||||
tx_spp(0), rx_spp(0),
|
||||
band((enum gsm_band)0), tx_spp(0), rx_spp(0),
|
||||
started(false), aligned(false), drop_cnt(0),
|
||||
prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
|
||||
{
|
||||
@@ -179,8 +240,47 @@ uhd_device::~uhd_device()
|
||||
delete rx_buffers[i];
|
||||
}
|
||||
|
||||
void uhd_device::assign_band_desc(enum gsm_band req_band)
|
||||
{
|
||||
dev_band_map_it it;
|
||||
|
||||
it = dev_band_nom_power_param_map.find(dev_band_key(dev_type, req_band));
|
||||
if (it == dev_band_nom_power_param_map.end()) {
|
||||
dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
|
||||
LOGC(DDEV, ERROR) << "No Power parameters exist for device "
|
||||
<< desc.str << " on band " << gsm_band_name(req_band)
|
||||
<< ", using B210 ones as fallback";
|
||||
it = dev_band_nom_power_param_map.find(dev_band_key(B210, req_band));
|
||||
}
|
||||
OSMO_ASSERT(it != dev_band_nom_power_param_map.end())
|
||||
band_desc = it->second;
|
||||
}
|
||||
|
||||
bool uhd_device::set_band(enum gsm_band req_band)
|
||||
{
|
||||
if (band != 0 && req_band != band) {
|
||||
LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
|
||||
<< " different from previous band " << gsm_band_name(band);
|
||||
return false;
|
||||
}
|
||||
|
||||
band = req_band;
|
||||
assign_band_desc(band);
|
||||
return true;
|
||||
}
|
||||
|
||||
void uhd_device::get_dev_band_desc(dev_band_desc& desc)
|
||||
{
|
||||
if (band == 0) {
|
||||
LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
|
||||
assign_band_desc(GSM_BAND_900);
|
||||
}
|
||||
desc = band_desc;
|
||||
}
|
||||
|
||||
void uhd_device::init_gains()
|
||||
{
|
||||
double tx_gain_min, tx_gain_max;
|
||||
uhd::gain_range_t range;
|
||||
|
||||
if (dev_type == UMTRX) {
|
||||
@@ -245,37 +345,6 @@ void uhd_device::set_rates()
|
||||
LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
|
||||
}
|
||||
|
||||
double uhd_device::setTxGain(double db, size_t chan)
|
||||
{
|
||||
if (chan >= tx_gains.size()) {
|
||||
LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (dev_type == UMTRX) {
|
||||
std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
|
||||
if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
|
||||
usrp_dev->set_tx_gain(db, chan);
|
||||
} else {
|
||||
// New UHD versions support split configuration of
|
||||
// Tx gain stages. We utilize this to set the gain
|
||||
// configuration, optimal for the Tx signal quality.
|
||||
// From our measurements, VGA1 must be 18dB plus-minus
|
||||
// one and VGA2 is the best when 23dB or lower.
|
||||
usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
|
||||
usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
|
||||
}
|
||||
} else {
|
||||
usrp_dev->set_tx_gain(db, chan);
|
||||
}
|
||||
|
||||
tx_gains[chan] = usrp_dev->get_tx_gain(chan);
|
||||
|
||||
LOGC(DDEV, INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
|
||||
|
||||
return tx_gains[chan];
|
||||
}
|
||||
|
||||
double uhd_device::setRxGain(double db, size_t chan)
|
||||
{
|
||||
if (chan >= rx_gains.size()) {
|
||||
@@ -301,14 +370,76 @@ double uhd_device::getRxGain(size_t chan)
|
||||
return rx_gains[chan];
|
||||
}
|
||||
|
||||
double uhd_device::getTxGain(size_t chan)
|
||||
double uhd_device::rssiOffset(size_t chan)
|
||||
{
|
||||
double rssiOffset;
|
||||
dev_band_desc desc;
|
||||
|
||||
if (chan >= rx_gains.size()) {
|
||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
get_dev_band_desc(desc);
|
||||
rssiOffset = rx_gains[chan] + desc.rxgain2rssioffset_rel;
|
||||
return rssiOffset;
|
||||
}
|
||||
|
||||
double uhd_device::setPowerAttenuation(int atten, size_t chan) {
|
||||
double tx_power, db;
|
||||
dev_band_desc desc;
|
||||
|
||||
if (chan >= tx_gains.size()) {
|
||||
LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
get_dev_band_desc(desc);
|
||||
tx_power = desc.nom_out_tx_power - atten;
|
||||
db = TxPower2TxGain(desc, tx_power);
|
||||
|
||||
if (dev_type == UMTRX) {
|
||||
std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
|
||||
if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
|
||||
usrp_dev->set_tx_gain(db, chan);
|
||||
} else {
|
||||
// New UHD versions support split configuration of
|
||||
// Tx gain stages. We utilize this to set the gain
|
||||
// configuration, optimal for the Tx signal quality.
|
||||
// From our measurements, VGA1 must be 18dB plus-minus
|
||||
// one and VGA2 is the best when 23dB or lower.
|
||||
usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
|
||||
usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
|
||||
}
|
||||
} else {
|
||||
usrp_dev->set_tx_gain(db, chan);
|
||||
}
|
||||
|
||||
tx_gains[chan] = usrp_dev->get_tx_gain(chan);
|
||||
|
||||
LOGC(DDEV, INFO) << "Set TX gain to " << tx_gains[chan] << "dB, ~"
|
||||
<< TxGain2TxPower(desc, tx_gains[chan]) << " dBm "
|
||||
<< "(asked for " << db << " dB, ~" << tx_power << " dBm)";
|
||||
|
||||
return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
|
||||
}
|
||||
double uhd_device::getPowerAttenuation(size_t chan) {
|
||||
dev_band_desc desc;
|
||||
if (chan >= tx_gains.size()) {
|
||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return tx_gains[chan];
|
||||
get_dev_band_desc(desc);
|
||||
return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
|
||||
}
|
||||
|
||||
int uhd_device::getNominalTxPower(size_t chan)
|
||||
{
|
||||
dev_band_desc desc;
|
||||
get_dev_band_desc(desc);
|
||||
|
||||
return desc.nom_out_tx_power;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -418,6 +549,16 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
||||
{
|
||||
const char *refstr;
|
||||
|
||||
/* Register msg handler. Different APIs depending on UHD version */
|
||||
#ifdef USE_UHD_3_11
|
||||
uhd::log::add_logger("OsmoTRX", &uhd_log_handler);
|
||||
uhd::log::set_log_level(uhd::log::debug);
|
||||
uhd::log::set_console_level(uhd::log::off);
|
||||
uhd::log::set_logger_level("OsmoTRX", uhd::log::debug);
|
||||
#else
|
||||
uhd::msg::register_handler(&uhd_msg_handler);
|
||||
#endif
|
||||
|
||||
// Find UHD devices
|
||||
uhd::device_addr_t addr(args);
|
||||
uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
|
||||
@@ -526,7 +667,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
||||
init_gains();
|
||||
|
||||
// Print configuration
|
||||
LOGC(DDEV, INFO) << "\n" << usrp_dev->get_pp_string();
|
||||
LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
|
||||
|
||||
if (iface == MULTI_ARFCN)
|
||||
return MULTI_ARFCN;
|
||||
@@ -604,10 +745,6 @@ bool uhd_device::start()
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef USE_UHD_3_11
|
||||
// Register msg handler
|
||||
uhd::msg::register_handler(&uhd_msg_handler);
|
||||
#endif
|
||||
// Start asynchronous event (underrun check) loop
|
||||
async_event_thrd = new Thread();
|
||||
async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
|
||||
@@ -638,6 +775,10 @@ bool uhd_device::stop()
|
||||
async_event_thrd->join();
|
||||
delete async_event_thrd;
|
||||
|
||||
/* reset internal buffer timestamps */
|
||||
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||
rx_buffers[i]->reset();
|
||||
|
||||
started = false;
|
||||
return true;
|
||||
}
|
||||
@@ -685,7 +826,7 @@ int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
|
||||
}
|
||||
|
||||
int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
|
||||
TIMESTAMP timestamp, bool *underrun)
|
||||
{
|
||||
ssize_t rc;
|
||||
uhd::time_spec_t ts;
|
||||
@@ -742,7 +883,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
for (size_t i = 0; i < rx_buffers.size(); i++) {
|
||||
rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
|
||||
num_smpls,
|
||||
metadata.time_spec.to_ticks(rx_rate));
|
||||
ts.to_ticks(rx_rate));
|
||||
|
||||
// Continue on local overrun, exit on other errors
|
||||
if ((rc < 0)) {
|
||||
@@ -768,7 +909,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
}
|
||||
|
||||
int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
unsigned long long timestamp,bool isControl)
|
||||
unsigned long long timestamp)
|
||||
{
|
||||
uhd::tx_metadata_t metadata;
|
||||
metadata.has_time_spec = true;
|
||||
@@ -778,12 +919,6 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
|
||||
|
||||
*underrun = false;
|
||||
|
||||
// No control packets
|
||||
if (isControl) {
|
||||
LOGC(DDEV, ERROR) << "Control packets not supported";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bufs.size() != chans) {
|
||||
LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
|
||||
return -1;
|
||||
@@ -883,15 +1018,18 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
|
||||
std::vector<double> freqs;
|
||||
uhd::tune_result_t tres;
|
||||
uhd::tune_request_t treq = select_freq(freq, chan, tx);
|
||||
std::string str_dir;
|
||||
|
||||
if (tx) {
|
||||
tres = usrp_dev->set_tx_freq(treq, chan);
|
||||
tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
|
||||
str_dir = "Tx";
|
||||
} else {
|
||||
tres = usrp_dev->set_rx_freq(treq, chan);
|
||||
rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
|
||||
str_dir = "Rx";
|
||||
}
|
||||
LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl;
|
||||
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
|
||||
|
||||
if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
|
||||
return true;
|
||||
@@ -911,7 +1049,7 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
|
||||
rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
|
||||
|
||||
}
|
||||
LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl;
|
||||
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -919,13 +1057,33 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
|
||||
|
||||
bool uhd_device::setTxFreq(double wFreq, size_t chan)
|
||||
{
|
||||
uint16_t req_arfcn;
|
||||
enum gsm_band req_band;
|
||||
|
||||
if (chan >= tx_freqs.size()) {
|
||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
||||
return false;
|
||||
}
|
||||
ScopedLock lock(tune_lock);
|
||||
|
||||
return set_freq(wFreq, chan, true);
|
||||
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
|
||||
if (req_arfcn == 0xffff) {
|
||||
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
|
||||
return false;
|
||||
}
|
||||
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
|
||||
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
|
||||
<< " Hz (ARFCN " << req_arfcn << " )";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!set_band(req_band))
|
||||
return false;
|
||||
|
||||
if (!set_freq(wFreq, chan, true))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uhd_device::setRxFreq(double wFreq, size_t chan)
|
||||
@@ -967,7 +1125,14 @@ bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
|
||||
return false;
|
||||
}
|
||||
|
||||
avail = usrp_dev->get_rx_antennas(chan);
|
||||
/* UHD may throw a LookupError/IndexError here (see OS#4636) */
|
||||
try {
|
||||
avail = usrp_dev->get_rx_antennas(chan);
|
||||
} catch (const uhd::index_error &e) {
|
||||
LOGC(DDEV, ALERT) << "UHD Error: " << e.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
|
||||
LOGC(DDEV, ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan;
|
||||
LOGC(DDEV, INFO) << "Available Rx antennas: ";
|
||||
@@ -1003,7 +1168,14 @@ bool uhd_device::setTxAntenna(const std::string &ant, size_t chan)
|
||||
return false;
|
||||
}
|
||||
|
||||
avail = usrp_dev->get_tx_antennas(chan);
|
||||
/* UHD may throw a LookupError/IndexError here (see OS#4636) */
|
||||
try {
|
||||
avail = usrp_dev->get_tx_antennas(chan);
|
||||
} catch (const uhd::index_error &e) {
|
||||
LOGC(DDEV, ALERT) << "UHD Error: " << e.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
|
||||
LOGC(DDEV, ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan;
|
||||
LOGC(DDEV, INFO) << "Available Tx antennas: ";
|
||||
@@ -1169,6 +1341,7 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata)
|
||||
return ost.str();
|
||||
}
|
||||
|
||||
#ifndef IPCMAGIC
|
||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||
InterfaceType iface, size_t chans, double lo_offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
@@ -1176,3 +1349,4 @@ RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||
{
|
||||
return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
#include <uhd/property_tree.hpp>
|
||||
#include <uhd/usrp/multi_usrp.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
}
|
||||
|
||||
|
||||
enum uhd_dev_type {
|
||||
USRP1,
|
||||
@@ -52,6 +56,21 @@ enum uhd_dev_type {
|
||||
LIMESDR,
|
||||
};
|
||||
|
||||
struct dev_band_desc {
|
||||
/* Maximum UHD Tx Gain which can be set/used without distorting the
|
||||
output signal, and the resulting real output power measured when that
|
||||
gain is used. Correct measured values only provided for B210 so far. */
|
||||
double nom_uhd_tx_gain; /* dB */
|
||||
double nom_out_tx_power; /* dBm */
|
||||
/* Factor used to infer base real RSSI offset on the Rx path based on current
|
||||
configured RxGain. The resulting rssiOffset is added to the per burst
|
||||
calculated energy in upper layers. These values were empirically
|
||||
found and may change based on multiple factors, see OS#4468.
|
||||
rssiOffset = rxGain + rxgain2rssioffset_rel;
|
||||
*/
|
||||
double rxgain2rssioffset_rel; /* dB */
|
||||
};
|
||||
|
||||
/*
|
||||
uhd_device - UHD implementation of the Device interface. Timestamped samples
|
||||
are sent to and received from the device. An intermediate buffer
|
||||
@@ -62,7 +81,7 @@ enum uhd_dev_type {
|
||||
class uhd_device : public RadioDevice {
|
||||
public:
|
||||
uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
|
||||
size_t chans, double offset,
|
||||
size_t chan_num, double offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
const std::vector<std::string>& rx_paths);
|
||||
~uhd_device();
|
||||
@@ -74,10 +93,10 @@ public:
|
||||
enum TxWindowType getWindowType() { return tx_window; }
|
||||
|
||||
int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
|
||||
TIMESTAMP timestamp, bool *underrun);
|
||||
|
||||
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
TIMESTAMP timestamp, bool isControl);
|
||||
TIMESTAMP timestamp);
|
||||
|
||||
bool updateAlignment(TIMESTAMP timestamp);
|
||||
|
||||
@@ -94,11 +113,12 @@ public:
|
||||
double getRxGain(size_t chan);
|
||||
double maxRxGain(void) { return rx_gain_max; }
|
||||
double minRxGain(void) { return rx_gain_min; }
|
||||
double rssiOffset(size_t chan);
|
||||
|
||||
double setTxGain(double db, size_t chan);
|
||||
double getTxGain(size_t chan = 0);
|
||||
double maxTxGain(void) { return tx_gain_max; }
|
||||
double minTxGain(void) { return tx_gain_min; }
|
||||
double setPowerAttenuation(int atten, size_t chan);
|
||||
double getPowerAttenuation(size_t chan = 0);
|
||||
|
||||
int getNominalTxPower(size_t chan = 0);
|
||||
|
||||
double getTxFreq(size_t chan);
|
||||
double getRxFreq(size_t chan);
|
||||
@@ -127,7 +147,7 @@ public:
|
||||
ERROR_UNHANDLED = -4,
|
||||
};
|
||||
|
||||
private:
|
||||
protected:
|
||||
uhd::usrp::multi_usrp::sptr usrp_dev;
|
||||
uhd::tx_streamer::sptr tx_stream;
|
||||
uhd::rx_streamer::sptr rx_stream;
|
||||
@@ -136,11 +156,12 @@ private:
|
||||
|
||||
double tx_rate, rx_rate;
|
||||
|
||||
double tx_gain_min, tx_gain_max;
|
||||
double rx_gain_min, rx_gain_max;
|
||||
|
||||
std::vector<double> tx_gains, rx_gains;
|
||||
std::vector<double> tx_freqs, rx_freqs;
|
||||
enum gsm_band band;
|
||||
struct dev_band_desc band_desc;
|
||||
size_t tx_spp, rx_spp;
|
||||
|
||||
bool started;
|
||||
@@ -169,6 +190,9 @@ private:
|
||||
|
||||
uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
|
||||
bool set_freq(double freq, size_t chan, bool tx);
|
||||
void get_dev_band_desc(dev_band_desc& desc);
|
||||
bool set_band(enum gsm_band req_band);
|
||||
void assign_band_desc(enum gsm_band req_band);
|
||||
|
||||
Thread *async_event_thrd;
|
||||
Mutex tune_lock;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
|
||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(USRP_CFLAGS)
|
||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(USRP_CFLAGS)
|
||||
|
||||
rev2dir = $(datadir)/usrp/rev2
|
||||
rev4dir = $(datadir)/usrp/rev4
|
||||
|
||||
@@ -61,10 +61,10 @@ const dboardConfigType dboardConfig = TXA_RXB;
|
||||
const double USRPDevice::masterClockRate = 52.0e6;
|
||||
|
||||
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
|
||||
size_t chans, double lo_offset,
|
||||
size_t chan_num, double lo_offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
const std::vector<std::string>& rx_paths):
|
||||
RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths)
|
||||
RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths)
|
||||
{
|
||||
LOGC(DDEV, INFO) << "creating USRP device...";
|
||||
|
||||
@@ -205,8 +205,8 @@ bool USRPDevice::start()
|
||||
writeLock.unlock();
|
||||
|
||||
// Set gains to midpoint
|
||||
setTxGain((minTxGain() + maxTxGain()) / 2);
|
||||
setRxGain((minRxGain() + maxRxGain()) / 2);
|
||||
setTxGain((m_dbTx->gain_min() + m_dbTx->gain_max()) / 2);
|
||||
setRxGain((m_dbTx->gain_min() + m_dbTx->gain_max()) / 2);
|
||||
|
||||
data = new short[currDataSize];
|
||||
dataStart = 0;
|
||||
@@ -243,16 +243,6 @@ bool USRPDevice::stop()
|
||||
#endif
|
||||
}
|
||||
|
||||
double USRPDevice::maxTxGain()
|
||||
{
|
||||
return m_dbTx->gain_max();
|
||||
}
|
||||
|
||||
double USRPDevice::minTxGain()
|
||||
{
|
||||
return m_dbTx->gain_min();
|
||||
}
|
||||
|
||||
double USRPDevice::maxRxGain()
|
||||
{
|
||||
return m_dbRx->gain_max();
|
||||
@@ -271,10 +261,10 @@ double USRPDevice::setTxGain(double dB, size_t chan)
|
||||
}
|
||||
|
||||
writeLock.lock();
|
||||
if (dB > maxTxGain())
|
||||
dB = maxTxGain();
|
||||
if (dB < minTxGain())
|
||||
dB = minTxGain();
|
||||
if (dB > m_dbTx->gain_max())
|
||||
dB = m_dbTx->gain_max();
|
||||
if (dB < m_dbTx->gain_min())
|
||||
dB = m_dbTx->gain_min();
|
||||
|
||||
LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB.";
|
||||
|
||||
@@ -314,6 +304,23 @@ double USRPDevice::setRxGain(double dB, size_t chan)
|
||||
return rxGain;
|
||||
}
|
||||
|
||||
double USRPDevice::setPowerAttenuation(int atten, size_t chan) {
|
||||
double rfGain;
|
||||
rfGain = setTxGain(m_dbTx->gain_max() - atten, chan);
|
||||
return m_dbTx->gain_max() - rfGain;
|
||||
}
|
||||
double USRPDevice::getPowerAttenuation(size_t chan) {
|
||||
return m_dbTx->gain_max() - getTxGain(chan);
|
||||
}
|
||||
|
||||
int USRPDevice::getNominalTxPower(size_t chan)
|
||||
{
|
||||
/* TODO: return value based on some experimentally generated table depending on
|
||||
* band/arfcn, which is known here thanks to TXTUNE
|
||||
*/
|
||||
return 23;
|
||||
}
|
||||
|
||||
bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
|
||||
{
|
||||
if (chan >= rx_paths.size()) {
|
||||
@@ -365,7 +372,7 @@ GSM::Time USRPDevice::minLatency() {
|
||||
|
||||
// NOTE: Assumes sequential reads
|
||||
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
|
||||
TIMESTAMP timestamp, bool *underrun)
|
||||
{
|
||||
#ifndef SWLOOPBACK
|
||||
if (!m_uRx)
|
||||
@@ -433,8 +440,10 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
*underrun = true;
|
||||
LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
|
||||
}
|
||||
if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
|
||||
|
||||
#if 0
|
||||
/* FIXME: Do something with this ? */
|
||||
unsigned RSSI = (word0 >> 21) & 0x3f;
|
||||
#endif
|
||||
if (!isAligned) continue;
|
||||
|
||||
unsigned cursorStart = pktTimestamp - timeStart + dataStart;
|
||||
@@ -513,9 +522,8 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
#endif
|
||||
}
|
||||
|
||||
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
|
||||
bool *underrun, unsigned long long timestamp,
|
||||
bool isControl)
|
||||
int USRPDevice::writeSamplesControl(std::vector<short *> &bufs, int len,
|
||||
bool *underrun, unsigned long long timestamp, bool isControl)
|
||||
{
|
||||
writeLock.lock();
|
||||
|
||||
@@ -569,6 +577,12 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
|
||||
#endif
|
||||
}
|
||||
|
||||
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
|
||||
bool *underrun, unsigned long long timestamp)
|
||||
{
|
||||
return writeSamplesControl(bufs, len, underrun, timestamp, false);
|
||||
}
|
||||
|
||||
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
||||
{
|
||||
#ifndef SWLOOPBACK
|
||||
@@ -578,7 +592,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
||||
bool tmpUnderrun;
|
||||
|
||||
std::vector<short *> buf(1, data);
|
||||
if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
|
||||
if (writeSamplesControl(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
|
||||
pingTimestamp = timestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in
|
||||
* the main directory for licensing information for this specific distribuion.
|
||||
* the main directory for licensing information for this specific distribution.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
@@ -82,6 +82,15 @@ private:
|
||||
double rxGain;
|
||||
double txGain;
|
||||
|
||||
int writeSamplesControl(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
|
||||
|
||||
/** sets the transmit chan gain, returns the gain setting **/
|
||||
double setTxGain(double dB, size_t chan = 0);
|
||||
|
||||
/** get transmit gain */
|
||||
double getTxGain(size_t chan = 0) { return txGain; }
|
||||
|
||||
#ifdef SWLOOPBACK
|
||||
short loopbackBuffer[1000000];
|
||||
int loopbackBufferSize;
|
||||
@@ -95,7 +104,7 @@ private:
|
||||
public:
|
||||
|
||||
/** Object constructor */
|
||||
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
|
||||
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||
const std::vector<std::string>& tx_paths,
|
||||
const std::vector<std::string>& rx_paths);
|
||||
|
||||
@@ -117,23 +126,20 @@ private:
|
||||
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
|
||||
@param timestamp The timestamp of the first samples to be read
|
||||
@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
|
||||
@param RSSI The received signal strength of the read result
|
||||
@return The number of samples actually read
|
||||
*/
|
||||
int readSamples(std::vector<short *> &buf, int len, bool *overrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
|
||||
unsigned *RSSI = NULL);
|
||||
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL);
|
||||
/**
|
||||
Write samples to the USRP.
|
||||
@param buf Contains the data to be written.
|
||||
@param len number of samples to write.
|
||||
@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
|
||||
@param timestamp The timestamp of the first sample of the data buffer.
|
||||
@param isControl Set if data is a control packet, e.g. a ping command
|
||||
@return The number of samples actually written
|
||||
*/
|
||||
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
|
||||
TIMESTAMP timestamp = 0xffffffff);
|
||||
|
||||
/** Update the alignment between the read and write timestamps */
|
||||
bool updateAlignment(TIMESTAMP timestamp);
|
||||
@@ -168,17 +174,12 @@ private:
|
||||
/** return minimum Rx Gain **/
|
||||
double minRxGain(void);
|
||||
|
||||
/** sets the transmit chan gain, returns the gain setting **/
|
||||
double setTxGain(double dB, size_t chan = 0);
|
||||
double rssiOffset(size_t chan) { return 0.0f; } /* FIXME: not implemented */
|
||||
|
||||
/** get transmit gain */
|
||||
double getTxGain(size_t chan = 0) { return txGain; }
|
||||
double setPowerAttenuation(int atten, size_t chan);
|
||||
double getPowerAttenuation(size_t chan=0);
|
||||
|
||||
/** return maximum Tx Gain **/
|
||||
double maxTxGain(void);
|
||||
|
||||
/** return minimum Rx Gain **/
|
||||
double minTxGain(void);
|
||||
int getNominalTxPower(size_t chan = 0);
|
||||
|
||||
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||
bool setRxAntenna(const std::string &ant, size_t chan = 0);
|
||||
|
||||
@@ -55,6 +55,7 @@ extern "C" {
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/vty/stats.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/cpu_sched_vty.h>
|
||||
|
||||
#include "convolve.h"
|
||||
#include "convert.h"
|
||||
@@ -79,7 +80,6 @@ static struct ctrl_handle *g_ctrlh;
|
||||
|
||||
static RadioDevice *usrp;
|
||||
static RadioInterface *radio;
|
||||
static Transceiver *transceiver;
|
||||
|
||||
/* Create radio interface
|
||||
* The interface consists of sample rate changes, frequency shifts,
|
||||
@@ -144,12 +144,8 @@ int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
|
||||
{
|
||||
VectorFIFO *fifo;
|
||||
|
||||
transceiver = new Transceiver(trx->cfg.base_port, trx->cfg.bind_addr,
|
||||
trx->cfg.remote_addr, trx->cfg.tx_sps,
|
||||
trx->cfg.rx_sps, trx->cfg.num_chans, GSM::Time(3,0),
|
||||
radio, trx->cfg.rssi_offset, trx->cfg.stack_size);
|
||||
if (!transceiver->init(trx->cfg.filler, trx->cfg.rtsc,
|
||||
trx->cfg.rach_delay, trx->cfg.egprs, trx->cfg.ext_rach)) {
|
||||
transceiver = new Transceiver(&trx->cfg, GSM::Time(3,0), radio);
|
||||
if (!transceiver->init()) {
|
||||
LOG(ALERT) << "Failed to initialize transceiver";
|
||||
return -1;
|
||||
}
|
||||
@@ -181,6 +177,17 @@ static void sig_handler(int signo)
|
||||
gshutdown = true;
|
||||
break;
|
||||
case SIGABRT:
|
||||
/* in case of abort, we want to obtain a talloc report and
|
||||
* then run default SIGABRT handler, who will generate coredump
|
||||
* and abort the process. abort() should do this for us after we
|
||||
* return, but program wouldn't exit if an external SIGABRT is
|
||||
* received.
|
||||
*/
|
||||
talloc_report(tall_trx_ctx, stderr);
|
||||
talloc_report_full(tall_trx_ctx, stderr);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
raise(SIGABRT);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
talloc_report(tall_trx_ctx, stderr);
|
||||
talloc_report_full(tall_trx_ctx, stderr);
|
||||
@@ -237,7 +244,7 @@ static void setup_signal_handlers()
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
osmo_fd_setup(&signal_ofd, sfd, BSC_FD_READ, signalfd_callback, NULL, 0);
|
||||
osmo_fd_setup(&signal_ofd, sfd, OSMO_FD_READ, signalfd_callback, NULL, 0);
|
||||
if (osmo_fd_register(&signal_ofd) < 0) {
|
||||
fprintf(stderr, "osmo_fd_register() failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -246,10 +253,13 @@ static void setup_signal_handlers()
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
fprintf(stdout, "Options:\n"
|
||||
" -h, --help This text\n"
|
||||
" -C, --config Filename The config file to use\n"
|
||||
" -V, --version Print the version of OsmoTRX\n"
|
||||
printf( "Some useful options:\n"
|
||||
" -h, --help This text\n"
|
||||
" -C, --config Filename The config file to use\n"
|
||||
" -V, --version Print the version of OsmoTRX\n"
|
||||
"\nVTY reference generation:\n"
|
||||
" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
|
||||
" --vty-ref-xml Generate the VTY reference XML output and exit.\n"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -260,16 +270,44 @@ static void print_deprecated(char opt)
|
||||
<< " All cmd line options are already being overridden by VTY options if set.";
|
||||
}
|
||||
|
||||
static void handle_long_options(const char *prog_name, const int long_option)
|
||||
{
|
||||
static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
|
||||
|
||||
switch (long_option) {
|
||||
case 1:
|
||||
vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
|
||||
if (vty_ref_mode < 0) {
|
||||
fprintf(stderr, "%s: Unknown VTY reference generation "
|
||||
"mode '%s'\n", prog_name, optarg);
|
||||
exit(2);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
|
||||
get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
|
||||
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
|
||||
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
|
||||
exit(0);
|
||||
default:
|
||||
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv, struct trx_ctx* trx)
|
||||
{
|
||||
int option;
|
||||
unsigned int i;
|
||||
std::vector<std::string> rx_paths, tx_paths;
|
||||
bool rx_paths_set = false, tx_paths_set = false;
|
||||
static int long_option = 0;
|
||||
static struct option long_options[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"config", 1, 0, 'C'},
|
||||
{"version", 0, 0, 'V'},
|
||||
{"vty-ref-mode", 1, &long_option, 1},
|
||||
{"vty-ref-xml", 0, &long_option, 2},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
@@ -280,6 +318,9 @@ static void handle_options(int argc, char **argv, struct trx_ctx* trx)
|
||||
print_help();
|
||||
exit(0);
|
||||
break;
|
||||
case 0:
|
||||
handle_long_options(argv[0], long_option);
|
||||
break;
|
||||
case 'a':
|
||||
print_deprecated(option);
|
||||
osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg);
|
||||
@@ -334,20 +375,19 @@ static void handle_options(int argc, char **argv, struct trx_ctx* trx)
|
||||
break;
|
||||
case 'r':
|
||||
print_deprecated(option);
|
||||
trx->cfg.rtsc_set = true;
|
||||
trx->cfg.rtsc = atoi(optarg);
|
||||
if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
|
||||
trx->cfg.filler = FILLER_NORM_RAND;
|
||||
break;
|
||||
case 'A':
|
||||
print_deprecated(option);
|
||||
trx->cfg.rach_delay_set = true;
|
||||
trx->cfg.rach_delay = atoi(optarg);
|
||||
trx->cfg.filler = FILLER_ACCESS_RAND;
|
||||
break;
|
||||
case 'R':
|
||||
print_deprecated(option);
|
||||
trx->cfg.rssi_offset = atof(optarg);
|
||||
trx->cfg.force_rssi_offset = true;
|
||||
break;
|
||||
case 'S':
|
||||
print_deprecated(option);
|
||||
@@ -383,6 +423,11 @@ static void handle_options(int argc, char **argv, struct trx_ctx* trx)
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > optind) {
|
||||
LOG(ERROR) << "Unsupported positional arguments on command line";
|
||||
goto bad_config;
|
||||
}
|
||||
|
||||
/* Cmd line option specific validation & setup */
|
||||
|
||||
if (trx->cfg.num_chans > TRX_CHAN_MAX) {
|
||||
@@ -433,7 +478,9 @@ static int set_sched_rr(unsigned int prio)
|
||||
int rc;
|
||||
memset(¶m, 0, sizeof(param));
|
||||
param.sched_priority = prio;
|
||||
LOG(INFO) << "Setting SCHED_RR priority " << param.sched_priority;
|
||||
LOG(INFO) << "Setting SCHED_RR priority " << param.sched_priority
|
||||
<< ". This setting is DEPRECATED, please use 'policy rr " << param.sched_priority
|
||||
<< "' under the 'sched' VTY node instead.";
|
||||
rc = sched_setscheduler(getpid(), SCHED_RR, ¶m);
|
||||
if (rc != 0) {
|
||||
LOG(ERROR) << "Config: Setting SCHED_RR failed";
|
||||
@@ -442,6 +489,38 @@ static int set_sched_rr(unsigned int prio)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_simd_info(void)
|
||||
{
|
||||
#ifdef HAVE_SSE3
|
||||
LOGP(DMAIN, LOGL_INFO, "SSE3 support compiled in");
|
||||
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
|
||||
if (__builtin_cpu_supports("sse3"))
|
||||
LOGPC(DMAIN, LOGL_INFO, " and supported by CPU\n");
|
||||
else
|
||||
LOGPC(DMAIN, LOGL_INFO, ", but not supported by CPU\n");
|
||||
#else
|
||||
LOGPC(DMAIN, LOGL_INFO, ", but runtime SIMD detection disabled\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE4_1
|
||||
LOGP(DMAIN, LOGL_INFO, "SSE4.1 support compiled in");
|
||||
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
|
||||
if (__builtin_cpu_supports("sse4.1"))
|
||||
LOGPC(DMAIN, LOGL_INFO, " and supported by CPU\n");
|
||||
else
|
||||
LOGPC(DMAIN, LOGL_INFO, ", but not supported by CPU\n");
|
||||
#else
|
||||
LOGPC(DMAIN, LOGL_INFO, ", but runtime SIMD detection disabled\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_ATOMIC_OPS
|
||||
#pragma message ("Built without atomic operation support. Using Mutex, it may affect performance!")
|
||||
LOG(NOTICE) << "Built without atomic operation support. Using Mutex, it may affect performance!";
|
||||
#endif
|
||||
}
|
||||
|
||||
static void print_config(struct trx_ctx *trx)
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -459,10 +538,14 @@ static void print_config(struct trx_ctx *trx)
|
||||
ost << " EDGE support............ " << trx->cfg.egprs << std::endl;
|
||||
ost << " Extended RACH support... " << trx->cfg.ext_rach << std::endl;
|
||||
ost << " Reference............... " << trx->cfg.clock_ref << std::endl;
|
||||
ost << " C0 Filler Table......... " << trx->cfg.filler << std::endl;
|
||||
ost << " Filler Burst Type....... " << get_value_string(filler_names, trx->cfg.filler) << std::endl;
|
||||
ost << " Filler Burst TSC........ " << trx->cfg.rtsc << std::endl;
|
||||
ost << " Filler Burst RACH Delay. " << trx->cfg.rach_delay << std::endl;
|
||||
ost << " Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl;
|
||||
ost << " Tuning offset........... " << trx->cfg.offset << std::endl;
|
||||
ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << std::endl;
|
||||
ost << " LO freq. offset......... " << trx->cfg.offset << std::endl;
|
||||
if (trx->cfg.freq_offset_khz != 0)
|
||||
ost << " Tune freq. offset....... " << trx->cfg.freq_offset_khz << std::endl;
|
||||
ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << (trx->cfg.force_rssi_offset ? "" : " (relative)") << std::endl;
|
||||
ost << " Swap channels........... " << trx->cfg.swap_channels << std::endl;
|
||||
ost << " Tx Antennas.............";
|
||||
for (i = 0; i < trx->cfg.num_chans; i++) {
|
||||
@@ -547,49 +630,18 @@ int main(int argc, char *argv[])
|
||||
|
||||
g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx);
|
||||
|
||||
#ifdef HAVE_SSE3
|
||||
printf("Info: SSE3 support compiled in");
|
||||
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
|
||||
if (__builtin_cpu_supports("sse3"))
|
||||
printf(" and supported by CPU\n");
|
||||
else
|
||||
printf(", but not supported by CPU\n");
|
||||
#else
|
||||
printf(", but runtime SIMD detection disabled\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE4_1
|
||||
printf("Info: SSE4.1 support compiled in");
|
||||
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
|
||||
if (__builtin_cpu_supports("sse4.1"))
|
||||
printf(" and supported by CPU\n");
|
||||
else
|
||||
printf(", but not supported by CPU\n");
|
||||
#else
|
||||
printf(", but runtime SIMD detection disabled\n");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_ATOMIC_OPS
|
||||
#pragma message ("Built without atomic operation support. Using Mutex, it may affect performance!")
|
||||
printf("Built without atomic operation support. Using Mutex, it may affect performance!\n");
|
||||
#endif
|
||||
|
||||
if (!log_mutex_init()) {
|
||||
fprintf(stderr, "Failed to initialize log mutex!\n");
|
||||
exit(2);
|
||||
}
|
||||
convolve_init();
|
||||
convert_init();
|
||||
|
||||
osmo_init_logging2(tall_trx_ctx, &log_info);
|
||||
log_enable_multithread();
|
||||
osmo_stats_init(tall_trx_ctx);
|
||||
vty_init(&g_vty_info);
|
||||
logging_vty_add_cmds();
|
||||
ctrl_vty_init(tall_trx_ctx);
|
||||
osmo_cpu_sched_vty_init(tall_trx_ctx);
|
||||
trx_vty_init(g_trx_ctx);
|
||||
|
||||
logging_vty_add_cmds();
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_stats_vty_add_cmds();
|
||||
|
||||
@@ -624,6 +676,7 @@ int main(int argc, char *argv[])
|
||||
" but expect your config to break in the future.";
|
||||
}
|
||||
|
||||
print_simd_info();
|
||||
print_config(g_trx_ctx);
|
||||
|
||||
if (trx_validate_config(g_trx_ctx) < 0) {
|
||||
|
||||
@@ -62,17 +62,17 @@ static void trxd_fill_burst_normalized255(uint8_t* soft_bits, const struct trx_u
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < bi->nbits; i++)
|
||||
soft_bits[i] = (char) round(bi->rx_burst[i] * 255.0);
|
||||
soft_bits[i] = (uint8_t) round(bi->rx_burst[i] * 255.0);
|
||||
}
|
||||
|
||||
bool trxd_send_burst_ind_v0(size_t chan, int fd, const struct trx_ul_burst_ind *bi) {
|
||||
int rc;
|
||||
|
||||
/* v0 doesn't support idle frames, they are simply dropped, not sent */
|
||||
if(bi->idle)
|
||||
if (bi->idle)
|
||||
return true;
|
||||
|
||||
/* +2: Historically (OpenBTS times), two extra non-used bytes are sent appeneded to each burst */
|
||||
/* +2: Historically (OpenBTS times), two extra non-used bytes are sent appended to each burst */
|
||||
char buf[sizeof(struct trxd_hdr_v0) + bi->nbits + 2];
|
||||
struct trxd_hdr_v0* pkt = (struct trxd_hdr_v0*)buf;
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
#include <Threads.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/vty/cpu_sched_vty.h>
|
||||
|
||||
#include "convert.h"
|
||||
}
|
||||
|
||||
@@ -103,7 +106,7 @@ double RadioInterface::fullScaleOutputValue(void) {
|
||||
|
||||
int RadioInterface::setPowerAttenuation(int atten, size_t chan)
|
||||
{
|
||||
double rfGain, digAtten;
|
||||
double rfAtten, digAtten;
|
||||
|
||||
if (chan >= mChans) {
|
||||
LOG(ALERT) << "Invalid channel requested";
|
||||
@@ -113,8 +116,8 @@ int RadioInterface::setPowerAttenuation(int atten, size_t chan)
|
||||
if (atten < 0.0)
|
||||
atten = 0.0;
|
||||
|
||||
rfGain = setTxGain(mDevice->maxTxGain() - (double) atten, chan);
|
||||
digAtten = (double) atten - mDevice->maxTxGain() + rfGain;
|
||||
rfAtten = mDevice->setPowerAttenuation((double) atten, chan);
|
||||
digAtten = (double) atten - rfAtten;
|
||||
|
||||
if (digAtten < 1.0)
|
||||
powerScaling[chan] = 1.0;
|
||||
@@ -124,6 +127,16 @@ int RadioInterface::setPowerAttenuation(int atten, size_t chan)
|
||||
return atten;
|
||||
}
|
||||
|
||||
int RadioInterface::getNominalTxPower(size_t chan)
|
||||
{
|
||||
if (chan >= mChans) {
|
||||
LOG(ALERT) << "Invalid channel requested";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return mDevice->getNominalTxPower(chan);
|
||||
}
|
||||
|
||||
int RadioInterface::radioifyVector(signalVector &wVector,
|
||||
size_t chan, bool zero)
|
||||
{
|
||||
@@ -161,6 +174,7 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
|
||||
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
|
||||
{
|
||||
set_selfthread_name("AlignRadio");
|
||||
OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
|
||||
while (1) {
|
||||
sleep(60);
|
||||
radioInterface->alignRadio();
|
||||
@@ -308,9 +322,9 @@ double RadioInterface::setRxGain(double dB, size_t chan)
|
||||
return mDevice->setRxGain(dB, chan);
|
||||
}
|
||||
|
||||
double RadioInterface::setTxGain(double dB, size_t chan)
|
||||
double RadioInterface::rssiOffset(size_t chan)
|
||||
{
|
||||
return mDevice->setTxGain(dB, chan);
|
||||
return mDevice->rssiOffset(chan);
|
||||
}
|
||||
|
||||
/* Receive a timestamped chunk from the device */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
bool start();
|
||||
bool stop();
|
||||
|
||||
/** intialization */
|
||||
/** initialization */
|
||||
virtual bool init(int type);
|
||||
virtual void close();
|
||||
|
||||
@@ -109,6 +109,9 @@ public:
|
||||
/** set receive gain */
|
||||
virtual double setRxGain(double dB, size_t chan = 0);
|
||||
|
||||
/** return base RSSI offset to apply for received samples **/
|
||||
virtual double rssiOffset(size_t chan = 0);
|
||||
|
||||
/** drive transmission of GSM bursts */
|
||||
void driveTransmitRadio(std::vector<signalVector *> &bursts,
|
||||
std::vector<bool> &zeros);
|
||||
@@ -116,7 +119,9 @@ public:
|
||||
/** drive reception of GSM bursts. -1: Error. 0: Radio off. 1: Received something. */
|
||||
int driveReceiveRadio();
|
||||
|
||||
int setPowerAttenuation(int atten, size_t chan = 0);
|
||||
/** set transmit power attenuation */
|
||||
virtual int setPowerAttenuation(int atten, size_t chan = 0);
|
||||
int getNominalTxPower(size_t chan = 0);
|
||||
|
||||
/** returns the full-scale transmit amplitude **/
|
||||
double fullScaleInputValue();
|
||||
@@ -134,9 +139,6 @@ protected:
|
||||
/** drive synchronization of Tx/Rx of USRP */
|
||||
void alignRadio();
|
||||
|
||||
/** set transmit gain */
|
||||
virtual double setTxGain(double dB, size_t chan = 0);
|
||||
|
||||
friend void *AlignRadioServiceLoopAdapter(RadioInterface*);
|
||||
};
|
||||
|
||||
@@ -156,16 +158,24 @@ public:
|
||||
void close();
|
||||
};
|
||||
|
||||
struct freq_cfg_state {
|
||||
bool set;
|
||||
double freq_hz;
|
||||
};
|
||||
|
||||
class RadioInterfaceMulti : public RadioInterface {
|
||||
private:
|
||||
bool pushBuffer();
|
||||
int pullBuffer();
|
||||
virtual double setTxGain(double dB, size_t chan);
|
||||
bool verify_arfcn_consistency(double freq, size_t chan, bool tx);
|
||||
virtual int setPowerAttenuation(int atten, size_t chan = 0);
|
||||
|
||||
signalVector *outerSendBuffer;
|
||||
signalVector *outerRecvBuffer;
|
||||
std::vector<signalVector *> history;
|
||||
std::vector<bool> active;
|
||||
std::vector<struct freq_cfg_state> rx_freq_state;
|
||||
std::vector<struct freq_cfg_state> tx_freq_state;
|
||||
|
||||
Resampler *dnsampler;
|
||||
Resampler *upsampler;
|
||||
@@ -183,4 +193,5 @@ public:
|
||||
bool tuneTx(double freq, size_t chan);
|
||||
bool tuneRx(double freq, size_t chan);
|
||||
virtual double setRxGain(double dB, size_t chan);
|
||||
virtual double rssiOffset(size_t chan = 0);
|
||||
};
|
||||
|
||||
@@ -38,6 +38,7 @@ extern "C" {
|
||||
/* Universal resampling parameters */
|
||||
#define NUMCHUNKS 24
|
||||
|
||||
/* number of narrow-band virtual ARFCNs in this wide-band multi-ARFCN device */
|
||||
#define MCHANS 4
|
||||
|
||||
RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
|
||||
@@ -69,14 +70,24 @@ void RadioInterfaceMulti::close()
|
||||
channelizer = NULL;
|
||||
synthesis = NULL;
|
||||
|
||||
|
||||
for (std::vector<signalVector*>::iterator it = history.begin(); it != history.end(); ++it)
|
||||
delete *it;
|
||||
|
||||
mReceiveFIFO.resize(0);
|
||||
powerScaling.resize(0);
|
||||
history.resize(0);
|
||||
active.resize(0);
|
||||
rx_freq_state.resize(0);
|
||||
tx_freq_state.resize(0);
|
||||
|
||||
RadioInterface::close();
|
||||
}
|
||||
|
||||
/*! we re-map the physical channels from the filter bank to logical per-TRX channels
|
||||
* \param[in] pchan physical channel number within the channelizer
|
||||
* \param[in] chans total number of narrow-band ARFCN channels
|
||||
* \returns logical (TRX) channel number, or -1 in case there is none */
|
||||
static int getLogicalChan(size_t pchan, size_t chans)
|
||||
{
|
||||
switch (chans) {
|
||||
@@ -111,6 +122,9 @@ static int getLogicalChan(size_t pchan, size_t chans)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! do we need to frequency shift our spectrum or not?
|
||||
* \param chans total number of channels
|
||||
* \returns 1 if we need to shift; 0 if not; -1 on error */
|
||||
static int getFreqShift(size_t chans)
|
||||
{
|
||||
switch (chans) {
|
||||
@@ -148,8 +162,11 @@ bool RadioInterfaceMulti::init(int type)
|
||||
mReceiveFIFO.resize(mChans);
|
||||
powerScaling.resize(mChans);
|
||||
history.resize(mChans);
|
||||
rx_freq_state.resize(mChans);
|
||||
tx_freq_state.resize(mChans);
|
||||
active.resize(MCHANS, false);
|
||||
|
||||
/* 4 == sps */
|
||||
inchunk = RESAMP_INRATE * 4;
|
||||
outchunk = RESAMP_OUTRATE * 4;
|
||||
|
||||
@@ -362,57 +379,83 @@ static bool fltcmp(double a, double b)
|
||||
return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
|
||||
}
|
||||
|
||||
bool RadioInterfaceMulti::verify_arfcn_consistency(double freq, size_t chan, bool tx)
|
||||
{
|
||||
double freq_i;
|
||||
std::string str_dir = tx ? "Tx" : "Rx";
|
||||
std::vector<struct freq_cfg_state> &v = tx ? tx_freq_state : rx_freq_state;
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
if (i == chan)
|
||||
continue;
|
||||
if (!v[i].set)
|
||||
continue;
|
||||
|
||||
freq_i = v[i].freq_hz + (double) ((int)chan - (int)i) * MCBTS_SPACING;
|
||||
if (!fltcmp(freq, freq_i)) {
|
||||
LOGCHAN(chan, DMAIN, ERROR)
|
||||
<< "Setting " << str_dir << " frequency " << freq
|
||||
<< " is incompatible: already configured channel "
|
||||
<< i << " uses frequency " << v[i].freq_hz
|
||||
<< " (expected " << freq_i << ")";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
v[chan].set = true;
|
||||
v[chan].freq_hz = freq;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
|
||||
{
|
||||
if (chan >= mChans)
|
||||
return false;
|
||||
double shift;
|
||||
|
||||
double shift = (double) getFreqShift(mChans);
|
||||
if (chan >= mChans)
|
||||
return false;
|
||||
|
||||
if (!chan)
|
||||
return mDevice->setTxFreq(freq + shift * MCBTS_SPACING);
|
||||
if (!verify_arfcn_consistency(freq, chan, true))
|
||||
return false;
|
||||
|
||||
double center = mDevice->getTxFreq();
|
||||
if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
|
||||
LOG(NOTICE) << "Channel " << chan << " RF Tx frequency offset is "
|
||||
<< freq / 1e6 << " MHz";
|
||||
}
|
||||
if (chan == 0) {
|
||||
shift = (double) getFreqShift(mChans);
|
||||
return mDevice->setTxFreq(freq + shift * MCBTS_SPACING);
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
|
||||
{
|
||||
if (chan >= mChans)
|
||||
return false;
|
||||
double shift;
|
||||
|
||||
double shift = (double) getFreqShift(mChans);
|
||||
if (chan >= mChans)
|
||||
return false;
|
||||
|
||||
if (!chan)
|
||||
return mDevice->setRxFreq(freq + shift * MCBTS_SPACING);
|
||||
if (!verify_arfcn_consistency(freq, chan, false))
|
||||
return false;
|
||||
|
||||
double center = mDevice->getRxFreq();
|
||||
if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
|
||||
LOG(NOTICE) << "Channel " << chan << " RF Rx frequency offset is "
|
||||
<< freq / 1e6 << " MHz";
|
||||
}
|
||||
if (chan == 0) {
|
||||
shift = (double) getFreqShift(mChans);
|
||||
return mDevice->setRxFreq(freq + shift * MCBTS_SPACING);
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
double RadioInterfaceMulti::setRxGain(double db, size_t chan)
|
||||
{
|
||||
if (chan == 0)
|
||||
return mDevice->setRxGain(db);
|
||||
else
|
||||
return mDevice->getRxGain();
|
||||
}
|
||||
|
||||
double RadioInterfaceMulti::setTxGain(double dB, size_t chan)
|
||||
{
|
||||
if (chan == 0)
|
||||
return mDevice->setTxGain(dB);
|
||||
return mDevice->setRxGain(db);
|
||||
else
|
||||
return mDevice->getTxGain();
|
||||
|
||||
return mDevice->getRxGain();
|
||||
}
|
||||
|
||||
double RadioInterfaceMulti::rssiOffset(size_t chan)
|
||||
{
|
||||
return mDevice->rssiOffset(0);
|
||||
}
|
||||
|
||||
int RadioInterfaceMulti::setPowerAttenuation(int atten, size_t chan)
|
||||
{
|
||||
return RadioInterface::setPowerAttenuation(atten, 0);
|
||||
}
|
||||
|
||||
@@ -120,38 +120,26 @@ GSM::Time VectorQueue::nextTime() const
|
||||
|
||||
radioVector* VectorQueue::getStaleBurst(const GSM::Time& targTime)
|
||||
{
|
||||
mLock.lock();
|
||||
if ((mQ.size()==0)) {
|
||||
mLock.unlock();
|
||||
if ((mQ.size()==0))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mQ.top()->getTime() < targTime) {
|
||||
radioVector* retVal = mQ.top();
|
||||
mQ.pop();
|
||||
mLock.unlock();
|
||||
return retVal;
|
||||
}
|
||||
mLock.unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
radioVector* VectorQueue::getCurrentBurst(const GSM::Time& targTime)
|
||||
{
|
||||
mLock.lock();
|
||||
if ((mQ.size()==0)) {
|
||||
mLock.unlock();
|
||||
if ((mQ.size()==0))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mQ.top()->getTime() == targTime) {
|
||||
radioVector* retVal = mQ.top();
|
||||
mQ.pop();
|
||||
mLock.unlock();
|
||||
return retVal;
|
||||
}
|
||||
mLock.unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ public:
|
||||
GSM::Time nextTime() const;
|
||||
radioVector* getStaleBurst(const GSM::Time& targTime);
|
||||
radioVector* getCurrentBurst(const GSM::Time& targTime);
|
||||
Mutex *getMutex() const { return &mLock; };
|
||||
};
|
||||
|
||||
#endif /* RADIOVECTOR_H */
|
||||
|
||||
@@ -345,7 +345,7 @@ static signalVector *convolve(const signalVector *x, const signalVector *h,
|
||||
_x = x;
|
||||
|
||||
/*
|
||||
* Four convovle types:
|
||||
* Four convolve types:
|
||||
* 1. Complex-Real (aligned)
|
||||
* 2. Complex-Complex (aligned)
|
||||
* 3. Complex-Real (!aligned)
|
||||
@@ -723,7 +723,7 @@ static signalVector *mapEdgeSymbols(const BitVector &bits)
|
||||
*
|
||||
* Delay the EDGE downlink bursts by one symbol in order to match GMSK pulse
|
||||
* shaping group delay. The difference in group delay arises from the dual
|
||||
* pulse filter combination of the GMSK Laurent represenation whereas 8-PSK
|
||||
* pulse filter combination of the GMSK Laurent representation whereas 8-PSK
|
||||
* uses a single pulse linear filter.
|
||||
*/
|
||||
static signalVector *shapeEdgeBurst(const signalVector &symbols)
|
||||
@@ -1792,15 +1792,15 @@ static SoftVector *signalToSoftVector(signalVector *dec)
|
||||
* stages.
|
||||
*/
|
||||
static signalVector *demodCommon(const signalVector &burst, int sps,
|
||||
complex chan, float toa)
|
||||
const struct estim_burst_params *ebp)
|
||||
{
|
||||
signalVector *delay, *dec;
|
||||
|
||||
if ((sps != 1) && (sps != 4))
|
||||
return NULL;
|
||||
|
||||
delay = delayVector(&burst, NULL, -toa * (float) sps);
|
||||
scaleVector(*delay, (complex) 1.0 / chan);
|
||||
delay = delayVector(&burst, NULL, -ebp->toa * (float) sps);
|
||||
scaleVector(*delay, (complex) 1.0 / ebp->amp);
|
||||
|
||||
if (sps == 1)
|
||||
return delay;
|
||||
@@ -1816,13 +1816,13 @@ static signalVector *demodCommon(const signalVector &burst, int sps,
|
||||
* 4 SPS (if activated) to minimize distortion through the fractional
|
||||
* delay filters. Symbol rotation and after always operates at 1 SPS.
|
||||
*/
|
||||
static SoftVector *demodGmskBurst(const signalVector &rxBurst,
|
||||
int sps, complex channel, float TOA)
|
||||
static SoftVector *demodGmskBurst(const signalVector &rxBurst, int sps,
|
||||
const struct estim_burst_params *ebp)
|
||||
{
|
||||
SoftVector *bits;
|
||||
signalVector *dec;
|
||||
|
||||
dec = demodCommon(rxBurst, sps, channel, TOA);
|
||||
dec = demodCommon(rxBurst, sps, ebp);
|
||||
if (!dec)
|
||||
return NULL;
|
||||
|
||||
@@ -1835,6 +1835,27 @@ static SoftVector *demodGmskBurst(const signalVector &rxBurst,
|
||||
return bits;
|
||||
}
|
||||
|
||||
static float computeEdgeCI(const signalVector *rot)
|
||||
{
|
||||
float err_pwr = 0.0f;
|
||||
float step = 2.0f * M_PI_F / 8.0f;
|
||||
|
||||
for (size_t i = 8; i < rot->size() - 8; i++) {
|
||||
/* Compute the ideal symbol */
|
||||
complex sym = (*rot)[i];
|
||||
float phase = step * roundf(sym.arg() / step);
|
||||
complex ideal = complex(cos(phase), sin(phase));
|
||||
|
||||
/* Compute the error vector */
|
||||
complex err = ideal - sym;
|
||||
|
||||
/* Accumulate power */
|
||||
err_pwr += err.norm2();
|
||||
}
|
||||
|
||||
return 3.0103f * log2f(1.0f * (rot->size() - 16) / err_pwr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Demodulate an 8-PSK burst. Prior to symbol rotation, operate at
|
||||
* 4 SPS (if activated) to minimize distortion through the fractional
|
||||
@@ -1845,19 +1866,20 @@ static SoftVector *demodGmskBurst(const signalVector &rxBurst,
|
||||
* through the fractional delay filters at 1 SPS renders signal
|
||||
* nearly unrecoverable.
|
||||
*/
|
||||
static SoftVector *demodEdgeBurst(const signalVector &burst,
|
||||
int sps, complex chan, float toa)
|
||||
static SoftVector *demodEdgeBurst(const signalVector &burst, int sps,
|
||||
struct estim_burst_params *ebp)
|
||||
{
|
||||
SoftVector *bits;
|
||||
signalVector *dec, *rot, *eq;
|
||||
|
||||
dec = demodCommon(burst, sps, chan, toa);
|
||||
dec = demodCommon(burst, sps, ebp);
|
||||
if (!dec)
|
||||
return NULL;
|
||||
|
||||
/* Equalize and derotate */
|
||||
eq = convolve(dec, GSMPulse4->c0_inv, NULL, NO_DELAY);
|
||||
rot = derotateEdgeBurst(*eq, 1);
|
||||
ebp->ci = computeEdgeCI(rot);
|
||||
|
||||
/* Soft slice and normalize */
|
||||
bits = softSliceEdgeBurst(*rot);
|
||||
@@ -1869,13 +1891,13 @@ static SoftVector *demodEdgeBurst(const signalVector &burst,
|
||||
return bits;
|
||||
}
|
||||
|
||||
SoftVector *demodAnyBurst(const signalVector &burst, int sps, complex amp,
|
||||
float toa, CorrType type)
|
||||
SoftVector *demodAnyBurst(const signalVector &burst, CorrType type,
|
||||
int sps, struct estim_burst_params *ebp)
|
||||
{
|
||||
if (type == EDGE)
|
||||
return demodEdgeBurst(burst, sps, amp, toa);
|
||||
return demodEdgeBurst(burst, sps, ebp);
|
||||
else
|
||||
return demodGmskBurst(burst, sps, amp, toa);
|
||||
return demodGmskBurst(burst, sps, ebp);
|
||||
}
|
||||
|
||||
bool sigProcLibSetup()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
@@ -134,7 +134,7 @@ int detectAnyBurst(const signalVector &burst,
|
||||
struct estim_burst_params *ebp);
|
||||
|
||||
/** Demodulate burst basde on type and output soft bits */
|
||||
SoftVector *demodAnyBurst(const signalVector &burst, int sps,
|
||||
complex amp, float toa, CorrType type);
|
||||
SoftVector *demodAnyBurst(const signalVector &burst, CorrType type,
|
||||
int sps, struct estim_burst_params *ebp);
|
||||
|
||||
#endif /* SIGPROCLIB_H */
|
||||
|
||||
@@ -47,7 +47,7 @@ void signalVector::operator=(const signalVector& vector)
|
||||
complex *src = vector.mData;
|
||||
for (i = 0; i < size(); i++, src++, dst++)
|
||||
*dst = *src;
|
||||
/* TODO: optimize for non non-trivially copyable types: */
|
||||
/* TODO: optimize for non non-trivially copiable types: */
|
||||
/*memcpy(mData, vector.mData, bytes()); */
|
||||
mStart = mData + vector.getStart();
|
||||
}
|
||||
@@ -70,7 +70,7 @@ size_t signalVector::updateHistory()
|
||||
complex *src = mStart + this->size() - num;
|
||||
for (i = 0; i < num; i++, src++, dst++)
|
||||
*dst = *src;
|
||||
/* TODO: optimize for non non-trivially copyable types: */
|
||||
/* TODO: optimize for non non-trivially copiable types: */
|
||||
/*memmove(mData, mStart + this->size() - num, num * sizeof(complex)); */
|
||||
|
||||
return num;
|
||||
|
||||
31
configure.ac
31
configure.ac
@@ -34,6 +34,8 @@ AC_CANONICAL_TARGET
|
||||
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl Linux kernel KBuild style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
@@ -51,6 +53,11 @@ AC_PROG_INSTALL
|
||||
AC_PATH_PROG([RM_PROG], [rm])
|
||||
AC_LANG([C++])
|
||||
|
||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
|
||||
AS_CASE(["$LD"],[*clang*],
|
||||
[AS_CASE(["${host_os}"],
|
||||
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
@@ -65,7 +72,7 @@ AC_PROG_LIBTOOL
|
||||
|
||||
dnl Checks for header files.
|
||||
AC_HEADER_STDC
|
||||
dnl This is required for GnuRadio includes to understand endianess correctly:
|
||||
dnl This is required for GnuRadio includes to understand endianness correctly:
|
||||
AC_CHECK_HEADERS([byteswap.h])
|
||||
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
@@ -75,9 +82,10 @@ AC_TYPE_SIZE_T
|
||||
AC_HEADER_TIME
|
||||
AC_C_BIGENDIAN
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.5.0)
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
@@ -125,6 +133,11 @@ AC_ARG_WITH(lms, [
|
||||
[enable LimeSuite based transceiver])
|
||||
])
|
||||
|
||||
AC_ARG_WITH(ipc, [
|
||||
AS_HELP_STRING([--with-ipc],
|
||||
[enable IPC])
|
||||
])
|
||||
|
||||
AC_ARG_WITH(singledb, [
|
||||
AS_HELP_STRING([--with-singledb],
|
||||
[enable single daughterboard use on USRP1])
|
||||
@@ -164,7 +177,7 @@ AS_IF([test "x$with_lms" = "xyes"], [
|
||||
PKG_CHECK_MODULES(LMS, LimeSuite)
|
||||
])
|
||||
|
||||
AS_IF([test "x$with_uhd" != "xno"],[
|
||||
AS_IF([test "x$with_uhd" = "xyes"],[
|
||||
PKG_CHECK_MODULES(UHD, uhd >= 003.011,
|
||||
[AC_DEFINE(USE_UHD_3_11, 1, UHD version 3.11.0 or higher)],
|
||||
[PKG_CHECK_MODULES(UHD, uhd >= 003.009,
|
||||
@@ -223,9 +236,10 @@ AS_IF([test "x$osmo_cv_cc_has___sync_fetch_and_and" = "xyes" && test "x$osmo_cv_
|
||||
AC_DEFINE(HAVE_ATOMIC_OPS, 1, [Support all required atomic operations], [AC_MSG_WARN("At least one aotmic operation missing, will use mutex")])
|
||||
])
|
||||
|
||||
AM_CONDITIONAL(DEVICE_UHD, [test "x$with_uhd" != "xno"])
|
||||
AM_CONDITIONAL(DEVICE_UHD, [test "x$with_uhd" = "xyes"])
|
||||
AM_CONDITIONAL(DEVICE_USRP1, [test "x$with_usrp1" = "xyes"])
|
||||
AM_CONDITIONAL(DEVICE_LMS, [test "x$with_lms" = "xyes"])
|
||||
AM_CONDITIONAL(DEVICE_IPC, [test "x$with_ipc" = "xyes"])
|
||||
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
|
||||
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
|
||||
|
||||
@@ -310,9 +324,11 @@ AC_CONFIG_FILES([\
|
||||
Transceiver52M/device/uhd/Makefile \
|
||||
Transceiver52M/device/usrp1/Makefile \
|
||||
Transceiver52M/device/lms/Makefile \
|
||||
Transceiver52M/device/ipc/Makefile \
|
||||
tests/Makefile \
|
||||
tests/CommonLibs/Makefile \
|
||||
tests/Transceiver52M/Makefile \
|
||||
utils/Makefile \
|
||||
doc/Makefile \
|
||||
doc/examples/Makefile \
|
||||
contrib/Makefile \
|
||||
@@ -320,4 +336,5 @@ AC_CONFIG_FILES([\
|
||||
])
|
||||
|
||||
AC_OUTPUT(
|
||||
doc/manuals/Makefile)
|
||||
doc/manuals/Makefile
|
||||
contrib/osmo-trx.spec)
|
||||
|
||||
@@ -15,7 +15,7 @@ substr() { [ -z "${2##*$1*}" ]; }
|
||||
mychroot_nocwd() {
|
||||
# LC_ALL + LANGUAGE set to avoid lots of print errors due to locale not being set inside container
|
||||
# PATH is needed to be able to reach binaries like ldconfig without logging in to root, which adds the paths to PATH.
|
||||
# PROOT_NO_SECCOMP is requried due to proot bug #106
|
||||
# PROOT_NO_SECCOMP is required due to proot bug #106
|
||||
LC_ALL=C LANGUAGE=C PATH="$PATH:/usr/sbin:/sbin" PROOT_NO_SECCOMP=1 proot -r "$ROOTFS" -w / -b /proc --root-id -q qemu-arm-static "$@"
|
||||
}
|
||||
|
||||
@@ -23,15 +23,8 @@ mychroot() {
|
||||
mychroot_nocwd -w / "$@"
|
||||
}
|
||||
|
||||
base="$PWD"
|
||||
deps="$base/deps"
|
||||
inst="$deps/install"
|
||||
export deps inst
|
||||
|
||||
if [ -z "${INSIDE_CHROOT}" ]; then
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
# Only use ARM chroot if host is not ARM and the target is ARM:
|
||||
if ! $(substr "arm" "$(uname -m)") && [ "x${INSTR}" = "x--with-neon" -o "x${INSTR}" = "x--with-neon-vfpv4" ]; then
|
||||
|
||||
@@ -69,6 +62,20 @@ if [ -z "${INSIDE_CHROOT}" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
set -ex
|
||||
|
||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
|
||||
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
base="$PWD"
|
||||
deps="$base/deps"
|
||||
inst="$deps/install"
|
||||
export deps inst
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
mkdir "$deps" || true
|
||||
|
||||
osmo-build-dep.sh libosmocore "" "--enable-sanitize --disable-doxygen --disable-pcsc"
|
||||
@@ -78,11 +85,10 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms $INSTR"
|
||||
CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms --with-ipc $INSTR"
|
||||
|
||||
# Additional configure options and depends
|
||||
if [ "$WITH_MANUALS" = "1" ]; then
|
||||
osmo-build-dep.sh osmo-gsm-manuals
|
||||
CONFIG="$CONFIG --enable-manuals"
|
||||
fi
|
||||
|
||||
@@ -100,12 +106,18 @@ autoreconf --install --force
|
||||
$MAKE $PARALLEL_MAKE
|
||||
$MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck \
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck \
|
||||
|| cat-testlogs.sh
|
||||
|
||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||
make -C "$base/doc/manuals" publish
|
||||
fi
|
||||
|
||||
$MAKE maintainer-clean
|
||||
$MAKE $PARALLEL_MAKE maintainer-clean
|
||||
|
||||
# Verify distro-specific package patches apply:
|
||||
for patch in debian/patches/*.patch; do
|
||||
patch --dry-run -p1 < "$patch"
|
||||
done
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
254
contrib/osmo-trx.spec.in
Normal file
254
contrib/osmo-trx.spec.in
Normal file
@@ -0,0 +1,254 @@
|
||||
#
|
||||
# spec file for package osmo-trx
|
||||
#
|
||||
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
# upon. The license for this file, and modifications and additions to the
|
||||
# file, is the same license as for the pristine package itself (unless the
|
||||
# license for the pristine package is not an Open Source License, in which
|
||||
# case the license is the MIT License). An "Open Source License" is a
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
Name: osmo-trx
|
||||
Version: @VERSION@
|
||||
Release: 0
|
||||
Summary: SDR transceiver that implements Layer 1 of a GSM BTS
|
||||
License: AGPL-3.0-or-later
|
||||
Group: Productivity/Telephony/Servers
|
||||
URL: https://osmocom.org/projects/osmotrx
|
||||
Source: %{name}-%{version}.tar.xz
|
||||
BuildRequires: autoconf
|
||||
BuildRequires: automake
|
||||
BuildRequires: fdupes
|
||||
BuildRequires: gcc-c++
|
||||
BuildRequires: libtool
|
||||
BuildRequires: pkgconfig >= 0.20
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
%if ! 0%{?centos_ver}
|
||||
BuildRequires: pkgconfig(LimeSuite)
|
||||
BuildRequires: pkgconfig(usrp) >= 3.3
|
||||
%endif
|
||||
BuildRequires: pkgconfig(fftw3f)
|
||||
BuildRequires: pkgconfig(libosmocoding) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libusb-1.0)
|
||||
BuildRequires: pkgconfig(uhd)
|
||||
%{?systemd_requires}
|
||||
%if 0%{?suse_version} > 1325
|
||||
BuildRequires: libboost_program_options-devel
|
||||
BuildRequires: libboost_system-devel
|
||||
BuildRequires: libboost_test-devel
|
||||
BuildRequires: libboost_thread-devel
|
||||
%else
|
||||
BuildRequires: boost-devel
|
||||
%endif
|
||||
|
||||
%description
|
||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
||||
physical layer of a BTS comprising the following 3GPP specifications:
|
||||
|
||||
TS 05.01 "Physical layer on the radio path"
|
||||
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
|
||||
TS 05.04 "Modulation"
|
||||
TS 05.10 "Radio subsystem synchronization"
|
||||
|
||||
In this context, BTS is "Base transceiver station". It's the stations that
|
||||
connect mobile phones to the mobile network.
|
||||
|
||||
3GPP is the "3rd Generation Partnership Project" which is the collaboration
|
||||
between different telecommunication associations for developing new
|
||||
generations of mobile phone networks. (post-2G/GSM)
|
||||
|
||||
%package uhd
|
||||
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (UHD)
|
||||
Group: Productivity/Telephony/Servers
|
||||
Requires: uhd-firmware
|
||||
|
||||
%description uhd
|
||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
||||
physical layer of a BTS comprising the following 3GPP specifications:
|
||||
|
||||
TS 05.01 "Physical layer on the radio path"
|
||||
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
|
||||
TS 05.04 "Modulation"
|
||||
TS 05.10 "Radio subsystem synchronization"
|
||||
|
||||
In this context, BTS is "Base transceiver station". It's the stations that
|
||||
connect mobile phones to the mobile network.
|
||||
|
||||
3GPP is the "3rd Generation Partnership Project" which is the collaboration
|
||||
between different telecommunication associations for developing new
|
||||
generations of mobile phone networks. (post-2G/GSM)
|
||||
|
||||
%if ! 0%{?centos_ver}
|
||||
%package usrp1
|
||||
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (USRP1)
|
||||
Group: Productivity/Telephony/Servers
|
||||
|
||||
%description usrp1
|
||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
||||
physical layer of a BTS comprising the following 3GPP specifications:
|
||||
|
||||
TS 05.01 "Physical layer on the radio path"
|
||||
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
|
||||
TS 05.04 "Modulation"
|
||||
TS 05.10 "Radio subsystem synchronization"
|
||||
|
||||
In this context, BTS is "Base transceiver station". It's the stations that
|
||||
connect mobile phones to the mobile network.
|
||||
|
||||
3GPP is the "3rd Generation Partnership Project" which is the collaboration
|
||||
between different telecommunication associations for developing new
|
||||
generations of mobile phone networks. (post-2G/GSM)
|
||||
|
||||
%package lms
|
||||
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite)
|
||||
Group: Productivity/Telephony/Servers
|
||||
|
||||
%description lms
|
||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
||||
physical layer of a BTS comprising the following 3GPP specifications:
|
||||
|
||||
TS 05.01 "Physical layer on the radio path"
|
||||
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
|
||||
TS 05.04 "Modulation"
|
||||
TS 05.10 "Radio subsystem synchronization"
|
||||
|
||||
In this context, BTS is "Base transceiver station". It's the stations that
|
||||
connect mobile phones to the mobile network.
|
||||
|
||||
3GPP is the "3rd Generation Partnership Project" which is the collaboration
|
||||
between different telecommunication associations for developing new
|
||||
generations of mobile phone networks. (post-2G/GSM)
|
||||
%endif
|
||||
|
||||
%package ipc
|
||||
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (IPC)
|
||||
Group: Productivity/Telephony/Servers
|
||||
|
||||
%description ipc
|
||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
||||
physical layer of a BTS comprising the following 3GPP specifications:
|
||||
|
||||
TS 05.01 "Physical layer on the radio path"
|
||||
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
|
||||
TS 05.04 "Modulation"
|
||||
TS 05.10 "Radio subsystem synchronization"
|
||||
|
||||
In this context, BTS is "Base transceiver station". It's the stations that
|
||||
connect mobile phones to the mobile network.
|
||||
|
||||
3GPP is the "3rd Generation Partnership Project" which is the collaboration
|
||||
between different telecommunication associations for developing new
|
||||
generations of mobile phone networks. (post-2G/GSM)
|
||||
|
||||
%package ipc-test
|
||||
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (IPC) driver test utility
|
||||
Group: Productivity/Telephony/Servers
|
||||
|
||||
%description ipc-test
|
||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
||||
physical layer of a BTS comprising the following 3GPP specifications:
|
||||
|
||||
This package include the test tools for osmo-trx-ipc
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
echo "%{version}" >.tarball-version
|
||||
autoreconf -fi
|
||||
|
||||
%if 0%{?centos_ver}
|
||||
%configure \
|
||||
--docdir=%{_docdir}/%{name} \
|
||||
--with-systemdsystemunitdir=%{_unitdir} \
|
||||
--without-lms \
|
||||
--with-uhd \
|
||||
--without-usrp1 \
|
||||
--with-ipc
|
||||
%else
|
||||
%configure \
|
||||
--docdir=%{_docdir}/%{name} \
|
||||
--with-systemdsystemunitdir=%{_unitdir} \
|
||||
--with-lms \
|
||||
--with-uhd \
|
||||
--with-usrp1 \
|
||||
--with-ipc
|
||||
%endif
|
||||
|
||||
make %{?_smp_mflags} V=1
|
||||
|
||||
%check
|
||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
|
||||
%install
|
||||
%make_install
|
||||
%fdupes -s %{buildroot}/%{_datadir}
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%pre lms %service_add_pre osmo-trx-lms.service
|
||||
%post lms %service_add_post osmo-trx-lms.service
|
||||
%preun lms %service_del_preun osmo-trx-lms.service
|
||||
%postun lms %service_del_postun osmo-trx-lms.service
|
||||
%pre uhd %service_add_pre osmo-trx-uhd.service
|
||||
%post uhd %service_add_post osmo-trx-uhd.service
|
||||
%preun uhd %service_del_preun osmo-trx-uhd.service
|
||||
%postun uhd %service_del_postun osmo-trx-uhd.service
|
||||
%pre usrp1 %service_add_pre osmo-trx-usrp1.service
|
||||
%post usrp1 %service_add_post osmo-trx-usrp1.service
|
||||
%preun usrp1 %service_del_preun osmo-trx-usrp1.service
|
||||
%postun usrp1 %service_del_postun osmo-trx-usrp1.service
|
||||
%pre ipc %service_add_pre osmo-trx-ipc.service
|
||||
%post ipc %service_add_post osmo-trx-ipc.service
|
||||
%preun ipc %service_del_preun osmo-trx-ipc.service
|
||||
%postun ipc %service_del_postun osmo-trx-ipc.service
|
||||
%endif
|
||||
|
||||
%files
|
||||
%license COPYING
|
||||
%doc README.md
|
||||
%doc %{_docdir}/%{name}/examples
|
||||
|
||||
%if ! 0%{?centos_ver}
|
||||
%files lms
|
||||
%{_bindir}/osmo-trx-lms
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-trx-lms.cfg
|
||||
%{_unitdir}/osmo-trx-lms.service
|
||||
%endif
|
||||
|
||||
%files uhd
|
||||
%{_bindir}/osmo-trx-uhd
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-trx-uhd.cfg
|
||||
%{_unitdir}/osmo-trx-uhd.service
|
||||
|
||||
%if ! 0%{?centos_ver}
|
||||
%files usrp1
|
||||
%{_bindir}/osmo-trx-usrp1
|
||||
%dir %{_datadir}/usrp
|
||||
%dir %{_datadir}/usrp/rev2
|
||||
%dir %{_datadir}/usrp/rev4
|
||||
%{_datadir}/usrp/rev2/std_inband.rbf
|
||||
%{_datadir}/usrp/rev4/std_inband.rbf
|
||||
%{_unitdir}/osmo-trx-usrp1.service
|
||||
%endif
|
||||
|
||||
%files ipc
|
||||
%{_bindir}/osmo-trx-ipc
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-trx-ipc.cfg
|
||||
%{_unitdir}/osmo-trx-ipc.service
|
||||
|
||||
%files ipc-test
|
||||
%{_bindir}/ipc-driver-test
|
||||
|
||||
%changelog
|
||||
@@ -1,7 +1,8 @@
|
||||
EXTRA_DIST = \
|
||||
osmo-trx-lms.service \
|
||||
osmo-trx-uhd.service \
|
||||
osmo-trx-usrp1.service
|
||||
osmo-trx-usrp1.service \
|
||||
osmo-trx-ipc.service
|
||||
|
||||
if HAVE_SYSTEMD
|
||||
SYSTEMD_SERVICES =
|
||||
@@ -18,5 +19,9 @@ if DEVICE_LMS
|
||||
SYSTEMD_SERVICES += osmo-trx-lms.service
|
||||
endif
|
||||
|
||||
if DEVICE_IPC
|
||||
SYSTEMD_SERVICES += osmo-trx-ipc.service
|
||||
endif
|
||||
|
||||
systemdsystemunit_DATA = $(SYSTEMD_SERVICES)
|
||||
endif # HAVE_SYSTEMD
|
||||
|
||||
11
contrib/systemd/osmo-trx-ipc.service
Normal file
11
contrib/systemd/osmo-trx-ipc.service
Normal file
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Osmocom SDR BTS L1 Transceiver (IPC Backend)
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/osmo-trx-ipc -C /etc/osmocom/osmo-trx-ipc.cfg
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
206
debian/changelog
vendored
206
debian/changelog
vendored
@@ -1,3 +1,209 @@
|
||||
osmo-trx (1.3.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Make logging category DLMS generic and reusable for other backends
|
||||
* uhd: Use DEVDRV log category and support UHD >=3.11 logging framework
|
||||
* uhd: Improve some logging lines printing UHD pretty-print output
|
||||
* doc: clarify number of channels on B210 with multi-arfcn enabled
|
||||
* radioInterfaceMulti: Fail to tune on freq not following multi-arfcn restrictions
|
||||
* doc: Update vty reference xml file
|
||||
* lms: Move initialization of field started to constructor
|
||||
* lms: Drop unused define
|
||||
* smpl_buf: Fix str_code() param and print unknown error val
|
||||
* lms: Improve smpl_buf error logging
|
||||
* lms: Change radioDevice constructor arg name to avoid masking instance attr
|
||||
* lms: Make reference to std::vector unambiguous
|
||||
* lms: Move rx_buffers allocation to constructor
|
||||
* lms: Store device type specific parameters in one place
|
||||
* lms: Make ts_offset and smpl rate coefs device-specific
|
||||
* lms: Initial multi-arfcn support
|
||||
* contrib/jenkins.sh: Reorder sanity checks
|
||||
* debug.h: Avoid printing pthread_t type
|
||||
* debug.h: Fix print format of chan in CLOGCHAN
|
||||
* cosmetic: fix several typos found by codespell
|
||||
* radioDevice: Drop unused RSSI param from readSamples API
|
||||
* radioDevice: Drop unused isControl param from WriteSamples API
|
||||
* Use OSMO_FD_READ instead of deprecated BSC_FD_READ
|
||||
* Transceiver: Fix extra space in RSP NOISELEV error
|
||||
* Transceiver: Implement TRXC cmd NOMTXPOWER
|
||||
* UHDDevice: Implement getNominalTxPower() based on TxFrequency
|
||||
* radioInterface: Operate on real Tx power attenuation rather than on device specific gains
|
||||
* UHDDevice: Compute TxGain on UHD API based on expected Tx output power
|
||||
* proto_trxd: Fix UndefinedBehaviorSanitizer from ubsan
|
||||
* Transceiver: Allow sending negative nominal tx power in RSP NOMTXPOWER
|
||||
* LMSDevice: Compute TxGain on LimeSuite API based on expected Tx output power
|
||||
* Drop old TxGain APIs from parent radioDevice abstract class
|
||||
* {UHD,LMS}Dervice: Log expected resulting TxPower when setting device specific TxGain
|
||||
* cosmetic: trx_rate_ctr: Fix whitespace
|
||||
* trx_rate_ctr: Fix immediate rescheduling on per-sec thresholds
|
||||
* Rename device specific rate counter multi-thread helpers
|
||||
* Introduce rate counter tx_stale_bursts
|
||||
* TransceiverState: Initialize ctrs field in constructor
|
||||
* doc/manuals: Update thread documentation after dropping CTRL sock threads
|
||||
* trx_rate_ctr: Fix locking wrong mutex
|
||||
* Introduce rate counters to detect issues in received Dl bursts from TRXD
|
||||
* Transceiver: Fix race condition obtaining Dl burst from Upper layer
|
||||
* Add rate counter for missing Txbursts when scheduled towards the radioInterface
|
||||
* Transceiver: Provide initial value for TransceiverState::mFiller in constructor
|
||||
* Transceiver: Use already obtained value from Rx msg structure
|
||||
* Transceiver: Restrict conditions where FN gaps are detected
|
||||
* trx_rate_ctr: Lower some log levels
|
||||
* Introduce CTR log category
|
||||
* Transceiver: Lower some log levels which have an associated counter
|
||||
* Transceiver: Check log level before generating burst str representation
|
||||
* Transceiver: Add several rate_ctr for rx error conditions
|
||||
* Use new libosmovty cpu sched config features
|
||||
* debian: Update debian8 osmo-trx specific patch
|
||||
* jenkins.sh: Verify distro-specific patches apply
|
||||
* ipc: fix var declaration in for loop
|
||||
* Add support for TRXC MUTE command
|
||||
* arch: x86: Fix convolve optimizations breaking signal
|
||||
* contrib/jenkins: Enable parallel make in make distcheck
|
||||
* Transceiver: Pass config struct instead of large list of params
|
||||
* Calculate RSSI offset based on RxGain configuration
|
||||
* main: generate coredump and exit upon SIGABRT received
|
||||
* ipc: Fix wrong reference to BTS in log line
|
||||
* ipc-driver-test: Allow setting dir prefix for UD socket
|
||||
* radioInterfaceMulti: Fix memory leak upon close()
|
||||
* ChannelizerBase: Fix memory leak
|
||||
* Threads.cpp: Use already existing gettid wrapper function
|
||||
* Replace my_gettid with libosmocore osmo_gettid API
|
||||
* tests: Explicitly drop category from log
|
||||
* tests: Replace deprecated API log_set_print_filename
|
||||
|
||||
[ Philipp Maier ]
|
||||
* debug: use LOGL_NOTICE for log category DDEV
|
||||
* doc: do not set the base-port of the trx
|
||||
* doc: apply an rssi-offset of 28 by default.
|
||||
* doc: switch log levels to notice
|
||||
* Transceiver: Log when sending of CLK indications begins
|
||||
* vty: add attributes to VTY commands indicating when they apply
|
||||
* osmo-trx: add commandline option --vty-ref-xml
|
||||
|
||||
[ Eric ]
|
||||
* configure.ac: fix libtool issue with clang and sanitizer
|
||||
* transceiver: check the right vector
|
||||
* transceiver: get rid of the ctrl threads
|
||||
* add kernel style .clang-format with 120 chars per line limit
|
||||
* devices: reset internal smart sample buffers upon stop
|
||||
* transceiver: optimize code if optimizations are enabled
|
||||
* transceiver: initialize reorder flag so we don't miscount
|
||||
|
||||
[ Harald Welte ]
|
||||
* PRBS tool sending PRBS sequence to TRX
|
||||
* prbs-tool: Add error simulation capabilities
|
||||
* utils: Ensure content of this directory is included in 'make dist'
|
||||
* prbs-tool: Don't require C99
|
||||
* RPM spec file: Require uhd-firmware for osmo-trx-uhd
|
||||
* osmo-trx.spec.in: Use %config(noreplace) to retain current config file
|
||||
* Fix build on Debian8
|
||||
* [cosmetic] radioInterfaceMulti: More comments
|
||||
* [cosmetic] radioIntefaceMulti: Fix whitespace / indent
|
||||
* ipc: Use OSMO_FD_* instead of deprecated BSC_FD_*
|
||||
* Use osmo_fd_setup() wherever applicable
|
||||
* Use osmo_fd_*_{disable,enable}
|
||||
* README update
|
||||
* manual: Fix typo OsmTRX -> OsmoTRX
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib: import RPM spec
|
||||
* contrib: integrate RPM spec
|
||||
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
|
||||
* contrib/jenkins: don't build osmo-gsm-manuals
|
||||
* configure.ac: set -std=gnu11
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* UHDDevice: catch LookupError/IndexError in set{Rx,Tx}Antenna()
|
||||
* debian/control: change maintainer to the Osmocom team / mailing list
|
||||
* proto_trxd: cosmetic: 'if' is not a function, add space
|
||||
* vty: add multi-ARFCN specific warning for chan N > 0
|
||||
* radioDevice: fix set_antennas(): consider MULTI_ARFCN mode
|
||||
* device/lms: fix: 'trx_vty.h' header requires C linkage
|
||||
* device/lms: fix missing semicolon in LMSDevice::assign_band_desc()
|
||||
* device/lms: get rid of 'using namespace std'
|
||||
* device/common/Makefile.am: remove $(LMS_CFLAGS) from AM_CXXFLAGS
|
||||
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
|
||||
* device: drop unreasonable LIBOSMO{CTRL,VTY}_{CFLAGS,LIBS}
|
||||
* Transceiver: use size_t and ARRAY_SIZE() in constructor
|
||||
* Transceiver: explicitly init m{Rx,Tx}LowerLoopThread
|
||||
* vty: fix documentation for 'swap-channels (disable|enable)'
|
||||
* vty: fix documentation for 'egprs (disable|enable)'
|
||||
* vty: fix documentation for 'rx-sps (1|4)' and 'tx-sps (1|4)'
|
||||
* vty: cosmetic: use VTY_IPV4_CMD in 'bind-ip' / 'remote-ip'
|
||||
* vty: fix documentation for 'multi-arfcn (disable|enable)'
|
||||
* vty: remove groundless statement about filler type 'dummy'
|
||||
* vty: auto-generate cmd and doc strings for cfg_filler_type_cmd
|
||||
* vty: fix documentation for 'ext-rach (disable|enable)'
|
||||
* main: use logging API to print SIMD info instead of printf()
|
||||
* doc/manuals: generate XML VTY reference at build-time
|
||||
* vty: fix swapped documentation for 'filler type' command
|
||||
* Transceiver: fix integer division in addRadioVector()
|
||||
* Transceiver: use proper factor for amplitude scaling
|
||||
* Add a (hidden) VTY parameter for Rx/Tx freq. shifting
|
||||
|
||||
[ Eric Wild ]
|
||||
* osmo-trx-ipc
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* osmo-trx.spec: move ipc-driver-test into package ipc-test
|
||||
|
||||
[ Sylvain Munaut ]
|
||||
* sigProcLib: fix C/I computation for 8-PSK modulated bursts
|
||||
|
||||
-- Pau Espin Pedrol <pespin@espeweb.net> Tue, 23 Feb 2021 14:27:15 +0100
|
||||
|
||||
osmo-trx (1.2.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* osmo-trx: log to stderr on signal received
|
||||
* Drop old setPriority related code
|
||||
* Transceiver: fix segfault during init if IP addr binding fails
|
||||
* Transceiver: Check return value when binding IP addr for clock socket
|
||||
* Transceiver: Clean up receival of downlink bursts
|
||||
* Transceiver: Fix idle ul burst indications being dropped
|
||||
* Transceiver: exit process when BTS drops connection
|
||||
* Transceiver: Enable EDGE detection only on PDCH timeslots
|
||||
* lms: Log available antennas if requested antenna fails
|
||||
* device: Use LOGCHAN in set_antennas()
|
||||
* Transceiver: Fix logging TN and version
|
||||
* Transceiver: Use LOGCHAN in logRxBurst to unify log format
|
||||
* Transceiver: Log error condition no burst in pullRadioVector()
|
||||
* Transceiver: pullRadioVector(): Fix use of uninitialized value bi->tn
|
||||
* Transceiver: Don't stop TRX if pulling from OFF timeslot
|
||||
* radioInterface: Rename mRadio to mDevice
|
||||
* radioInterfaceMulti: Check equals zero explicitly
|
||||
* USRPDevice: Fix setRxGain return on error and getRxGain() returning always 0
|
||||
* USRPDevice: Return previous txGain if setting value failed
|
||||
* LMSDevice: Return previous txGain/rxGain if setting value failed
|
||||
* radioInterface: Remove unusued getRxGain()
|
||||
* radioDevice: Introduce getTxGain() API
|
||||
* radioInterfaceMulti: Override setTxGain() to avoid chan!=0 calls
|
||||
* UHDDevice: Drop unneeded MULTI_ARFCN checks
|
||||
* radioInterface{Multi,Resamp}: Fix successful writeSamples() masking underrun from readSamples()
|
||||
* radioInterface: Mark setRxGain as virtual
|
||||
* Move multi-ARFCN chan amount modification from UHDDevice to parent class
|
||||
* radioInterface: Atomically fetch and change underrun variable
|
||||
* radioInterfaceMulti: write frequency offset direction (rx/tx) in log line
|
||||
* Use new libosmocore logging lock API
|
||||
* Transceiver: Fix wrong response upon CMD HANDOVER failure
|
||||
* uhd: use value already cached in tmp variable
|
||||
* Transceiver.cpp: Introduce and use new logging categories
|
||||
|
||||
[ Timo Jacobus ]
|
||||
* Transceiver: Fixed copying of history into and from channelizer buffer.
|
||||
|
||||
[ Alexander Chemeris ]
|
||||
* vty: Don't enable random filler bursts automatically with EDGE.
|
||||
* vty: Simplify filler burst settings and improve help and readability.
|
||||
|
||||
[ Martin Hauke ]
|
||||
* Fix common misspellings and typos
|
||||
|
||||
[ Harald Welte ]
|
||||
* trx: exit() on unsupported positional arguments on command line
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 03 Jan 2020 19:54:00 +0100
|
||||
|
||||
osmo-trx (1.1.1) unstable; urgency=medium
|
||||
|
||||
* UNRELEASED
|
||||
|
||||
25
debian/control
vendored
25
debian/control
vendored
@@ -1,7 +1,7 @@
|
||||
Source: osmo-trx
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Ivan Klyuchnikov <ivan.kluchnikov@fairwaves.ru>
|
||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||
Build-Depends: debhelper (>= 9),
|
||||
autotools-dev,
|
||||
autoconf-archive,
|
||||
@@ -14,7 +14,7 @@ Build-Depends: debhelper (>= 9),
|
||||
libtalloc-dev,
|
||||
libusrp-dev,
|
||||
liblimesuite-dev,
|
||||
libosmocore-dev (>= 0.12.0),
|
||||
libosmocore-dev (>= 1.5.0),
|
||||
osmo-gsm-manuals-dev
|
||||
Standards-Version: 3.9.6
|
||||
Vcs-Browser: http://cgit.osmocom.org/osmo-trx
|
||||
@@ -30,7 +30,7 @@ Package: osmo-trx-dbg
|
||||
Architecture: any
|
||||
Section: debug
|
||||
Priority: extra
|
||||
Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-lms (= ${binary:Version}), ${misc:Depends}
|
||||
Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-lms (= ${binary:Version}), osmo-trx-ipc (= ${binary:Version}), ${misc:Depends}
|
||||
Description: Debug symbols for the osmo-trx-*
|
||||
Make debugging possible
|
||||
|
||||
@@ -91,6 +91,25 @@ Description: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite)
|
||||
between different telecommunication associations for developing new
|
||||
generations of mobile phone networks. (post-2G/GSM)
|
||||
|
||||
Package: osmo-trx-ipc
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: SDR transceiver that implements Layer 1 of a GSM BTS (generic IPC)
|
||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
||||
physical layer of a BTS comprising the following 3GPP specifications:
|
||||
.
|
||||
TS 05.01 "Physical layer on the radio path"
|
||||
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
|
||||
TS 05.04 "Modulation"
|
||||
TS 05.10 "Radio subsystem synchronization"
|
||||
.
|
||||
In this context, BTS is "Base transceiver station". It's the stations that
|
||||
connect mobile phones to the mobile network.
|
||||
.
|
||||
3GPP is the "3rd Generation Partnership Project" which is the collaboration
|
||||
between different telecommunication associations for developing new
|
||||
generations of mobile phone networks. (post-2G/GSM)
|
||||
|
||||
Package: osmo-trx-doc
|
||||
Architecture: all
|
||||
Section: doc
|
||||
|
||||
4
debian/osmo-trx-ipc.install
vendored
Normal file
4
debian/osmo-trx-ipc.install
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
etc/osmocom/osmo-trx-ipc.cfg
|
||||
lib/systemd/system/osmo-trx-ipc.service
|
||||
/usr/bin/osmo-trx-ipc
|
||||
/usr/share/doc/osmo-trx/examples/osmo-trx-ipc/osmo-trx-ipc.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-ipc/
|
||||
20
debian/patches/build-for-debian8.patch
vendored
20
debian/patches/build-for-debian8.patch
vendored
@@ -1,5 +1,5 @@
|
||||
diff --git a/debian/control b/debian/control
|
||||
index 8ff59f0..126c16a 100644
|
||||
index 12d9af5..27b9d60 100644
|
||||
--- a/debian/control
|
||||
+++ b/debian/control
|
||||
@@ -13,7 +13,6 @@ Build-Depends: debhelper (>= 9),
|
||||
@@ -7,15 +7,15 @@ index 8ff59f0..126c16a 100644
|
||||
libtalloc-dev,
|
||||
libusrp-dev,
|
||||
- liblimesuite-dev,
|
||||
libosmocore-dev (>= 0.12.0),
|
||||
libosmocore-dev (>= 1.5.0),
|
||||
osmo-gsm-manuals-dev
|
||||
Standards-Version: 3.9.6
|
||||
@@ -30,7 +29,7 @@ Package: osmo-trx-dbg
|
||||
Architecture: any
|
||||
Section: debug
|
||||
Priority: extra
|
||||
-Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-lms (= ${binary:Version}), ${misc:Depends}
|
||||
+Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), ${misc:Depends}
|
||||
-Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-lms (= ${binary:Version}), osmo-trx-ipc (= ${binary:Version}), ${misc:Depends}
|
||||
+Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-ipc (= ${binary:Version}), ${misc:Depends}
|
||||
Description: Debug symbols for the osmo-trx-*
|
||||
Make debugging possible
|
||||
|
||||
@@ -42,19 +42,19 @@ index 8ff59f0..126c16a 100644
|
||||
- between different telecommunication associations for developing new
|
||||
- generations of mobile phone networks. (post-2G/GSM)
|
||||
-
|
||||
Package: osmo-trx-doc
|
||||
Architecture: all
|
||||
Section: doc
|
||||
Package: osmo-trx-ipc
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
diff --git a/debian/rules b/debian/rules
|
||||
index 627c0c8..d9285e2 100755
|
||||
index 5795643..5937c17 100755
|
||||
--- a/debian/rules
|
||||
+++ b/debian/rules
|
||||
@@ -9,7 +9,7 @@ override_dh_shlibdeps:
|
||||
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
|
||||
|
||||
override_dh_auto_configure:
|
||||
- dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
+ dh_auto_configure -- --with-uhd --with-usrp1 --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
- dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-ipc --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
+ dh_auto_configure -- --with-uhd --with-usrp1 --with-ipc --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
|
||||
override_dh_strip:
|
||||
dh_strip --dbg-package=osmo-trx-dbg
|
||||
|
||||
2
debian/rules
vendored
2
debian/rules
vendored
@@ -9,7 +9,7 @@ override_dh_shlibdeps:
|
||||
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
|
||||
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-ipc --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
|
||||
override_dh_strip:
|
||||
dh_strip --dbg-package=osmo-trx-dbg
|
||||
|
||||
@@ -14,6 +14,10 @@ if DEVICE_LMS
|
||||
OSMOCONF_FILES += osmo-trx-lms/osmo-trx-lms.cfg
|
||||
endif
|
||||
|
||||
if DEVICE_IPC
|
||||
OSMOCONF_FILES += osmo-trx-ipc/osmo-trx-ipc.cfg
|
||||
endif
|
||||
|
||||
osmoconf_DATA = $(OSMOCONF_FILES)
|
||||
EXTRA_DIST = $(OSMOCONF_FILES)
|
||||
|
||||
|
||||
32
doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg
Normal file
32
doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg
Normal file
@@ -0,0 +1,32 @@
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category 1
|
||||
logging timestamp 1
|
||||
logging print file basename
|
||||
logging level set-all notice
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
cpu-sched
|
||||
policy rr 18
|
||||
trx
|
||||
bind-ip 127.0.0.1
|
||||
remote-ip 127.0.0.1
|
||||
! 28 dB offset below is valid only for the B2xx in 1800 MHz band, see
|
||||
! https://osmocom.org/issues/4468 for more details
|
||||
rssi-offset 28.000000
|
||||
tx-sps 4
|
||||
rx-sps 4
|
||||
clock-ref external
|
||||
egprs disable
|
||||
ext-rach disable
|
||||
dev-args ipc_msock=/tmp/ipc_sock0
|
||||
multi-arfcn disable
|
||||
chan 0
|
||||
tx-path TX/RX
|
||||
rx-path RX2
|
||||
chan 1
|
||||
tx-path TX/RX
|
||||
rx-path RX2
|
||||
@@ -4,19 +4,19 @@ log stderr
|
||||
logging print category 1
|
||||
logging timestamp 1
|
||||
logging print file basename
|
||||
logging level set-all info
|
||||
logging level set-all notice
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
cpu-sched
|
||||
policy rr 18
|
||||
trx
|
||||
bind-ip 127.0.0.1
|
||||
remote-ip 127.0.0.1
|
||||
base-port 5700
|
||||
egprs disable
|
||||
tx-sps 4
|
||||
rx-sps 4
|
||||
rt-prio 18
|
||||
chan 0
|
||||
tx-path BAND1
|
||||
rx-path LNAW
|
||||
|
||||
@@ -4,19 +4,19 @@ log stderr
|
||||
logging print category 1
|
||||
logging timestamp 1
|
||||
logging print file basename
|
||||
logging level set-all info
|
||||
logging level set-all notice
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
cpu-sched
|
||||
policy rr 18
|
||||
trx
|
||||
bind-ip 127.0.0.1
|
||||
remote-ip 127.0.0.1
|
||||
base-port 5700
|
||||
egprs disable
|
||||
tx-sps 4
|
||||
rx-sps 4
|
||||
rt-prio 18
|
||||
chan 0
|
||||
tx-path BAND1
|
||||
rx-path LNAW
|
||||
|
||||
@@ -4,20 +4,20 @@ log stderr
|
||||
logging print category 1
|
||||
logging timestamp 1
|
||||
logging print file basename
|
||||
logging level set-all info
|
||||
logging level set-all notice
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
cpu-sched
|
||||
policy rr 18
|
||||
trx
|
||||
bind-ip 127.0.0.1
|
||||
remote-ip 127.0.0.1
|
||||
base-port 5700
|
||||
dev-args addr=192.168.10.2,pa=NONE,pa_power_max_dbm=23,fifo_ctrl_window=0,status_port=12345
|
||||
egprs disable
|
||||
tx-sps 4
|
||||
rx-sps 4
|
||||
rssi-offset 38
|
||||
rt-prio 18
|
||||
chan 0
|
||||
chan 1
|
||||
|
||||
@@ -4,19 +4,18 @@ log stderr
|
||||
logging print category 1
|
||||
logging timestamp 1
|
||||
logging print file basename
|
||||
logging level set-all info
|
||||
logging level set-all notice
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
cpu-sched
|
||||
policy rr 18
|
||||
trx
|
||||
bind-ip 127.0.0.1
|
||||
remote-ip 127.0.0.1
|
||||
base-port 5700
|
||||
egprs disable
|
||||
tx-sps 4
|
||||
rx-sps 4
|
||||
clock-ref external
|
||||
rt-prio 18
|
||||
chan 0
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
EXTRA_DIST = osmotrx-usermanual.adoc \
|
||||
osmotrx-usermanual-docinfo.xml \
|
||||
osmotrx-vty-reference.xml \
|
||||
chapters \
|
||||
vty
|
||||
|
||||
@@ -9,8 +8,24 @@ if BUILD_MANUALS
|
||||
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
|
||||
|
||||
VTY_REFERENCE = osmotrx-vty-reference.xml
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||
VARIANTS = $(NULL)
|
||||
|
||||
if DEVICE_UHD
|
||||
VARIANTS += uhd
|
||||
endif
|
||||
if DEVICE_USRP1
|
||||
VARIANTS += usrp1
|
||||
endif
|
||||
if DEVICE_LMS
|
||||
VARIANTS += lms
|
||||
endif
|
||||
if DEVICE_IPC
|
||||
VARIANTS += ipc
|
||||
endif
|
||||
|
||||
# This is a significantly modified, multi-target adopted copy of
|
||||
# $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||
include $(srcdir)/vty/Makefile.vty-reference.inc
|
||||
|
||||
OSMO_REPOSITORY = osmo-trx
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
|
||||
|
||||
@@ -39,8 +39,7 @@ trans [label="Transceiver"];
|
||||
radioiface [label="RadioInterface"];
|
||||
radiodev [label="RadioDevice"];
|
||||
|
||||
trans:nw->trans:ne [label="Trx.ControlServiceLoop_0"];
|
||||
trans:nw->trans:ne [label="Trx.ControlServiceLoop_1"];
|
||||
trans:nw->trans:ne [label="Main"];
|
||||
trans:w->radioiface:w [label="Trx.TxPriorityQueueServiceLoop_0"];
|
||||
trans:w->radioiface:w [label="Trx.TxPriorityQueueServiceLoop_1"];
|
||||
radioiface:e->trans:e [label="Trx.RxServiceLoop_0"];
|
||||
@@ -92,14 +91,6 @@ Static amount of Threads (1 per `Transceiver` instance):
|
||||
|
||||
Dynamic amount of Threads (1 per RF logical channel on the `Transceiver` instance):
|
||||
|
||||
* `ControlServiceLoop`: Handles commands from the Per-ARFCN Control Interface
|
||||
socket (<<trx_if_control>>). Each thread is responsible for managing one
|
||||
socket related to one ARFCN or which is the same, to one RF logical channel.
|
||||
These are the only threads expected to use the private `start()` and `stop()`
|
||||
methods of the `Transceiver()` class, since those methods don't stop any of
|
||||
the `ControlServiceLoop` threads as they must keep running to handle new
|
||||
commands (for instance, to re-start processing samples with the _POWERON_
|
||||
command).
|
||||
* `RxServiceLoop`: Each thread of this type pulls bursts from the
|
||||
`RadioInterface` FIFO for one specific logical RF channel and handles it
|
||||
according to the slot and burst correlation type, finally sending proper data
|
||||
@@ -108,6 +99,10 @@ Dynamic amount of Threads (1 per RF logical channel on the `Transceiver` instanc
|
||||
Manager UDP socket (<<trx_if>>), and fills the `RadioInterface` with it
|
||||
setting clock related information.
|
||||
|
||||
All the Per-ARFCN Control Interface socket (<<trx_if_control>>) commands are
|
||||
handled by the event loop runnnig on the main thread. This is the only thread
|
||||
expected to use the private `start()` and `stop()` methods.
|
||||
|
||||
[[code_component_radioiface]]
|
||||
=== RadioInterface
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
== Configuring OsmTRX
|
||||
== Configuring OsmoTRX
|
||||
|
||||
OsmoTRX will read the configuration at startup time and configure the
|
||||
transceiver accordingly after validating the configuration.
|
||||
@@ -46,15 +46,16 @@ Multi-ARFCN support is available since osmo-trx release `0.2.0`, and it was
|
||||
added specifically in commit `76764278169d252980853251daeb9f1ba0c246e1`.
|
||||
|
||||
This feature is useful for instance if you want to run more than 1 TRX with an
|
||||
Ettus B200 device, or 2 TRX with an Ettus B210 device, since they support only 1
|
||||
and 2 physical RF channels respectively. No device from other providers or even
|
||||
other devices than B200 and B210 from Ettus are known to support this feature.
|
||||
Ettus B200 device, or more than 2 TRXs with an Ettus B210 device, since they
|
||||
support only 1 and 2 physical RF channels respectively. No device from other
|
||||
providers or even other devices than B200 and B210 from Ettus are known to
|
||||
support this feature.
|
||||
|
||||
With multi-ARFCN enabled, ARFCN spacing is fixed at 800 kHz or 4 GSM channels.
|
||||
So if TRX-0 is set to ARFCN 51, TRX-1 _must_ be set to 55, and so on. Up to
|
||||
three ARFCN's is supported for multi-TRX.
|
||||
|
||||
From BTS and BSC point of view, supporting multiple TRX through multi-ARFCN
|
||||
From BTS and BSC point of view, supporting multiple TRXs through multi-ARFCN
|
||||
feature in OsmoTRX doesn't make any difference from a regular multi-TRX setup,
|
||||
leaving apart of course the mentioned ARFCN limitations explained above and as a
|
||||
consequence physical installation and operational differences.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
This section specifies the format of USB packets used for in-band data
|
||||
transmission and signaling on the USRP1. All packets are 512-byte long, and are
|
||||
transfered using USB "bulk" transfers.
|
||||
transferred using USB "bulk" transfers.
|
||||
|
||||
IN packets are sent towards the host. OUT packets are sent away from the host.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ B200 family and Fairwaves UmTRX family, and used to be the default backend used
|
||||
for legacy @osmo-trx@ binary when per-backend binaries didn't exist yet.
|
||||
|
||||
Any device providing generic support for UHD should theoretically be able to be
|
||||
run through this backend without much effort, but pracitcal experience showed
|
||||
run through this backend without much effort, but practical experience showed
|
||||
that some devices don't play well with it, such as the LimeSDR family of
|
||||
devices, which showed far better results when using its native interface.
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ include::{srcdir}/chapters/trx-backends.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/code-architecture.adoc[]
|
||||
|
||||
include::./common/chapters/vty_cpu_sched.adoc[]
|
||||
|
||||
include::./common/chapters/trx_if.adoc[]
|
||||
|
||||
include::./common/chapters/port_numbers.adoc[]
|
||||
|
||||
37
doc/manuals/vty/Makefile.vty-reference.inc
Normal file
37
doc/manuals/vty/Makefile.vty-reference.inc
Normal file
@@ -0,0 +1,37 @@
|
||||
DOCBOOKS = $(foreach v,$(VARIANTS),vty/osmotrx-$(v)-vty-reference.xml)
|
||||
DOCBOOKS_DEPS = $(DOCBOOKS) $(addsuffix .inc,$(DOCBOOKS))
|
||||
INC_DIR = $(abspath $(builddir)/vty)
|
||||
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.docbook.inc
|
||||
|
||||
CLEAN_FILES += $(DOCBOOKS_DEPS)
|
||||
CLEAN_FILES += $(addsuffix .inc.gen,$(DOCBOOKS))
|
||||
CLEAN_FILES += $(addsuffix .inc.merged,$(DOCBOOKS))
|
||||
|
||||
$(INC_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
vty/osmotrx-%-vty-reference.xml: $(top_builddir)/Transceiver52M/osmo-trx-% $(INC_DIR)
|
||||
sed -e "s|@@GENERATED@@|$@.inc|" \
|
||||
-e "s|@@VARIANT@@|$(notdir $<)|" \
|
||||
-e "s|@@REV_NUMBER@@|$(VERSION)|" \
|
||||
-e "s|@@REV_DATE@@|$(shell date +"%dth %B %Y")|" \
|
||||
-e "s|@@CR_YEAR@@|$(shell date +"%Y")|" \
|
||||
$(srcdir)/vty/osmotrx-vty-reference.xml > $@
|
||||
|
||||
vty/osmotrx-%-vty-reference.xml.inc: $(top_builddir)/Transceiver52M/osmo-trx-% \
|
||||
$(OSMO_GSM_MANUALS_DIR)/common/vty_additions.xml \
|
||||
$(OSMO_GSM_MANUALS_DIR)/common/chapters/vty.xml \
|
||||
$(OSMO_GSM_MANUALS_DIR)/vty_reference.xsl \
|
||||
$(srcdir)/vty/*.xml $(INC_DIR)
|
||||
# a) Invoke osmo-trx-% to generate the list of commands first
|
||||
$< --vty-ref-mode default --vty-ref-xml > "$@.gen"
|
||||
# ... filter garbage potentially printed by libraries to stdout
|
||||
sed -i '/^<vtydoc/,$$!d' "$@.gen"
|
||||
# b) Merge the result of a) with global and local additions
|
||||
$(OSMO_GSM_MANUALS_DIR)/build/vty_reference_combine.sh \
|
||||
$(realpath $(OSMO_GSM_MANUALS_DIR)/merge_doc.xsl) "$@.gen" \
|
||||
$(OSMO_GSM_MANUALS_DIR)/common/vty_additions.xml \
|
||||
$(srcdir)/vty/*additions*.xml > "$@.merged"
|
||||
# c) Convert the result of b) into a valid docbook
|
||||
xsltproc $(OSMO_GSM_MANUALS_DIR)/vty_reference.xsl "$@.merged" > $@
|
||||
@@ -6,7 +6,7 @@
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
|
||||
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
|
||||
<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml" >
|
||||
<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
|
||||
<!ENTITY sections-vty SYSTEM "@@GENERATED@@" >
|
||||
]>
|
||||
|
||||
<book>
|
||||
@@ -18,12 +18,19 @@
|
||||
<authorinitials>pe</authorinitials>
|
||||
<revremark>Initial</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>v2</revnumber>
|
||||
<date>@@REV_DATE@@</date>
|
||||
<authorinitials>s.f.m.c.</authorinitials>
|
||||
<revremark>Automatic build (@@REV_NUMBER@@)</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
<title>OsmoTRX VTY Reference</title>
|
||||
<subtitle>@@VARIANT@@</subtitle>
|
||||
|
||||
<copyright>
|
||||
<year>2018</year>
|
||||
<year>@@CR_YEAR@@</year>
|
||||
</copyright>
|
||||
|
||||
<legalnotice>
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user