Compare commits

..

1 Commits

Author SHA1 Message Date
Sylvain Munaut
d40f11962a Add C/I computation
Change-Id: Ib4ceec553f2e5f77bf3f6777724968456a180f5e
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2019-05-14 18:24:35 +02:00
155 changed files with 5194 additions and 9122 deletions

View File

@@ -1,521 +0,0 @@
# 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
View File

@@ -5,9 +5,6 @@
Transceiver52M/osmo-trx-uhd
Transceiver52M/osmo-trx-usrp1
Transceiver52M/osmo-trx-lms
Transceiver52M/osmo-trx-ipc
.clang-format
# tests
tests/CommonLibs/BitVectorTest
@@ -22,7 +19,6 @@ tests/CommonLibs/VectorTest
tests/CommonLibs/PRBSTest
tests/Transceiver52M/convolve_test
tests/Transceiver52M/LMSDeviceTest
Transceiver52M/device/ipc/ipc-driver-test
# automake/autoconf
*.in
@@ -64,14 +60,6 @@ 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

127
AUTHORS Normal file
View File

@@ -0,0 +1,127 @@
#
# Copyright 2008, 2009 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# GNU Radio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
David A. Burgess, dburgess@kestrelsp.com:
CLI/CLI.cpp
CLI/CLI.h
CommonLibs/Assert.h
CommonLibs/BitVector.cpp
CommonLibs/Interthread.h
CommonLibs/LinkedLists.cpp
CommonLibs/LinkedLists.h
CommonLibs/Regexp.h
CommonLibs/Sockets.cpp
CommonLibs/Sockets.h
CommonLibs/Threads.cpp
CommonLibs/Threads.h
CommonLibs/Timeval.cpp
CommonLibs/Timeval.h
CommonLibs/Vector.h
GSM/GSM610Tables.cpp
GSM/GSM610Tables.h
GSM/GSMCommon.cpp
GSM/GSMCommon.h
GSM/GSMConfig.h
GSM/GSML1FEC.cpp
GSM/GSML1FEC.h
GSM/GSML2LAPDm.cpp
GSM/GSML2LAPDm.h
GSM/GSML3CCElements.cpp
GSM/GSML3CCElements.h
GSM/GSML3CCMessages.cpp
GSM/GSML3CCMessages.h
GSM/GSML3CommonElements.cpp
GSM/GSML3CommonElements.h
GSM/GSML3MMElements.cpp
GSM/GSML3MMElements.h
GSM/GSML3MMMessages.cpp
GSM/GSML3MMMessages.h
GSM/GSML3Message.cpp
GSM/GSML3Message.h
GSM/GSML3RRElements.cpp
GSM/GSML3RRElements.h
GSM/GSML3RRMessages.cpp
GSM/GSML3RRMessages.h
GSM/GSMLogicalChannel.h
GSM/GSMTDMA.cpp
GSM/GSMTDMA.h
GSM/GSMTransfer.cpp
GSM/GSMTransfer.h
LICENSEBLOCK
TRXManager/TRXManager.cpp
Transceiver/Complex.h
tests/CommonLibs/BitVectorTest.cpp
tests/CommonLibs/InterthreadTest.cpp
tests/CommonLibs/SocketsTest.cpp
tests/CommonLibs/TimevalTest.cpp
tests/CommonLibs/VectorTest.cpp
Harvind S. Samra, hssamra@kestrelsp.com:
GSM/GSMConfig.h
GSM/GSMTransfer.h
LICENSEBLOCK
Transceiver/ComplexTest.cpp
Transceiver/Transceiver.cpp
Transceiver/Transceiver.h
Transceiver/USRPDevice.cpp
Transceiver/USRPDevice.h
Transceiver/USRPping.cpp
Transceiver/radioInterface.cpp
Transceiver/radioInterface.h
Transceiver/rcvLPF_651.h
Transceiver/runTransceiver.cpp
Transceiver/sendLPF_961.h
Transceiver/sigProcLib.cpp
Transceiver/sigProcLib.h
Transceiver/sigProcLibTest.cpp
Transceiver/sweepGenerator.cpp
Transceiver/testRadio.cpp
Raffi Sevlian, raffisev@gmail.com:
GSM/GSMCommon.h
GSM/GSMConfig.h
GSM/GSML1FEC.h
GSM/GSML3CCElements.cpp
GSM/GSML3CCElements.h
GSM/GSML3CCMessages.cpp
GSM/GSML3CCMessages.h
GSM/GSML3CommonElements.cpp
GSM/GSML3CommonElements.h
GSM/GSML3MMElements.cpp
GSM/GSML3MMElements.h
GSM/GSML3MMMessages.cpp
GSM/GSML3MMMessages.h
GSM/GSML3Message.cpp
GSM/GSML3Message.h
GSM/GSML3RRElements.cpp
GSM/GSML3RRElements.h
GSM/GSML3RRMessages.cpp
GSM/GSML3RRMessages.h
GSM/GSMLogicalChannel.h
GSM/GSMSAPMux.cpp
GSM/GSMSAPMux.h
GSM/GSMTransfer.h
LICENSEBLOCK
TRXManager/TRXManager.h
Alon Levy, alonlevy1@gmail.com
RRLPMessages.cpp
RRLPMessages.h
RRLPTest.cpp

View File

@@ -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 convenience but constituting a distinct document, not part of the
same file for convience 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.

View File

@@ -1,7 +1,6 @@
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -37,7 +36,7 @@ using namespace std;
/**
Apply a Galois polymonial to a binary sequence.
Apply a Galois polymonial to a binary seqeunce.
@param val The input sequence.
@param poly The polynomial.
@param order The order of the polynomial.

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2008, 2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -47,7 +45,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 auxiliary structures for the queue,
// and is implemented by allocating auxilliary 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*).
@@ -55,7 +53,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
protected:
Fifo mQ;
Fifo mQ;
mutable Mutex mLock;
mutable Signal mWriteSignal;
@@ -162,7 +160,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
protected:
Fifo mQ;
Fifo mQ;
mutable Mutex mLock;
mutable Signal mWriteSignal;
@@ -258,7 +256,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
// This recurs (and the InterthreadQueue fills up with data)
// until the read thread's accumulated temporary priority causes it to
// get a second pre-emptive activation over the writing thread,
// resulting in bursts of activity by the read thread.
// resulting in bursts of activity by the read thread.
{ ScopedLock lock(mLock);
mQ.put(val);
}
@@ -283,7 +281,7 @@ template <class T> class InterthreadQueueWithWait {
protected:
PointerFIFO mQ;
PointerFIFO mQ;
mutable Mutex mLock;
mutable Signal mWriteSignal;
mutable Signal mReadSignal;

View File

@@ -1,7 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.

View File

@@ -1,10 +1,7 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* 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 distribution.
* 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 the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.

View File

@@ -1,7 +1,6 @@
/*
* Copyright (C) 2018 sysmocom - s.f.m.c. GmbH
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -36,6 +35,8 @@
using namespace std;
Mutex gLogToLock;
std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
{
return os << ss.str();
@@ -44,13 +45,13 @@ std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
Log::~Log()
{
int old_state;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
int mlen = mStream.str().size();
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
const char *fmt = neednl ? "%s\n" : "%s";
/* 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);
ScopedLock lock(gLogToLock);
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
// so just use std::cout.
LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str());
pthread_setcancelstate(old_state, NULL);
}

View File

@@ -2,8 +2,6 @@
* Copyright 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -58,9 +56,6 @@ 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 << "] "

View File

@@ -30,11 +30,11 @@ noinst_LTLIBRARIES = libcommon.la
libcommon_la_SOURCES = \
BitVector.cpp \
LinkedLists.cpp \
Sockets.cpp \
Threads.cpp \
Timeval.cpp \
Logger.cpp \
Utils.cpp \
trx_rate_ctr.cpp \
trx_vty.c \
debug.c
libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
@@ -44,12 +44,12 @@ noinst_HEADERS = \
PRBS.h \
Interthread.h \
LinkedLists.h \
Sockets.h \
Threads.h \
Timeval.h \
Vector.h \
Logger.h \
Utils.h \
trx_rate_ctr.h \
trx_vty.h \
debug.h \
osmo_signal.h \

View File

@@ -1,8 +1,6 @@
/*
* Copyright (C) 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

287
CommonLibs/Sockets.cpp Normal file
View File

@@ -0,0 +1,287 @@
/*
* Copyright 2008, 2010 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
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/>.
*/
#include <config.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <sys/select.h>
#include "Threads.h"
#include "Sockets.h"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort)
{
assert(address);
assert(hostAndPort);
char *copy = strdup(hostAndPort);
char *colon = strchr(copy,':');
if (!colon) return false;
*colon = '\0';
char *host = copy;
unsigned port = strtol(colon+1,NULL,10);
bool retVal = resolveAddress(address,host,port);
free(copy);
return retVal;
}
bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port)
{
assert(address);
assert(host);
// FIXME -- Need to ignore leading/trailing spaces in hostname.
struct hostent *hp;
int h_errno_local;
#ifdef HAVE_GETHOSTBYNAME2_R
struct hostent hostData;
char tmpBuffer[2048];
// There are different flavors of gethostbyname_r(), but
// latest Linux use the following form:
if (gethostbyname2_r(host, AF_INET, &hostData, tmpBuffer, sizeof(tmpBuffer), &hp, &h_errno_local)!=0) {
CERR("WARNING -- gethostbyname2_r() failed for " << host << ", " << hstrerror(h_errno_local));
return false;
}
#else
static Mutex sGethostbynameMutex;
// gethostbyname() is NOT thread-safe, so we should use a mutex here.
// Ideally it should be a global mutex for all non thread-safe socket
// operations and it should protect access to variables such as
// global h_errno.
sGethostbynameMutex.lock();
hp = gethostbyname(host);
h_errno_local = h_errno;
sGethostbynameMutex.unlock();
#endif
if (hp==NULL) {
CERR("WARNING -- gethostbyname() failed for " << host << ", " << hstrerror(h_errno_local));
return false;
}
if (hp->h_addrtype != AF_INET) {
CERR("WARNING -- gethostbyname() resolved " << host << " to something other then AF_INET");
return false;
}
address->sin_family = hp->h_addrtype;
assert(sizeof(address->sin_addr) == hp->h_length);
memcpy(&(address->sin_addr), hp->h_addr_list[0], hp->h_length);
address->sin_port = htons(port);
return true;
}
DatagramSocket::DatagramSocket()
{
memset(mDestination, 0, sizeof(mDestination));
}
void DatagramSocket::nonblocking()
{
fcntl(mSocketFD,F_SETFL,O_NONBLOCK);
}
void DatagramSocket::blocking()
{
fcntl(mSocketFD,F_SETFL,0);
}
void DatagramSocket::close()
{
::close(mSocketFD);
}
DatagramSocket::~DatagramSocket()
{
close();
}
int DatagramSocket::write( const char * message, size_t length )
{
assert(length<=MAX_UDP_LENGTH);
int retVal = sendto(mSocketFD, message, length, 0,
(struct sockaddr *)mDestination, addressSize());
if (retVal == -1 ) perror("DatagramSocket::write() failed");
return retVal;
}
int DatagramSocket::writeBack( const char * message, size_t length )
{
assert(length<=MAX_UDP_LENGTH);
int retVal = sendto(mSocketFD, message, length, 0,
(struct sockaddr *)mSource, addressSize());
if (retVal == -1 ) perror("DatagramSocket::write() failed");
return retVal;
}
int DatagramSocket::write( const char * message)
{
size_t length=strlen(message)+1;
return write(message,length);
}
int DatagramSocket::writeBack( const char * message)
{
size_t length=strlen(message)+1;
return writeBack(message,length);
}
int DatagramSocket::send(const struct sockaddr* dest, const char * message, size_t length )
{
assert(length<=MAX_UDP_LENGTH);
int retVal = sendto(mSocketFD, message, length, 0, dest, addressSize());
if (retVal == -1 ) perror("DatagramSocket::send() failed");
return retVal;
}
int DatagramSocket::send(const struct sockaddr* dest, const char * message)
{
size_t length=strlen(message)+1;
return send(dest,message,length);
}
int DatagramSocket::read(char* buffer, size_t length)
{
socklen_t addr_len = sizeof(mSource);
int rd_length = recvfrom(mSocketFD, (void *) buffer, length, 0,
(struct sockaddr*) &mSource, &addr_len);
if ((rd_length==-1) && (errno!=EAGAIN)) {
perror("DatagramSocket::read() failed");
throw SocketError();
}
return rd_length;
}
int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(mSocketFD,&fds);
struct timeval tv;
tv.tv_sec = timeout/1000;
tv.tv_usec = (timeout%1000)*1000;
int sel = select(mSocketFD+1,&fds,NULL,NULL,&tv);
if (sel<0) {
perror("DatagramSocket::read() select() failed");
throw SocketError();
}
if (sel==0) return -1;
if (FD_ISSET(mSocketFD,&fds)) return read(buffer, length);
return -1;
}
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort)
:DatagramSocket()
{
open(wSrcPort, wSrcIP);
}
UDPSocket::UDPSocket(const char *wSrcIP, unsigned short wSrcPort,
const char *wDestIP, unsigned short wDestPort)
:DatagramSocket()
{
open(wSrcPort, wSrcIP);
destination(wDestPort, wDestIP);
}
void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
{
resolveAddress((sockaddr_in*)mDestination, wDestIP, wDestPort );
}
void UDPSocket::open(unsigned short localPort, const char *wlocalIP)
{
// create
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
if (mSocketFD<0) {
perror("socket() failed");
throw SocketError();
}
// pat added: This lets the socket be reused immediately, which is needed if OpenBTS crashes.
int on = 1;
setsockopt(mSocketFD, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
// bind
struct sockaddr_in address;
size_t length = sizeof(address);
bzero(&address,length);
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(wlocalIP);
address.sin_port = htons(localPort);
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
perror("bind() failed");
throw SocketError();
}
}
unsigned short UDPSocket::port() const
{
struct sockaddr_in name;
socklen_t nameSize = sizeof(name);
int retVal = getsockname(mSocketFD, (struct sockaddr*)&name, &nameSize);
if (retVal==-1) throw SocketError();
return ntohs(name.sin_port);
}
// vim:ts=4:sw=4

173
CommonLibs/Sockets.h Normal file
View File

