Compare commits

..

2 Commits

Author SHA1 Message Date
Sergey Kostanbaev
3e3507d09d Initial XTRX support
Change-Id: I1067dfef53aa2669cc7c189cccae10074c674390
2019-10-09 01:47:55 +03:00
Sergey Kostanbaev
bcd1e72558 CommonLibs: check HAVE_CONFIG_H before including it
Change-Id: Idb9e938e7794b67b1db23a31e106c8945f79cf24
2019-10-09 01:47:55 +03:00
87 changed files with 1239 additions and 5534 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
...

7
.gitignore vendored
View File

@@ -5,7 +5,6 @@
Transceiver52M/osmo-trx-uhd
Transceiver52M/osmo-trx-usrp1
Transceiver52M/osmo-trx-lms
Transceiver52M/osmo-trx-ipc
# tests
tests/CommonLibs/BitVectorTest
@@ -20,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
@@ -65,8 +63,3 @@ doc/manuals/generated/
doc/manuals/osmomsc-usermanual.xml
doc/manuals/common
doc/manuals/build
contrib/osmo-trx.spec
!contrib/osmo-trx.spec.in
utils/osmo-prbs-tool

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

@@ -37,7 +37,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

@@ -47,7 +47,7 @@
// (pat) The elements in the queue are type T*, and
// the Fifo class implements the underlying queue.
// The default is class PointerFIFO, which does not place any restrictions on the type of T,
// and is implemented by allocating 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*).

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under multiple licenses; see the COPYING file in
* the main directory for licensing information for this specific distribution.
* 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

@@ -48,11 +48,9 @@ Log::~Log()
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);
log_mutex_lock_canceldisable(&old_state);
LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str());
pthread_setcancelstate(old_state, NULL);
log_mutex_unlock_canceldisable(old_state);
}
ostringstream& Log::get()

View File

@@ -58,9 +58,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

@@ -186,7 +186,7 @@ class Thread {
}
}
/** Send cancellation to thread */
/** Send cancelation to thread */
void cancel() { pthread_cancel(mThread); }
};

View File

@@ -84,7 +84,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

@@ -36,7 +36,7 @@
#include <assert.h>
#include <stdlib.h>
// 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;}
@@ -232,7 +232,7 @@ template <class T> class Vector {
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)); */
}

View File

@@ -21,17 +21,7 @@
* See the COPYING file in the main directory for details.
*/
#include "config.h"
/* If HAVE_GETTID, then "_GNU_SOURCE" may need to be defined to use gettid() */
#if HAVE_GETTID
#define _GNU_SOURCE
#endif
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "config.h"
#include <pthread.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
@@ -45,39 +35,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",
[DLMS] = {
.name = "DLMS",
.description = "Logging from within LimeSuite itself",
.color = NULL,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
@@ -88,14 +60,48 @@ const struct log_info log_info = {
.num_cat = ARRAY_SIZE(default_categories),
};
pid_t my_gettid(void)
{
#if HAVE_GETTID
return gettid();
#elif defined(LINUX) && defined(__NR_gettid)
return (pid_t) syscall(__NR_gettid);
#else
#pragma message ("use pid as tid")
return getpid();
#endif
pthread_mutex_t log_mutex;
bool log_mutex_init() {
int rc;
pthread_mutexattr_t attr;
if ((rc = pthread_mutexattr_init(&attr))) {
fprintf(stderr, "pthread_mutexattr_init() failed: %d\n", rc);
return false;
}
if ((rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE))) {
fprintf(stderr, "pthread_mutexattr_settype() failed: %d\n", rc);
return false;
}
if ((rc = pthread_mutex_init(&log_mutex, &attr))) {
fprintf(stderr, "pthread_mutex_init() failed: %d\n", rc);
return false;
}
if ((rc = pthread_mutexattr_destroy(&attr))) {
fprintf(stderr, "pthread_mutexattr_destroy() failed: %d\n", rc);
return false;
}
return true;
/* FIXME: do we need to call pthread_mutex_destroy() during process exit? */
}
/* If called inside a C++ destructor, use log_mutex_(un)lock_canceldisable() APIs instead.
See osmo-trx commit 86be40b4eb762d5c12e8e3f7388ca9f254e77b36 for more information */
void log_mutex_lock() {
OSMO_ASSERT(!pthread_mutex_lock(&log_mutex));
}
void log_mutex_unlock() {
OSMO_ASSERT(!pthread_mutex_unlock(&log_mutex));
}
void log_mutex_lock_canceldisable(int *st) {
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, st);
log_mutex_lock();
}
void log_mutex_unlock_canceldisable(int st) {
log_mutex_unlock();
pthread_setcancelstate(st, NULL);
}

View File