@@ -0,0 +1,173 @@
/*
* Copyright 2008, 2010 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
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/>.
*/
#ifndef SOCKETS_H
#define SOCKETS_H
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <errno.h>
#include <list>
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#define MAX_UDP_LENGTH 1500
/** A function to resolve IP host names. */
bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port);
/** Resolve an address of the form "<host>:<port>". */
bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort);
/** An exception to throw when a critical socket operation fails. */
class SocketError {};
#define SOCKET_ERROR {throw SocketError(); }
/** Abstract class for connectionless sockets. */
class DatagramSocket {
protected:
int mSocketFD; ///< underlying file descriptor
char mDestination[256]; ///< address to which packets are sent
char mSource[256]; ///< return address of most recent received packet
public:
/** An almost-does-nothing constructor. */
DatagramSocket();
virtual ~DatagramSocket();
/** Return the address structure size for this socket type. */
virtual size_t addressSize() const = 0;
/**
Send a binary packet.
@param buffer The data bytes to send to mDestination.
@param length Number of bytes to send, or strlen(buffer) if defaulted to -1.
@return number of bytes written, or -1 on error.
*/
int write( const char * buffer, size_t length);
/**
Send a C-style string packet.
@param buffer The data bytes to send to mDestination.
@return number of bytes written, or -1 on error.
*/
int write( const char * buffer);
/**
Send a binary packet.
@param buffer The data bytes to send to mSource.
@param length Number of bytes to send, or strlen(buffer) if defaulted to -1.
@return number of bytes written, or -1 on error.
*/
int writeBack(const char * buffer, size_t length);
/**
Send a C-style string packet.
@param buffer The data bytes to send to mSource.
@return number of bytes written, or -1 on error.
*/
int writeBack(const char * buffer);
/**
Receive a packet.
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
@return The number of bytes received or -1 on non-blocking pass.
*/
int read(char* buffer, size_t length);
/**
Receive a packet with a timeout.
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
@param maximum wait time in milliseconds
@return The number of bytes received or -1 on timeout.
*/
int read(char* buffer, size_t length, unsigned timeout);
/** Send a packet to a given destination, other than the default. */
int send(const struct sockaddr *dest, const char * buffer, size_t length);
/** Send a C-style string to a given destination, other than the default. */
int send(const struct sockaddr *dest, const char * buffer);
/** Make the socket non-blocking. */
void nonblocking();
/** Make the socket blocking (the default). */
void blocking();
/** Close the socket. */
void close();
};
/** UDP/IP User Datagram Socket */
class UDPSocket : public DatagramSocket {
public:
/** Open a USP socket with an OS-assigned port and no default destination. */
UDPSocket(const char *localIP, unsigned short localPort);
/** Given a full specification, open the socket and set the dest address. */
UDPSocket(const char *localIP, unsigned short localPort,
const char *remoteIP, unsigned short remotePort);
/** Set the destination port. */
void destination( unsigned short wDestPort, const char * wDestIP );
/** Return the actual port number in use. */
unsigned short port() const;
/** Open and bind the UDP socket to a local port. */
void open(unsigned short localPort=0, const char *wlocalIP="127.0.0.1");
/** Give the return address of the most recently received packet. */
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
size_t addressSize() const { return sizeof(struct sockaddr_in); }
};
#endif
// vim:ts=4:sw=4

View File

@@ -1,7 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -32,13 +31,15 @@
#include "Timeval.h"
#include "Logger.h"
#include <osmocom/core/thread.h>
#ifndef gettid
#include <sys/syscall.h>
#define gettid() syscall(SYS_gettid)
#endif
using namespace std;
#ifndef HAVE_ATOMIC_OPS
pthread_mutex_t atomic_ops_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
Mutex gStreamLock; ///< Global lock to control access to cout and cerr.
@@ -110,7 +111,7 @@ void Signal::wait(Mutex& wMutex, unsigned timeout) const
void set_selfthread_name(const char *name)
{
pthread_t selfid = pthread_self();
pid_t tid = osmo_gettid();
pid_t tid = gettid();
if (pthread_setname_np(selfid, name) == 0) {
LOG(INFO) << "Thread "<< selfid << " (task " << tid << ") set name: " << name;
} else {
@@ -134,10 +135,8 @@ void Thread::start(void *(*task)(void*), void *arg)
// (pat) Moved initialization to constructor to avoid crash in destructor.
//res = pthread_attr_init(&mAttrib);
//assert(!res);
if (mStackSize != 0) {
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
assert(!res);
}
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
assert(!res);
res = pthread_create(&mThread, &mAttrib, task, arg);
assert(!res);
}

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2008, 2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -28,8 +26,6 @@
#ifndef THREADS_H
#define THREADS_H
#include "config.h"
#include <pthread.h>
#include <iostream>
#include <assert.h>
@@ -162,7 +158,7 @@ class Thread {
public:
/** Create a thread in a non-running state. */
Thread(size_t wStackSize = 0):mThread((pthread_t)0) {
Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) {
pthread_attr_init(&mAttrib); // (pat) moved this here.
mStackSize=wStackSize;
}
@@ -186,34 +182,12 @@ class Thread {
}
}
/** Send cancellation to thread */
/** Send cancelation to thread */
void cancel() { pthread_cancel(mThread); }
};
#ifdef HAVE_ATOMIC_OPS
#define osmo_trx_sync_fetch_and_and(ptr, value) __sync_fetch_and_and((ptr), (value))
#define osmo_trx_sync_or_and_fetch(ptr, value) __sync_or_and_fetch((ptr), (value))
#else
extern pthread_mutex_t atomic_ops_mutex;
static inline int osmo_trx_sync_fetch_and_and(int *ptr, int value)
{
pthread_mutex_lock(&atomic_ops_mutex);
int tmp = *ptr;
*ptr &= value;
pthread_mutex_unlock(&atomic_ops_mutex);
return tmp;
}
static inline int osmo_trx_sync_or_and_fetch(int *ptr, int value)
{
int tmp;
pthread_mutex_lock(&atomic_ops_mutex);
*ptr |= value;
tmp = *ptr;
pthread_mutex_unlock(&atomic_ops_mutex);
return tmp;
}
#endif
#endif
// vim: ts=4 sw=4

View File

@@ -1,7 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -82,15 +81,14 @@ long Timeval::delta(const Timeval& other) const
int32_t deltaNs = other.nsec() - nsec();
return 1000*deltaS + deltaNs/1000000;
}
ostream& operator<<(ostream& os, const Timeval& tv)
{
ios_base::fmtflags flags_backup = os.setf( ios::fixed, ios::floatfield );
os.setf( ios::fixed, ios::floatfield );
os << tv.seconds();
os.flags( flags_backup );
return os;
}

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -84,7 +82,7 @@ class Timeval {
uint32_t usec() const { return mTimespec.tv_nsec / 1000; }
uint32_t nsec() const { return mTimespec.tv_nsec; }
/** Return difference from other (other-self), in ms. */
/** Return differnce from other (other-self), in ms. */
long delta(const Timeval& other) const;
/** Elapsed time in ms. */

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,8 +2,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -36,12 +34,7 @@
#include <assert.h>
#include <stdlib.h>
#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...
// We cant use Logger.h in this file...
extern int gVectorDebug;
#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
@@ -86,8 +79,8 @@ template <class T> class Vector {
/** Return the size of the Vector. */
size_t size() const
{
assert_no_opt(mStart>=mData);
assert_no_opt(mEnd>=mStart);
assert(mStart>=mData);
assert(mEnd>=mStart);
return mEnd - mStart;
}
@@ -117,7 +110,7 @@ template <class T> class Vector {
/** Reduce addressable size of the Vector, keeping content. */
void shrink(size_t newSize)
{
assert_no_opt(newSize <= mEnd - mStart);
assert(newSize <= mEnd - mStart);
mEnd = mStart + newSize;
}
@@ -204,7 +197,7 @@ template <class T> class Vector {
{
T* wStart = mStart + start;
T* wEnd = wStart + span;
assert_no_opt(wEnd<=mEnd);
assert(wEnd<=mEnd);
return Vector<T>(NULL,wStart,wEnd);
}
@@ -213,7 +206,7 @@ template <class T> class Vector {
{
T* wStart = mStart + start;
T* wEnd = wStart + span;
assert_no_opt(wEnd<=mEnd);
assert(wEnd<=mEnd);
return Vector<T>(NULL,wStart,wEnd);
}
@@ -233,11 +226,11 @@ template <class T> class Vector {
unsigned int i;
T* dst = other.mStart + start;
T* src = mStart;
assert_no_opt(dst+span<=other.mEnd);
assert_no_opt(mStart+span<=mEnd);
assert(dst+span<=other.mEnd);
assert(mStart+span<=mEnd);
for (i = 0; i < span; i++, src++, dst++)
*dst = *src;
/*TODO if not non-trivially copiable type class, optimize:
/*TODO if not non-trivially copyable type class, optimize:
memcpy(dst,mStart,span*sizeof(T)); */
}
@@ -255,8 +248,8 @@ template <class T> class Vector {
void segmentCopyTo(Vector<T>& other, size_t start, size_t span) const
{
const T* base = mStart + start;
assert_no_opt(base+span<=mEnd);
assert_no_opt(other.mStart+span<=other.mEnd);
assert(base+span<=mEnd);
assert(other.mStart+span<=other.mEnd);
memcpy(other.mStart,base,span*sizeof(T));
}
@@ -270,8 +263,8 @@ template <class T> class Vector {
{
const T* baseFrom = mStart + from;
T* baseTo = mStart + to;
assert_no_opt(baseFrom+span<=mEnd);
assert_no_opt(baseTo+span<=mEnd);
assert(baseFrom+span<=mEnd);
assert(baseTo+span<=mEnd);
memmove(baseTo,baseFrom,span*sizeof(T));
}
@@ -285,7 +278,7 @@ template <class T> class Vector {
{
T* dp=mStart+start;
T* end=dp+length;
assert_no_opt(end<=mEnd);
assert(end<=mEnd);
while (dp<end) *dp++=val;
}
@@ -297,13 +290,13 @@ template <class T> class Vector {
T& operator[](size_t index)
{
assert_no_opt(mStart+index<mEnd);
assert(mStart+index<mEnd);
return mStart[index];
}
const T& operator[](size_t index) const
{
assert_no_opt(mStart+index<mEnd);
assert(mStart+index<mEnd);
return mStart[index];
}

View File

@@ -5,8 +5,6 @@
* osmo-trx (CXX, dir Transceiver52)
*/
#include <stdbool.h>
enum FillerType {
FILLER_DUMMY,
FILLER_ZERO,
@@ -20,40 +18,3 @@ 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];
};

View File

@@ -1,26 +1,3 @@
/*
* Copyright (C) 2018-2019 sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* 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.
*/
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include "debug.h"
@@ -33,45 +10,21 @@ 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_NOTICE,
.enabled = 1, .loglevel = LOGL_INFO,
},
[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",
[DLMS] = {
.name = "DLMS",
.description = "Logging from within LimeSuite itself",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},

View File

@@ -1,28 +1,11 @@
#pragma once
#include <stdbool.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,
DDEVDRV,
DCTR,
DLMS,
};
#define CLOGC(category, level, fmt, args...) do { \
LOGP(category, level, "[tid=%ld] " fmt, (long int) osmo_gettid(), ##args); \
} while(0)
#define CLOGCHAN(chan, category, level, fmt, args...) do { \
LOGP(category, level, "[tid=%ld][chan=%zu] " fmt, (long int) osmo_gettid(), chan, ##args); \
} while(0)

View File

@@ -5,8 +5,6 @@
*
* All Rights Reserved
*
* 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
@@ -28,44 +26,10 @@
/* Signalling subsystems */
enum signal_subsystems {
SS_MAIN,
SS_DEVICE,
SS_TRANSC,
};
/* SS_MAIN signals */
enum SS_MAIN {
S_MAIN_STOP_REQUIRED, /* TRX fatal error, it should be stopped */
};
/* SS_DEVICE signals */
enum SS_DEVICE {
/* Device internal counters changed. Counters are provided as cb data
(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> */
struct device_counters {
size_t chan;
unsigned int rx_overruns;
unsigned int tx_underruns;
unsigned int rx_dropped_events;
unsigned int rx_dropped_samples;
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;
/* SS_TRANSC signals */
enum SS_TRANSC {
S_TRANSC_STOP_REQUIRED, /* Transceiver fatal error, it should be stopped */
};

View File

@@ -1,407 +0,0 @@
/*
* Copyright (C) 2019 sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* 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.
*/
/*
* rate_ctr API uses several osmocom select loop features, and as a result,
* calls to it must be done through the main thread (the one running the osmocom
* loop in osmo-trx).
* Since read/write from/to SDR is done in separate threads (even read and write
* each use a different thread), we must use some sort of message passing system
* between main thread feeding rate_ctr structures and the Rx/Tx threads
* generating the events.
* The idea is that upon read/write issues, lower layers (SDR APIs) provide us with
* underrun/overrun/droppedPackets information, and in that case we pass that up
* the stack through signal <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> with signal_cb
* being a pointer to a "struct device_counters" structure, which contains
* device (implementation agnostic) statful counters for different kind of
* statistics.
* 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 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
* in case the main thread decides to cancel other threads due to a shutdown
* operation (fi SIGKILL received)
*/
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <arpa/inet.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/select.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/timer.h>
#include "osmo_signal.h"
#include "trx_vty.h"
#include "trx_rate_ctr.h"
}
#include "Threads.h"
#include "Logger.h"
/* 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* dev_ctrs_pending;
static struct trx_counters* trx_ctrs_pending;
static size_t chan_len;
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 unsigned int threshold_timer_sched_secs;
static bool threshold_initied;
const struct value_string rate_ctr_intv[] = {
{ RATE_CTR_INTV_SEC, "per-second" },
{ RATE_CTR_INTV_MIN, "per-minute" },
{ RATE_CTR_INTV_HOUR, "per-hour" },
{ RATE_CTR_INTV_DAY, "per-day" },
{ 0, NULL }
};
const struct value_string trx_chan_ctr_names[] = {
{ 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_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 = {
.group_name_prefix = "trx:chan",
.group_description = "osmo-trx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(trx_chan_ctr_desc),
.ctr_desc = trx_chan_ctr_desc,
};
static int dev_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 Device counters";
dev_rate_ctr_mutex.lock();
for (chan = 0; chan < chan_len; chan++) {
if (dev_ctrs_pending[chan].chan == PENDING_CHAN_NONE)
continue;
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 */
dev_ctrs_pending[chan].chan = PENDING_CHAN_NONE;
}
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;
}
/* Callback function to be called every time we receive a signal from DEVICE */
static int device_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
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};
/* no automatic re-trigger */
struct timespec intv_sched = {.tv_sec = 0, .tv_nsec = 0};
switch (signal) {
case S_DEVICE_COUNTER_CHANGE:
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);
}
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;
}
return 0;
}
/************************************
* ctr_threshold APIs
************************************/
static const char* ctr_threshold_2_vty_str(struct ctr_threshold *ctr)
{
static char buf[256];
int rc = 0;
rc += snprintf(buf, sizeof(buf), "ctr-error-threshold %s", get_value_string(trx_chan_ctr_names, ctr->ctr_id));
rc += snprintf(buf + rc, sizeof(buf) - rc, " %d %s", ctr->val, get_value_string(rate_ctr_intv, ctr->intv));
return buf;
}
static void threshold_timer_cb(void *data)
{
struct ctr_threshold *ctr_thr;
struct rate_ctr *rate_ctr;
size_t chan;
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, 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, 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;
}
}
}
osmo_timer_schedule(&threshold_timer, threshold_timer_sched_secs, 0);
}
static size_t ctr_threshold_2_seconds(struct ctr_threshold *ctr)
{
size_t mult = 0;
switch (ctr->intv) {
case RATE_CTR_INTV_SEC:
mult = 1;
break;
case RATE_CTR_INTV_MIN:
mult = 60;
break;
case RATE_CTR_INTV_HOUR:
mult = 60*60;
break;
case RATE_CTR_INTV_DAY:
mult = 60*60*24;
break;
default:
OSMO_ASSERT(false);
}
return mult;
}
static void threshold_timer_update_intv() {
struct ctr_threshold *ctr, *min_ctr;
size_t secs, min_secs;
/* Avoid scheduling timer until itself and other structures are prepared
by trx_rate_ctr_init */
if (!threshold_initied)
return;
if (llist_empty(&threshold_list)) {
if (osmo_timer_pending(&threshold_timer))
osmo_timer_del(&threshold_timer);
return;
}
min_ctr = llist_first_entry(&threshold_list, struct ctr_threshold, list);
min_secs = ctr_threshold_2_seconds(min_ctr);
llist_for_each_entry(ctr, &threshold_list, list) {
secs = ctr_threshold_2_seconds(ctr);
if (min_secs > secs)
min_secs = secs;
}
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);
}
/* Init rate_ctr subsystem. Expected to be called during process start by main thread before VTY is ready */
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;
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++) {
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, DCTR, ERROR) << "Failed to allocate rate ctr";
exit(1);
}
}
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);
/* Now set up threshold checks */
threshold_initied = true;
osmo_timer_setup(&threshold_timer, threshold_timer_cb, NULL);
threshold_timer_update_intv();
}
void trx_rate_ctr_threshold_add(struct ctr_threshold *ctr)
{
struct ctr_threshold *new_ctr;
new_ctr = talloc_zero(trx_rate_ctr_ctx, struct ctr_threshold);
*new_ctr = *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();
}
int trx_rate_ctr_threshold_del(struct ctr_threshold *del_ctr)
{
struct ctr_threshold *ctr;
llist_for_each_entry(ctr, &threshold_list, list) {
if (ctr->intv != del_ctr->intv ||
ctr->ctr_id != del_ctr->ctr_id ||
ctr->val != del_ctr->val)
continue;
LOGC(DCTR, NOTICE) << "Deleting threshold check: " << ctr_threshold_2_vty_str(del_ctr);
llist_del(&ctr->list);
talloc_free(ctr);
threshold_timer_update_intv();
return 0;
}
return -1;
}
void trx_rate_ctr_threshold_write_config(struct vty *vty, char *indent_prefix)
{
struct ctr_threshold *ctr;
llist_for_each_entry(ctr, &threshold_list, list) {
vty_out(vty, "%s%s%s", indent_prefix, ctr_threshold_2_vty_str(ctr), VTY_NEWLINE);
}
}

View File

@@ -1,38 +0,0 @@
#pragma once
#include <osmocom/core/rate_ctr.h>
#include <osmocom/vty/command.h>
enum TrxCtr {
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 {
/*! Linked list of all counter groups in the system */
struct llist_head list;
enum rate_ctr_intv intv;
enum TrxCtr ctr_id;
uint32_t val;
};
extern const struct value_string rate_ctr_intv[];
extern const struct value_string trx_chan_ctr_names[];
struct trx_ctx;
void trx_rate_ctr_init(void *ctx, struct trx_ctx* trx_ctx);
void trx_rate_ctr_threshold_add(struct ctr_threshold *ctr);
int trx_rate_ctr_threshold_del(struct ctr_threshold *del_ctr);
void trx_rate_ctr_threshold_write_config(struct vty *vty, char *indent_prefix);

View File

@@ -1,24 +1,20 @@
/*
* Copyright (C) 2018-2019 sysmocom - s.f.m.c. GmbH
* Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* 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
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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.
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* You should have received a copy of the GNU 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.
*
*/
#include <string.h>
@@ -32,51 +28,30 @@
#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>
#include "trx_rate_ctr.h"
#include "trx_vty.h"
#include "../config.h"
static struct trx_ctx* g_trx_ctx;
const struct value_string clock_ref_names[] = {
static const struct value_string clock_ref_names[] = {
{ REF_INTERNAL, "internal" },
{ REF_EXTERNAL, "external" },
{ REF_GPS, "gpsdo" },
{ 0, NULL }
};
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" },
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" },
{ 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
@@ -121,7 +96,7 @@ DEFUN(cfg_trx, cfg_trx_cmd,
}
DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
"bind-ip " VTY_IPV4_CMD,
"bind-ip A.B.C.D",
"Set the IP address for the local bind\n"
"IPv4 Address\n")
{
@@ -133,7 +108,7 @@ DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
}
DEFUN(cfg_remote_ip, cfg_remote_ip_cmd,
"remote-ip " VTY_IPV4_CMD,
"remote-ip A.B.C.D",
"Set the IP address for the remote BTS\n"
"IPv4 Address\n")
{
@@ -171,9 +146,7 @@ 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"
"1 Sample-per-Symbol\n"
"4 Samples-per-Symbol\n")
"Tx Samples-per-Symbol\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -185,9 +158,7 @@ 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"
"1 Sample-per-Symbol\n"
"4 Samples-per-Symbol\n")
"Rx Samples-per-Symbol\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -196,10 +167,57 @@ 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 reference (default)\n"
"Enable internal referece (default)\n"
"Enable external 10 MHz reference\n"
"Enable GPSDO reference\n")
{
@@ -212,8 +230,7 @@ DEFUN(cfg_clock_ref, cfg_clock_ref_cmd,
DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd,
"multi-arfcn (disable|enable)",
"Multi-ARFCN transceiver mode (default=disable)\n"
"Enable multi-ARFCN mode\n" "Disable multi-ARFCN mode\n")
"Enable multi-ARFCN transceiver (default=disable)\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -244,38 +261,21 @@ 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 [relative]",
"rssi-offset FLOAT",
"Set the RSSI to dBm offset in dB (default=0)\n"
"RSSI to dBm offset in dB\n"
"Add to the default rssi-offset value instead of completely replacing it\n")
"RSSI to dBm offset in dB\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 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")
"Swap channels (default=disable)\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -292,9 +292,7 @@ DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
DEFUN(cfg_egprs, cfg_egprs_cmd,
"egprs (disable|enable)",
"EGPRS (8-PSK demodulation) support (default=disable)\n"
"Disable EGPRS (8-PSK demodulation) support\n"
"Enable EGPRS (8-PSK demodulation) support\n")
"Enable EDGE receiver (default=disable)\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -302,6 +300,7 @@ 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;
}
@@ -311,9 +310,7 @@ DEFUN(cfg_egprs, cfg_egprs_cmd,
DEFUN(cfg_ext_rach, cfg_ext_rach_cmd,
"ext-rach (disable|enable)",
"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")
"Enable extended (11-bit) RACH (default=disable)\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -326,7 +323,7 @@ DEFUN(cfg_ext_rach, cfg_ext_rach_cmd,
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_rt_prio, cfg_rt_prio_cmd,
DEFUN(cfg_rt_prio, cfg_rt_prio_cmd,
"rt-prio <1-32>",
"Set the SCHED_RR real-time priority\n"
"Real time priority\n")
@@ -334,176 +331,18 @@ DEFUN_DEPRECATED(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;
}
DEFUN(cfg_stack_size, cfg_stack_size_cmd,
"stack-size <0-2147483647>",
"Set the stack size per thread in BYTE, 0 = OS default\n"
"Stack size per thread in BYTE\n")
DEFUN(cfg_filler, cfg_filler_cmd,
"filler dummy",
"Enable C0 filler table\n"
"Dummy method\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.stack_size = atoi(argv[0]);
return CMD_SUCCESS;
}
#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.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;
}
static int vty_ctr_name_2_id(const char* str) {
size_t i;
for (i = 0; trx_chan_ctr_names[i].str; i++) {
if (strstr(trx_chan_ctr_names[i].str, str)) {
return i;
}
}
return -1;
}
static int vty_intv_name_2_id(const char* str) {
size_t i;
for (i = 0; rate_ctr_intv[i].str; i++) {
if (strcmp(rate_ctr_intv[i].str, str) == 0) {
return i;
}
}
return -1;
}
#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) \
THRESHOLD_STR_VAL(tx_underruns) \
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_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 \
INTV_STR_VAL(per-second) \
INTV_STR_VAL(per-minute) \
INTV_STR_VAL(per-hour) \
INTV_STR_VAL(per-day)
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;
struct trx_ctx *trx = trx_from_vty(vty);
rc = vty_ctr_name_2_id(argv[0]);
if (rc < 0) {
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ctr.ctr_id = (enum TrxCtr)rc;
ctr.val = atoi(argv[1]);
rc = vty_intv_name_2_id(argv[2]);
if (rc < 0) {
vty_out(vty, "No valid time frame found for ctr-error-threshold %s %d %s%s",
argv[0], ctr.val, argv[2], VTY_NEWLINE);
return CMD_WARNING;
}
ctr.intv = (enum rate_ctr_intv) rc;
trx_rate_ctr_threshold_add(&ctr);
return CMD_SUCCESS;
}
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;
struct trx_ctx *trx = trx_from_vty(vty);
rc = vty_ctr_name_2_id(argv[0]);
if (rc < 0) {
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ctr.ctr_id = (enum TrxCtr)rc;
ctr.val = atoi(argv[1]);
rc = vty_intv_name_2_id(argv[2]);
if (rc < 0) {
vty_out(vty, "No valid time frame found for ctr-error-threshold %s %d %s%s",
argv[0], ctr.val, argv[2], VTY_NEWLINE);
return CMD_WARNING;
}
ctr.intv = (enum rate_ctr_intv) rc;
if (trx_rate_ctr_threshold_del(&ctr) < 0) {
vty_out(vty, "no ctr-error-threshold: Entry to delete not found%s", VTY_NEWLINE);
return CMD_WARNING;
}
trx->cfg.filler = FILLER_DUMMY;
return CMD_SUCCESS;
}
@@ -548,12 +387,6 @@ 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;
@@ -566,12 +399,6 @@ 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;
@@ -601,30 +428,22 @@ 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.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);
if (trx->cfg.rssi_offset != 0)
vty_out(vty, " rssi-offset %f%s", trx->cfg.rssi_offset, 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, " ");
for (i = 0; i < trx->cfg.num_chans; i++) {
chan = &trx->cfg.chans[i];
@@ -649,9 +468,11 @@ 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, " 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, " 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, " 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);
@@ -661,7 +482,6 @@ static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx)
vty_out(vty, " Extended RACH support: %s%s", trx->cfg.ext_rach ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Real Time Priority: %u (%s)%s", trx->cfg.sched_rr,
trx->cfg.sched_rr ? "Enabled" : "Disabled", VTY_NEWLINE);
vty_out(vty, " Stack size per Thread in BYTE (0 = OS default): %u%s", trx->cfg.stack_size, VTY_NEWLINE);
vty_out(vty, " Channels: %u%s", trx->cfg.num_chans, VTY_NEWLINE);
for (i = 0; i < trx->cfg.num_chans; i++) {
chan = &trx->cfg.chans[i];
@@ -720,7 +540,6 @@ 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"
@@ -745,19 +564,12 @@ 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);
@@ -770,28 +582,22 @@ 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_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);
install_element(TRX_NODE, &cfg_filler_cmd);
install_element(TRX_NODE, &cfg_chan_cmd);
install_node(&chan_node, dummy_config_write);
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;
}

View File

@@ -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,8 +33,39 @@ extern const struct value_string filler_names[];
#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 trx_cfg cfg;
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 num_chans;
struct trx_chan chans[TRX_CHAN_MAX];
} cfg;
};
int trx_vty_init(struct trx_ctx* trx);

View File

@@ -2,8 +2,6 @@
* Copyright 2008 Free Software Foundation, Inc.
* Copyright 2011 Range Networks, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*

View File