@@ -1,7 +1,7 @@
#pragma once
#include <stdbool.h>
#include <sys/types.h>
#include <pthread.h>
#include <osmocom/core/logging.h>
@@ -10,20 +10,26 @@ extern const struct log_info log_info;
/* Debug Areas of the code */
enum {
DMAIN,
DTRXCLK,
DTRXCTRL,
DTRXDDL,
DTRXDUL,
DDEV,
DDEVDRV,
DLMS,
};
pid_t my_gettid(void);
bool log_mutex_init();
void log_mutex_lock();
void log_mutex_unlock();
void log_mutex_lock_canceldisable(int *st);
void log_mutex_unlock_canceldisable(int st);
#define CLOGC(category, level, fmt, args...) do { \
LOGP(category, level, "[tid=%ld] " fmt, (long int) my_gettid(), ##args); \
log_mutex_lock(); \
LOGP(category, level, "[tid=%lu] " fmt, pthread_self(), ##args); \
log_mutex_unlock(); \
} while(0)
#define CLOGCHAN(chan, category, level, fmt, args...) do { \
LOGP(category, level, "[tid=%ld][chan=%zu] " fmt, (long int) my_gettid(), chan, ##args); \
log_mutex_lock(); \
LOGP(category, level, "[tid=%lu][chan=%lu] " fmt, pthread_self(), chan, ##args); \
log_mutex_unlock(); \
} while(0)

View File

@@ -38,7 +38,7 @@
* That signal is processed here in device_sig_cb, where a copy of the "struct
* device_counters" structure is held and the main thread is instructed through
* a timerfd to update rate_ctr APIs against this copy. All this is done inside
* a mutex to avoid different race conditions (between Rx andTx threads, and
* a mutex to avoid different race conditons (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

View File

@@ -32,42 +32,34 @@
#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"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
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 }
};
struct trx_ctx *trx_from_vty(struct vty *v)
{
/* It can't hurt to force callers to continue to pass the vty instance
@@ -183,10 +175,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")
{
@@ -269,6 +308,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;
}
@@ -315,57 +355,14 @@ DEFUN(cfg_stack_size, cfg_stack_size_cmd,
return CMD_SUCCESS;
}
DEFUN(cfg_filler, cfg_filler_type_cmd,
"filler type (zero|dummy|random-nb-gmsk|random-nb-8psk|random-ab)",
"Filler burst settings\n"
"Filler burst type (default=zero)\n"
"Send an empty burst when there is nothing to send (default)\n"
"Send a dummy burst when there is nothing to send on C0 (TRX0) and empty burst on other channels."
" Use for OpenBTS compatibility only, don't use with OsmoBTS as it breaks encryption.\n"
"Send a GMSK modulated Normal Burst with random bits when there is nothing to send."
" Use for spectrum mask testing. Configure 'filler tsc' to set training sequence.\n"
"Send an 8-PSK modulated Normal Burst with random bits when there is nothing to send."
" Use for spectrum mask testing. Configure 'filler tsc' to set training sequence.\n"
"Send an Access Burst with random bits when there is nothing to send. Use for Rx/Tx alignment."
" Configure 'filler access-burst-delay' to introduce artificial delay.\n"
)
{
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>",
"Filler burst settings\n"
"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")
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.rtsc = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_test_rach_delay, cfg_filler_rach_delay_cmd,
"filler access-burst-delay <0-68>",
"Filler burst settings\n"
"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]);
trx->cfg.filler = FILLER_DUMMY;
return CMD_SUCCESS;
}
@@ -553,6 +550,10 @@ 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);
@@ -565,12 +566,6 @@ static int config_write_trx(struct vty *vty)
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, " ");
@@ -598,9 +593,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);
@@ -669,7 +666,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"
@@ -712,6 +708,8 @@ 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);
@@ -720,9 +718,7 @@ int trx_vty_init(struct trx_ctx* trx)
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_filler_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);
@@ -732,7 +728,5 @@ int trx_vty_init(struct trx_ctx* trx)
install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
logging_vty_add_deprecated_subsys(g_trx_ctx, "lms");
return 0;
}

View File

@@ -5,8 +5,6 @@
#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
@@ -53,7 +51,9 @@ struct trx_ctx {
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;

View File

@@ -33,16 +33,12 @@ SUBDIRS = \
GSM \
Transceiver52M \
contrib \
tests \
utils
tests
EXTRA_DIST = \
LEGAL \
COPYING \
README.md \
contrib/osmo-trx.spec.in \
debian \
$(NULL)
README.md
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)

View File

@@ -98,7 +98,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

@@ -225,7 +225,7 @@ bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen)
}
/*
* 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),

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

@@ -105,10 +105,12 @@ osmo_trx_lms_LDADD = \
osmo_trx_lms_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
endif
bin_PROGRAMS += osmo-trx-ipc
osmo_trx_ipc_SOURCES = osmo-trx.cpp
osmo_trx_ipc_LDADD = \
$(builddir)/device/ipc/libdevice.la \
if DEVICE_XTRX
bin_PROGRAMS += osmo-trx-xtrx
osmo_trx_xtrx_SOURCES = osmo-trx.cpp
osmo_trx_xtrx_LDADD = \
$(builddir)/device/xtrx/libdevice.la \
$(COMMON_LDADD) \
$(UHD_LIBS)
osmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
$(XTRX_LIBS)
osmo_trx_xtrx_CPPFLAGS = $(AM_CPPFLAGS) $(XTRX_CFLAGS)
endif

View File

@@ -35,12 +35,12 @@ public:
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.

View File

@@ -45,8 +45,6 @@ extern "C" {
using namespace GSM;
Transceiver *transceiver;
#define USB_LATENCY_INTRVL 10,0
/* Number of running values use in noise average */
@@ -153,30 +151,20 @@ Transceiver::~Transceiver()
close(mClockSocket);
for (size_t i = 0; i < mChans; i++) {
if (mControlServiceLoopThreads[i]) {
mControlServiceLoopThreads[i]->cancel();
mControlServiceLoopThreads[i]->join();
delete mControlServiceLoopThreads[i];
}
mTxPriorityQueues[i].clear();
if (mCtrlSockets[i] >= 0)
close(mCtrlSockets[i]);
if (mDataSockets[i] >= 0)
close(mDataSockets[i]);
}
}
int Transceiver::ctrl_sock_cb(struct osmo_fd *bfd, unsigned int flags)
{
int rc = 0;
int chan = static_cast<int>(reinterpret_cast<uintptr_t>(bfd->data));
if (flags & OSMO_FD_READ)
rc = transceiver->ctrl_sock_handle_rx(chan);
if (rc < 0)
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
if (flags & OSMO_FD_WRITE)
rc = transceiver->ctrl_sock_write(chan);
if (rc < 0)
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
return rc;
}
/*
* Initialize transceiver
*
@@ -192,12 +180,12 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
int d_srcport, d_dstport, c_srcport, c_dstport;
if (!mChans) {
LOG(FATAL) << "No channels assigned";
LOG(ALERT) << "No channels assigned";
return false;
}
if (!sigProcLibSetup()) {
LOG(FATAL) << "Failed to initialize signal processing library";
LOG(ALERT) << "Failed to initialize signal processing library";
return false;
}
@@ -205,7 +193,8 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
mEdge = edge;
mDataSockets.resize(mChans, -1);
mCtrlSockets.resize(mChans);
mCtrlSockets.resize(mChans, -1);
mControlServiceLoopThreads.resize(mChans);
mTxPriorityQueueServiceLoopThreads.resize(mChans);
mRxServiceLoopThreads.resize(mChans);
@@ -227,34 +216,24 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
return false;
for (size_t i = 0; i < mChans; i++) {
int rv;
c_srcport = mBasePort + 2 * i + 1;
c_dstport = mBasePort + 2 * i + 101;
d_srcport = mBasePort + 2 * i + 2;
d_dstport = mBasePort + 2 * i + 102;
rv = osmo_sock_init2_ofd(&mCtrlSockets[i].conn_bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
mCtrlSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
mLocalAddr.c_str(), c_srcport,
mRemoteAddr.c_str(), c_dstport,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (rv < 0)
if (mCtrlSockets[i] < 0)
return false;
mCtrlSockets[i].conn_bfd.cb = ctrl_sock_cb;
mCtrlSockets[i].conn_bfd.data = reinterpret_cast<void*>(i);
mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
mLocalAddr.c_str(), d_srcport,
mRemoteAddr.c_str(), d_dstport,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (mDataSockets[i] < 0)
if (mCtrlSockets[i] < 0)
return false;
if (i && filler == FILLER_DUMMY)
filler = FILLER_ZERO;
mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
}
/* Randomize the central clock */
@@ -264,6 +243,21 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
mLastClockUpdateTime = startTime;
mLatencyUpdateTime = startTime;
/* Start control threads */
for (size_t i = 0; i < mChans; i++) {
TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
params->trx = this;
params->num = i;
mControlServiceLoopThreads[i] = new Thread(stackSize);
mControlServiceLoopThreads[i]->start((void * (*)(void*))
ControlServiceLoopAdapter, (void*) params);
if (i && filler == FILLER_DUMMY)
filler = FILLER_ZERO;
mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
}
return true;
}
@@ -291,7 +285,7 @@ bool Transceiver::start()
mLatencyUpdateTime = time;
if (!mRadioInterface->start()) {
LOG(FATAL) << "Device failed to start";
LOG(ALERT) << "Device failed to start";
return false;
}
@@ -377,12 +371,12 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
radioVector *radio_burst;
if (chan >= mTxPriorityQueues.size()) {
LOGCHAN(chan, DTRXDDL, FATAL) << "Invalid channel";
LOG(ALERT) << "Invalid channel " << chan;
return;
}
if (wTime.TN() > 7) {
LOGCHAN(chan, DTRXDDL, FATAL) << "Received burst with invalid slot " << wTime.TN();
LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
return;
}
@@ -425,7 +419,7 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
state = &mStates[i];
while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
LOGCHAN(i, DTRXDDL, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
<< burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
if (state->mRetrans)
updateFillerTable(i, burst);
@@ -612,7 +606,7 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
/* Blocking FIFO read */
radioVector *radio_burst = mReceiveFIFO[chan]->read();
if (!radio_burst) {
LOGCHAN(chan, DTRXDUL, ERROR) << "ReceiveFIFO->read() returned no burst";
LOGCHAN(chan, DMAIN, ERROR) << "ReceiveFIFO->read() returned no burst";
return -EIO;
}
@@ -657,7 +651,7 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
}
if (max_i < 0) {
LOGCHAN(chan, DTRXDUL, FATAL) << "Received empty burst";
LOG(ALERT) << "Received empty burst";
goto ret_idle;
}
@@ -684,9 +678,9 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
if (rc <= 0) {
if (rc == -SIGERR_CLIP)
LOGCHAN(chan, DTRXDUL, NOTICE) << "Clipping detected on received RACH or Normal Burst";
LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
else if (rc != SIGERR_NONE)
LOGCHAN(chan, DTRXDUL, NOTICE) << "Unhandled RACH or Normal Burst detection error";
LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
goto ret_idle;
}
@@ -725,6 +719,8 @@ void Transceiver::reset()
}
#define MAX_PACKET_LENGTH 100
/**
* Matches a buffer with a command.
* @param buf a buffer to look command in
@@ -754,77 +750,27 @@ static bool match_cmd(char *buf,
return true;
}
void Transceiver::ctrl_sock_send(ctrl_msg& m, int chan)
bool Transceiver::driveControl(size_t chan)
{
ctrl_sock_state& s = mCtrlSockets[chan];
struct osmo_fd *conn_bfd = &s.conn_bfd;
s.txmsgqueue.push_back(m);
conn_bfd->when |= OSMO_FD_WRITE;
}
int Transceiver::ctrl_sock_write(int chan)
{
int rc;
ctrl_sock_state& s = mCtrlSockets[chan];
if (s.conn_bfd.fd < 0) {
return -EIO;
}
while (s.txmsgqueue.size()) {
const ctrl_msg m = s.txmsgqueue.front();
s.conn_bfd.when &= ~OSMO_FD_WRITE;
/* try to send it over the socket */
rc = write(s.conn_bfd.fd, m.data, strlen(m.data) + 1);
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
s.conn_bfd.when |= OSMO_FD_WRITE;
break;
}
goto close;
}
s.txmsgqueue.pop_front();
}
return 0;
close:
LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets write(" << s.conn_bfd.fd << ") failed: " << rc;
return -1;
}
int Transceiver::ctrl_sock_handle_rx(int chan)
{
ctrl_msg cmd_received;
ctrl_msg cmd_to_send;
char *buffer = cmd_received.data;
char *response = cmd_to_send.data;
char buffer[MAX_PACKET_LENGTH + 1];
char response[MAX_PACKET_LENGTH + 1];
char *command, *params;
int msgLen;
ctrl_sock_state& s = mCtrlSockets[chan];
/* Attempt to read from control socket */
msgLen = read(s.conn_bfd.fd, buffer, sizeof(cmd_received.data)-1);
if (msgLen < 0 && errno == EAGAIN)
return 0; /* Try again later */
msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
if (msgLen <= 0) {
LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets read(" << s.conn_bfd.fd << ") failed: " << msgLen;
return -EIO;
LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
return false;
}
/* Zero-terminate received string */
buffer[msgLen] = '\0';
/* Verify a command signature */
if (strncmp(buffer, "CMD ", 4)) {
LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
return -EIO;
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
return false;
}
/* Set command pointer */
@@ -848,7 +794,7 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
unsigned ts = 0, ss = 0;
sscanf(params, "%u %u", &ts, &ss);
if (ts > 7 || ss > 7) {
sprintf(response, "RSP HANDOVER 1 %u %u", ts, ss);
sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
} else {
mHandover[ts][ss] = true;
sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
@@ -908,7 +854,7 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
sscanf(params, "%d", &freqKhz);
mRxFreq = freqKhz * 1e3;
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune";
LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
}
else
@@ -919,7 +865,7 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
sscanf(params, "%d", &freqKhz);
mTxFreq = freqKhz * 1e3;
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune";
LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
}
else
@@ -941,9 +887,9 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
int timeslot;
sscanf(params, "%d %d", &timeslot, &corrCode);
if ((timeslot < 0) || (timeslot > 7)) {
LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
return 0;
return true;
}
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
setModulus(timeslot, chan);
@@ -963,20 +909,24 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
}
} else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
// debug command! may change or disappear without notice
// debug command! may change or disapear without notice
// set a mask which bursts to dump to disk
int mask;
sscanf(params, "%d", &mask);
mWriteBurstToDiskMask = mask;
sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
} else {
LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus command " << command << " on control interface.";
LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
sprintf(response,"RSP ERR 1");
}
LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
transceiver->ctrl_sock_send(cmd_to_send, chan);
return 0;
msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
if (msgLen <= 0) {
LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
return false;
}
return true;
}
bool Transceiver::driveTxPriorityQueue(size_t chan)
@@ -990,7 +940,7 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
// check data socket
msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
if (msgLen <= 0) {
LOGCHAN(chan, DTRXDDL, NOTICE) << "mDataSockets read(" << mDataSockets[chan] << ") failed: " << msgLen;
LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
return false;
}
@@ -1000,13 +950,13 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
break;
case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */
if (mSPSTx != 4) {
LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << mSPSTx;
LOG(ERR) << "EDGE burst received but SPS is set to " << mSPSTx;
return false;
}
burstLen = EDGE_BURST_NBITS;
break;
default:
LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
LOG(ERR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
return false;
}
@@ -1022,12 +972,13 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
case 1:
break;
default:
LOGCHAN(chan, DTRXDDL, ERROR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
LOG(ERR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
return false;
}
LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version)
<< "): fn=" << fn << ", tn=" << unsigned(dl->common.tn) << ", burst_len=" << burstLen;
LOG(DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version) << "): "
<< "fn=" << fn << ", tn=" << unsigned(dl->common.tn) << ", "
<< "burst_len=" << burstLen;
BitVector newBurst(burstLen);
BitVector::iterator itr = newBurst.begin();
@@ -1053,8 +1004,6 @@ bool Transceiver::driveReceiveRadio()
return false;
if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
if (mForceClockInterface)
LOGC(DTRXCLK, NOTICE) << "Sending CLOCK indications";
mForceClockInterface = false;
return writeClockInterface();
}
@@ -1071,7 +1020,7 @@ void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
else os << "-";
}
LOGCHAN(chan, DTRXDUL, DEBUG) << std::fixed << std::right
LOGCHAN(chan, DMAIN, DEBUG) << std::fixed << std::right
<< " time: " << unsigned(bi->tn) << ":" << bi->fn
<< " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
<< "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
@@ -1089,7 +1038,7 @@ bool Transceiver::driveReceiveFIFO(size_t chan)
if ((rc = pullRadioVector(chan, &bi)) < 0) {
if (rc == -ENOENT) { /* timeslot off, continue processing */
LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
LOGCHAN(chan, DMAIN, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
return true;
}
return false; /* other errors: we want to stop the process */
@@ -1126,7 +1075,7 @@ void Transceiver::driveTxFIFO()
if (mOn) {
//radioClock->wait(); // wait until clock updates
LOGC(DTRXCLK, DEBUG) << "radio clock " << radioClock->get();
LOG(DEBUG) << "radio clock " << radioClock->get();
while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
// if underrun, then we're not providing bursts to radio/USRP fast
// enough. Need to increase latency by one GSM frame.
@@ -1135,9 +1084,8 @@ void Transceiver::driveTxFIFO()
// only update latency at the defined frame interval
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
LOGC(DTRXCLK, INFO) << "new latency: " << mTransmitLatency << " (underrun "
<< radioClock->get() << " vs "
<< mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
<< radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
mLatencyUpdateTime = radioClock->get();
}
}
@@ -1147,7 +1095,7 @@ void Transceiver::driveTxFIFO()
if (mTransmitLatency > mRadioInterface->minLatency()) {
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
mTransmitLatency.decTN();
LOGC(DTRXCLK, INFO) << "reduced latency: " << mTransmitLatency;
LOG(INFO) << "reduced latency: " << mTransmitLatency;
mLatencyUpdateTime = radioClock->get();
}
}
@@ -1171,11 +1119,11 @@ bool Transceiver::writeClockInterface()
// FIXME -- This should be adaptive.
sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
LOGC(DTRXCLK, INFO) << "sending " << command;
LOG(INFO) << "ClockInterface: sending " << command;
msgLen = write(mClockSocket, command, strlen(command) + 1);
if (msgLen <= 0) {
LOGC(DTRXCLK, ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
LOG(ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
return false;
}
@@ -1196,7 +1144,7 @@ void *RxUpperLoopAdapter(TrxChanThParams *params)
while (1) {
if (!trx->driveReceiveFIFO(num)) {
LOGCHAN(num, DTRXDUL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
LOGCHAN(num, DMAIN, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
break;
}
@@ -1211,7 +1159,7 @@ void *RxLowerLoopAdapter(Transceiver *transceiver)
while (1) {
if (!transceiver->driveReceiveRadio()) {
LOGC(DTRXDUL, FATAL) << "Something went wrong in thread RxLower, requesting stop";
LOG(FATAL) << "Something went wrong in thread RxLower, requesting stop";
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
break;
}
@@ -1231,6 +1179,28 @@ void *TxLowerLoopAdapter(Transceiver *transceiver)
return NULL;
}
void *ControlServiceLoopAdapter(TrxChanThParams *params)
{
char thread_name[16];
Transceiver *trx = params->trx;
size_t num = params->num;
free(params);
snprintf(thread_name, 16, "CtrlService%zu", num);
set_selfthread_name(thread_name);
while (1) {
if (!trx->driveControl(num)) {
LOGCHAN(num, DTRXCTRL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
break;
}
pthread_testcancel();
}
return NULL;
}
void *TxUpperLoopAdapter(TrxChanThParams *params)
{
char thread_name[16];
@@ -1244,7 +1214,7 @@ void *TxUpperLoopAdapter(TrxChanThParams *params)
while (1) {
if (!trx->driveTxPriorityQueue(num)) {
LOGCHAN(num, DTRXDDL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
LOGCHAN(num, DMAIN, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
break;
}

View File

@@ -33,14 +33,11 @@
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;
@@ -145,33 +142,12 @@ public:
} ChannelCombination;
private:
struct ctrl_msg {
char data[101];
ctrl_msg() {};
};
struct ctrl_sock_state {
osmo_fd conn_bfd;
std::deque<ctrl_msg> txmsgqueue;
ctrl_sock_state() {
conn_bfd.fd = -1;
}
~ctrl_sock_state() {
if(conn_bfd.fd >= 0) {
close(conn_bfd.fd);
conn_bfd.fd = -1;
osmo_fd_unregister(&conn_bfd);
}
}
};
int mBasePort;
std::string mLocalAddr;
std::string mRemoteAddr;
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
std::vector<int> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
int mClockSocket; ///< socket for writing clock updates to GSM core
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
@@ -180,6 +156,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
@@ -216,12 +193,6 @@ struct ctrl_sock_state {
/** 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);
int mSPSTx; ///< number of samples per Tx symbol
int mSPSRx; ///< number of samples per Rx symbol
size_t mChans;
@@ -245,7 +216,7 @@ struct ctrl_sock_state {
bool start();
void stop();
/** Protect destructor accessible stop call */
/** Protect destructor accessable stop call */
Mutex mLock;
protected:
@@ -258,6 +229,9 @@ protected:
/** drive transmission of GSM bursts */
void driveTxFIFO();
/** drive handling of control messages from GSM core */
bool driveControl(size_t chan);
/**
drive modulation and sorting of GSM bursts from GSM core
@return true if a burst was transferred successfully
@@ -268,6 +242,7 @@ protected:
friend void *TxUpperLoopAdapter(TrxChanThParams *params);
friend void *RxLowerLoopAdapter(Transceiver *transceiver);
friend void *TxLowerLoopAdapter(Transceiver *transceiver);
friend void *ControlServiceLoopAdapter(TrxChanThParams *params);
void reset();
@@ -281,5 +256,8 @@ void *RxUpperLoopAdapter(TrxChanThParams *params);
void *RxLowerLoopAdapter(Transceiver *transceiver);
void *TxLowerLoopAdapter(Transceiver *transceiver);
/** control message handler thread loop */
void *ControlServiceLoopAdapter(TrxChanThParams *params);
/** transmit queueing thread loop */
void *TxUpperLoopAdapter(TrxChanThParams *params);

View File

@@ -58,7 +58,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

@@ -103,7 +103,7 @@ void free_fft(struct fft_hdl *hdl)
}
/*! \brief Run multiple DFT operations with the initialized plan
* \param[in] hdl handle to an 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;

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
@@ -17,3 +13,7 @@ endif
if DEVICE_LMS
SUBDIRS += lms
endif
if DEVICE_XTRX
SUBDIRS += xtrx
endif

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.
@@ -77,20 +77,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;

View File

@@ -154,7 +154,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:
@@ -166,8 +166,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

@@ -33,7 +33,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:
@@ -68,7 +68,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,235 +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 software is distributed under multiple licenses; see the COPYING file in
* the main directory for licensing information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
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.
*/
#ifndef _IPC_DEVICE_H_
#define _IPC_DEVICE_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
extern "C" {
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include "shm.h"
#include "ipc_shm.h"
}
#include "radioDevice.h"
#include "smpl_buf.h"
#include <sys/time.h>
#include <math.h>
#include <limits.h>
#include <string>
#include <iostream>
struct ipc_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 */
};
/** A class to handle a LimeSuite supported device */
class IPCDevice : public RadioDevice {
protected:
struct ipc_sock_state sk_state;
/* FIXME: current limit 8 chans, make dynamic */
struct ipc_sock_state sk_chan_state[8];
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; ///< the actual USRP sampling rate
bool started; ///< flag indicates LMS has started
bool skipRx; ///< set if LMS is transmit-only.
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 do_calib(size_t chan);
bool do_filters(size_t chan);
int get_ant_idx(const std::string &name, bool dir_tx, size_t chan);
virtual 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);
public:
virtual void ipc_sock_close(ipc_sock_state *state);
virtual int ipc_sock_read(struct osmo_fd *bfd);
virtual int ipc_sock_write(struct osmo_fd *bfd);
virtual int ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim);
virtual int ipc_rx_greeting_cnf(const struct ipc_sk_if_greeting *greeting_cnf);
virtual int ipc_rx_info_cnf(const struct ipc_sk_if_info_cnf *info_cnf);
virtual int ipc_rx_open_cnf(const struct ipc_sk_if_open_cnf *open_cnf);
virtual int ipc_tx_open_req(struct ipc_sock_state *state, uint32_t num_chans, uint32_t ref);
/** 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 LMS */
virtual int open(const std::string &args, int ref, bool swap_channels) override;
/** Start the LMS */
virtual bool start() override;
/** Stop the LMS */
virtual bool stop() override;
enum TxWindowType getWindowType() override
{
return TX_WINDOW_LMS1;
}
/**
Read samples from the LMS.
@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 LMS 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 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.
@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;
/** sets the transmit chan gain, returns the gain setting **/
virtual double setTxGain(double dB, size_t chan = 0) override;
/** get transmit gain */
virtual double getTxGain(size_t chan = 0) override
{
return tx_gains[chan];
}
/** return maximum Tx Gain **/
virtual double maxTxGain(void) override;
/** return minimum Rx Gain **/
virtual double minTxGain(void) 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;
}
int ipc_chan_sock_read(osmo_fd *bfd);
int ipc_chan_sock_write(osmo_fd *bfd);
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);
};
#endif // _IPC_DEVICE_H_

View File

@@ -1,29 +0,0 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_LDFLAGS = -lpthread -lrt
noinst_HEADERS = IPCDevice.h shm.h ../uhd/UHDDevice.h uhdwrap.h
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = ipc-driver-test.c IPCDevice.cpp shm.c ../uhd/UHDDevice.cpp uhdwrap.cpp 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) $(UHD_CFLAGS) -DIPCMAGIC
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 ../uhd/UHDDevice.cpp
ipc_driver_test_LDADD = \
shm.lo \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS)
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

View File

@@ -1,467 +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.
*/
#ifdef __cplusplus
extern "C" {
#endif
#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"
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;
/* Debug Areas of the code */
//enum { DMAIN,
//};
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),
};
#ifdef __cplusplus
}
#endif
#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_%d", IPC_SOCK_PATH_PREFIX, 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;
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 (unsigned int 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;
}
void *uplink_thread(void *x_void_ptr)
{
uint32_t chann = decoded_region->num_chans;
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;
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;
/* no per-chan start/stop */
rc = uhdwrap_start(global_dev, chan_nr);
pthread_t rx, tx;
pthread_create(&rx, NULL, uplink_thread, 0);
pthread_create(&tx, NULL, downlink_thread, 0);
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_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;
}
bfd->when = BSC_FD_READ;
bfd->cb = sock_callback_fn;
bfd->data = state;
bfd->priv_nr = 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;
}
//osmo_signal_register_handler(SS_GLOBAL, IPC_if_signal_cb, NULL);
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"
" -n --sock-num NR Master socket suffix number NR\n"
);
}
static int msocknum = 0;
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
const struct option long_options[] = {
{ "help", 0, 0, 'h' },
{ "sock-num", 1, 0, 'n' },
{0,0,0,0}
};
c = getopt_long(argc, argv, "hn:",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
print_help();
exit(0);
break;
case 'n':
msocknum = atoi(optarg);
break;
default:
exit(2);
break;
}
}
if (argc > optind) {
fprintf(stderr, "Unsupported positional arguments on command line\n");
exit(2);
}
}
#if defined(IPCMAGIC) && defined(__cplusplus)
extern "C" int osmo_ctx_init(const char *id);
extern "C" int magicmain(int argc, char **argv)
{
osmo_ctx_init("main");
osmo_select_init();
#else
int main(int argc, char **argv)
{
#endif
char ipc_msock_path[sizeof(IPC_SOCK_PATH_PREFIX)+3];
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);
snprintf(ipc_msock_path,sizeof(ipc_msock_path), "%s%d", IPC_SOCK_PATH_PREFIX, 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);
//ipc_sock_close()
return 0;
}