@@ -2,8 +2,6 @@
/*
* Copyright 2008-2011 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -168,7 +166,7 @@ class Time {
unsigned newTN = (mTN + other.mTN) % 8;
uint64_t newFN = (mFN+other.mFN + (mTN + other.mTN)/8) % gHyperframe;
return Time(newFN,newTN);
}
}
int operator-(const Time& other) const
{

19
INSTALLATION Normal file
View File

@@ -0,0 +1,19 @@
Installation Requirements
osmo-trx compiles to a simple Unix binary and does not require special
installation.
One some systems (Ubuntu), you will need to define LIBS = -lpthread prior to
running configure.
To run osmo-trx, the following should be installed:
libuhd (https://gnuradio.org).
This is part of the GNURadio installation.
For information on specific executables, see tests/README.tests and
apps/README.apps.
See https://osmocom.org/projects/osmotrx/wiki/OsmoTRX for more
information.

View File

@@ -28,22 +28,19 @@ AM_CXXFLAGS = -Wall -pthread
# Order must be preserved
SUBDIRS = \
doc \
CommonLibs \
GSM \
Transceiver52M \
contrib \
tests \
utils \
doc \
$(NULL)
tests
EXTRA_DIST = \
autogen.sh \
INSTALLATION \
LEGAL \
COPYING \
README.md \
contrib/osmo-trx.spec.in \
debian \
$(NULL)
README
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)

0
NEWS Normal file
View File

116
README Normal file
View File

@@ -0,0 +1,116 @@
This is the interface to the transcevier.
Each TRX Manager UDP socket interface represents a single ARFCN.
Each of these per-ARFCN interfaces is a pair of UDP sockets, one for control and one for data.
Give a base port B (5700), the master clock interface is at port P=B.
The TRX-side control interface for C(N) is on port P=B+2N+1 and the data interface is on an odd numbered port P=B+2N+2.
The corresponding core-side interface for every socket is at P+100.
For any given build, the number of ARFCN interfaces can be fixed.
Indications on the Master Clock Interface
The master clock interface is output only (from the radio).
Messages are "indications".
CLOCK gives the current value of the transceiver clock to be used by the core.
This message is sent whenever a trasmission packet arrives that is too late or too early. The clock value is NOT the current transceiver time. It is a time setting the the core should use to give better packet arrival times.
IND CLOCK <totalFrames>
Commands on the Per-ARFCN Control Interface
The per-ARFCN control interface uses a command-reponse protocol.
Commands are NULL-terminated ASCII strings, one per UDP socket.
Each command has a corresponding response.
Every command is of the form:
CMD <cmdtype> [params]
The <cmdtype> is the actual command.
Parameters are optional depending on the commands type.
Every response is of the form:
RSP <cmdtype> <status> [result]
The <status> is 0 for success and a non-zero error code for failure.
Successful responses may include results, depending on the command type.
Power Control
POWEROFF shuts off transmitter power and stops the demodulator.
CMD POWEROFF
RSP POWEROFF <status>
POWERON starts the transmitter and starts the demodulator. Initial power level is very low.
This command fails if the transmitter and receiver are not yet tuned.
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
If the transceiver is already on, it response with success to this command.
CMD POWERON
RSP POWERON <status>
SETPOWER sets output power in dB wrt full scale.
This command fails if the transmitter and receiver are not running.
CMD SETPOWER <dB>
RSP SETPOWER <status> <dB>
ADJPOWER adjusts power by the given dB step. Response returns resulting power level wrt full scale.
This command fails if the transmitter and receiver are not running.
CMD ADJPOWER <dBStep>
RSP ADJPOWER <status> <dBLevel>
Tuning Control
RXTUNE tunes the receiver to a given frequency in kHz.
This command fails if the receiver is already running.
(To re-tune you stop the radio, re-tune, and restart.)
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
CMD RXTUNE <kHz>
RSP RXTUNE <status> <kHz>
TXTUNE tunes the transmitter to a given frequency in kHz.
This command fails if the transmitter is already running.
(To re-tune you stop the radio, re-tune, and restart.)
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
CMD TXTUNE <kHz>
RSP TXTUNE <status> <kHz>
Timeslot Control
SETSLOT sets the format of the uplink timeslots in the ARFCN.
The <timeslot> indicates the timeslot of interest.
The <chantype> indicates the type of channel that occupies the timeslot.
A chantype of zero indicates the timeslot is off.
CMD SETSLOT <timeslot> <chantype>
RSP SETSLOT <status> <timeslot> <chantype>
Messages on the per-ARFCN Data Interface
Messages on the data interface carry one radio burst per UDP message.
Received Data Burst
1 byte timeslot index
4 bytes GSM frame number, big endian
1 byte RSSI in -dBm
2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, big endian
148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1"
Transmit Data Burst
1 byte timeslot index
4 bytes GSM frame number, big endian
1 byte transmit level wrt ARFCN max, -dB (attenuation)
148 bytes output symbol values, 0 & 1

View File

@@ -1,64 +0,0 @@
About OsmoTRX
=============
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"
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, 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
--------
The official homepage of the project is
<https://osmocom.org/projects/osmotrx/wiki/OsmoTRX>
GIT Repository
--------------
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 <https://git.osmocom.org/osmo-trx/>
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
<https://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>.
Mailing List
------------
Discussions related to OsmoTRX are happening on the openbsc@lists.osmocom.org
mailing list, please see <https://lists.osmocom.org/mailman/listinfo/openbsc>
for subscription options and the list archive.
Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
Contributing
------------
Our coding standards are described at
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
We us a gerrit based patch submission/review process for managing contributions.
Please see <https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit>
for more details
The current patch queue for OsmoTRX can be seen at
<https://gerrit.osmocom.org/q/project:osmo-trx+status:open>

View File

@@ -1,11 +1,9 @@
/*
* Polyphase channelizer
*
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* 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
@@ -65,7 +63,7 @@ float *Channelizer::outputBuffer(size_t chan) const
return hInputs[chan];
}
/*
/*
* Implementation based on material found in:
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
@@ -80,8 +78,8 @@ bool Channelizer::rotate(const float *in, size_t len)
deinterleave(in, len, hInputs, blockLen, m);
/*
* Convolve through filterbank while applying and saving sample history
/*
* Convolve through filterbank while applying and saving sample history
*/
for (size_t i = 0; i < m; i++) {
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
@@ -98,7 +96,7 @@ bool Channelizer::rotate(const float *in, size_t len)
return true;
}
/* Setup channelizer parameters */
/* Setup channelizer paramaters */
Channelizer::Channelizer(size_t m, size_t blockLen, size_t hLen)
: ChannelizerBase(m, blockLen, hLen)
{

View File

@@ -1,10 +1,8 @@
/*
* Polyphase channelizer
*
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* SPDX-License-Identifier: AGPL-3.0+
* Copyright (C) 2015 Ettus Research LLC
*
* 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
@@ -57,10 +55,10 @@ static void reverse(float *buf, size_t len)
}
}
/*
/*
* Create polyphase filterbank
*
* Implementation based material found in,
* Implementation based material found in,
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
* Prentice Hall, 2006."
@@ -72,7 +70,7 @@ bool ChannelizerBase::initFilters()
float sum = 0.0f, scale = 0.0f;
float midpt = (float) (protoLen - 1.0) / 2.0;
/*
/*
* Allocate 'M' partition filters and the temporary prototype
* filter. Coefficients are real only and must be 16-byte memory
* aligned for SSE usage.
@@ -92,7 +90,7 @@ bool ChannelizerBase::initFilters()
memalign(16, hLen * 2 * sizeof(float));
}
/*
/*
* Generate the prototype filter with a Blackman-harris window.
* Scale coefficients with DC filter gain set to unity divided
* by the number of channels.
@@ -112,7 +110,7 @@ bool ChannelizerBase::initFilters()
}
scale = (float) m / sum;
/*
/*
* Populate partition filters and reverse the coefficients per
* convolution requirements.
*/
@@ -176,7 +174,7 @@ bool ChannelizerBase::mapBuffers()
return true;
}
/*
/*
* Setup filterbank internals
*/
bool ChannelizerBase::init()
@@ -224,12 +222,11 @@ bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen)
return true;
}
/*
* Setup channelizer parameters
/*
* Setup channelizer paramaters
*/
ChannelizerBase::ChannelizerBase(size_t m, size_t blockLen, size_t hLen)
: subFilters(NULL), hInputs(NULL), hOutputs(NULL), hist(NULL),
fftInput(NULL), fftOutput(NULL), fftHandle(NULL)
: fftInput(NULL), fftOutput(NULL), fftHandle(NULL)
{
this->m = m;
this->hLen = hLen;
@@ -244,7 +241,6 @@ ChannelizerBase::~ChannelizerBase()
free(subFilters[i]);
delete[] hist[i];
}
free(subFilters);
fft_free(fftInput);
fft_free(fftOutput);

View File

@@ -32,7 +32,7 @@ protected:
/* Buffer length validity checking */
bool checkLen(size_t innerLen, size_t outerLen);
public:
/* Initialize channelizer/synthesis filter internals */
/* Initilize channelizer/synthesis filter internals */
bool init();
};

View File

@@ -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 distribution.
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.

View File

@@ -25,7 +25,14 @@ SUBDIRS = arch device
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device/common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
rev2dir = $(datadir)/usrp/rev2
rev4dir = $(datadir)/usrp/rev4
dist_rev2_DATA = std_inband.rbf
dist_rev4_DATA = std_inband.rbf
EXTRA_DIST = README
noinst_LTLIBRARIES = libtransceiver_common.la
@@ -39,8 +46,7 @@ COMMON_SOURCES = \
Transceiver.cpp \
ChannelizerBase.cpp \
Channelizer.cpp \
Synthesis.cpp \
proto_trxd.c
Synthesis.cpp
libtransceiver_common_la_SOURCES = \
$(COMMON_SOURCES) \
@@ -60,8 +66,7 @@ noinst_HEADERS = \
Resampler.h \
ChannelizerBase.h \
Channelizer.h \
Synthesis.h \
proto_trxd.h
Synthesis.h
COMMON_LDADD = \
libtransceiver_common.la \
@@ -104,13 +109,3 @@ 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

35
Transceiver52M/README Normal file
View File

@@ -0,0 +1,35 @@
The Transceiver
The transceiver consists of three modules:
--- transceiver
--- radioInterface
--- USRPDevice
The USRPDevice module is basically a driver that reads/writes
packets to a USRP with two RFX900 daughterboards, board
A is the Tx chain and board B is the Rx chain.
The radioInterface module is basically an interface b/w the
transceiver and the USRP. It operates the basestation clock
based upon the sample count of received USRP samples. Packets
from the USRP are queued and segmented into GSM bursts that are
passed up to the transceiver; bursts from the transceiver are
passed down to the USRP.
The transceiver basically operates "layer 0" of the GSM stack,
performing the modulation, detection, and demodulation of GSM
bursts. It communicates with the GSM stack via three UDP sockets,
one socket for data, one for control messages, and one socket to
pass clocking information. The transceiver contains a priority
queue to sort to-be-transmitted bursts, and a filler table to fill
in timeslots that do not have bursts in the priority queue. The
transceiver tries to stay ahead of the basestation clock, adapting
its latency when underruns are reported by the radioInterface/USRP.
Received bursts (from the radioInterface) pass through a simple
energy detector, a RACH or midamble correlator, and a DFE-based demodulator.
NOTE: There's a SWLOOPBACK #define statement, where the USRP is replaced
with a memory buffer. In this mode, data written to the USRP is actually stored
in a buffer, and read commands to the USRP simply pull data from this buffer.
This was very useful in early testing, and still may be useful in testing basic
Transceiver and radioInterface functionality.

View File

@@ -2,8 +2,6 @@
* Rational Sample Rate Conversion
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -53,7 +51,7 @@ void Resampler::initFilters(float bw)
float cutoff;
float sum = 0.0f, scale = 0.0f;
/*
/*
* Allocate partition filters and the temporary prototype filter
* according to numerator of the rational rate. Coefficients are
* real only and must be 16-byte memory aligned for SSE usage.
@@ -62,10 +60,10 @@ void Resampler::initFilters(float bw)
for (auto &part : partitions)
part = (complex<float> *) memalign(16, filt_len * sizeof(complex<float>));
/*
/*
* Generate the prototype filter with a Blackman-harris window.
* Scale coefficients with DC filter gain set to unity divided
* by the number of filter partitions.
* by the number of filter partitions.
*/
float a0 = 0.35875;
float a1 = 0.48829;
@@ -99,7 +97,6 @@ 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) {
@@ -130,19 +127,18 @@ 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];
path = out_path[i];
n = in_index[i];
path = out_path[i];
convolve_real(in, in_len,
reinterpret_cast<float *>(partitions[path]),

View File

@@ -2,8 +2,6 @@
* Rational Sample Rate Conversion
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -30,17 +28,17 @@ public:
/* Constructor for rational sample rate conversion
* @param p numerator of resampling ratio
* @param q denominator of resampling ratio
* @param filt_len length of each polyphase subfilter
* @param filt_len length of each polyphase subfilter
*/
Resampler(size_t p, size_t q, size_t filt_len = 16);
~Resampler();
/* Initialize resampler filterbank.
/* Initilize 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 bandwidth
* a Blackman-Harris window. Adjustment is made through a bandwith
* 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.
@@ -60,7 +58,7 @@ public:
int rotate(const float *in, size_t in_len, float *out, size_t out_len);
/* Get filter length
* @return number of taps in each filter partition
* @return number of taps in each filter partition
*/
size_t len();

View File

@@ -1,11 +1,9 @@
/*
* Polyphase synthesis filter
*
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* 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
@@ -76,7 +74,7 @@ bool Synthesis::resetBuffer(size_t chan)
return true;
}
/*
/*
* Implementation based on material found in:
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
@@ -94,8 +92,8 @@ bool Synthesis::rotate(float *out, size_t len)
cxvec_fft(fftHandle);
/*
* Convolve through filterbank while applying and saving sample history
/*
* Convolve through filterbank while applying and saving sample history
*/
for (size_t i = 0; i < m; i++) {
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: GPL-3.0+
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
@@ -27,24 +25,32 @@
#include "radioInterface.h"
#include "Interthread.h"
#include "GSMCommon.h"
#include "Sockets.h"
#include <sys/types.h>
#include <sys/socket.h>
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;
size_t num;
struct TransceiverChannel {
TransceiverChannel(Transceiver *trx, int num)
{
this->trx = trx;
this->num = num;
}
~TransceiverChannel()
{
}
Transceiver *trx;
size_t num;
};
/** Internal transceiver state variables */
@@ -63,7 +69,6 @@ struct TransceiverState {
/* The filler table */
signalVector *fillerTable[102][8];
int fillerModulus[8];
FillerType mFiller;
bool mRetrans;
/* Most recent channel estimate of all timeslots */
@@ -84,35 +89,33 @@ 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 cfg VTY populated config
@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 wTransmitLatency initial setting of transmit latency
@param radioInterface associated radioInterface object
*/
Transceiver(const struct trx_cfg *cfg,
Transceiver(int wBasePort,
const char *TRXAddress,
const char *GSMcoreAddress,
size_t tx_sps, size_t rx_sps, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface);
RadioInterface *wRadioInterface,
double wRssiOffset);
/** Destructor */
~Transceiver();
/** Start the control loop */
bool init(void);
bool init(FillerType filler, size_t rtsc, unsigned rach_delay,
bool edge, bool ext_rach);
/** attach the radioInterface receive FIFO */
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
@@ -125,7 +128,9 @@ public:
}
/** accessor for number of channels */
size_t numChans() const { return cfg->num_chans; };
size_t numChans() const { return mChans; };
void setSignalHandler(osmo_signal_cbfn cbfn);
/** Codes for channel combinations */
typedef enum {
@@ -148,30 +153,13 @@ public:
} ChannelCombination;
private:
struct ctrl_msg {
char data[101];
ctrl_msg() {};
};
int mBasePort;
std::string mLocalAddr;
std::string mRemoteAddr;
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<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<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
@@ -179,6 +167,7 @@ struct ctrl_sock_state {
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
@@ -190,6 +179,10 @@ struct ctrl_sock_state {
double txFullScale; ///< full scale input to radio
double rxFullScale; ///< full scale output to radio
double rssiOffset; ///< RSSI to dBm conversion offset
osmo_signal_cbfn *sig_cbfn; ///< Registered Signal Handler to announce events.
/** modulate and add a burst to the transmit queue */
void addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime);
@@ -201,7 +194,9 @@ struct ctrl_sock_state {
void pushRadioVector(GSM::Time &nowTime);
/** Pull and demodulate a burst from the receive FIFO */
int pullRadioVector(size_t chan, struct trx_ul_burst_ind *ind);
SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
double &timingOffset, double &noise,
size_t chan = 0);
/** Set modulus for specific timeslot */
void setModulus(size_t timeslot, size_t chan);
@@ -210,15 +205,14 @@ struct ctrl_sock_state {
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
/** send messages over the clock socket */
bool writeClockInterface(void);
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);
void writeClockInterface(void);
int mSPSTx; ///< number of samples per Tx symbol
int mSPSRx; ///< number of samples per Rx symbol
size_t mChans;
bool mExtRACH;
bool mEdge;
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
@@ -229,48 +223,62 @@ struct ctrl_sock_state {
unsigned mMaxExpectedDelayNB; ///< maximum expected time-of-arrival offset in GSM symbols for Normal Bursts
unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk
std::vector<unsigned> mVersionTRXD; ///< Format version to use for TRXD protocol communication, per channel
std::vector<TransceiverState> mStates;
/** Start and stop I/O threads through the control socket API */
bool start();
void stop();
/** Protect destructor accessible stop call */
/** Protect destructor accessable stop call */
Mutex mLock;
protected:
/** drive lower receive I/O and burst generation */
bool driveReceiveRadio();
void driveReceiveRadio();
/** drive demodulation of GSM bursts */
bool driveReceiveFIFO(size_t chan);
void driveReceiveFIFO(size_t chan);
/** drive transmission of GSM bursts */
void driveTxFIFO();
/** drive handling of control messages from GSM core */
void driveControl(size_t chan);
/**
drive modulation and sorting of GSM bursts from GSM core
@return true if a burst was transferred successfully
*/
bool driveTxPriorityQueue(size_t chan);
friend void *RxUpperLoopAdapter(TrxChanThParams *params);
friend void *TxUpperLoopAdapter(TrxChanThParams *params);
friend void *RxLowerLoopAdapter(Transceiver *transceiver);
friend void *TxLowerLoopAdapter(Transceiver *transceiver);
friend void *RxUpperLoopAdapter(TransceiverChannel *);
friend void *TxUpperLoopAdapter(TransceiverChannel *);
friend void *RxLowerLoopAdapter(Transceiver *);
friend void *TxLowerLoopAdapter(Transceiver *);
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
double rssiOffset(size_t chan);
void reset();
void logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi);
/** set priority on current thread */
void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
void logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
double rssi, double noise, double toa);
};
void *RxUpperLoopAdapter(TrxChanThParams *params);
void *RxUpperLoopAdapter(TransceiverChannel *);
/** Main drive threads */
void *RxLowerLoopAdapter(Transceiver *transceiver);
void *TxLowerLoopAdapter(Transceiver *transceiver);
void *RxLowerLoopAdapter(Transceiver *);
void *TxLowerLoopAdapter(Transceiver *);
/** control message handler thread loop */
void *ControlServiceLoopAdapter(TransceiverChannel *);
/** transmit queueing thread loop */
void *TxUpperLoopAdapter(TrxChanThParams *params);
void *TxUpperLoopAdapter(TransceiverChannel *);

View File

@@ -2,8 +2,6 @@
* NEON type conversions
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,8 +2,6 @@
* NEON type conversions
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,8 +2,6 @@
* NEON Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -58,7 +56,7 @@ static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
}
#endif
/* API: Initialize convolve module */
/* API: Initalize convolve module */
void convolve_init(void)
{
/* Stub */

View File

@@ -2,8 +2,6 @@
* NEON Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,8 +2,6 @@
* NEON scaling
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -1,9 +1,7 @@
/*
* NEON complex multiplication
* NEON complex multiplication
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,8 +2,6 @@
* NEON scaling
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,8 +2,6 @@
* ARM NEON Scaling
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -2,8 +2,6 @@
* Conversion
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -33,3 +31,4 @@ void base_convert_short_float(float *out, const short *in, int len)
for (int i = 0; i < len; i++)
out[i] = in[i];
}

View File

@@ -2,8 +2,6 @@
* Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either

View File

@@ -1,20 +1,18 @@
/*
* Fast Fourier transform
* Fast Fourier transform
*
* Copyright (C) 2012 Tom Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
*
* 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.
@@ -34,9 +32,9 @@ struct fft_hdl {
fftwf_plan fft_plan;
};
/*! \brief Initialize FFT backend
/*! \brief Initialize FFT backend
* \param[in] reverse FFT direction
* \param[in] m FFT length
* \param[in] m FFT length
* \param[in] istride input stride count
* \param[in] ostride output stride count
* \param[in] in input buffer (FFTW aligned)
@@ -94,7 +92,7 @@ void fft_free(void *ptr)
free(ptr);
}
/*! \brief Free FFT backend resources
/*! \brief Free FFT backend resources
*/
void free_fft(struct fft_hdl *hdl)
{
@@ -103,7 +101,7 @@ void free_fft(struct fft_hdl *hdl)
}
/*! \brief Run multiple DFT operations with the initialized plan
* \param[in] hdl handle to an initialized fft struct
* \param[in] hdl handle to an intitialized fft struct
*
* Input and output buffers are configured with init_fft().
*/

View File

@@ -27,7 +27,7 @@
#include "config.h"
#endif
/* Architecture dependent function pointers */
/* Architecture dependant function pointers */
struct convert_cpu_context {
void (*convert_si16_ps_16n) (float *, const short *, int);
void (*convert_si16_ps) (float *, const short *, int);

View File

@@ -27,7 +27,7 @@
#include "config.h"
#endif
/* Architecture dependent function pointers */
/* Architecture dependant 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: Initialize convolve module */
/* API: Initalize convolve module */
void convolve_init(void)
{
c.conv_cmplx_4n = (void *)_base_convolve_complex;
@@ -99,10 +99,9 @@ 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) {
@@ -139,10 +138,9 @@ 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))

View File

@@ -2,10 +2,6 @@ include $(top_srcdir)/Makefile.common
SUBDIRS = common
if DEVICE_IPC
SUBDIRS += ipc
endif
if DEVICE_USRP1
SUBDIRS += usrp1
endif

View File

@@ -1,7 +1,7 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
noinst_HEADERS = radioDevice.h smpl_buf.h

View File

@@ -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 distribution.
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
@@ -23,7 +23,6 @@
extern "C" {
#include "config_defs.h"
#include "osmo_signal.h"
}
#ifdef HAVE_CONFIG_H
@@ -70,6 +69,9 @@ class RadioDevice {
/** Get the Tx window type */
virtual enum TxWindowType getWindowType()=0;
/** Enable thread priority */
virtual void setPriority(float prio = 0.5) = 0;
/**
Read samples from the radio.
@param buf preallocated buf to contain read result
@@ -77,20 +79,23 @@ 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) = 0;
TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0,
unsigned *RSSI = 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) = 0;
TIMESTAMP timestamp, bool isControl = false) = 0;
/** Update the alignment between the read and write timestamps */
virtual bool updateAlignment(TIMESTAMP timestamp)=0;
@@ -125,11 +130,14 @@ class RadioDevice {
/** return minimum Rx Gain **/
virtual double minRxGain(void) = 0;
/** return base RSSI offset to apply for received samples **/
virtual double rssiOffset(size_t chan) = 0;
/** sets the transmit chan gain, returns the gain setting **/
virtual double setTxGain(double dB, size_t chan = 0) = 0;
/** returns the Nominal transmit output power of the transceiver in dBm, negative on error **/
virtual int getNominalTxPower(size_t chan = 0) = 0;
/** return maximum Tx Gain **/
virtual double maxTxGain(void) = 0;
/** return minimum Tx Gain **/
virtual double minTxGain(void) = 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;
@@ -154,34 +162,19 @@ 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;
size_t chans;
double lo_offset;
std::vector<std::string> tx_paths, rx_paths;
std::vector<struct device_counters> m_ctr;
RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chan_num, double offset,
RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chans, double offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chan_num), lo_offset(offset),
tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chans), lo_offset(offset),
tx_paths(tx_paths), rx_paths(rx_paths)
{
if (iface == MULTI_ARFCN) {
LOGC(DDEV, INFO) << "Multi-ARFCN: "<< chan_num << " logical chans -> 1 physical chans";
chans = 1;
}
m_ctr.resize(chans);
for (size_t i = 0; i < chans; i++) {
memset(&m_ctr[i], 0, sizeof(m_ctr[i]));
m_ctr[i].chan = i;
}
}
{ }
bool set_antennas() {
unsigned int i;
@@ -189,16 +182,9 @@ 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];
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i];
if (!setTxAntenna(tx_paths[i], i)) {
LOGCHAN(i, DDEV, ALERT) << "Failed configuring Tx antenna " << tx_paths[i];
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i];
return false;
}
}
@@ -206,16 +192,9 @@ 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];
LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i];
if (!setRxAntenna(rx_paths[i], i)) {
LOGCHAN(i, DDEV, ALERT) << "Failed configuring Rx antenna " << rx_paths[i];
LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i];
return false;
}
}

View File

@@ -7,8 +7,6 @@
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* 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
@@ -28,9 +26,9 @@
#include <inttypes.h>
smpl_buf::smpl_buf(size_t len)
: buf_len(len)
: buf_len(len), time_start(0), time_end(0),
data_start(0), data_end(0)
{
reset();
data = new uint32_t[len];
}
@@ -39,14 +37,6 @@ 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)
@@ -162,7 +152,7 @@ std::string smpl_buf::str_status(TIMESTAMP timestamp) const
return ost.str();
}
std::string smpl_buf::str_code(int code)
std::string smpl_buf::str_code(ssize_t code)
{
switch (code) {
case ERROR_TIMESTAMP:
@@ -174,8 +164,6 @@ std::string smpl_buf::str_code(int code)
case ERROR_OVERFLOW:
return "Sample buffer: Overrun";
default:
std::stringstream ss;
ss << "Sample buffer: Unknown error " << code;
return ss.str();
return "Sample buffer: Unknown error";
}
}

View File