View File

@@ -1,42 +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 to lcr */
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);

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;
default:
LOGP(DMAIN, LOGL_ERROR, "Received unknown IPC msg type %d\n", msg_type);
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(DMAIN, 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(DMAIN, 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(DMAIN, LOGL_NOTICE,
"IPC socket not connected, "
"dropping message\n");
msgb_free(msg);
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
conn_bfd->when |= BSC_FD_WRITE;
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;
bfd->when &= ~BSC_FD_WRITE;
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
LOGP(DMAIN, 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) {
bfd->when |= BSC_FD_WRITE;
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_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags)
{
int rc = 0;
if (flags & BSC_FD_READ)
rc = ipc_chan_sock_read(bfd);
if (rc < 0)
return rc;
if (flags & BSC_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(DMAIN, LOGL_ERROR, "Failed to accept a new connection\n");
return -1;
}
if (conn_bfd->fd >= 0) {
LOGP(DMAIN, LOGL_NOTICE,
"osmo-trx connects but we already have "
"another active connection ?!?\n");
/* We already have one IPC connected, this is all we support */
state->listen_bfd.when &= ~BSC_FD_READ;
close(rc);
return 0;
}
conn_bfd->fd = rc;
conn_bfd->when = BSC_FD_READ;
conn_bfd->cb = ipc_chan_sock_cb;
conn_bfd->data = state;
if (osmo_fd_register(conn_bfd) != 0) {
LOGP(DMAIN, LOGL_ERROR,
"Failed to register new connection "
"fd\n");
close(conn_bfd->fd);
conn_bfd->fd = -1;
return -1;
}
LOGP(DMAIN, LOGL_NOTICE, "Unix socket connected to external osmo-trx\n");
/* send current info */
//IPC_tx_info_ind();
return 0;
}

View File

@@ -1,23 +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,204 +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>
#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) {
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
rv = pthread_mutexattr_setrobust(&att, PTHREAD_MUTEX_ROBUST);
if (rv != 0) {
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
rv = pthread_mutexattr_setpshared(&att, PTHREAD_PROCESS_SHARED);
if (rv != 0) {
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
rv = pthread_mutex_init((pthread_mutex_t *)&r->this_stream->lock, &att);
if (rv != 0) {
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
pthread_mutexattr_destroy(&att);
rv = pthread_condattr_setpshared(&t1, PTHREAD_PROCESS_SHARED);
if (rv != 0) {
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
rv = pthread_condattr_setpshared(&t2, PTHREAD_PROCESS_SHARED);
if (rv != 0) {
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
rv = pthread_cond_init((pthread_cond_t *)&r->this_stream->cf, &t1);
if (rv != 0) {
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
rv = pthread_cond_init((pthread_cond_t *)&r->this_stream->ce, &t2);
if (rv != 0) {
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
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) {
if (r->buf_ptrs)
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,43 +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; // plus num_buffers at end
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,271 +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(DMAIN, LOGL_ERROR, "Received unknown IPC msg type %d\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;
//struct ipc_sk_if *ipc_prim = (struct ipc_sk_if *) msg->data;
if (!state) {
LOGP(DMAIN, 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(DMAIN, LOGL_NOTICE,
"IPC socket not connected, "
"dropping message\n");
msgb_free(msg);
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
conn_bfd->when |= BSC_FD_WRITE;
return 0;
}
void ipc_sock_close(struct ipc_sock_state *state)
{
struct osmo_fd *bfd = &state->conn_bfd;
LOGP(DMAIN, 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 */
state->listen_bfd.when |= BSC_FD_READ;
/* 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(DMAIN, 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;
bfd->when &= ~BSC_FD_WRITE;
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
LOGP(DMAIN, 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) {
bfd->when |= BSC_FD_WRITE;
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 & BSC_FD_READ)
rc = ipc_sock_read(bfd);
if (rc < 0)
return rc;
if (flags & BSC_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(DMAIN, LOGL_ERROR, "Failed to accept a new connection\n");
return -1;
}
if (conn_bfd->fd >= 0) {
LOGP(DMAIN, LOGL_NOTICE,
"ip clent connects but we already have "
"another active connection ?!?\n");
/* We already have one IPC connected, this is all we support */
state->listen_bfd.when &= ~BSC_FD_READ;
close(rc);
return 0;
}
conn_bfd->fd = rc;
conn_bfd->when = BSC_FD_READ;
conn_bfd->cb = ipc_sock_cb;
conn_bfd->data = state;
if (osmo_fd_register(conn_bfd) != 0) {
LOGP(DMAIN, LOGL_ERROR,
"Failed to register new connection "
"fd\n");
close(conn_bfd->fd);
conn_bfd->fd = -1;
return -1;
}
LOGP(DMAIN, LOGL_NOTICE, "Unix socket connected to external osmo-trx\n");
/* send current info */
//IPC_tx_info_ind();
return 0;
}

View File

@@ -1,24 +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,165 +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 <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <osmocom/core/talloc.h>
#include "shm.h"
//#define ENCDECDEBUG
/* 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++) {
#ifdef ENCDECDEBUG
fprintf(stderr, "decode: smpl_buf %d at offset %u\n", i, stream_raw->buffer_offset[i]);
#endif
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;
#ifdef ENCDECDEBUG
fprintf(stderr, "decode: streams at offset %u and %u\n", chan_raw->dl_buf_offset, chan_raw->ul_buf_offset);
#endif
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++) {
#ifdef ENCDECDEBUG
fprintf(stderr, "decode: channel %d at offset %u\n", i, root_raw->chan_offset[i]);
#endif
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)
{
uint8_t *start = (uint8_t *)smpl_buf_raw;
unsigned int offset = sizeof(struct ipc_shm_raw_smpl_buf);
offset = (((uintptr_t)offset + 7) & ~0x07ULL);
#ifdef ENCDECDEBUG
fprintf(stderr, "encode: smpl_buf at offset %lu\n", (start - (uint8_t *)root_raw));
#endif
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);
#ifdef ENCDECDEBUG
fprintf(stderr, "encode: stream at offset %lu\n", (start - (ptrdiff_t)root_raw));
#endif
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);
#ifdef ENCDECDEBUG
fprintf(stderr, "encode: channel at offset %lu\n", (start - (uint8_t *)root_raw));
#endif
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++) {
uint32_t ofs = (start + offset - (uintptr_t)root_raw);
if (root_raw)
root_raw->chan_offset[i] = (start + offset - (uintptr_t)root_raw);
#ifdef ENCDECDEBUG
fprintf(stderr, "encode: channel %d chan_offset[i]=%u\n", i, ofs);
#endif
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,224 +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;
pthread_cond_t cf;
pthread_cond_t ce;
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_PATH_PREFIX "/tmp/ipc_sock"
#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];
} __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 min_rx_gain;
double max_rx_gain;
double min_tx_gain;
double max_tx_gain;
double iq_scaling_val_rx;
double iq_scaling_val_tx;
uint32_t max_num_chans;
char dev_desc[200];
struct ipc_sk_if_info_chan chan_info[0];
} __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[0];
} __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[0];
} __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 + 0x00
#define IPC_IF_MSG_START_CNF IPC_IF_CHAN_MSG_OFFSET + 0x01
#define IPC_IF_MSG_STOP_REQ IPC_IF_CHAN_MSG_OFFSET + 0x02
#define IPC_IF_MSG_STOP_CNF IPC_IF_CHAN_MSG_OFFSET + 0x03
#define IPC_IF_MSG_SETGAIN_REQ IPC_IF_CHAN_MSG_OFFSET + 0x04
#define IPC_IF_MSG_SETGAIN_CNF IPC_IF_CHAN_MSG_OFFSET + 0x05
#define IPC_IF_MSG_SETFREQ_REQ IPC_IF_CHAN_MSG_OFFSET + 0x06
#define IPC_IF_MSG_SETFREQ_CNF IPC_IF_CHAN_MSG_OFFSET + 0x07
#define IPC_IF_NOTIFY_UNDERFLOW IPC_IF_CHAN_MSG_OFFSET + 0x08
#define IPC_IF_NOTIFY_OVERFLOW IPC_IF_CHAN_MSG_OFFSET + 0x09
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 {
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;
} u;
} __attribute__((packed));

View File

@@ -1,243 +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 "trx_vty.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);
}
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);
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;
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" 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.min_rx_gain = rx_range.start();
ipc_prim->u.info_cnf.max_rx_gain = rx_range.stop();
ipc_prim->u.info_cnf.min_tx_gain = tx_range.start();
ipc_prim->u.info_cnf.max_tx_gain = tx_range.stop();
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++;
}
}

View File

@@ -1,79 +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);
#endif
#endif // IPC_B210_H

View File

@@ -20,10 +20,6 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <map>
#include "trx_vty.h"
#include "Logger.h"
#include "Threads.h"
#include "LMSDevice.h"
@@ -43,74 +39,17 @@ extern "C" {
using namespace std;
#define MAX_ANTENNA_LIST_SIZE 10
#define LMS_SAMPLE_RATE GSMRATE*32
#define GSM_CARRIER_BW 270000.0 /* 270kHz */
#define LMS_MIN_BW_SUPPORTED 2.5e6 /* 2.5mHz, minimum supported by LMS */
#define LMS_CALIBRATE_BW_HZ OSMO_MAX(GSM_CARRIER_BW, LMS_MIN_BW_SUPPORTED)
#define SAMPLE_BUF_SZ (1 << 20) /* Size of Rx timestamp based Ring buffer, in bytes */
/* 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;
/* Device specific maximum tx levels selected by phasenoise measurements, in dB */
double max_tx_gain;
/* 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, 73.0, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_SDR_USB_PREFIX_NAME } },
{ LMS_DEV_SDR_MINI, { false, true, 66.0, GSMRATE, MCBTS_SPACING, 8.9e-5, 8.2e-5, LMS_DEV_SDR_MINI_PREFIX_NAME } },
{ LMS_DEV_NET_MICRO, { true, false, 71.0, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_NET_MICRO_PREFIX_NAME } },
{ LMS_DEV_UNKNOWN, { true, true, 73.0, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
};
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), 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...";
@@ -120,11 +59,6 @@ LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t c
tx_gains.resize(chans);
rx_buffers.resize(chans);
/* Set up per-channel Rx timestamp based Ring buffers */
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
}
LMSDevice::~LMSDevice()
@@ -159,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)
@@ -176,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<string> filters;
vector<string> filters;
filters = comma_delimited_to_vector(args.c_str());
@@ -200,11 +134,11 @@ int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::str
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..";
@@ -241,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;
}
@@ -264,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++) {
@@ -285,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()) {
@@ -308,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();
@@ -402,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)
{
@@ -480,7 +385,7 @@ bool LMSDevice::do_filters(size_t chan)
double LMSDevice::maxTxGain()
{
return dev_param_map.at(m_dev_type).max_tx_gain;
return maxTxGainClamp;
}
double LMSDevice::minTxGain()
@@ -719,7 +624,7 @@ void LMSDevice::update_stream_stats_rx(size_t chan, bool *overrun)
m_ctr[chan].rx_overruns += status.overrun;
/* Dropped packets in Rx are counted when gaps in Rx timestamps are
detected (likely because buffer overflow in hardware). Value count
detected (likely because buffer oveflow in hardware). Value count
since the last call to LMS_GetStreamStatus(stream). */
if (status.droppedPackets) {
changed = true;
@@ -739,7 +644,7 @@ void LMSDevice::update_stream_stats_rx(size_t chan, bool *overrun)
// NOTE: Assumes sequential reads
int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
TIMESTAMP timestamp, bool * underrun)
TIMESTAMP timestamp, bool * underrun, unsigned *RSSI)
{
int rc, num_smpls, expect_smpls;
ssize_t avail_smpls;
@@ -806,9 +711,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;
}
}
@@ -858,7 +762,8 @@ void LMSDevice::update_stream_stats_tx(size_t chan, bool *underrun)
}
int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
bool * underrun, unsigned long long timestamp)
bool * underrun, unsigned long long timestamp,
bool isControl)
{
int rc = 0;
unsigned int i;
@@ -867,6 +772,11 @@ int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
tx_metadata.waitForTimestamp = true;
tx_metadata.timestamp = timestamp - ts_offset; /* Shift Tx time by offset */
if (isControl) {
LOGC(DDEV, ERROR) << "Control packets not supported";
return 0;
}
if (bufs.size() != chans) {
LOGC(DDEV, ERROR) << "Invalid channel combination " << bufs.size();
return -1;

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under multiple licenses; see the COPYING file in
* the main directory for licensing information for this specific distribution.
* 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.
@@ -41,13 +41,6 @@
* 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,
};
/** A class to handle a LimeSuite supported device */
class LMSDevice:public RadioDevice {
@@ -66,8 +59,7 @@ private:
TIMESTAMP ts_initial, ts_offset;
std::vector<double> tx_gains, rx_gains;
enum lms_dev_type m_dev_type;
double maxTxGainClamp;
bool do_calib(size_t chan);
bool do_filters(size_t chan);
@@ -76,12 +68,11 @@ private:
bool flush_recv(size_t num_pkts);
void update_stream_stats_rx(size_t chan, bool *overrun);
void update_stream_stats_tx(size_t chan, bool *underrun);
bool do_clock_src_freq(enum ReferenceType ref, double freq);
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();
@@ -106,21 +97,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);

View File

@@ -33,12 +33,11 @@
#include "config.h"
#endif
#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
@@ -135,52 +134,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;
@@ -189,10 +159,10 @@ static void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
#endif
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),
tx_spp(0), rx_spp(0),
@@ -448,16 +418,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);
@@ -566,7 +526,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;
@@ -644,6 +604,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);
@@ -721,7 +685,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;
@@ -778,7 +742,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)) {
@@ -804,7 +768,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;
@@ -814,6 +778,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;
@@ -913,18 +883,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;
@@ -944,7 +911,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;
@@ -1202,7 +1169,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,
@@ -1210,4 +1176,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

@@ -62,7 +62,7 @@ enum uhd_dev_type {
class uhd_device : public RadioDevice {
public:
uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
size_t 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();
@@ -74,10 +74,10 @@ public:
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);
@@ -127,7 +127,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;

View File

@@ -61,10 +61,10 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
size_t 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...";
@@ -365,7 +365,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)
@@ -433,10 +433,8 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
*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;
@@ -515,8 +513,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();
@@ -570,12 +569,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
@@ -585,7 +578,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

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under multiple licenses; see the COPYING file in
* the main directory for licensing information for this specific distribution.
* 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.
@@ -82,9 +82,6 @@ private:
double rxGain;
double txGain;
int writeSamplesControl(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
#ifdef SWLOOPBACK
short loopbackBuffer[1000000];
int loopbackBufferSize;
@@ -98,7 +95,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);
@@ -120,20 +117,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);