@@ -7,8 +7,6 @@
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* 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
@@ -33,7 +31,7 @@
/*
Sample Buffer - Allows reading and writing of timed samples using osmo-trx
timestamps. Time conversions are handled
internally or accessible through the static convert calls.
internally or accessable through the static convert calls.
*/
class smpl_buf {
public:
@@ -44,10 +42,6 @@ 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
@@ -72,7 +66,7 @@ public:
@param code an error code
@return a formatted error string
*/
static std::string str_code(int code);
static std::string str_code(ssize_t code);
enum err_code {
ERROR_TIMESTAMP = -1,

File diff suppressed because it is too large Load Diff

View File

@@ -1,242 +0,0 @@
/*
* 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_

View File

@@ -1,42 +0,0 @@
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
#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
BUILT_SOURCES = uhddev_ipc.cpp
if DEVICE_UHD
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

View File

@@ -1,492 +0,0 @@
/*
* 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;
}

View File

@@ -1,45 +0,0 @@
/*
* 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);

View File

@@ -1,254 +0,0 @@
/*
* 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;
}

View File

@@ -1,25 +0,0 @@
/*
* 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

View File

@@ -1,200 +0,0 @@
/*
* 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;
}

View File

@@ -1,45 +0,0 @@
/*
* 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

View File

@@ -1,266 +0,0 @@
/*
* 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;
}

View File

@@ -1,26 +0,0 @@
/*
* 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

View File

@@ -1,149 +0,0 @@
/*
* 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;
}

View File

@@ -1,234 +0,0 @@
/*
* 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));

View File

@@ -1,255 +0,0 @@
/*
* 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, &timestamp, 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++;
}
}

View File

@@ -1,83 +0,0 @@
/*
* 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

View File

@@ -1,7 +1,5 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* 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
@@ -20,9 +18,6 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <map>
#include "Logger.h"
#include "Threads.h"
#include "LMSDevice.h"
@@ -30,126 +25,40 @@
#include <lime/LimeSuite.h>
extern "C" {
#include "trx_vty.h"
#include "osmo_signal.h"
#include <osmocom/core/utils.h>
}
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
using namespace std;
constexpr double LMSDevice::masterClockRate;
#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 */
/* 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,
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
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)
RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths),
m_lms_dev(NULL)
{
LOGC(DDEV, INFO) << "creating LMS device...";
m_lms_stream_rx.resize(chans);
m_lms_stream_tx.resize(chans);
rx_gains.resize(chans);
tx_gains.resize(chans);
m_last_rx_underruns.resize(chans, 0);
m_last_rx_overruns.resize(chans, 0);
m_last_rx_dropped.resize(chans, 0);
m_last_tx_underruns.resize(chans, 0);
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()
@@ -184,7 +93,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(DDEVDRV, lvl_map[lvl]) << msg;
LOGLV(DLMS, lvl_map[lvl]) << msg;
}
static void print_range(const char* name, lms_range_t *range)
@@ -201,7 +110,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;
std::vector<std::string> filters;
vector<string> filters;
filters = comma_delimited_to_vector(args.c_str());
@@ -222,52 +131,14 @@ 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..";
@@ -304,20 +175,19 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
delete [] info_list;
m_dev_type = parse_dev_type(m_lms_dev);
dev_desc = dev_param_map.at(m_dev_type);
device_info = LMS_GetDeviceInfo(m_lms_dev);
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 */
/* if reference clock is external setup must happen _before_ calling LMS_Init */
/* FIXME make external reference frequency configurable */
if (ref == REF_EXTERNAL) {
LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
/* FIXME: Assume an external 10 MHz reference clock. make
external reference frequency configurable */
if (!do_clock_src_freq(REF_EXTERNAL, 10000000.0))
/* Assume an external 10 MHz reference clock */
if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, 10000000.0) < 0)
goto out_close;
}
@@ -327,13 +197,22 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
goto out_close;
}
/* 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;
}
/* 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?*/
/* enable all used channels */
for (i=0; i<chans; i++) {
@@ -348,22 +227,16 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
goto out_close;
print_range("Sample Rate", &range_sr);
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)
LOGC(DDEV, INFO) << "Setting sample rate to " << GSMRATE*tx_sps << " " << tx_sps;
if (LMS_SetSampleRate(m_lms_dev, GSMRATE*tx_sps, 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;
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);
/* 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 */
/* configure antennas */
if (!set_antennas()) {
@@ -371,7 +244,13 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
goto out_close;
}
return iface == MULTI_ARFCN ? MULTI_ARFCN : NORMAL;
/* 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;
out_close:
LOGC(DDEV, FATAL) << "Error in LMS open, closing: " << LMS_GetLastErrorMessage();
@@ -385,20 +264,17 @@ 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 */
LMS_SetGaindB(m_lms_dev, LMS_CH_TX, i, TxPower2TxGain(desc, desc.nom_out_tx_power));
setTxGain(maxTxGain(), i);
/* RX gain to midpoint */
setRxGain((minRxGain() + maxRxGain()) / 2, i);
@@ -468,43 +344,6 @@ 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)
{
@@ -543,6 +382,17 @@ 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;
@@ -553,6 +403,21 @@ 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";
return dB;
}
double LMSDevice::setRxGain(double dB, size_t chan)
{
if (dB > maxRxGain())
@@ -564,80 +429,8 @@ double LMSDevice::setRxGain(double dB, size_t chan)
if (LMS_SetGaindB(m_lms_dev, LMS_CH_RX, chan, dB) < 0)
LOGCHAN(chan, DDEV, ERR) << "Error setting RX gain to " << dB << " dB";
else
rx_gains[chan] = dB;
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. */
int num_names;
int i;
num_names = LMS_GetAntennaList(m_lms_dev, dir_tx, chan, name_list);
for (i = 0; i < num_names; i++) {
if (i)
os << ", ";
os << "'" << name_list[i] << "'";
}
return dB;
}
int LMSDevice::get_ant_idx(const std::string & name, bool dir_tx, size_t chan)
@@ -693,10 +486,7 @@ bool LMSDevice::setRxAntenna(const std::string & ant, size_t chan)
idx = get_ant_idx(ant, LMS_CH_RX, chan);
if (idx < 0) {
std::ostringstream os;
LOGCHAN(chan, DDEV, ERROR) << "Invalid Rx Antenna: " << ant;
log_ant_list(LMS_CH_RX, chan, os);
LOGCHAN(chan, DDEV, NOTICE) << "Available Rx Antennas: " << os;
LOGCHAN(chan, DDEV, ERROR) << "Invalid Rx Antenna";
return false;
}
@@ -742,10 +532,7 @@ bool LMSDevice::setTxAntenna(const std::string & ant, size_t chan)
idx = get_ant_idx(ant, LMS_CH_TX, chan);
if (idx < 0) {
std::ostringstream os;
LOGCHAN(chan, DDEV, ERROR) << "Invalid Tx Antenna: " << ant;
log_ant_list(LMS_CH_TX, chan, os);
LOGCHAN(chan, DDEV, NOTICE) << "Available Tx Antennas: " << os;
LOGCHAN(chan, DDEV, ERROR) << "Invalid Rx Antenna";
return false;
}
@@ -789,53 +576,39 @@ GSM::Time LMSDevice::minLatency() {
/* UNUSED on limesdr (only used on usrp1/2) */
return GSM::Time(0,0);
}
/*!
* Issue tracking description of several events: https://github.com/myriadrf/LimeSuite/issues/265
*/
void LMSDevice::update_stream_stats_rx(size_t chan, bool *overrun)
void LMSDevice::update_stream_stats(size_t chan, bool * underrun, bool * overrun)
{
lms_stream_status_t status;
bool changed = false;
if (LMS_GetStreamStatus(&m_lms_stream_rx[chan], &status) == 0) {
if (status.underrun > m_last_rx_underruns[chan]) {
*underrun = true;
LOGCHAN(chan, DDEV, ERROR) << "recv Underrun! ("
<< m_last_rx_underruns[chan] << " -> "
<< status.underrun << ")";
}
m_last_rx_underruns[chan] = status.underrun;
if (LMS_GetStreamStatus(&m_lms_stream_rx[chan], &status) != 0) {
LOGCHAN(chan, DDEV, ERROR) << "Rx LMS_GetStreamStatus failed";
return;
if (status.overrun > m_last_rx_overruns[chan]) {
*overrun = true;
LOGCHAN(chan, DDEV, ERROR) << "recv Overrun! ("
<< m_last_rx_overruns[chan] << " -> "
<< status.overrun << ")";
}
m_last_rx_overruns[chan] = status.overrun;
if (status.droppedPackets > m_last_rx_dropped[chan]) {
LOGCHAN(chan, DDEV, ERROR) << "recv Dropped packets by HW! ("
<< m_last_rx_dropped[chan] << " -> "
<< status.droppedPackets << ")";
}
m_last_rx_dropped[chan] = m_last_rx_overruns[chan];
}
/* FIFO overrun is counted when Rx FIFO is full but new data comes from
the board and oldest samples in FIFO are overwritte. Value count
since the last call to LMS_GetStreamStatus(stream). */
if (status.overrun) {
changed = true;
*overrun = true;
LOGCHAN(chan, DDEV, ERROR) << "Rx Overrun! ("
<< m_ctr[chan].rx_overruns << " -> "
<< status.overrun << ")";
}
m_ctr[chan].rx_overruns += status.overrun;
/* Dropped packets in Rx are counted when gaps in Rx timestamps are
detected (likely because buffer overflow in hardware). Value count
since the last call to LMS_GetStreamStatus(stream). */
if (status.droppedPackets) {
changed = true;
LOGCHAN(chan, DDEV, ERROR) << "Rx Dropped packets by HW! ("
<< m_ctr[chan].rx_dropped_samples << " -> "
<< m_ctr[chan].rx_dropped_samples +
status.droppedPackets
<< ")";
m_ctr[chan].rx_dropped_events++;
}
m_ctr[chan].rx_dropped_samples += status.droppedPackets;
if (changed)
osmo_signal_dispatch(SS_DEVICE, S_DEVICE_COUNTER_CHANGE, &m_ctr[chan]);
}
// NOTE: Assumes sequential reads
int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
TIMESTAMP timestamp, bool * underrun)
TIMESTAMP timestamp, bool * underrun, unsigned *RSSI)
{
int rc, num_smpls, expect_smpls;
ssize_t avail_smpls;
@@ -867,7 +640,7 @@ int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
while ((avail_smpls = rx_buffers[i]->avail_smpls(timestamp)) < len) {
thread_enable_cancel(false);
num_smpls = LMS_RecvStream(&m_lms_stream_rx[i], bufs[i], len - avail_smpls, &rx_metadata, 100);
update_stream_stats_rx(i, overrun);
update_stream_stats(i, underrun, overrun);
thread_enable_cancel(true);
if (num_smpls <= 0) {
LOGCHAN(i, DDEV, ERROR) << "Device receive timed out (" << rc << " vs exp " << len << ").";
@@ -902,9 +675,8 @@ 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)) {
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc) << ". "
<< rx_buffers[i]->str_status(timestamp)
<< ", (len=" << len << ")";
LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
return 0;
}
}
@@ -912,57 +684,23 @@ int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
return len;
}
void LMSDevice::update_stream_stats_tx(size_t chan, bool *underrun)
{
lms_stream_status_t status;
bool changed = false;
if (LMS_GetStreamStatus(&m_lms_stream_tx[chan], &status) != 0) {
LOGCHAN(chan, DDEV, ERROR) << "Tx LMS_GetStreamStatus failed";
return;
}
/* FIFO underrun is counted when Tx is running but FIFO is empty for
>100 ms (500ms in older versions). Value count since the last call to
LMS_GetStreamStatus(stream). */
if (status.underrun) {
changed = true;
*underrun = true;
LOGCHAN(chan, DDEV, ERROR) << "Tx Underrun! ("
<< m_ctr[chan].tx_underruns << " -> "
<< status.underrun << ")";
}
m_ctr[chan].tx_underruns += status.underrun;
/* Dropped packets in Tx are counted only when timestamps are enabled
and SDR drops packet because of late timestamp. Value count since the
last call to LMS_GetStreamStatus(stream). */
if (status.droppedPackets) {
changed = true;
LOGCHAN(chan, DDEV, ERROR) << "Tx Dropped packets by HW! ("
<< m_ctr[chan].tx_dropped_samples << " -> "
<< m_ctr[chan].tx_dropped_samples +
status.droppedPackets
<< ")";
m_ctr[chan].tx_dropped_events++;
}
m_ctr[chan].tx_dropped_samples += status.droppedPackets;
if (changed)
osmo_signal_dispatch(SS_DEVICE, S_DEVICE_COUNTER_CHANGE, &m_ctr[chan]);
}
int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
bool * underrun, unsigned long long timestamp)
bool * underrun, unsigned long long timestamp,
bool isControl)
{
int rc = 0;
unsigned int i;
lms_stream_status_t status;
lms_stream_meta_t tx_metadata = {};
tx_metadata.flushPartialPacket = false;
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;
@@ -974,12 +712,16 @@ int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
LOGCHAN(i, DDEV, DEBUG) << "send buffer of len " << len << " timestamp " << std::hex << tx_metadata.timestamp;
thread_enable_cancel(false);
rc = LMS_SendStream(&m_lms_stream_tx[i], bufs[i], len, &tx_metadata, 100);
update_stream_stats_tx(i, underrun);
thread_enable_cancel(true);
if (rc != len) {
LOGCHAN(i, DDEV, ERROR) << "LMS: Device Tx timed out (" << rc << " vs exp " << len << ").";
return -1;
LOGCHAN(i, DDEV, ERROR) << "LMS: Device send timed out";
}
if (LMS_GetStreamStatus(&m_lms_stream_tx[i], &status) == 0) {
if (status.underrun > m_last_tx_underruns[i])
*underrun = true;
m_last_tx_underruns[i] = status.underrun;
}
thread_enable_cancel(true);
}
return rc;
@@ -992,39 +734,13 @@ 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;
}

View File

@@ -1,10 +1,7 @@
/*
* Copyright 2018 sysmocom - s.f.m.c. GmbH
*
* 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 distribution.
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
@@ -32,10 +29,6 @@
#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:
@@ -45,38 +38,22 @@ extern "C" {
* 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 {
private:
static constexpr double masterClockRate = 52.0e6;
lms_device_t *m_lms_dev;
std::vector<lms_stream_t> m_lms_stream_rx;
std::vector<lms_stream_t> m_lms_stream_tx;
std::vector<uint32_t> m_last_rx_underruns;
std::vector<uint32_t> m_last_rx_overruns;
std::vector<uint32_t> m_last_rx_dropped;
std::vector<uint32_t> m_last_tx_underruns;
std::vector<smpl_buf *> rx_buffers;
double actualSampleRate; ///< the actual USRP sampling rate
@@ -86,27 +63,19 @@ private:
TIMESTAMP ts_initial, ts_offset;
std::vector<double> tx_gains, rx_gains;
enum gsm_band band;
struct dev_band_desc band_desc;
enum lms_dev_type m_dev_type;
double rxGain;
double maxTxGainClamp;
bool do_calib(size_t chan);
bool do_filters(size_t chan);
void log_ant_list(bool dir_tx, size_t chan, std::ostringstream& os);
int get_ant_idx(const std::string & name, bool dir_tx, size_t chan);
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);
void update_stream_stats(size_t chan, bool * underrun, bool * overrun);
public:
/** Object constructor */
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
~LMSDevice();
@@ -120,6 +89,10 @@ public:
/** Stop the LMS */
bool stop();
/** Set priority not supported */
void setPriority(float prio = 0.5) {
}
enum TxWindowType getWindowType() {
return TX_WINDOW_LMS1;
}
@@ -131,21 +104,24 @@ 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);
NULL, unsigned *RSSI = 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);
TIMESTAMP timestamp = 0xffffffff, bool isControl =
false);
/** Update the alignment between the read and write timestamps */
bool updateAlignment(TIMESTAMP timestamp);
@@ -181,7 +157,7 @@ public:
/** get the current receive gain */
double getRxGain(size_t chan = 0) {
return rx_gains[chan];
return rxGain;
}
/** return maximum Rx Gain **/
@@ -190,12 +166,14 @@ public:
/** return minimum Rx Gain **/
double minRxGain(void);
double rssiOffset(size_t chan);
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
double setPowerAttenuation(int atten, size_t chan);
double getPowerAttenuation(size_t chan = 0);
/** return maximum Tx Gain **/
double maxTxGain(void);
int getNominalTxPower(size_t chan = 0);
/** return minimum Rx Gain **/
double minTxGain(void);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setRxAntenna(const std::string & ant, size_t chan = 0);

View File

@@ -1,7 +1,7 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LMS_CFLAGS)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
noinst_HEADERS = LMSDevice.h

View File

@@ -1,7 +1,7 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
noinst_HEADERS = UHDDevice.h

View File

@@ -6,8 +6,6 @@
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* 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
@@ -33,18 +31,11 @@
#include "config.h"
#endif
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
#ifndef USE_UHD_3_11
#include <uhd/utils/msg.hpp>
#include <uhd/utils/thread_priority.hpp>
#else
#include <uhd/utils/thread.hpp>
#endif
#define USRP_TX_AMPL 0.3
@@ -129,23 +120,10 @@ 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();
dev->setPriority(0.43);
while (1) {
dev->recv_async_msg();
@@ -155,52 +133,23 @@ void *async_event_loop(uhd_device *dev)
return NULL;
}
#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
#ifndef USE_UHD_3_11
/*
Catch and drop underrun 'U' and overrun 'O' messages from stdout
since we already report using the logging facility. Direct
everything else appropriately.
*/
static void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
{
switch (type) {
case uhd::msg::status:
LOGC(DDEVDRV, INFO) << msg;
LOGC(DDEV, INFO) << msg;
break;
case uhd::msg::warning:
LOGC(DDEVDRV, NOTICE) << msg;
LOGC(DDEV, WARNING) << msg;
break;
case uhd::msg::error:
LOGC(DDEVDRV, ERROR) << msg;
LOGC(DDEV, ERROR) << msg;
break;
case uhd::msg::fastpath:
break;
@@ -208,25 +157,14 @@ static 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 chan_num, double lo_offset,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths)
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, 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),
rx_gain_min(0.0), rx_gain_max(0.0),
band((enum gsm_band)0), tx_spp(0), rx_spp(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)
{
@@ -240,47 +178,8 @@ 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) {
@@ -345,59 +244,16 @@ void uhd_device::set_rates()
LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
}
double uhd_device::setRxGain(double db, size_t chan)
double uhd_device::setTxGain(double db, size_t chan)
{
if (chan >= rx_gains.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return 0.0f;
}
usrp_dev->set_rx_gain(db, chan);
rx_gains[chan] = usrp_dev->get_rx_gain(chan);
LOGC(DDEV, INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
return rx_gains[chan];
}
double uhd_device::getRxGain(size_t chan)
{
if (chan >= rx_gains.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return 0.0f;
}
return rx_gains[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 (iface == MULTI_ARFCN)
chan = 0;
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") {
@@ -417,29 +273,40 @@ double uhd_device::setPowerAttenuation(int atten, size_t 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)";
LOGC(DDEV, INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
return tx_gains[chan];
}
double uhd_device::getPowerAttenuation(size_t chan) {
dev_band_desc desc;
if (chan >= tx_gains.size()) {
double uhd_device::setRxGain(double db, size_t chan)
{
if (iface == MULTI_ARFCN)
chan = 0;
if (chan >= rx_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]);
usrp_dev->set_rx_gain(db, chan);
rx_gains[chan] = usrp_dev->get_rx_gain(chan);
LOGC(DDEV, INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
return rx_gains[chan];
}
int uhd_device::getNominalTxPower(size_t chan)
double uhd_device::getRxGain(size_t chan)
{
dev_band_desc desc;
get_dev_band_desc(desc);
if (iface == MULTI_ARFCN)
chan = 0;
return desc.nom_out_tx_power;
if (chan >= rx_gains.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return 0.0f;
}
return rx_gains[chan];
}
/*
@@ -513,6 +380,7 @@ void uhd_device::set_channels(bool swap)
if (dev_type != B200 && dev_type != B210)
throw std::invalid_argument("Device does not support MCBTS");
dev_type = B2XX_MCBTS;
chans = 1;
}
if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
@@ -549,16 +417,6 @@ 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);
@@ -667,7 +525,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
init_gains();
// Print configuration
LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
LOGC(DDEV, INFO) << "\n" << usrp_dev->get_pp_string();
if (iface == MULTI_ARFCN)
return MULTI_ARFCN;
@@ -745,6 +603,10 @@ 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);
@@ -775,14 +637,16 @@ 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;
}
void uhd_device::setPriority(float prio)
{
uhd::set_thread_priority_safe(prio);
return;
}
int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
{
if (!num_smpls) {
@@ -826,7 +690,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)
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
{
ssize_t rc;
uhd::time_spec_t ts;
@@ -883,7 +747,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,
ts.to_ticks(rx_rate));
metadata.time_spec.to_ticks(rx_rate));
// Continue on local overrun, exit on other errors
if ((rc < 0)) {
@@ -909,7 +773,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)
unsigned long long timestamp,bool isControl)
{
uhd::tx_metadata_t metadata;
metadata.has_time_spec = true;
@@ -919,6 +783,12 @@ 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;
@@ -1018,18 +888,15 @@ 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";
}
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl;
if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
return true;
@@ -1049,7 +916,7 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
}
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl;
}
return true;
@@ -1057,33 +924,13 @@ 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);
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;
return set_freq(wFreq, chan, true);
}
bool uhd_device::setRxFreq(double wFreq, size_t chan)
@@ -1125,14 +972,7 @@ bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
return false;
}
/* 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;
}
avail = usrp_dev->get_rx_antennas(chan);
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: ";
@@ -1168,14 +1008,7 @@ bool uhd_device::setTxAntenna(const std::string &ant, size_t chan)
return false;
}
/* 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;
}
avail = usrp_dev->get_tx_antennas(chan);
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: ";
@@ -1341,7 +1174,6 @@ 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,
@@ -1349,4 +1181,3 @@ 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

View File

@@ -7,8 +7,6 @@
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* 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
@@ -37,10 +35,6 @@
#include <uhd/property_tree.hpp>
#include <uhd/usrp/multi_usrp.hpp>
extern "C" {
#include <osmocom/gsm/gsm_utils.h>
}
enum uhd_dev_type {
USRP1,
@@ -56,21 +50,6 @@ 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
@@ -81,7 +60,7 @@ struct dev_band_desc {
class uhd_device : public RadioDevice {
public:
uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
size_t chan_num, double offset,
size_t chans, double offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
~uhd_device();
@@ -90,13 +69,14 @@ public:
bool start();
bool stop();
bool restart();
void setPriority(float prio);
enum TxWindowType getWindowType() { return tx_window; }
int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun);
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp);
TIMESTAMP timestamp, bool isControl);
bool updateAlignment(TIMESTAMP timestamp);
@@ -113,12 +93,10 @@ 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 setPowerAttenuation(int atten, size_t chan);
double getPowerAttenuation(size_t chan = 0);
int getNominalTxPower(size_t chan = 0);
double setTxGain(double db, size_t chan);
double maxTxGain(void) { return tx_gain_max; }
double minTxGain(void) { return tx_gain_min; }
double getTxFreq(size_t chan);
double getRxFreq(size_t chan);
@@ -147,7 +125,7 @@ public:
ERROR_UNHANDLED = -4,
};
protected:
private:
uhd::usrp::multi_usrp::sptr usrp_dev;
uhd::tx_streamer::sptr tx_stream;
uhd::rx_streamer::sptr rx_stream;
@@ -156,12 +134,11 @@ protected:
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;
@@ -190,9 +167,6 @@ protected:
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;

View File

@@ -1,13 +1,7 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(USRP_CFLAGS)
rev2dir = $(datadir)/usrp/rev2
rev4dir = $(datadir)/usrp/rev4
dist_rev2_DATA = std_inband.rbf
dist_rev4_DATA = std_inband.rbf
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(USRP_CFLAGS)
noinst_HEADERS = USRPDevice.h

View File

@@ -1,8 +1,6 @@
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -61,17 +59,16 @@ 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 chan_num, double lo_offset,
size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths)
RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths)
{
LOGC(DDEV, INFO) << "creating USRP device...";
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps));
actualSampleRate = masterClockRate/decimRate;
rxGain = 0;
txGain = 0;
/*
* Undetermined delay b/w ping response timestamp and true
@@ -205,8 +202,8 @@ bool USRPDevice::start()
writeLock.unlock();
// Set gains to midpoint
setTxGain((m_dbTx->gain_min() + m_dbTx->gain_max()) / 2);
setRxGain((m_dbTx->gain_min() + m_dbTx->gain_max()) / 2);
setTxGain((minTxGain() + maxTxGain()) / 2);
setRxGain((minRxGain() + maxRxGain()) / 2);
data = new short[currDataSize];
dataStart = 0;
@@ -243,6 +240,16 @@ 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();
@@ -261,20 +268,19 @@ double USRPDevice::setTxGain(double dB, size_t chan)
}
writeLock.lock();
if (dB > m_dbTx->gain_max())
dB = m_dbTx->gain_max();
if (dB < m_dbTx->gain_min())
dB = m_dbTx->gain_min();
if (dB > maxTxGain())
dB = maxTxGain();
if (dB < minTxGain())
dB = minTxGain();
LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB.";
if (!m_dbTx->set_gain(dB))
LOGC(DDEV, ERR) << "Error setting TX gain";
else
txGain = dB;
writeLock.unlock();
return txGain;
return dB;
}
@@ -297,28 +303,10 @@ double USRPDevice::setRxGain(double dB, size_t chan)
if (!m_dbRx->set_gain(dB))
LOGC(DDEV, ERR) << "Error setting RX gain";
else
rxGain = dB;
writeLock.unlock();
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;
return dB;
}
bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan)
@@ -372,7 +360,7 @@ GSM::Time USRPDevice::minLatency() {
// NOTE: Assumes sequential reads
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun)
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
{
#ifndef SWLOOPBACK
if (!m_uRx)
@@ -387,7 +375,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
return len;
}
*underrun = false;
if (underrun) *underrun = false;
uint32_t readBuf[2000];
@@ -437,13 +425,11 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
continue;
}
if ((word0 >> 28) & 0x04) {
*underrun = true;
if (underrun) *underrun = true;
LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
}
#if 0
/* FIXME: Do something with this ? */
unsigned RSSI = (word0 >> 21) & 0x3f;
#endif
if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
if (!isAligned) continue;
unsigned cursorStart = pktTimestamp - timeStart + dataStart;
@@ -522,8 +508,9 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
#endif
}
int USRPDevice::writeSamplesControl(std::vector<short *> &bufs, int len,
bool *underrun, unsigned long long timestamp, bool isControl)
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
bool *underrun, unsigned long long timestamp,
bool isControl)
{
writeLock.lock();
@@ -577,12 +564,6 @@ int USRPDevice::writeSamplesControl(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
@@ -592,7 +573,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
bool tmpUnderrun;
std::vector<short *> buf(1, data);
if (writeSamplesControl(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
pingTimestamp = timestamp;
return true;
}

View File

@@ -1,10 +1,7 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* 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 distribution.
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
@@ -80,16 +77,6 @@ private:
unsigned long lastPktTimestamp;
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];
@@ -104,7 +91,7 @@ private:
public:
/** Object constructor */
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
@@ -117,6 +104,9 @@ private:
/** Stop the USRP */
bool stop();
/** Set priority not supported */
void setPriority(float prio = 0.5) { }
enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
/**
@@ -126,20 +116,23 @@ 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);
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
unsigned *RSSI = 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);
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
/** Update the alignment between the read and write timestamps */
bool updateAlignment(TIMESTAMP timestamp);
@@ -174,12 +167,14 @@ private:
/** return minimum Rx Gain **/
double minRxGain(void);
double rssiOffset(size_t chan) { return 0.0f; } /* FIXME: not implemented */
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
double setPowerAttenuation(int atten, size_t chan);
double getPowerAttenuation(size_t chan=0);
/** return maximum Tx Gain **/
double maxTxGain(void);
int getNominalTxPower(size_t chan = 0);
/** return minimum Rx Gain **/
double minTxGain(void);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setRxAntenna(const std::string &ant, size_t chan = 0);

View File

@@ -0,0 +1,314 @@
This file specifies the format of USB packets used for in-band data
transmission and signaling on the USRP. All packets are 512-byte long,
and are transfered using USB "bulk" transfers.
IN packets are sent towards the host.
OUT packets are sent away from the host.
The layout is 32-bits wide. All data is transmitted in little-endian
format across the USB.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|O|U|D|S|E| RSSI | Chan | mbz | Tag | Payload Len |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| Payload |
. .
. .
. .
| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... | .
+-+-+-+-+-+-+-+ .
. .
. Padding .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
mbz Must be Zero: these bits must be zero in both IN and OUT packets.
O Overrun Flag: set in an IN packet if an overrun condition was
detected. Must be zero in OUT packets. Overrun occurs when
the FPGA has data to transmit to the host and there is no
buffer space available. This generally indicates a problem on
the host. Either it is not keeping up, or it has configured
the FPGA to transmit data at a higher rate than the transport
(USB) can support.
U Underrun Flag: set in an IN packet if an underrun condition
was detected. Must be zero in OUT packets. Underrun occurs
when the FPGA runs out of samples, and it's not between
bursts. See the "End of Burst flag" below.
D Dropped Packet Flag: Set in an IN packet if the FPGA
discarded an OUT packet because its timestamp had already
passed.
S Start of Burst Flag: Set in an OUT packet if the data is the
first segment of what is logically a continuous burst of data.
Must be zero in IN packets.
E End of Burst Flag: Set in an OUT packet if the data is the
last segment of what is logically a continuous burst of data.
Must be zero in IN packets. Underruns are not reported
when the FPGA runs out of samples between bursts.
RSSI 6-bit Received Strength Signal Indicator: Must be zero in OUT
packets. In IN packets, indicates RSSI as reported by front end.
FIXME The format and interpretation are to be determined.
Chan 5-bit logical channel number. Channel number 0x1f is reserved
for control information. See "Control Channel" below. Other
channels are "data channels." Each data channel is logically
independent of the others. A data channel payload field
contains a sequence of homogeneous samples. The format of the
samples is determined by the configuration associated with the
given channel. It is often the case that the payload field
contains 32-bit complex samples, each containing 16-bit real
and imaginary components.
Tag 4-bit tag for matching IN packets with OUT packets.
[FIXME, write more...]
Payload Len: 9-bit field that specifies the length of the payload
field in bytes. Must be in the range 0 to 504 inclusive.
Timestamp: 32-bit timestamp.
On IN packets, the timestamp indicates the time at which the
first sample of the packet was produced by the A/D converter(s)
for that channel. On OUT packets, the timestamp specifies the
time at which the first sample in the packet should go out the
D/A converter(s) for that channel. If a packet reaches the
head of the transmit queue, and the current time is later than
the timestamp, an error is assumed to have occurred and the
packet is discarded. As a special case, the timestamp
0xffffffff is interpreted as "Now".
The time base is a free running 32-bit counter that is
incremented by the A/D sample-clock.
Payload: Variable length field. Length is specified by the
Payload Len field.
Padding: This field is 504 - Payload Len bytes long, and its content
is unspecified. This field pads the packet out to a constant
512 bytes.
"Data Channel" payload format:
-------------------------------
If Chan != 0x1f, the packet is a "data packet" and the payload is a
sequence of homogeneous samples. The format of the samples is
determined by the configuration associated with the given channel.
It is often the case that the payload field contains 32-bit complex
samples, each containing 16-bit real and imaginary components.
"Control Channel" payload format:
---------------------------------
If Chan == 0x1f, the packet is a "control packet". The control channel
payload consists of a sequence of 0 or more sub-packets.
Each sub-packet starts on a 32-bit boundary, and consists of an 8-bit
Opcode field, an 8-bit Length field, Length bytes of arguments, and 0,
1, 2 or 3 bytes of padding to align the tail of the sub-packet to
a 32-bit boundary.
Control channel packets shall be processed at the head of the queue,
and shall observe the timestamp semantics described above.
General sub-packet format:
--------------------------
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-//-+-+-+-+-+-+-+-+
| Opcode | Length | <length bytes> ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-//-+-+-+-+-+-+-+-+
Specific sub-packet formats:
----------------------------
RID: 6-bit Request-ID. Copied from request sub-packet into corresponding
reply sub-packet. RID allows the host to match requests and replies.
Reg Number: 10-bit Register Number.
Ping Fixed Length:
Opcode: OP_PING_FIXED
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 2 | RID | Ping Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Ping Fixed Length Reply:
Opcode: OP_PING_FIXED_REPLY
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 2 | RID | Ping Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Write Register:
Opcode: OP_WRITE_REG
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 6 | mbz | Reg Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Register Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Write Register Masked:
Opcode: OP_WRITE_REG_MASKED
REG[Num] = (REG[Num] & ~Mask) | (Value & Mask)
That is, only the register bits that correspond to 1's in the
mask are written with the new value.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 10 | mbz | Reg Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Register Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Mask Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Read Register:
Opcode: OP_READ_REG
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 2 | RID | Reg Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Read Register Reply:
Opcode: OP_READ_REG_REPLY
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 6 | RID | Reg Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Register Value |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
I2C Write:
Opcode: OP_I2C_WRITE
I2C Addr: 7-bit I2C address
Data: The bytes to write to the I2C bus
Length: Length of Data + 2
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | Length | mbz | I2C Addr |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
I2C Read:
Opcode: OP_I2C_READ
I2C Addr: 7-bit I2C address
Nbytes: Number of bytes to read from I2C bus
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 3 | RID | mbz | I2C Addr |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Nbytes | unspecified padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
I2C Read Reply:
Opcode: OP_I2C_READ_REPLY
I2C Addr: 7-bit I2C address
Data: Length - 2 bytes of data read from I2C bus.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | Length | RID | mbz | I2C Addr |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
SPI Write:
Opcode: OP_SPI_WRITE
Enables: Which SPI enables to assert (mask)
Format: Specifies format of SPI data and Opt Header Bytes
Opt Header Bytes: 2-byte field containing optional Tx bytes; see Format
Data: The bytes to write to the SPI bus
Length: Length of Data + 6
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | Length | mbz |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Enables | Format | Opt Header Bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
SPI Read:
Opcode: OP_SPI_READ
Enables: Which SPI enables to assert (mask)
Format: Specifies format of SPI data and Opt Header Bytes
Opt Header Bytes: 2-byte field containing optional Tx bytes; see Format
Nbytes: Number of bytes to read from SPI bus.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 7 | RID | mbz |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Enables | Format | Opt Header Bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Nbytes | unspecified padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
SPI Read Reply:
Opcode: OP_SPI_READ_REPLY
Data: Length - 2 bytes of data read from SPI bus.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | Length | RID | mbz |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ... .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Delay:
Opcode: OP_DELAY
Ticks: 16-bit unsigned delay count
Delay Ticks clock ticks before executing next operation.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opcode | 2 | Ticks |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

View File

@@ -1,8 +1,6 @@
/*
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* SPDX-License-Identifier: LGPL-2.1+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
@@ -55,14 +53,12 @@ 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"
#include "trx_vty.h"
#include "debug.h"
#include "osmo_signal.h"
#include "trx_rate_ctr.h"
}
#define DEFAULT_CONFIG_FILE "osmo-trx.cfg"
@@ -80,6 +76,7 @@ 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,
@@ -125,7 +122,7 @@ static int transc_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
switch (signal) {
case S_MAIN_STOP_REQUIRED:
case S_TRANSC_STOP_REQUIRED:
gshutdown = true;
break;
default:
@@ -144,12 +141,18 @@ int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
{
VectorFIFO *fifo;
transceiver = new Transceiver(&trx->cfg, GSM::Time(3,0), radio);
if (!transceiver->init()) {
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);
if (!transceiver->init(trx->cfg.filler, trx->cfg.rtsc,
trx->cfg.rach_delay, trx->cfg.egprs, trx->cfg.ext_rach)) {
LOG(ALERT) << "Failed to initialize transceiver";
return -1;
}
transceiver->setSignalHandler(transc_sig_cb);
for (size_t i = 0; i < trx->cfg.num_chans; i++) {
fifo = radio->receiveFIFO(i);
if (fifo && transceiver->receiveFIFO(fifo, i))
@@ -169,25 +172,14 @@ static void sig_handler(int signo)
action like printing */
return;
fprintf(stderr, "signal %d received\n", signo);
fprintf(stdout, "signal %d received\n", signo);
switch (signo) {
case SIGINT:
case SIGTERM:
fprintf(stderr, "shutting down\n");
fprintf(stdout, "shutting down\n");
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);
@@ -244,7 +236,7 @@ static void setup_signal_handlers()
exit(EXIT_FAILURE);
}
osmo_fd_setup(&signal_ofd, sfd, OSMO_FD_READ, signalfd_callback, NULL, 0);
osmo_fd_setup(&signal_ofd, sfd, BSC_FD_READ, signalfd_callback, NULL, 0);
if (osmo_fd_register(&signal_ofd) < 0) {
fprintf(stderr, "osmo_fd_register() failed.\n");
exit(EXIT_FAILURE);
@@ -253,13 +245,10 @@ static void setup_signal_handlers()
static void print_help()
{
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"
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"
);
}
@@ -267,32 +256,7 @@ static void print_deprecated(char opt)
{
LOG(WARNING) << "Cmd line option '" << opt << "' is deprecated and will be soon removed."
<< " Please use VTY cfg option instead."
<< " 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);
}
<< " All cmd line options are already being overriden by VTY options if set.";
}
static void handle_options(int argc, char **argv, struct trx_ctx* trx)
@@ -301,13 +265,10 @@ static void handle_options(int argc, char **argv, struct trx_ctx* trx)
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}
};
@@ -318,9 +279,6 @@ 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);
@@ -375,19 +333,20 @@ 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);
@@ -423,11 +382,6 @@ 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) {
@@ -478,9 +432,7 @@ static int set_sched_rr(unsigned int prio)
int rc;
memset(&param, 0, sizeof(param));
param.sched_priority = prio;
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.";
LOG(INFO) << "Setting SCHED_RR priority " << param.sched_priority;
rc = sched_setscheduler(getpid(), SCHED_RR, &param);
if (rc != 0) {
LOG(ERROR) << "Config: Setting SCHED_RR failed";
@@ -489,38 +441,6 @@ 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;
@@ -538,14 +458,10 @@ 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 << " 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 << " C0 Filler Table......... " << trx->cfg.filler << std::endl;
ost << " Multi-Carrier........... " << trx->cfg.multi_arfcn << 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 << " Tuning offset........... " << trx->cfg.offset << std::endl;
ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << std::endl;
ost << " Swap channels........... " << trx->cfg.swap_channels << std::endl;
ost << " Tx Antennas.............";
for (i = 0; i < trx->cfg.num_chans; i++) {
@@ -630,18 +546,40 @@ 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
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();
@@ -659,7 +597,7 @@ int main(int argc, char *argv[])
if (rc < 0)
exit(1);
g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_TRX, NULL);
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL);
if (!g_ctrlh) {
LOG(ERROR) << "Failed to create CTRL interface.\n";
exit(1);
@@ -676,7 +614,6 @@ 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) {
@@ -689,9 +626,6 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
osmo_signal_register_handler(SS_MAIN, transc_sig_cb, NULL);
trx_rate_ctr_init(tall_trx_ctx, g_trx_ctx);
srandom(time(NULL));
if(trx_start(g_trx_ctx) < 0)
@@ -704,6 +638,5 @@ int main(int argc, char *argv[])
osmo_fd_unregister(&signal_ofd);
osmo_fd_close(&signal_ofd);
osmo_signal_unregister_handler(SS_MAIN, transc_sig_cb, NULL);
return 0;
}

View File

@@ -1,117 +0,0 @@
/*
* Copyright (C) 2019 sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* 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.
*/
#include "proto_trxd.h"
#include <osmocom/core/bits.h>
static void trxd_fill_common(struct trxd_hdr_common *common, const struct trx_ul_burst_ind *bi, uint8_t version)
{
common->version = version & 0b1111;
common->reserved = 0;
common->tn = bi->tn;
osmo_store32be(bi->fn, &common->fn);
}
static void trxd_fill_v0_specific(struct trxd_hdr_v0_specific *v0, const struct trx_ul_burst_ind *bi)
{
int toa_int;
/* in 1/256 symbols, round to closest integer */
toa_int = (int) (bi->toa * 256.0 + 0.5);
v0->rssi = bi->rssi;
osmo_store16be(toa_int, &v0->toa);
}
static void trxd_fill_v1_specific(struct trxd_hdr_v1_specific *v1, const struct trx_ul_burst_ind *bi)
{
int16_t ci_int_cB;
/* deciBels->centiBels, round to closest integer */
ci_int_cB = (int16_t)((bi->ci * 10) + 0.5);
v1->idle = !!bi->idle;
v1->modulation = (bi->modulation == MODULATION_GMSK) ?
TRXD_MODULATION_GMSK(bi->tss) :
TRXD_MODULATION_8PSK(bi->tss);
v1->tsc = bi->tsc;
osmo_store16be(ci_int_cB, &v1->ci);
}
static void trxd_fill_burst_normalized255(uint8_t* soft_bits, const struct trx_ul_burst_ind *bi)
{
unsigned i;
for (i = 0; i < bi->nbits; i++)
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)
return true;
/* +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;
trxd_fill_common(&pkt->common, bi, 0);
trxd_fill_v0_specific(&pkt->v0, bi);
trxd_fill_burst_normalized255(&pkt->soft_bits[0], bi);
/* +1: Historical reason. There's an uninitizalied byte in there: pkt->soft_bits[bi->nbits] */
pkt->soft_bits[bi->nbits + 1] = '\0';
rc = write(fd, buf, sizeof(struct trxd_hdr_v0) + bi->nbits + 2);
if (rc <= 0) {
CLOGCHAN(chan, DMAIN, LOGL_NOTICE, "mDataSockets write(%d) failed: %d\n", fd, rc);
return false;
}
return true;
}
bool trxd_send_burst_ind_v1(size_t chan, int fd, const struct trx_ul_burst_ind *bi) {
int rc;
size_t buf_len;
buf_len = sizeof(struct trxd_hdr_v1);
if (!bi->idle)
buf_len += bi->nbits;
char buf[buf_len];
struct trxd_hdr_v1* pkt = (struct trxd_hdr_v1*)buf;
trxd_fill_common(&pkt->common, bi, 1);
trxd_fill_v0_specific(&pkt->v0, bi);
trxd_fill_v1_specific(&pkt->v1, bi);
if (!bi->idle)
trxd_fill_burst_normalized255(&pkt->soft_bits[0], bi);
rc = write(fd, buf, buf_len);
if (rc <= 0) {
CLOGCHAN(chan, DMAIN, LOGL_NOTICE, "mDataSockets write(%d) failed: %d\n", fd, rc);
return false;
}
return true;
}