View File

@@ -0,0 +1,10 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
noinst_HEADERS = XTRXDevice.h
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = XTRXDevice.cpp

View File

@@ -0,0 +1,456 @@
/*
* Copyright 2018 Sergey Kostanbaev <sergey.kostanbaev@fairwaves.co>
* Copyright 2019 Alexander Chemeris <alexander.chemeris@fairwaves.co>
*
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 <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "Threads.h"
#include "XTRXDevice.h"
#include <Logger.h>
#include <errno.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
using namespace std;
const double defaultRXBandwidth = 0.5e6;
const double defaultTXBandwidth = 1.5e6;
static int time_tx_corr = 60;
XTRXDevice::XTRXDevice(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, chans, lo_offset, tx_paths, rx_paths)
{
LOG(INFO) << "creating XTRX device:"
<< " RXSPS: " << rx_sps
<< " TXSPS: " << tx_sps
<< " chans: " << chans
<< " lo_off: " << lo_offset
<< " rx_path(0): " << (rx_paths.size() ? rx_paths[0] : "<>")
<< " tx_path(0): " << (tx_paths.size() ? tx_paths[0] : "<>");
txsps = tx_sps;
rxsps = rx_sps;
rxGain = 0;
txGain = 0;
loopback = false;
device = NULL;
}
static int parse_config(const char* line, const char* argument, int default_value)
{
const char* arg_found = strstr(line, argument);
if (!arg_found)
return default_value;
const char* qe_pos = strchr(arg_found, '=');
if (!qe_pos)
return default_value;
int res = strtol(qe_pos + 1, NULL, 10);
if (res == 0 && errno) {
return default_value;
}
return res;
}
int XTRXDevice::open(const std::string &args, int ref, bool swap_channels)
{
LOG(INFO) << "opening XTRX device '" << args << "'..";
int loglevel = parse_config(args.c_str(), "loglevel", 3);
int lb_param = parse_config(args.c_str(), "loopback", 0);
time_tx_corr = parse_config(args.c_str(), "tcorr", time_tx_corr);
int fref = parse_config(args.c_str(), "refclk", 26000000);
int rxdec = parse_config(args.c_str(), "rxdec", 0);
char xtrx_name[500];
const char* lend = strchr(args.c_str(), ',');
int len = (lend) ? (lend - args.c_str()) : sizeof(xtrx_name) - 1;
strncpy(xtrx_name, args.c_str(), len);
xtrx_name[len] = 0;
if ((txsps % 2) || (rxsps % 2)) {
LOG(ALERT) << "XTRX TxSPS/RxSPS must be even!";
return -1;
}
if (lb_param) {
LOG(ALERT) << "XTRX LOOPBACK mode is set!";
loopback = true;
}
int res = xtrx_open(xtrx_name, loglevel, &device);
if (res) {
LOG(ALERT) << "XTRX creating failed, device " << xtrx_name << " code " << res;
return -1;
}
double actualMasterClock = 0;
if (fref > 0) {
xtrx_set_ref_clk(device, fref, XTRX_CLKSRC_INT);
}
res = xtrx_set_samplerate(device,
GSMRATE * (double) std::min(txsps, rxsps) * 32 * 4 * ((rxdec) ? 2 : 1),
GSMRATE * (double) rxsps,
GSMRATE * (double) txsps,
(rxdec) ? XTRX_SAMPLERATE_FORCE_RX_DECIM : 0,
&actualMasterClock,
&actualRXSampleRate,
&actualTXSampleRate);
if (res) {
LOG(ALERT) << "XTRX failed to set samplerate RX: " << GSMRATE * (double) rxsps
<< " TX: " << GSMRATE * (double) txsps
<< " res: " << res;
return -1;
} else {
LOG(INFO) << "XTRX set samplerate Master: " << actualMasterClock
<< " RX: " << actualRXSampleRate
<< " TX: " << actualTXSampleRate;
}
double bw;
double actualbw;
actualbw = 0;
bw = defaultRXBandwidth;
res = xtrx_tune_rx_bandwidth(device, XTRX_CH_AB, bw, &actualbw);
if (res) {
LOG(ALERT) << "XTRX failed to set RX bandwidth: " << bw
<< " res: " << res;
} else {
LOG(INFO) << "XTRX set RX bandwidth: " << actualbw;
}
actualbw = 0;
bw = defaultTXBandwidth;
res = xtrx_tune_tx_bandwidth(device, XTRX_CH_AB, bw, &actualbw);
if (res) {
LOG(ALERT) << "XTRX failed to set TX bandwidth: " << bw
<< " res: " << res;
} else {
LOG(INFO) << "XTRX set TX bandwidth: " << actualbw;
}
samplesRead = 0;
samplesWritten = 0;
started = false;
return NORMAL;
}
XTRXDevice::~XTRXDevice()
{
if (device) {
xtrx_close(device);
}
}
bool XTRXDevice::start()
{
LOG(INFO) << "starting XTRX...";
if (started) {
return false;
}
dataStart = 0;
dataEnd = 0;
timeStart = 0;
timeEnd = 0;
timeRx = initialReadTimestamp();
timestampOffset = 0;
latestWriteTimestamp = 0;
lastPktTimestamp = 0;
hi32Timestamp = 0;
isAligned = false;
xtrx_stop(device, XTRX_TX);
xtrx_stop(device, XTRX_RX);
xtrx_set_antenna(device, XTRX_TX_AUTO);
xtrx_set_antenna(device, XTRX_RX_AUTO);
xtrx_run_params_t params;
params.dir = XTRX_TRX;
params.nflags = (loopback) ? XTRX_RUN_DIGLOOPBACK : 0;
params.rx.chs = XTRX_CH_AB;
params.rx.flags = XTRX_RSP_SISO_MODE;
params.rx.hfmt = XTRX_IQ_INT16;
params.rx.wfmt = XTRX_WF_16;
params.rx.paketsize = 625 * rxsps;
params.tx.chs = XTRX_CH_AB;
params.tx.flags = XTRX_RSP_SISO_MODE;
params.tx.hfmt = XTRX_IQ_INT16;
params.tx.wfmt = XTRX_WF_16;
params.tx.paketsize = 625 * txsps;
if (loopback) {
params.tx.flags |= XTRX_RSP_SWAP_AB | XTRX_RSP_SWAP_IQ;
}
params.tx_repeat_buf = NULL;
params.rx_stream_start = initialReadTimestamp();
int res = xtrx_run_ex(device, &params);
if (res) {
LOG(ALERT) << "XTRX start failed res: " << res;
} else {
LOG(INFO) << "XTRX started";
started = true;
}
return started;
}
bool XTRXDevice::stop()
{
if (started) {
int res = xtrx_stop(device, XTRX_TRX);
if (res) {
LOG(ALERT) << "XTRX stop failed res: " << res;
} else {
LOG(INFO) << "XTRX stopped";
started = false;
}
}
return !started;
}
TIMESTAMP XTRXDevice::initialWriteTimestamp()
{
if (/*(iface == MULTI_ARFCN) || */(rxsps == txsps))
return initialReadTimestamp();
else
return initialReadTimestamp() * txsps;
}
double XTRXDevice::maxTxGain()
{
return 30;
}
double XTRXDevice::minTxGain()
{
return 0;
}
double XTRXDevice::maxRxGain()
{
return 30;
}
double XTRXDevice::minRxGain()
{
return 0;
}
double XTRXDevice::setTxGain(double dB, size_t chan)
{
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return 0.0;
}
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
int res = xtrx_set_gain(device, XTRX_CH_AB, XTRX_TX_PAD_GAIN, dB - 30, &txGain);
if (res) {
LOG(ERR) << "Error setting TX gain res: " << res;
} else {
LOG(NOTICE) << "Actual TX gain: " << txGain << " dB.";
}
return txGain;
}
double XTRXDevice::setRxGain(double dB, size_t chan)
{
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return 0.0;
}
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
int res = xtrx_set_gain(device, XTRX_CH_AB, XTRX_RX_LNA_GAIN, dB, &rxGain);
if (res) {
LOG(ERR) << "Error setting RX gain res: " << res;
} else {
LOG(NOTICE) << "Actual RX gain: " << rxGain << " dB.";
}
return rxGain;
}
// NOTE: Assumes sequential reads
int XTRXDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
{
if (!started)
return -1;
struct xtrx_recv_ex_info ri;
ri.samples = len;
ri.buffer_count = bufs.size();
ri.buffers = (void* const*)&bufs[0];
ri.flags = 0;
int res = xtrx_recv_sync_ex(device, &ri);
if (res) {
LOG(ALERT) << "xtrx_recv_sync failed res " << res << " current TS " << timeRx << " req TS" << timestamp;
return -1;
}
timeRx += len;
// TODO get rid of it!
int i;
for (i = 0; i < len * 2; i++)
bufs[0][i] <<= 4;
if (underrun)
*underrun = (ri.out_events & RCVEX_EVENT_FILLED_ZERO);
return len;
}
int XTRXDevice::writeSamples(std::vector<short *> &bufs, int len,
bool *underrun, unsigned long long timestamp,
bool isControl)
{
if (!started)
return 0;
xtrx_send_ex_info_t nfo;
nfo.buffers = (const void* const*)&bufs[0];
nfo.buffer_count = bufs.size();
nfo.flags = XTRX_TX_DONT_BUFFER;
nfo.samples = len;
nfo.ts = timestamp - time_tx_corr;
int res = xtrx_send_sync_ex(device, &nfo);
if (res != 0) {
LOG(ALERT) << "xtrx_send_sync_ex returned " << res << " len=" << len << " ts=" << timestamp;
return 0;
}
if (*underrun) {
*underrun = (nfo.out_flags & XTRX_TX_DISCARDED_TO);
}
return len;
}
bool XTRXDevice::setRxAntenna(const std::string & ant, size_t chan)
{
LOG(ALERT) << "CH" << chan << ": RX ANTENNA: " << ant.c_str() << " (SETTING RX ANTENNA IS NOT IMPLEMENTED)";
return true;
}
std::string XTRXDevice::getRxAntenna(size_t chan)
{
return "";
}
bool XTRXDevice::setTxAntenna(const std::string & ant, size_t chan)
{
LOG(ALERT) << "CH" << chan << ": TX ANTENNA: " << ant.c_str() << " (SETTING TX ANTENNA IS NOT IMPLEMENTED)";
return true;
}
std::string XTRXDevice::getTxAntenna(size_t chan )
{
return "";
}
bool XTRXDevice::requiresRadioAlign()
{
return false;
}
GSM::Time XTRXDevice::minLatency()
{
return GSM::Time(6,7);
}
bool XTRXDevice::updateAlignment(TIMESTAMP timestamp)
{
LOG(ALERT) << "Update Aligment " << timestamp;
return true;
}
bool XTRXDevice::setTxFreq(double wFreq, size_t chan)
{
int res;
double actual = 0;
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return false;
}
if ((res = xtrx_tune(device, XTRX_TUNE_TX_FDD, wFreq, &actual)) == 0) {
LOG(INFO) << "set RX: " << wFreq << std::endl
<< " actual freq: " << actual << std::endl;
return true;
}
else {
LOG(ALERT) << "set RX: " << wFreq << "failed (code: " << res << ")" << std::endl;
return false;
}
}
bool XTRXDevice::setRxFreq(double wFreq, size_t chan)
{
int res;
double actual = 0;
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return false;
}
if ((res = xtrx_tune(device, XTRX_TUNE_RX_FDD, wFreq, &actual)) == 0) {
LOG(INFO) << "set RX: " << wFreq << std::endl
<< " actual freq: " << actual << std::endl;
return true;
}
else {
LOG(ALERT) << "set RX: " << wFreq << "failed (code: " << res << ")" << std::endl;
return false;
}
}
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,
const std::vector < std::string > &rx_paths)
{
return new XTRXDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}

View File

@@ -0,0 +1,180 @@
#ifndef _XTRX_DEVICE_H_
#define _XTRX_DEVICE_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "radioDevice.h"
#include <stdint.h>
#include <sys/time.h>
#include <string>
#include <iostream>
#include "Threads.h"
#include <xtrx_api.h>
class XTRXDevice: public RadioDevice {
private:
int txsps;
int rxsps;
double actualTXSampleRate; ///< the actual XTRX sampling rate
double actualRXSampleRate; ///< the actual XTRX sampling rate
//unsigned int decimRate; ///< the XTRX decimation rate
//unsigned int interRate; ///< the XTRX decimation rate
unsigned long long samplesRead; ///< number of samples read from XTRX
unsigned long long samplesWritten; ///< number of samples sent to XTRX
bool started; ///< flag indicates XTRX has started
short *data;
unsigned long dataStart;
unsigned long dataEnd;
TIMESTAMP timeStart;
TIMESTAMP timeEnd;
TIMESTAMP timeRx;
bool isAligned;
Mutex writeLock;
short *currData; ///< internal data buffer when reading from XTRX
TIMESTAMP currTimestamp; ///< timestamp of internal data buffer
unsigned currLen; ///< size of internal data buffer
TIMESTAMP timestampOffset; ///< timestamp offset b/w Tx and Rx blocks
TIMESTAMP latestWriteTimestamp; ///< timestamp of most recent ping command
TIMESTAMP pingTimestamp; ///< timestamp of most recent ping response
unsigned long hi32Timestamp;
unsigned long lastPktTimestamp;
double rxGain;
double txGain;
bool loopback;
xtrx_dev* device;
public:
/** Object constructor */
XTRXDevice(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);
~XTRXDevice();
/** Instantiate the XTRX */
int open(const std::string &args, int ref, bool swap_channels);
/** Start the XTRX */
bool start();
/** Stop the XTRX */
bool stop();
/** Set priority not supported */
void setPriority(float prio = 0.5) { }
enum TxWindowType getWindowType() { return TX_WINDOW_FIXED; }
/**
Read samples from the XTRX.
@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 XTRX does not have data to transmit, e.g. data not being sent fast enough
@param RSSI The received signal strength of the read result
@return The number of samples actually read
*/
int readSamples(std::vector<short *> &buf, int len, bool *overrun,
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
unsigned *RSSI = NULL);
/**
Write samples to the XTRX.
@param buf Contains the data to be written.
@param len number of samples to write.
@param underrun Set if XTRX does not have data to transmit, e.g. data not being sent fast enough
@param timestamp The timestamp of the first sample of the data buffer.
@param isControl Set if data is a control packet, e.g. a ping command
@return The number of samples actually written
*/
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
/** Update the alignment between the read and write timestamps */
bool updateAlignment(TIMESTAMP timestamp);
/** Set the transmitter frequency */
bool setTxFreq(double wFreq, size_t chan = 0);
/** Set the receiver frequency */
bool setRxFreq(double wFreq, size_t chan = 0);
/** Returns the starting write Timestamp*/
TIMESTAMP initialWriteTimestamp(void);
/** Returns the starting read Timestamp*/
TIMESTAMP initialReadTimestamp(void) { return 20000;}
/** returns the full-scale transmit amplitude **/
double fullScaleInputValue() {return (double) 32767*0.7;}
/** returns the full-scale receive amplitude **/
double fullScaleOutputValue() {return (double) 32767;}
/** sets the receive chan gain, returns the gain setting **/
double setRxGain(double dB, size_t chan = 0);
/** get the current receive gain */
double getRxGain(size_t chan = 0) { return rxGain; }
/** return maximum Rx Gain **/
double maxRxGain(void);
/** return minimum Rx Gain **/
double minRxGain(void);
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
/** gets the current transmit gain **/
double getTxGain(size_t chan = 0) { return txGain; }
/** return maximum Tx Gain **/
double maxTxGain(void);
/** 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);
/** return the used RX path */
std::string getRxAntenna(size_t chan = 0);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setTxAntenna(const std::string & ant, size_t chan = 0);
/** return the used RX path */
std::string getTxAntenna(size_t chan = 0);
/** return whether user drives synchronization of Tx/Rx of USRP */
bool requiresRadioAlign();
/** return whether user drives synchronization of Tx/Rx of USRP */
virtual GSM::Time minLatency();
/** Return internal status values */
inline double getTxFreq(size_t chan = 0) { return 0; }
inline double getRxFreq(size_t chan = 0) { return 0; }
inline double getSampleRate() { return actualTXSampleRate; }
inline double numberRead() { return samplesRead; }
inline double numberWritten() { return samplesWritten; }
};
#endif // _XTRX_DEVICE_H_

View File

@@ -79,6 +79,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,
@@ -236,7 +237,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);
@@ -333,12 +334,14 @@ 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;
@@ -380,11 +383,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) {
@@ -461,9 +459,7 @@ 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 << " Tuning offset........... " << trx->cfg.offset << std::endl;
ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << std::endl;
@@ -580,17 +576,20 @@ int main(int argc, char *argv[])
printf("Built without atomic operation support. Using Mutex, it may affect performance!\n");
#endif
if (!log_mutex_init()) {
fprintf(stderr, "Failed to initialize log mutex!\n");
exit(2);
}
convolve_init();
convert_init();
osmo_init_logging2(tall_trx_ctx, &log_info);
log_enable_multithread();
osmo_stats_init(tall_trx_ctx);
vty_init(&g_vty_info);
logging_vty_add_cmds();
ctrl_vty_init(tall_trx_ctx);
trx_vty_init(g_trx_ctx);
logging_vty_add_cmds();
osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds();

View File

@@ -72,7 +72,7 @@ bool trxd_send_burst_ind_v0(size_t chan, int fd, const struct trx_ul_burst_ind *
if(bi->idle)
return true;
/* +2: Historically (OpenBTS times), two extra non-used bytes are sent appended to each burst */
/* +2: Historically (OpenBTS times), two extra non-used bytes are sent appeneded to each burst */
char buf[sizeof(struct trxd_hdr_v0) + bi->nbits + 2];
struct trxd_hdr_v0* pkt = (struct trxd_hdr_v0*)buf;

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.
@@ -79,7 +79,7 @@ public:
bool start();
bool stop();
/** initialization */
/** intialization */
virtual bool init(int type);
virtual void close();
@@ -156,24 +156,16 @@ public:
void close();
};
struct freq_cfg_state {
bool set;
double freq_hz;
};
class RadioInterfaceMulti : public RadioInterface {
private:
bool pushBuffer();
int pullBuffer();
bool verify_arfcn_consistency(double freq, size_t chan, bool tx);
virtual double setTxGain(double dB, size_t chan);
signalVector *outerSendBuffer;
signalVector *outerRecvBuffer;
std::vector<signalVector *> history;
std::vector<bool> active;
std::vector<struct freq_cfg_state> rx_freq_state;
std::vector<struct freq_cfg_state> tx_freq_state;
Resampler *dnsampler;
Resampler *upsampler;

View File

@@ -73,8 +73,6 @@ void RadioInterfaceMulti::close()
powerScaling.resize(0);
history.resize(0);
active.resize(0);
rx_freq_state.resize(0);
tx_freq_state.resize(0);
RadioInterface::close();
}
@@ -150,8 +148,6 @@ bool RadioInterfaceMulti::init(int type)
mReceiveFIFO.resize(mChans);
powerScaling.resize(mChans);
history.resize(mChans);
rx_freq_state.resize(mChans);
tx_freq_state.resize(mChans);
active.resize(MCHANS, false);
inchunk = RESAMP_INRATE * 4;
@@ -366,67 +362,42 @@ static bool fltcmp(double a, double b)
return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
}
bool RadioInterfaceMulti::verify_arfcn_consistency(double freq, size_t chan, bool tx)
{
double freq_i;
std::string str_dir = tx ? "Tx" : "Rx";
std::vector<struct freq_cfg_state> &v = tx ? tx_freq_state : rx_freq_state;
for (size_t i = 0; i < mChans; i++) {
if (i == chan)
continue;
if (!v[i].set)
continue;
freq_i = v[i].freq_hz + (double) ((int)chan - (int)i) * MCBTS_SPACING;
if (!fltcmp(freq, freq_i)) {
LOGCHAN(chan, DMAIN, ERROR)
<< "Setting " << str_dir << " frequency " << freq
<< " is incompatible: already configured channel "
<< i << " uses frequency " << v[i].freq_hz
<< " (expected " << freq_i << ")";
return false;
}
}
v[chan].set = true;
v[chan].freq_hz = freq;
return true;
}
bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
{
double shift;
if (chan >= mChans)
return false;
if (chan >= mChans)
return false;
double shift = (double) getFreqShift(mChans);
if (!verify_arfcn_consistency(freq, chan, true))
return false;
if (!chan)
return mDevice->setTxFreq(freq + shift * MCBTS_SPACING);
if (chan == 0) {
shift = (double) getFreqShift(mChans);
return mDevice->setTxFreq(freq + shift * MCBTS_SPACING);
}
double center = mDevice->getTxFreq();
if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
LOG(NOTICE) << "Channel " << chan << " RF Tx frequency offset is "
<< freq / 1e6 << " MHz";
}
return true;
return true;
}
bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
{
double shift;
if (chan >= mChans)
return false;
if (chan >= mChans)
return false;
double shift = (double) getFreqShift(mChans);
if (!verify_arfcn_consistency(freq, chan, false))
return false;
if (!chan)
return mDevice->setRxFreq(freq + shift * MCBTS_SPACING);
if (chan == 0) {
shift = (double) getFreqShift(mChans);
return mDevice->setRxFreq(freq + shift * MCBTS_SPACING);
}
double center = mDevice->getRxFreq();
if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
LOG(NOTICE) << "Channel " << chan << " RF Rx frequency offset is "
<< freq / 1e6 << " MHz";
}
return true;
return true;
}
double RadioInterfaceMulti::setRxGain(double db, size_t chan)

View File

@@ -345,7 +345,7 @@ static signalVector *convolve(const signalVector *x, const signalVector *h,
_x = x;
/*
* Four convolve types:
* Four convovle types:
* 1. Complex-Real (aligned)
* 2. Complex-Complex (aligned)
* 3. Complex-Real (!aligned)
@@ -723,7 +723,7 @@ static signalVector *mapEdgeSymbols(const BitVector &bits)
*
* Delay the EDGE downlink bursts by one symbol in order to match GMSK pulse
* shaping group delay. The difference in group delay arises from the dual
* pulse filter combination of the GMSK Laurent representation whereas 8-PSK
* pulse filter combination of the GMSK Laurent represenation whereas 8-PSK
* uses a single pulse linear filter.
*/
static signalVector *shapeEdgeBurst(const signalVector &symbols)

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.

View File

@@ -47,7 +47,7 @@ void signalVector::operator=(const signalVector& vector)
complex *src = vector.mData;
for (i = 0; i < size(); i++, src++, dst++)
*dst = *src;
/* TODO: optimize for non non-trivially copiable types: */
/* TODO: optimize for non non-trivially copyable types: */
/*memcpy(mData, vector.mData, bytes()); */
mStart = mData + vector.getStart();
}
@@ -70,7 +70,7 @@ size_t signalVector::updateHistory()
complex *src = mStart + this->size() - num;
for (i = 0; i < num; i++, src++, dst++)
*dst = *src;
/* TODO: optimize for non non-trivially copiable types: */
/* TODO: optimize for non non-trivially copyable types: */
/*memmove(mData, mStart + this->size() - num, num * sizeof(complex)); */
return num;

View File