View File

@@ -1,101 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <math.h>
#include <osmocom/core/endian.h>
#include "debug.h"
#define MAX_RX_BURST_BUF_SIZE 444 /* 444 = EDGE_BURST_NBITS */
enum Modulation {
MODULATION_GMSK,
MODULATION_8PSK,
/* Not supported yet:
MODULATION_AQPSK,
MODULATION_16QAM,
MODULATION_32QAM
*/
};
struct trx_ul_burst_ind {
float rx_burst[MAX_RX_BURST_BUF_SIZE]; /* soft bits normalized 0..1 */
unsigned nbits; // number of symbols per slot in rxBurst, not counting guard periods
uint32_t fn; // TDMA frame number
uint8_t tn; // TDMA time-slot number
double rssi; // in dBFS
double toa; // in symbols
double noise; // noise level in dBFS
bool idle; // true if no valid burst is included
enum Modulation modulation; // modulation type
uint8_t tss; // training sequence set
uint8_t tsc; // training sequence code
float ci; // Carrier-to-Interference ratio, in dB
};
bool trxd_send_burst_ind_v0(size_t chan, int fd, const struct trx_ul_burst_ind *bi);
bool trxd_send_burst_ind_v1(size_t chan, int fd, const struct trx_ul_burst_ind *bi);
/* The latest supported TRXD header format version */
#define TRX_DATA_FORMAT_VER 1
struct trxd_hdr_common {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t tn:3,
reserved:1,
version:4;
#elif OSMO_IS_BIG_ENDIAN
uint8_t version:4,
reserved:1,
tn:3;
#endif
uint32_t fn; /* big endian */
} __attribute__ ((packed));
struct trxd_hdr_v0_specific {
uint8_t rssi;
uint16_t toa; /* big endian */
} __attribute__ ((packed));
struct trxd_hdr_v0 {
struct trxd_hdr_common common;
struct trxd_hdr_v0_specific v0;
uint8_t soft_bits[0];
} __attribute__ ((packed));
/* Downlink burst (BTS->TRX), v0 anf v1 use same format */
struct trxd_hdr_v01_dl {
struct trxd_hdr_common common;
uint8_t tx_att; /* Tx Attentuation */
uint8_t soft_bits[0];
} __attribute__ ((packed));
#define TRXD_MODULATION_GMSK(ts_set) (0b0000 | (ts_set & 0b0011))
#define TRXD_MODULATION_8PSK(ts_set) (0b0100 | (ts_set & 0b0001))
#define TRXD_MODULATION_AQPSK(ts_set) (0b0110 | (ts_set & 0b0001))
#define TRXD_MODULATION_16QAM(ts_set) (0b1000 | (ts_set & 0b0001))
#define TRXD_MODULATION_32QAM(ts_set) (0b1010 | (ts_set & 0b0001))
struct trxd_hdr_v1_specific {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t tsc:3,
modulation:4,
idle:1;
#elif OSMO_IS_BIG_ENDIAN
uint8_t idle:1,
modulation:4,
tsc:3;
#endif
int16_t ci; /* big endian, in centiBels */
} __attribute__ ((packed));
struct trxd_hdr_v1 {
struct trxd_hdr_common common;
struct trxd_hdr_v0_specific v0;
struct trxd_hdr_v1_specific v1;
uint8_t soft_bits[0];
} __attribute__ ((packed));

View File

@@ -5,8 +5,6 @@
*
* Author: Tom Tsou <tom@tsou.cc>
*
* 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
@@ -98,7 +96,7 @@ const float *RadioBuffer::getReadSegment()
/*
* Output direction
*
* Write a non-segment length of samples to the buffer.
* Write a non-segment length of samples to the buffer.
*/
bool RadioBuffer::write(const float *wr, size_t len)
{

View File

@@ -4,8 +4,6 @@
*
* Copyright 2011 Free Software Foundation, Inc.
*
* 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

Some files were not shown because too many files have changed in this diff Show More