@@ -51,11 +51,6 @@ AC_PROG_INSTALL
AC_PATH_PROG([RM_PROG], [rm])
AC_LANG([C++])
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
AS_CASE(["$LD"],[*clang*],
[AS_CASE(["${host_os}"],
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
@@ -70,7 +65,7 @@ AC_PROG_LIBTOOL
dnl Checks for header files.
AC_HEADER_STDC
dnl This is required for GnuRadio includes to understand endianness correctly:
dnl This is required for GnuRadio includes to understand endianess correctly:
AC_CHECK_HEADERS([byteswap.h])
dnl Checks for typedefs, structures, and compiler characteristics.
@@ -80,19 +75,9 @@ AC_TYPE_SIZE_T
AC_HEADER_TIME
AC_C_BIGENDIAN
# Check if gettid is available (despite not being documented in glibc doc, it requires __USE_GNU on some systems)
# C compiler is used since __USE_GNU seems to be always defined for g++.
save_CPPFLAGS=$CPPFLAGS
AC_LANG_PUSH(C)
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
AC_CHECK_FUNCS([gettid])
AC_LANG_POP(C)
CPPFLAGS=$save_CPPFLAGS
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.12.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.12.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.12.0)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
@@ -140,9 +125,9 @@ AC_ARG_WITH(lms, [
[enable LimeSuite based transceiver])
])
AC_ARG_WITH(ipc, [
AS_HELP_STRING([--with-ipc],
[enable IPC])
AC_ARG_WITH(xtrx, [
AS_HELP_STRING([--with-xtrx],
[enable XTRX based transceiver])
])
AC_ARG_WITH(singledb, [
@@ -184,6 +169,10 @@ AS_IF([test "x$with_lms" = "xyes"], [
PKG_CHECK_MODULES(LMS, LimeSuite)
])
AS_IF([test "x$with_xtrx" = "xyes"], [
PKG_CHECK_MODULES(XTRX, libxtrx)
])
AS_IF([test "x$with_uhd" != "xno"],[
PKG_CHECK_MODULES(UHD, uhd >= 003.011,
[AC_DEFINE(USE_UHD_3_11, 1, UHD version 3.11.0 or higher)],
@@ -246,7 +235,7 @@ AS_IF([test "x$osmo_cv_cc_has___sync_fetch_and_and" = "xyes" && test "x$osmo_cv_
AM_CONDITIONAL(DEVICE_UHD, [test "x$with_uhd" != "xno"])
AM_CONDITIONAL(DEVICE_USRP1, [test "x$with_usrp1" = "xyes"])
AM_CONDITIONAL(DEVICE_LMS, [test "x$with_lms" = "xyes"])
AM_CONDITIONAL(DEVICE_IPC, [test "x$with_ipc" = "xyes"])
AM_CONDITIONAL(DEVICE_XTRX, [test "x$with_xtrx" = "xyes"])
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
@@ -331,11 +320,10 @@ AC_CONFIG_FILES([\
Transceiver52M/device/uhd/Makefile \
Transceiver52M/device/usrp1/Makefile \
Transceiver52M/device/lms/Makefile \
Transceiver52M/device/ipc/Makefile \
Transceiver52M/device/xtrx/Makefile \
tests/Makefile \
tests/CommonLibs/Makefile \
tests/Transceiver52M/Makefile \
utils/Makefile \
doc/Makefile \
doc/examples/Makefile \
contrib/Makefile \
@@ -343,5 +331,4 @@ AC_CONFIG_FILES([\
])
AC_OUTPUT(
doc/manuals/Makefile
contrib/osmo-trx.spec)
doc/manuals/Makefile)

View File

@@ -15,7 +15,7 @@ substr() { [ -z "${2##*$1*}" ]; }
mychroot_nocwd() {
# LC_ALL + LANGUAGE set to avoid lots of print errors due to locale not being set inside container
# PATH is needed to be able to reach binaries like ldconfig without logging in to root, which adds the paths to PATH.
# PROOT_NO_SECCOMP is required due to proot bug #106
# PROOT_NO_SECCOMP is requried due to proot bug #106
LC_ALL=C LANGUAGE=C PATH="$PATH:/usr/sbin:/sbin" PROOT_NO_SECCOMP=1 proot -r "$ROOTFS" -w / -b /proc --root-id -q qemu-arm-static "$@"
}
@@ -23,8 +23,15 @@ mychroot() {
mychroot_nocwd -w / "$@"
}
base="$PWD"
deps="$base/deps"
inst="$deps/install"
export deps inst
if [ -z "${INSIDE_CHROOT}" ]; then
osmo-clean-workspace.sh
# Only use ARM chroot if host is not ARM and the target is ARM:
if ! $(substr "arm" "$(uname -m)") && [ "x${INSTR}" = "x--with-neon" -o "x${INSTR}" = "x--with-neon-vfpv4" ]; then
@@ -62,20 +69,6 @@ if [ -z "${INSIDE_CHROOT}" ]; then
fi
fi
set -ex
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
exit 2
fi
base="$PWD"
deps="$base/deps"
inst="$deps/install"
export deps inst
osmo-clean-workspace.sh
mkdir "$deps" || true
osmo-build-dep.sh libosmocore "" "--enable-sanitize --disable-doxygen --disable-pcsc"

View File

@@ -1,241 +0,0 @@
#
# spec file for package osmo-trx
#
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
Name: osmo-trx
Version: @VERSION@
Release: 0
Summary: SDR transceiver that implements Layer 1 of a GSM BTS
License: AGPL-3.0-or-later
Group: Productivity/Telephony/Servers
URL: https://osmocom.org/projects/osmotrx
Source: %{name}-%{version}.tar.xz
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: fdupes
BuildRequires: gcc-c++
BuildRequires: libtool
BuildRequires: pkgconfig >= 0.20
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
%if ! 0%{?centos_ver}
BuildRequires: pkgconfig(LimeSuite)
BuildRequires: pkgconfig(usrp) >= 3.3
%endif
BuildRequires: pkgconfig(fftw3f)
BuildRequires: pkgconfig(libosmocoding) >= 1.3.0
BuildRequires: pkgconfig(libosmocore) >= 0.12.0
BuildRequires: pkgconfig(libosmoctrl) >= 0.12.0
BuildRequires: pkgconfig(libosmovty) >= 0.12.0
BuildRequires: pkgconfig(libusb-1.0)
BuildRequires: pkgconfig(uhd)
%{?systemd_requires}
%if 0%{?suse_version} > 1325
BuildRequires: libboost_program_options-devel
BuildRequires: libboost_system-devel
BuildRequires: libboost_test-devel
BuildRequires: libboost_thread-devel
%else
BuildRequires: boost-devel
%endif
%description
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
TS 05.01 "Physical layer on the radio path"
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
TS 05.04 "Modulation"
TS 05.10 "Radio subsystem synchronization"
In this context, BTS is "Base transceiver station". It's the stations that
connect mobile phones to the mobile network.
3GPP is the "3rd Generation Partnership Project" which is the collaboration
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
%package uhd
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (UHD)
Group: Productivity/Telephony/Servers
%description uhd
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
TS 05.01 "Physical layer on the radio path"
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
TS 05.04 "Modulation"
TS 05.10 "Radio subsystem synchronization"
In this context, BTS is "Base transceiver station". It's the stations that
connect mobile phones to the mobile network.
3GPP is the "3rd Generation Partnership Project" which is the collaboration
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
%if ! 0%{?centos_ver}
%package usrp1
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (USRP1)
Group: Productivity/Telephony/Servers
%description usrp1
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
TS 05.01 "Physical layer on the radio path"
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
TS 05.04 "Modulation"
TS 05.10 "Radio subsystem synchronization"
In this context, BTS is "Base transceiver station". It's the stations that
connect mobile phones to the mobile network.
3GPP is the "3rd Generation Partnership Project" which is the collaboration
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
%package lms
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite)
Group: Productivity/Telephony/Servers
%description lms
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
TS 05.01 "Physical layer on the radio path"
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
TS 05.04 "Modulation"
TS 05.10 "Radio subsystem synchronization"
In this context, BTS is "Base transceiver station". It's the stations that
connect mobile phones to the mobile network.
3GPP is the "3rd Generation Partnership Project" which is the collaboration
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
%endif
%package ipc
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (IPC)
Group: Productivity/Telephony/Servers
%description ipc
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
TS 05.01 "Physical layer on the radio path"
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
TS 05.04 "Modulation"
TS 05.10 "Radio subsystem synchronization"
In this context, BTS is "Base transceiver station". It's the stations that
connect mobile phones to the mobile network.
3GPP is the "3rd Generation Partnership Project" which is the collaboration
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
%prep
%setup -q
%build
echo "%{version}" >.tarball-version
autoreconf -fi
%if 0%{?centos_ver}
%configure \
--docdir=%{_docdir}/%{name} \
--with-systemdsystemunitdir=%{_unitdir} \
--without-lms \
--with-uhd \
--without-usrp1 \
--with-ipc
%else
%configure \
--docdir=%{_docdir}/%{name} \
--with-systemdsystemunitdir=%{_unitdir} \
--with-lms \
--with-uhd \
--with-usrp1 \
--with-ipc
%endif
make %{?_smp_mflags} V=1
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%install
%make_install
%fdupes -s %{buildroot}/%{_datadir}
%if 0%{?suse_version}
%pre lms %service_add_pre osmo-trx-lms.service
%post lms %service_add_post osmo-trx-lms.service
%preun lms %service_del_preun osmo-trx-lms.service
%postun lms %service_del_postun osmo-trx-lms.service
%pre uhd %service_add_pre osmo-trx-uhd.service
%post uhd %service_add_post osmo-trx-uhd.service
%preun uhd %service_del_preun osmo-trx-uhd.service
%postun uhd %service_del_postun osmo-trx-uhd.service
%pre usrp1 %service_add_pre osmo-trx-usrp1.service
%post usrp1 %service_add_post osmo-trx-usrp1.service
%preun usrp1 %service_del_preun osmo-trx-usrp1.service
%postun usrp1 %service_del_postun osmo-trx-usrp1.service
%pre ipc %service_add_pre osmo-trx-ipc.service
%post ipc %service_add_post osmo-trx-ipc.service
%preun ipc %service_del_preun osmo-trx-ipc.service
%postun ipc %service_del_postun osmo-trx-ipc.service
%endif
%files
%license COPYING
%doc README.md
%doc %{_docdir}/%{name}/examples
%if ! 0%{?centos_ver}
%files lms
%{_bindir}/osmo-trx-lms
%dir %{_sysconfdir}/osmocom
%config %{_sysconfdir}/osmocom/osmo-trx-lms.cfg
%{_unitdir}/osmo-trx-lms.service
%endif
%files uhd
%{_bindir}/osmo-trx-uhd
%dir %{_sysconfdir}/osmocom
%config %{_sysconfdir}/osmocom/osmo-trx-uhd.cfg
%{_unitdir}/osmo-trx-uhd.service
%if ! 0%{?centos_ver}
%files usrp1
%{_bindir}/osmo-trx-usrp1
%dir %{_datadir}/usrp
%dir %{_datadir}/usrp/rev2
%dir %{_datadir}/usrp/rev4
%{_datadir}/usrp/rev2/std_inband.rbf
%{_datadir}/usrp/rev4/std_inband.rbf
%{_unitdir}/osmo-trx-usrp1.service
%endif
%files ipc
%{_bindir}/osmo-trx-ipc
%{_bindir}/ipc-driver-test
%dir %{_sysconfdir}/osmocom
# FIXME: missing: osmo-trx-ipc.cfg
%{_unitdir}/osmo-trx-ipc.service
%changelog

View File

@@ -1,8 +1,7 @@
EXTRA_DIST = \
osmo-trx-lms.service \
osmo-trx-uhd.service \
osmo-trx-usrp1.service \
osmo-trx-ipc.service
osmo-trx-usrp1.service
if HAVE_SYSTEMD
SYSTEMD_SERVICES =
@@ -19,8 +18,8 @@ if DEVICE_LMS
SYSTEMD_SERVICES += osmo-trx-lms.service
endif
if DEVICE_IPC
SYSTEMD_SERVICES += osmo-trx-ipc.service
if DEVICE_XTRX
SYSTEMD_SERVICES += osmo-trx-xtrx.service
endif
systemdsystemunit_DATA = $(SYSTEMD_SERVICES)

View File

@@ -1,11 +0,0 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (IPC Backend)
[Service]
Type=simple
Restart=always
ExecStart=/usr/bin/osmo-trx-ipc -C /etc/osmocom/osmo-trx-ipc.cfg
RestartSec=2
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,11 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (XTRX backend)
[Service]
Type=simple
Restart=always
ExecStart=/usr/bin/osmo-trx-xtrx -C /etc/osmocom/osmo-trx-xtrx.cfg
RestartSec=2
[Install]
WantedBy=multi-user.target

52
debian/changelog vendored
View File

@@ -1,55 +1,3 @@
osmo-trx (1.2.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* osmo-trx: log to stderr on signal received
* Drop old setPriority related code
* Transceiver: fix segfault during init if IP addr binding fails
* Transceiver: Check return value when binding IP addr for clock socket
* Transceiver: Clean up receival of downlink bursts
* Transceiver: Fix idle ul burst indications being dropped
* Transceiver: exit process when BTS drops connection
* Transceiver: Enable EDGE detection only on PDCH timeslots
* lms: Log available antennas if requested antenna fails
* device: Use LOGCHAN in set_antennas()
* Transceiver: Fix logging TN and version
* Transceiver: Use LOGCHAN in logRxBurst to unify log format
* Transceiver: Log error condition no burst in pullRadioVector()
* Transceiver: pullRadioVector(): Fix use of uninitialized value bi->tn
* Transceiver: Don't stop TRX if pulling from OFF timeslot
* radioInterface: Rename mRadio to mDevice
* radioInterfaceMulti: Check equals zero explicitly
* USRPDevice: Fix setRxGain return on error and getRxGain() returning always 0
* USRPDevice: Return previous txGain if setting value failed
* LMSDevice: Return previous txGain/rxGain if setting value failed
* radioInterface: Remove unusued getRxGain()
* radioDevice: Introduce getTxGain() API
* radioInterfaceMulti: Override setTxGain() to avoid chan!=0 calls
* UHDDevice: Drop unneeded MULTI_ARFCN checks
* radioInterface{Multi,Resamp}: Fix successful writeSamples() masking underrun from readSamples()
* radioInterface: Mark setRxGain as virtual
* Move multi-ARFCN chan amount modification from UHDDevice to parent class
* radioInterface: Atomically fetch and change underrun variable
* radioInterfaceMulti: write frequency offset direction (rx/tx) in log line
* Use new libosmocore logging lock API
* Transceiver: Fix wrong response upon CMD HANDOVER failure
* uhd: use value already cached in tmp variable
* Transceiver.cpp: Introduce and use new logging categories
[ Timo Jacobus ]
* Transceiver: Fixed copying of history into and from channelizer buffer.
[ Alexander Chemeris ]
* vty: Don't enable random filler bursts automatically with EDGE.
* vty: Simplify filler burst settings and improve help and readability.
[ Martin Hauke ]
* Fix common misspellings and typos
[ Harald Welte ]
* trx: exit() on unsupported positional arguments on command line
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 03 Jan 2020 19:54:00 +0100
osmo-trx (1.1.1) unstable; urgency=medium
* UNRELEASED

21
debian/control vendored
View File

@@ -14,7 +14,7 @@ Build-Depends: debhelper (>= 9),
libtalloc-dev,
libusrp-dev,
liblimesuite-dev,
libosmocore-dev (>= 1.3.0),
libosmocore-dev (>= 0.12.0),
osmo-gsm-manuals-dev
Standards-Version: 3.9.6
Vcs-Browser: http://cgit.osmocom.org/osmo-trx
@@ -91,6 +91,25 @@ Description: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite)
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
Package: osmo-trx-xtrx
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: SDR transceiver that implements Layer 1 of a GSM BTS (XTRX)
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
.
TS 05.01 "Physical layer on the radio path"
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
TS 05.04 "Modulation"
TS 05.10 "Radio subsystem synchronization"
.
In this context, BTS is "Base transceiver station". It's the stations that
connect mobile phones to the mobile network.
.
3GPP is the "3rd Generation Partnership Project" which is the collaboration
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
Package: osmo-trx-doc
Architecture: all
Section: doc

4
debian/osmo-trx-xtrx.install vendored Normal file
View File

@@ -0,0 +1,4 @@
etc/osmocom/osmo-trx-xtrx.cfg
lib/systemd/system/osmo-trx-xtrx.service
/usr/bin/osmo-trx-xtrx
/usr/share/doc/osmo-trx/examples/osmo-trx-xtrx/osmo-trx-xtrx.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-xtrx/

View File

@@ -7,7 +7,7 @@ index 8ff59f0..126c16a 100644
libtalloc-dev,
libusrp-dev,
- liblimesuite-dev,
libosmocore-dev (>= 1.3.0),
libosmocore-dev (>= 0.12.0),
osmo-gsm-manuals-dev
Standards-Version: 3.9.6
@@ -30,7 +29,7 @@ Package: osmo-trx-dbg

View File

@@ -14,6 +14,10 @@ if DEVICE_LMS
OSMOCONF_FILES += osmo-trx-lms/osmo-trx-lms.cfg
endif
if DEVICE_XTRX
OSMOCONF_FILES += osmo-trx-xtrx/osmo-trx-xtrx.cfg
endif
osmoconf_DATA = $(OSMOCONF_FILES)
EXTRA_DIST = $(OSMOCONF_FILES)

View File

@@ -4,7 +4,7 @@ log stderr
logging print category 1
logging timestamp 1
logging print file basename
logging level set-all notice
logging level set-all info
!
line vty
no login
@@ -12,6 +12,7 @@ line vty
trx
bind-ip 127.0.0.1
remote-ip 127.0.0.1
base-port 5700
egprs disable
tx-sps 4
rx-sps 4

View File

@@ -4,7 +4,7 @@ log stderr
logging print category 1
logging timestamp 1
logging print file basename
logging level set-all notice
logging level set-all info
!
line vty
no login
@@ -12,6 +12,7 @@ line vty
trx
bind-ip 127.0.0.1
remote-ip 127.0.0.1
base-port 5700
egprs disable
tx-sps 4
rx-sps 4

View File

@@ -4,7 +4,7 @@ log stderr
logging print category 1
logging timestamp 1
logging print file basename
logging level set-all notice
logging level set-all info
!
line vty
no login
@@ -12,6 +12,7 @@ line vty
trx
bind-ip 127.0.0.1
remote-ip 127.0.0.1
base-port 5700
dev-args addr=192.168.10.2,pa=NONE,pa_power_max_dbm=23,fifo_ctrl_window=0,status_port=12345
egprs disable
tx-sps 4

View File

@@ -4,7 +4,7 @@ log stderr
logging print category 1
logging timestamp 1
logging print file basename
logging level set-all notice
logging level set-all info
!
line vty
no login
@@ -12,10 +12,8 @@ line vty
trx
bind-ip 127.0.0.1
remote-ip 127.0.0.1
base-port 5700
egprs disable
! 28 dB offset below is valid only for the B2xx in 1800 MHz band, see
! https://osmocom.org/issues/4468 for more details
rssi-offset 28.000000
tx-sps 4
rx-sps 4
clock-ref external

View File

@@ -0,0 +1,20 @@
log stderr
logging filter all 1
logging color 1
logging print category 1
logging timestamp 1
logging print file basename
logging level set-all info
!
line vty
no login
!
trx
bind-ip 127.0.0.1
remote-ip 127.0.0.1
base-port 5700
egprs disable
tx-sps 4
rx-sps 4
rt-prio 18
chan 0

View File

@@ -46,16 +46,15 @@ Multi-ARFCN support is available since osmo-trx release `0.2.0`, and it was
added specifically in commit `76764278169d252980853251daeb9f1ba0c246e1`.
This feature is useful for instance if you want to run more than 1 TRX with an
Ettus B200 device, or more than 2 TRXs with an Ettus B210 device, since they
support only 1 and 2 physical RF channels respectively. No device from other
providers or even other devices than B200 and B210 from Ettus are known to
support this feature.
Ettus B200 device, or 2 TRX with an Ettus B210 device, since they support only 1
and 2 physical RF channels respectively. No device from other providers or even
other devices than B200 and B210 from Ettus are known to support this feature.
With multi-ARFCN enabled, ARFCN spacing is fixed at 800 kHz or 4 GSM channels.
So if TRX-0 is set to ARFCN 51, TRX-1 _must_ be set to 55, and so on. Up to
three ARFCN's is supported for multi-TRX.
From BTS and BSC point of view, supporting multiple TRXs through multi-ARFCN
From BTS and BSC point of view, supporting multiple TRX through multi-ARFCN
feature in OsmoTRX doesn't make any difference from a regular multi-TRX setup,
leaving apart of course the mentioned ARFCN limitations explained above and as a
consequence physical installation and operational differences.

View File

@@ -3,7 +3,7 @@
This section specifies the format of USB packets used for in-band data
transmission and signaling on the USRP1. All packets are 512-byte long, and are
transferred using USB "bulk" transfers.
transfered using USB "bulk" transfers.
IN packets are sent towards the host. OUT packets are sent away from the host.

View File

@@ -12,7 +12,7 @@ B200 family and Fairwaves UmTRX family, and used to be the default backend used
for legacy @osmo-trx@ binary when per-backend binaries didn't exist yet.
Any device providing generic support for UHD should theoretically be able to be
run through this backend without much effort, but practical experience showed
run through this backend without much effort, but pracitcal experience showed
that some devices don't play well with it, such as the LimeSDR family of
devices, which showed far better results when using its native interface.

View File

@@ -18,11 +18,10 @@
<param name='terminal' doc='Write to terminal' />
</params>
</command>
<command id='write file [PATH]'>
<command id='write file'>
<params>
<param name='write' doc='Write running configuration to memory, network, or terminal' />
<param name='file' doc='Write to configuration file' />
<param name='[PATH]' doc='Set file path to store the config, or replace if already exists' />
</params>
</command>
<command id='write memory'>
@@ -97,6 +96,12 @@
<param name='history' doc='Display the session command history' />
</params>
</command>
<command id='show trx'>
<params>
<param name='show' doc='Show running system information' />
<param name='trx' doc='Display information on the TRX' />
</params>
</command>
<command id='logging enable'>
<params>
<param name='logging' doc='Configure logging' />
@@ -188,17 +193,14 @@
<param name='MASK' doc='List of logging categories to log, e.g. &apos;abc:mno:xyz&apos;. Available log categories depend on the specific application, refer to the &apos;logging level&apos; command. Optionally add individual log levels like &apos;abc,1:mno,3:xyz,5&apos;, where the level numbers are LOGL_DEBUG=1 LOGL_INFO=3 LOGL_NOTICE=5 LOGL_ERROR=7 LOGL_FATAL=8' />
</params>
</command>
<command id='logging level (main|trxclk|trxctrl|trxddl|trxdul|dev|devdrv|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'>
<command id='logging level (main|trxctrl|dev|lms|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'>
<params>
<param name='logging' doc='Configure logging' />
<param name='level' doc='Set the log level for a specified category' />
<param name='main' doc='Main generic category' />
<param name='trxclk' doc='TRX Master Clock' />
<param name='trxctrl' doc='TRX CTRL interface' />
<param name='trxddl' doc='TRX Data interface Downlink' />
<param name='trxdul' doc='TRX CTRL interface Uplink' />
<param name='dev' doc='Device/Driver specific code' />
<param name='devdrv' doc='Logging from external device driver library implementing lower level specifics' />
<param name='lms' doc='Logging from within LimeSuite itself' />
<param name='lglobal' doc='Library-internal global log family' />
<param name='llapd' doc='LAPD in libosmogsm' />
<param name='linp' doc='A-bis Intput Subsystem' />
@@ -257,43 +259,6 @@
<param name='force-all' doc='Release any globally forced log level set with &apos;logging level force-all &lt;level&gt;&apos;' />
</params>
</command>
<command id='logp (main|trxclk|trxctrl|trxddl|trxdul|dev|devdrv|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal) .LOGMESSAGE'>
<params>
<param name='logp' doc='Print a message on all log outputs; useful for placing markers in test logs' />
<param name='main' doc='Main generic category' />
<param name='trxclk' doc='TRX Master Clock' />
<param name='trxctrl' doc='TRX CTRL interface' />
<param name='trxddl' doc='TRX Data interface Downlink' />
<param name='trxdul' doc='TRX CTRL interface Uplink' />
<param name='dev' doc='Device/Driver specific code' />
<param name='devdrv' doc='Logging from external device driver library implementing lower level specifics' />
<param name='lglobal' doc='Library-internal global log family' />
<param name='llapd' doc='LAPD in libosmogsm' />
<param name='linp' doc='A-bis Intput Subsystem' />
<param name='lmux' doc='A-bis B-Subchannel TRAU Frame Multiplex' />
<param name='lmi' doc='A-bis Input Driver for Signalling' />
<param name='lmib' doc='A-bis Input Driver for B-Channels (voice)' />
<param name='lsms' doc='Layer3 Short Message Service (SMS)' />
<param name='lctrl' doc='Control Interface' />
<param name='lgtp' doc='GPRS GTP library' />
<param name='lstats' doc='Statistics messages and logging' />
<param name='lgsup' doc='Generic Subscriber Update Protocol' />
<param name='loap' doc='Osmocom Authentication Protocol' />
<param name='lss7' doc='libosmo-sigtran Signalling System 7' />
<param name='lsccp' doc='libosmo-sigtran SCCP Implementation' />
<param name='lsua' doc='libosmo-sigtran SCCP User Adaptation' />
<param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
<param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
<param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
<param name='lrspro' doc='Remote SIM protocol' />
<param name='debug' doc='Log debug messages and higher levels' />
<param name='info' doc='Log informational messages and higher levels' />
<param name='notice' doc='Log noticeable messages and higher levels' />
<param name='error' doc='Log error messages and higher levels' />
<param name='fatal' doc='Log only fatal messages' />
<param name='.LOGMESSAGE' doc='Arbitrary message to log on given category and log level' />
</params>
</command>
<command id='show logging vty'>
<params>
<param name='show' doc='Show running system information' />
@@ -307,12 +272,6 @@
<param name='alarms' doc='Show current logging configuration' />
</params>
</command>
<command id='show trx'>
<params>
<param name='show' doc='Show running system information' />
<param name='trx' doc='Display information on the TRX' />
</params>
</command>
<command id='show talloc-context (application|all) (full|brief|DEPTH)'>
<params>
<param name='show' doc='Show running system information' />
@@ -456,6 +415,12 @@
<param name='monitor' doc='Copy debug output to the current terminal line' />
</params>
</command>
<command id='show trx'>
<params>
<param name='show' doc='Show running system information' />
<param name='trx' doc='Display information on the TRX' />
</params>
</command>
<command id='logging enable'>
<params>
<param name='logging' doc='Configure logging' />
@@ -547,17 +512,14 @@
<param name='MASK' doc='List of logging categories to log, e.g. &apos;abc:mno:xyz&apos;. Available log categories depend on the specific application, refer to the &apos;logging level&apos; command. Optionally add individual log levels like &apos;abc,1:mno,3:xyz,5&apos;, where the level numbers are LOGL_DEBUG=1 LOGL_INFO=3 LOGL_NOTICE=5 LOGL_ERROR=7 LOGL_FATAL=8' />
</params>
</command>
<command id='logging level (main|trxclk|trxctrl|trxddl|trxdul|dev|devdrv|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'>
<command id='logging level (main|trxctrl|dev|lms|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'>
<params>
<param name='logging' doc='Configure logging' />
<param name='level' doc='Set the log level for a specified category' />
<param name='main' doc='Main generic category' />
<param name='trxclk' doc='TRX Master Clock' />
<param name='trxctrl' doc='TRX CTRL interface' />
<param name='trxddl' doc='TRX Data interface Downlink' />
<param name='trxdul' doc='TRX CTRL interface Uplink' />
<param name='dev' doc='Device/Driver specific code' />
<param name='devdrv' doc='Logging from external device driver library implementing lower level specifics' />
<param name='lms' doc='Logging from within LimeSuite itself' />
<param name='lglobal' doc='Library-internal global log family' />
<param name='llapd' doc='LAPD in libosmogsm' />
<param name='linp' doc='A-bis Intput Subsystem' />
@@ -616,43 +578,6 @@
<param name='force-all' doc='Release any globally forced log level set with &apos;logging level force-all &lt;level&gt;&apos;' />
</params>
</command>
<command id='logp (main|trxclk|trxctrl|trxddl|trxdul|dev|devdrv|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal) .LOGMESSAGE'>
<params>
<param name='logp' doc='Print a message on all log outputs; useful for placing markers in test logs' />
<param name='main' doc='Main generic category' />
<param name='trxclk' doc='TRX Master Clock' />
<param name='trxctrl' doc='TRX CTRL interface' />
<param name='trxddl' doc='TRX Data interface Downlink' />
<param name='trxdul' doc='TRX CTRL interface Uplink' />
<param name='dev' doc='Device/Driver specific code' />
<param name='devdrv' doc='Logging from external device driver library implementing lower level specifics' />
<param name='lglobal' doc='Library-internal global log family' />
<param name='llapd' doc='LAPD in libosmogsm' />
<param name='linp' doc='A-bis Intput Subsystem' />
<param name='lmux' doc='A-bis B-Subchannel TRAU Frame Multiplex' />
<param name='lmi' doc='A-bis Input Driver for Signalling' />
<param name='lmib' doc='A-bis Input Driver for B-Channels (voice)' />
<param name='lsms' doc='Layer3 Short Message Service (SMS)' />
<param name='lctrl' doc='Control Interface' />
<param name='lgtp' doc='GPRS GTP library' />
<param name='lstats' doc='Statistics messages and logging' />
<param name='lgsup' doc='Generic Subscriber Update Protocol' />
<param name='loap' doc='Osmocom Authentication Protocol' />
<param name='lss7' doc='libosmo-sigtran Signalling System 7' />
<param name='lsccp' doc='libosmo-sigtran SCCP Implementation' />
<param name='lsua' doc='libosmo-sigtran SCCP User Adaptation' />
<param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
<param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
<param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
<param name='lrspro' doc='Remote SIM protocol' />
<param name='debug' doc='Log debug messages and higher levels' />
<param name='info' doc='Log informational messages and higher levels' />
<param name='notice' doc='Log noticeable messages and higher levels' />
<param name='error' doc='Log error messages and higher levels' />
<param name='fatal' doc='Log only fatal messages' />
<param name='.LOGMESSAGE' doc='Arbitrary message to log on given category and log level' />
</params>
</command>
<command id='show logging vty'>
<params>
<param name='show' doc='Show running system information' />
@@ -666,12 +591,6 @@
<param name='alarms' doc='Show current logging configuration' />
</params>
</command>
<command id='show trx'>
<params>
<param name='show' doc='Show running system information' />
<param name='trx' doc='Display information on the TRX' />
</params>
</command>
<command id='show talloc-context (application|all) (full|brief|DEPTH)'>
<params>
<param name='show' doc='Show running system information' />
@@ -853,6 +772,16 @@
<param name='history' doc='Display the session command history' />
</params>
</command>
<command id='ctrl'>
<params>
<param name='ctrl' doc='Configure the Control Interface' />
</params>
</command>
<command id='trx'>
<params>
<param name='trx' doc='Configure the TRX' />
</params>
</command>
<command id='log stderr'>
<params>
<param name='log' doc='Configure logging sub-system' />
@@ -932,16 +861,6 @@
<param name='[HOSTNAME]' doc='Host name to send the GSMTAP logging to (UDP port 4729)' />
</params>
</command>
<command id='ctrl'>
<params>
<param name='ctrl' doc='Configure the Control Interface' />
</params>
</command>
<command id='trx'>
<params>
<param name='trx' doc='Configure the TRX' />
</params>
</command>
<command id='stats reporter statsd'>
<params>
<param name='stats' doc='Configure stats sub-system' />
@@ -1054,17 +973,14 @@
<param name='[last]' doc='Log source file info at the end of a log line. If omitted, log source file info just before the log text.' />
</params>
</command>
<command id='logging level (main|trxclk|trxctrl|trxddl|trxdul|dev|devdrv|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'>
<command id='logging level (main|trxctrl|dev|lms|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'>
<params>
<param name='logging' doc='Configure logging' />
<param name='level' doc='Set the log level for a specified category' />
<param name='main' doc='Main generic category' />
<param name='trxclk' doc='TRX Master Clock' />
<param name='trxctrl' doc='TRX CTRL interface' />
<param name='trxddl' doc='TRX Data interface Downlink' />
<param name='trxdul' doc='TRX CTRL interface Uplink' />
<param name='dev' doc='Device/Driver specific code' />
<param name='devdrv' doc='Logging from external device driver library implementing lower level specifics' />
<param name='lms' doc='Logging from within LimeSuite itself' />
<param name='lglobal' doc='Library-internal global log family' />
<param name='llapd' doc='LAPD in libosmogsm' />
<param name='linp' doc='A-bis Intput Subsystem' />
@@ -1263,10 +1179,24 @@
<param name='4' doc='(null)' />
</params>
</command>
<command id='test rtsc &lt;0-7&gt;'>
<params>
<param name='test' doc='Set the Random Normal Burst test mode with TSC' />
<param name='rtsc' doc='TSC' />
<param name='&lt;0-7&gt;' doc='(null)' />
</params>
</command>
<command id='test rach-delay &lt;0-68&gt;'>
<params>
<param name='test' doc='Set the Random Access Burst test mode with delay' />
<param name='rach-delay' doc='RACH delay' />
<param name='&lt;0-68&gt;' doc='(null)' />
</params>
</command>
<command id='clock-ref (internal|external|gpsdo)'>
<params>
<param name='clock-ref' doc='Set the Reference Clock' />
<param name='internal' doc='Enable internal reference (default)' />
<param name='internal' doc='Enable internal referece (default)' />
<param name='external' doc='Enable external 10 MHz reference' />
<param name='gpsdo' doc='Enable GPSDO reference' />
</params>
@@ -1317,29 +1247,10 @@
<param name='&lt;1-32&gt;' doc='Real time priority' />
</params>
</command>
<command id='filler type (zero|dummy|random-nb-gmsk|random-nb-8psk|random-ab)'>
<command id='filler dummy'>
<params>
<param name='filler' doc='Filler burst settings' />
<param name='type' doc='Filler burst type (default=zero)' />
<param name='zero' doc='Send an empty burst when there is nothing to send (default)' />
<param name='dummy' doc='Send a dummy burst when there is nothing to send on C0 (TRX0) and empty burst on other channels. Use for OpenBTS compatibility only, don&apos;t use with OsmoBTS as it breaks encryption.' />
<param name='random-nb-gmsk' doc='Send a GMSK modulated Normal Burst with random bits when there is nothing to send. Use for spectrum mask testing. Configure &apos;filler tsc&apos; to set training sequence.' />
<param name='random-nb-8psk' doc='Send an 8-PSK modulated Normal Burst with random bits when there is nothing to send. Use for spectrum mask testing. Configure &apos;filler tsc&apos; to set training sequence.' />
<param name='random-ab' doc='Send an Access Burst with random bits when there is nothing to send. Use for Rx/Tx alignment. Configure &apos;filler access-burst-delay&apos; to introduce artificial delay.' />
</params>
</command>
<command id='filler tsc &lt;0-7&gt;'>
<params>
<param name='filler' doc='Filler burst settings' />
<param name='tsc' doc='Set the TSC for GMSK/8-PSK Normal Burst random fillers. Used only with &apos;random-nb-gmsk&apos; and &apos;random-nb-8psk&apos; filler types. (default=0)' />
<param name='&lt;0-7&gt;' doc='TSC' />
</params>
</command>
<command id='filler access-burst-delay &lt;0-68&gt;'>
<params>
<param name='filler' doc='Filler burst settings' />
<param name='access-burst-delay' doc='Set the delay for Access Burst random fillers. Used only with &apos;random-ab&apos; filler type. (default=0)' />
<param name='&lt;0-68&gt;' doc='RACH delay in symbols' />
<param name='filler' doc='Enable C0 filler table' />
<param name='dummy' doc='Dummy method' />
</params>
</command>
<command id='ctr-error-threshold (rx_overruns|tx_underruns|rx_drop_events|rx_drop_samples|tx_drop_events|tx_drop_samples) &lt;0-65535&gt; (per-second|per-minute|per-hour|per-day)'>

View File

@@ -146,9 +146,9 @@ struct test_vec
float *h;
float *y;
int x_len; /* These are in # of _floats_ ! */
int h_len; /* These are in # of _floats_ ! */
int y_len; /* These are in # of _floats_ ! */
int x_len; /* Theses are in # of _floats_ ! */
int h_len; /* Theses are in # of _floats_ ! */
int y_len; /* Theses are in # of _floats_ ! */
};
/* Reset test vectors */

View File

@@ -1,9 +0,0 @@
AM_CPPFLAGS = $(LIBOSMOCODING_CFLAGS)
AM_CFLAGS = -Wall
EXTRA_DIST = clockdump.sh matlab
noinst_PROGRAMS = osmo-prbs-tool
osmo_prbs_tool_SOURCES = prbs-tool.c
osmo_prbs_tool_LDADD = $(LIBOSMOCODING_LIBS)

View File

@@ -1,386 +0,0 @@
/* Dummy TRX for sening PRBS test sequences into osmo-bts-trx to test
* the decoder/receiver processing in osmo-bts-trx as well as any
* additional PRBS testing code.
*
* The purpose of this program is to use it as a mock dummy MS-side
* transmitter of GSM bursts that contain encoded PRBS sequences,
* similar to what one would normally do with an arbitrary
* function/waveform generator or BERT tester in hardware.
*
* (C) 2017 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* Licensed under terms of the GNU Generral Public License, Version 2,
* or (at your option) any later version.
*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/prbs.h>
#include <osmocom/core/socket.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/coding/gsm0503_coding.h>
/***********************************************************************
* GSM Constants
***********************************************************************/
#define GSM_FR_BYTES 33
#define GSM_BURST_BITS 116
#define GSM_4BURST_BITS (GSM_BURST_BITS*4)
#define GSM_BURST_LEN 148
/***********************************************************************
* TRX Interface / Protocol
***********************************************************************/
#define TRX_BASE_PORT 5700
/* DATA port on the TRX side */
#define TRX_PORT_CTRL_TRX(C) (TRX_BASE_PORT+(2*(C))+1)
#define TRX_PORT_DATA_TRX(C) (TRX_BASE_PORT+(2*(C))+2)
#define TRX_PORT_CTRL_BTS(C) (TRX_PORT_CTRL_TRX(C)+100)
#define TRX_PORT_DATA_BTS(C) (TRX_PORT_DATA_TRX(C)+100)
struct trx_ul_msg {
uint8_t ts;
uint32_t fn;
uint8_t rssi;
uint16_t t_offs;
uint8_t bits[148]; /* 0..255, *NOT* sbit_t */
} __attribute__((packed));
struct trx_dl_msg {
uint8_t ts;
uint32_t fn;
uint8_t att_db;
ubit_t bits[148];
} __attribute__((packed));
/***********************************************************************
* Helper Functions
***********************************************************************/
static int ubits2trxbits(uint8_t *sbits, const ubit_t *ubits, unsigned int count)
{
unsigned int i;
for (i = 0; i < count; i++) {
if ((*ubits++) & 1) {
*sbits++ = 255;
} else {
*sbits++ = 0;
}
}
return count;
}
static int __attribute__((__unused__)) dec(const ubit_t *bursts_u)
{
sbit_t bursts_s[GSM_4BURST_BITS*2];
uint8_t dec_tch_data[GSM_FR_BYTES];
int n_errors, n_bits_total;
int rc;
/* convert from u_bit (tx) to s_bit (rx) */
osmo_ubit2sbit(bursts_s, bursts_u, sizeof(bursts_s));
rc = gsm0503_tch_fr_decode(dec_tch_data, bursts_s, 1, 0, &n_errors, &n_bits_total);
printf("rc=%d, n_errors=%d, n_bits_total=%d: %s\n", rc, n_errors, n_bits_total,
osmo_hexdump(dec_tch_data, sizeof(dec_tch_data)));
return rc;
}
/*! \brief Training Sequences (TS 05.02 Chapter 5.2.3) */
static const ubit_t _sched_tsc[8][26] = {
{ 0,0,1,0,0,1,0,1,1,1,0,0,0,0,1,0,0,0,1,0,0,1,0,1,1,1, },
{ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,1,0,0,0,1,0,1,1,0,1,1,1, },
{ 0,1,0,0,0,0,1,1,1,0,1,1,1,0,1,0,0,1,0,0,0,0,1,1,1,0, },
{ 0,1,0,0,0,1,1,1,1,0,1,1,0,1,0,0,0,1,0,0,0,1,1,1,1,0, },
{ 0,0,0,1,1,0,1,0,1,1,1,0,0,1,0,0,0,0,0,1,1,0,1,0,1,1, },
{ 0,1,0,0,1,1,1,0,1,0,1,1,0,0,0,0,0,1,0,0,1,1,1,0,1,0, },
{ 1,0,1,0,0,1,1,1,1,1,0,1,1,0,0,0,1,0,1,0,0,1,1,1,1,1, },
{ 1,1,1,0,1,1,1,1,0,0,0,1,0,0,1,0,1,1,1,0,1,1,1,1,0,0, },
};
/***********************************************************************
* state + processing functions
***********************************************************************/
/* state we have to keep for one physical channel */
struct pchan_data {
/* PRBS state */
struct osmo_prbs_state st;
/* unpacked PRBS bits, generated from PRBS */
ubit_t prbs_u[4+260];
/* packed frame (to be sent) */
uint8_t tch_data[GSM_FR_BYTES];
/* burst bits (ubit) to be transmitted */
ubit_t bursts[GSM_4BURST_BITS*2]; /* 116 * 8 */
/* burst bits (sbit) 'as if received' */
sbit_t bursts_s[GSM_4BURST_BITS*2];
/* next to-be transmitted burst number */
unsigned int burst_nr;
/* training sequence code */
unsigned int tsc;
/* loose 'count' bursts every 'nth_mframe' on TRX-BTS interface */
struct {
unsigned int count;
unsigned int nth_mframe;
} sim_lost_bursts;
/* zero 'count' bursts every 'nth_mframe' on TRX-BTS interface */
struct {
unsigned int count;
unsigned int nth_mframe;
} sim_zero_bursts;
/* flip every 'nth_bit' of the PRNG oudput before encoding */
struct {
unsigned int nth_bit;
unsigned int i;
} sim_flip_codec_bits;
unsigned int i;
};
struct ts_data {
struct pchan_data pchan[2];
};
struct trx_data {
struct ts_data ts[8];
};
static struct trx_data g_trx_data;
/* initialize the state for one TRX */
static void trx_data_init(struct trx_data *trx)
{
int i;
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
struct ts_data *ts = &trx->ts[i];
int j;
for (j = 0; j < ARRAY_SIZE(ts->pchan); j++) {
struct pchan_data *pchan = &ts->pchan[j];
memset(pchan, 0, sizeof(*pchan));
osmo_prbs_state_init(&pchan->st, &osmo_prbs9);
pchan->tsc = 7;
}
}
}
/* apply any intentional errors to the output of the PRBS sequence */
static void apply_errors_prbs(struct pchan_data *pchan)
{
int i;
for (i = 0; i < sizeof(pchan->prbs_u)-4; i++) {
pchan->sim_flip_codec_bits.i++;
if (pchan->sim_flip_codec_bits.i == pchan->sim_flip_codec_bits.nth_bit) {
pchan->sim_flip_codec_bits.i = 0;
pchan->prbs_u[4+i] ^= 0x01;
}
}
}
/*! obtain the next to-be-transmitted burst for the given pchan
* \param pchan physical channel on which we operate
* \param[in] fn frame number
* \param[out] burst_out caller-provided buffer for 148 unpacked output bits
* \retruns number of bits stored in \a burst_out */
static int pchan_get_next_burst(struct pchan_data *pchan, uint32_t fn, ubit_t *burst_out)
{
uint32_t fn26 = fn % 26;
int rc;
if (fn26 == 0 || fn26 == 4 || fn26 == 8 || fn26 == 13 || fn26 == 17 || fn26 == 21)
pchan->burst_nr = 0;
if (fn26 == 12 || fn26 == 25) {
memset(burst_out, 0, GSM_BURST_LEN);
return GSM_BURST_LEN;
}
if (pchan->burst_nr == 0) {
/* generate PRBS output in ubit format, skipping first nibble for 260-264 padding */
const uint8_t prefix[] = { 0xd0 };
osmo_pbit2ubit(pchan->prbs_u, prefix, 4);
rc = osmo_prbs_get_ubits(pchan->prbs_u+4, sizeof(pchan->prbs_u)-4, &pchan->st);
OSMO_ASSERT(rc == sizeof(pchan->prbs_u)-4);
apply_errors_prbs(pchan);
/* pack to PBIT format */
rc = osmo_ubit2pbit(pchan->tch_data, pchan->prbs_u, sizeof(pchan->prbs_u));
//memset(pchan->tch_data, 0xff, sizeof(pchan->tch_data));
printf("%s\n", osmo_hexdump(pchan->tch_data, GSM_FR_BYTES));
/* shift buffer by 4 bursts for interleaving */
memcpy(pchan->bursts, pchan->bursts + GSM_4BURST_BITS, GSM_4BURST_BITS);
memset(pchan->bursts + GSM_4BURST_BITS, 0, GSM_4BURST_BITS);
/* encode block (codec frame) into four bursts */
rc = gsm0503_tch_fr_encode(pchan->bursts, pchan->tch_data, GSM_FR_BYTES, 1);
OSMO_ASSERT(rc == 0);
#if 0
int i;
for (i = 0; i < sizeof(pchan->bursts); i += GSM_BURST_BITS)
printf("\t%s\n", osmo_ubit_dump(pchan->bursts + i, GSM_BURST_BITS));
dec(pchan->bursts);
#endif
}
/* for all bursts: format 148 symbols from 116 input bits */
ubit_t *burst = pchan->bursts + pchan->burst_nr * GSM_BURST_BITS;
// printf("TX(%u): %s\n", pchan->burst_nr, osmo_ubit_dump(burst, GSM_BURST_BITS));
memset(burst_out, 0, 3); /* guard bits */
memcpy(burst_out+3, burst, 58); /* firrst half */
memcpy(burst_out+61, _sched_tsc[pchan->tsc], 26); /* midamble */
memcpy(burst_out+87, burst+58, 58); /* second half */
memset(burst_out+145, 0, 3); /* guard bits */
/* increment burst number for next call */
pchan->burst_nr += 1;
return GSM_BURST_LEN;
}
static int pchan_process_ts_fn(struct pchan_data *pchan, uint32_t fn, uint8_t *burst_t)
{
ubit_t burst_u[GSM_BURST_LEN];
int rc;
rc = pchan_get_next_burst(pchan, fn, burst_u);
OSMO_ASSERT(rc == sizeof(burst_u));
/* convert from u_bit (tx) to s_bit (rx) */
ubits2trxbits(burst_t, burst_u, GSM_BURST_LEN);
return GSM_BURST_LEN;
}
/* read TRX DL data from BTS, write TRX UL data to BTS */
static int read_and_process(int fd)
{
/* receive (downlink) buffer */
uint8_t rx_dl_buf[1024];
struct trx_dl_msg *dl_msg = (struct trx_dl_msg *) rx_dl_buf;
/* transmit (uplink) buffer */
uint8_t tx_ul_buf[1024];
struct trx_ul_msg *ul_msg = (struct trx_ul_msg *) tx_ul_buf;
/* other variables */
struct pchan_data *pchan;
uint32_t fn;
uint8_t rc;
/* do a blocking read on the socket and receive DL from BTS */
rc = read(fd, rx_dl_buf, sizeof(rx_dl_buf));
if (rc < sizeof(*dl_msg))
return rc;
fn = ntohl(dl_msg->fn);
if (dl_msg->ts >= ARRAY_SIZE(g_trx_data.ts))
return -ENODEV;
if (dl_msg->ts != 2)
return 0;
printf("FN=%s TS=%u\n", gsm_fn_as_gsmtime_str(fn), dl_msg->ts);
/* FIXME: second pchan for TCH/H */
pchan = &g_trx_data.ts[dl_msg->ts].pchan[0];
rc = pchan_process_ts_fn(pchan, fn, (uint8_t *) ul_msg->bits);
OSMO_ASSERT(rc == sizeof(ul_msg->bits));
/* copy over timeslot and frame number */
ul_msg->fn = htonl(fn);
ul_msg->ts = dl_msg->ts;
/* simulate lost frames on TRX <-> BTS interface */
if (pchan->sim_lost_bursts.count) {
/* count number of 26-multiframes */
static int count = 0;
if (fn % 26 == 0)
count++;
/* every 10th multiframe, drop two entire block of 8 bursts */
if (count % pchan->sim_lost_bursts.nth_mframe == 0 &&
(fn % 26) <= pchan->sim_lost_bursts.count) {
printf("===> SKIPPING BURST\n");
return 0;
}
}
/* simulate zero-ed frames on TRX <-> BTS interface */
if (pchan->sim_zero_bursts.count) {
/* count number of 26-multiframes */
static int count = 0;
if (fn % 26 == 0)
count++;
/* every 10th multiframe, drop two entire block of 8 bursts */
if (count % pchan->sim_zero_bursts.nth_mframe == 0 &&
(fn % 26) <= pchan->sim_zero_bursts.count) {
memset(ul_msg->bits, 0, sizeof(ul_msg->bits));
printf("===> ZEROING BURST\n");
}
}
/* write uplink message towards BTS */
rc = write(fd, tx_ul_buf, sizeof(*ul_msg));
if (rc < sizeof(*ul_msg))
return -EIO;
return 0;
}
static int open_trx_data_sock(unsigned int trx_nr, const char *bts_host)
{
int rc;
rc = osmo_sock_init2(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, TRX_PORT_DATA_TRX(trx_nr),
bts_host, TRX_PORT_DATA_BTS(trx_nr),
OSMO_SOCK_F_CONNECT | OSMO_SOCK_F_BIND);
return rc;
}
int main(int argc, char **argv)
{
int fd;
trx_data_init(&g_trx_data);
//g_trx_data.ts[2].pchan[0].sim_zero_bursts.count = 8;
//g_trx_data.ts[2].pchan[0].sim_zero_bursts.nth_mframe = 10;
g_trx_data.ts[2].pchan[0].sim_flip_codec_bits.nth_bit = 260*4;
fd = open_trx_data_sock(0, "127.0.0.1");
if (fd < 0)
exit(1);
while (1) {
read_and_process(fd);
}
return 0;
}