mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-11-04 06:03:17 +00:00 
			
		
		
		
	Compare commits
	
		
			70 Commits
		
	
	
		
			fairwaves/
			...
			osmith/ipc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7cc7e3de99 | ||
| 
						 | 
					0f00e676f2 | ||
| 
						 | 
					8b9ec56e5d | ||
| 
						 | 
					106347dc31 | ||
| 
						 | 
					14ee5eacb5 | ||
| 
						 | 
					803a90028c | ||
| 
						 | 
					bd8ca9f95e | ||
| 
						 | 
					aec2c35861 | ||
| 
						 | 
					c53a7fb4f4 | ||
| 
						 | 
					420e5f6112 | ||
| 
						 | 
					453c32c091 | ||
| 
						 | 
					c3299d945a | ||
| 
						 | 
					ac037980d7 | ||
| 
						 | 
					ebc9e07476 | ||
| 
						 | 
					58425b9653 | ||
| 
						 | 
					b36c69d102 | ||
| 
						 | 
					4d2e2a9e83 | ||
| 
						 | 
					3eede6b455 | ||
| 
						 | 
					3b35c447e1 | ||
| 
						 | 
					162ea392c3 | ||
| 
						 | 
					036b85e85a | ||
| 
						 | 
					9eff30845b | ||
| 
						 | 
					cf66e6a45e | ||
| 
						 | 
					fdefbfac39 | ||
| 
						 | 
					5e16f79f0f | ||
| 
						 | 
					f3155e33b9 | ||
| 
						 | 
					7bbe19ee9d | ||
| 
						 | 
					f669bf43e7 | ||
| 
						 | 
					4a575b02b5 | ||
| 
						 | 
					cf35c37c94 | ||
| 
						 | 
					6e369348b0 | ||
| 
						 | 
					546516d79c | ||
| 
						 | 
					6879bb0db9 | ||
| 
						 | 
					a7143bf7a1 | ||
| 
						 | 
					4ffdca10d4 | ||
| 
						 | 
					1a26b4f085 | ||
| 
						 | 
					c5d5586fbd | ||
| 
						 | 
					cc971aa1a6 | ||
| 
						 | 
					748d8edbf8 | ||
| 
						 | 
					dfc6e5ffc7 | ||
| 
						 | 
					f8c0c464b8 | ||
| 
						 | 
					93707d0227 | ||
| 
						 | 
					5291e8a654 | ||
| 
						 | 
					7a07de1efd | ||
| 
						 | 
					fd67262df8 | ||
| 
						 | 
					0569845a08 | ||
| 
						 | 
					c69b87f9bd | ||
| 
						 | 
					a7bf6cd8a4 | ||
| 
						 | 
					e7f6a27ab6 | ||
| 
						 | 
					a979f5f32b | ||
| 
						 | 
					b0e54265ad | ||
| 
						 | 
					62c9280590 | ||
| 
						 | 
					1421adbc61 | ||
| 
						 | 
					dccc82491c | ||
| 
						 | 
					bf58370675 | ||
| 
						 | 
					7c84925ea4 | ||
| 
						 | 
					fd99c6ce05 | ||
| 
						 | 
					e947db8d98 | ||
| 
						 | 
					9279e0e123 | ||
| 
						 | 
					84231bd8b7 | ||
| 
						 | 
					aebbfe0ee7 | ||
| 
						 | 
					da7fee8ea8 | ||
| 
						 | 
					9f2baf3e04 | ||
| 
						 | 
					0a2a40f43c | ||
| 
						 | 
					501d053b70 | ||
| 
						 | 
					0fafe03199 | ||
| 
						 | 
					9a87d90c1e | ||
| 
						 | 
					066fd04f47 | ||
| 
						 | 
					aeaba02e02 | ||
| 
						 | 
					b7e992703c | 
							
								
								
									
										521
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										521
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,521 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
#
 | 
			
		||||
# clang-format configuration file. Intended for clang-format >= 4.
 | 
			
		||||
#
 | 
			
		||||
# For more information, see:
 | 
			
		||||
#
 | 
			
		||||
#   Documentation/process/clang-format.rst
 | 
			
		||||
#   https://clang.llvm.org/docs/ClangFormat.html
 | 
			
		||||
#   https://clang.llvm.org/docs/ClangFormatStyleOptions.html
 | 
			
		||||
#
 | 
			
		||||
---
 | 
			
		||||
AccessModifierOffset: -4
 | 
			
		||||
AlignAfterOpenBracket: Align
 | 
			
		||||
AlignConsecutiveAssignments: false
 | 
			
		||||
AlignConsecutiveDeclarations: false
 | 
			
		||||
#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
 | 
			
		||||
AlignOperands: true
 | 
			
		||||
AlignTrailingComments: false
 | 
			
		||||
AllowAllParametersOfDeclarationOnNextLine: false
 | 
			
		||||
AllowShortBlocksOnASingleLine: false
 | 
			
		||||
AllowShortCaseLabelsOnASingleLine: false
 | 
			
		||||
AllowShortFunctionsOnASingleLine: None
 | 
			
		||||
AllowShortIfStatementsOnASingleLine: false
 | 
			
		||||
AllowShortLoopsOnASingleLine: false
 | 
			
		||||
AlwaysBreakAfterDefinitionReturnType: None
 | 
			
		||||
AlwaysBreakAfterReturnType: None
 | 
			
		||||
AlwaysBreakBeforeMultilineStrings: false
 | 
			
		||||
AlwaysBreakTemplateDeclarations: false
 | 
			
		||||
BinPackArguments: true
 | 
			
		||||
BinPackParameters: true
 | 
			
		||||
BraceWrapping:
 | 
			
		||||
  AfterClass: false
 | 
			
		||||
  AfterControlStatement: false
 | 
			
		||||
  AfterEnum: false
 | 
			
		||||
  AfterFunction: true
 | 
			
		||||
  AfterNamespace: true
 | 
			
		||||
  AfterObjCDeclaration: false
 | 
			
		||||
  AfterStruct: false
 | 
			
		||||
  AfterUnion: false
 | 
			
		||||
  #AfterExternBlock: false # Unknown to clang-format-5.0
 | 
			
		||||
  BeforeCatch: false
 | 
			
		||||
  BeforeElse: false
 | 
			
		||||
  IndentBraces: false
 | 
			
		||||
  #SplitEmptyFunction: true # Unknown to clang-format-4.0
 | 
			
		||||
  #SplitEmptyRecord: true # Unknown to clang-format-4.0
 | 
			
		||||
  #SplitEmptyNamespace: true # Unknown to clang-format-4.0
 | 
			
		||||
BreakBeforeBinaryOperators: None
 | 
			
		||||
BreakBeforeBraces: Custom
 | 
			
		||||
#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
 | 
			
		||||
BreakBeforeTernaryOperators: false
 | 
			
		||||
BreakConstructorInitializersBeforeComma: false
 | 
			
		||||
#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
 | 
			
		||||
BreakAfterJavaFieldAnnotations: false
 | 
			
		||||
BreakStringLiterals: false
 | 
			
		||||
ColumnLimit: 120
 | 
			
		||||
CommentPragmas: '^ IWYU pragma:'
 | 
			
		||||
#CompactNamespaces: false # Unknown to clang-format-4.0
 | 
			
		||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
 | 
			
		||||
ConstructorInitializerIndentWidth: 8
 | 
			
		||||
ContinuationIndentWidth: 8
 | 
			
		||||
Cpp11BracedListStyle: false
 | 
			
		||||
DerivePointerAlignment: false
 | 
			
		||||
DisableFormat: false
 | 
			
		||||
ExperimentalAutoDetectBinPacking: false
 | 
			
		||||
#FixNamespaceComments: false # Unknown to clang-format-4.0
 | 
			
		||||
 | 
			
		||||
# Taken from:
 | 
			
		||||
#   git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \
 | 
			
		||||
#   | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$,  - '\1'," \
 | 
			
		||||
#   | sort | uniq
 | 
			
		||||
ForEachMacros:
 | 
			
		||||
  - 'apei_estatus_for_each_section'
 | 
			
		||||
  - 'ata_for_each_dev'
 | 
			
		||||
  - 'ata_for_each_link'
 | 
			
		||||
  - '__ata_qc_for_each'
 | 
			
		||||
  - 'ata_qc_for_each'
 | 
			
		||||
  - 'ata_qc_for_each_raw'
 | 
			
		||||
  - 'ata_qc_for_each_with_internal'
 | 
			
		||||
  - 'ax25_for_each'
 | 
			
		||||
  - 'ax25_uid_for_each'
 | 
			
		||||
  - '__bio_for_each_bvec'
 | 
			
		||||
  - 'bio_for_each_bvec'
 | 
			
		||||
  - 'bio_for_each_integrity_vec'
 | 
			
		||||
  - '__bio_for_each_segment'
 | 
			
		||||
  - 'bio_for_each_segment'
 | 
			
		||||
  - 'bio_for_each_segment_all'
 | 
			
		||||
  - 'bio_list_for_each'
 | 
			
		||||
  - 'bip_for_each_vec'
 | 
			
		||||
  - 'bitmap_for_each_clear_region'
 | 
			
		||||
  - 'bitmap_for_each_set_region'
 | 
			
		||||
  - 'blkg_for_each_descendant_post'
 | 
			
		||||
  - 'blkg_for_each_descendant_pre'
 | 
			
		||||
  - 'blk_queue_for_each_rl'
 | 
			
		||||
  - 'bond_for_each_slave'
 | 
			
		||||
  - 'bond_for_each_slave_rcu'
 | 
			
		||||
  - 'bpf_for_each_spilled_reg'
 | 
			
		||||
  - 'btree_for_each_safe128'
 | 
			
		||||
  - 'btree_for_each_safe32'
 | 
			
		||||
  - 'btree_for_each_safe64'
 | 
			
		||||
  - 'btree_for_each_safel'
 | 
			
		||||
  - 'card_for_each_dev'
 | 
			
		||||
  - 'cgroup_taskset_for_each'
 | 
			
		||||
  - 'cgroup_taskset_for_each_leader'
 | 
			
		||||
  - 'cpufreq_for_each_entry'
 | 
			
		||||
  - 'cpufreq_for_each_entry_idx'
 | 
			
		||||
  - 'cpufreq_for_each_valid_entry'
 | 
			
		||||
  - 'cpufreq_for_each_valid_entry_idx'
 | 
			
		||||
  - 'css_for_each_child'
 | 
			
		||||
  - 'css_for_each_descendant_post'
 | 
			
		||||
  - 'css_for_each_descendant_pre'
 | 
			
		||||
  - 'device_for_each_child_node'
 | 
			
		||||
  - 'dma_fence_chain_for_each'
 | 
			
		||||
  - 'drm_atomic_crtc_for_each_plane'
 | 
			
		||||
  - 'drm_atomic_crtc_state_for_each_plane'
 | 
			
		||||
  - 'drm_atomic_crtc_state_for_each_plane_state'
 | 
			
		||||
  - 'drm_atomic_for_each_plane_damage'
 | 
			
		||||
  - 'drm_client_for_each_connector_iter'
 | 
			
		||||
  - 'drm_client_for_each_modeset'
 | 
			
		||||
  - 'drm_connector_for_each_possible_encoder'
 | 
			
		||||
  - 'drm_for_each_bridge_in_chain'
 | 
			
		||||
  - 'drm_for_each_connector_iter'
 | 
			
		||||
  - 'drm_for_each_crtc'
 | 
			
		||||
  - 'drm_for_each_encoder'
 | 
			
		||||
  - 'drm_for_each_encoder_mask'
 | 
			
		||||
  - 'drm_for_each_fb'
 | 
			
		||||
  - 'drm_for_each_legacy_plane'
 | 
			
		||||
  - 'drm_for_each_plane'
 | 
			
		||||
  - 'drm_for_each_plane_mask'
 | 
			
		||||
  - 'drm_for_each_privobj'
 | 
			
		||||
  - 'drm_mm_for_each_hole'
 | 
			
		||||
  - 'drm_mm_for_each_node'
 | 
			
		||||
  - 'drm_mm_for_each_node_in_range'
 | 
			
		||||
  - 'drm_mm_for_each_node_safe'
 | 
			
		||||
  - 'flow_action_for_each'
 | 
			
		||||
  - 'for_each_active_dev_scope'
 | 
			
		||||
  - 'for_each_active_drhd_unit'
 | 
			
		||||
  - 'for_each_active_iommu'
 | 
			
		||||
  - 'for_each_available_child_of_node'
 | 
			
		||||
  - 'for_each_bio'
 | 
			
		||||
  - 'for_each_board_func_rsrc'
 | 
			
		||||
  - 'for_each_bvec'
 | 
			
		||||
  - 'for_each_card_auxs'
 | 
			
		||||
  - 'for_each_card_auxs_safe'
 | 
			
		||||
  - 'for_each_card_components'
 | 
			
		||||
  - 'for_each_card_pre_auxs'
 | 
			
		||||
  - 'for_each_card_prelinks'
 | 
			
		||||
  - 'for_each_card_rtds'
 | 
			
		||||
  - 'for_each_card_rtds_safe'
 | 
			
		||||
  - 'for_each_cgroup_storage_type'
 | 
			
		||||
  - 'for_each_child_of_node'
 | 
			
		||||
  - 'for_each_clear_bit'
 | 
			
		||||
  - 'for_each_clear_bit_from'
 | 
			
		||||
  - 'for_each_cmsghdr'
 | 
			
		||||
  - 'for_each_compatible_node'
 | 
			
		||||
  - 'for_each_component_dais'
 | 
			
		||||
  - 'for_each_component_dais_safe'
 | 
			
		||||
  - 'for_each_comp_order'
 | 
			
		||||
  - 'for_each_console'
 | 
			
		||||
  - 'for_each_cpu'
 | 
			
		||||
  - 'for_each_cpu_and'
 | 
			
		||||
  - 'for_each_cpu_not'
 | 
			
		||||
  - 'for_each_cpu_wrap'
 | 
			
		||||
  - 'for_each_dev_addr'
 | 
			
		||||
  - 'for_each_dev_scope'
 | 
			
		||||
  - 'for_each_displayid_db'
 | 
			
		||||
  - 'for_each_dma_cap_mask'
 | 
			
		||||
  - 'for_each_dpcm_be'
 | 
			
		||||
  - 'for_each_dpcm_be_rollback'
 | 
			
		||||
  - 'for_each_dpcm_be_safe'
 | 
			
		||||
  - 'for_each_dpcm_fe'
 | 
			
		||||
  - 'for_each_drhd_unit'
 | 
			
		||||
  - 'for_each_dss_dev'
 | 
			
		||||
  - 'for_each_efi_handle'
 | 
			
		||||
  - 'for_each_efi_memory_desc'
 | 
			
		||||
  - 'for_each_efi_memory_desc_in_map'
 | 
			
		||||
  - 'for_each_element'
 | 
			
		||||
  - 'for_each_element_extid'
 | 
			
		||||
  - 'for_each_element_id'
 | 
			
		||||
  - 'for_each_endpoint_of_node'
 | 
			
		||||
  - 'for_each_evictable_lru'
 | 
			
		||||
  - 'for_each_fib6_node_rt_rcu'
 | 
			
		||||
  - 'for_each_fib6_walker_rt'
 | 
			
		||||
  - 'for_each_free_mem_pfn_range_in_zone'
 | 
			
		||||
  - 'for_each_free_mem_pfn_range_in_zone_from'
 | 
			
		||||
  - 'for_each_free_mem_range'
 | 
			
		||||
  - 'for_each_free_mem_range_reverse'
 | 
			
		||||
  - 'for_each_func_rsrc'
 | 
			
		||||
  - 'for_each_hstate'
 | 
			
		||||
  - 'for_each_if'
 | 
			
		||||
  - 'for_each_iommu'
 | 
			
		||||
  - 'for_each_ip_tunnel_rcu'
 | 
			
		||||
  - 'for_each_irq_nr'
 | 
			
		||||
  - 'for_each_link_codecs'
 | 
			
		||||
  - 'for_each_link_platforms'
 | 
			
		||||
  - 'for_each_lru'
 | 
			
		||||
  - 'for_each_matching_node'
 | 
			
		||||
  - 'for_each_matching_node_and_match'
 | 
			
		||||
  - 'for_each_member'
 | 
			
		||||
  - 'for_each_memblock'
 | 
			
		||||
  - 'for_each_memblock_type'
 | 
			
		||||
  - 'for_each_memcg_cache_index'
 | 
			
		||||
  - 'for_each_mem_pfn_range'
 | 
			
		||||
  - 'for_each_mem_range'
 | 
			
		||||
  - 'for_each_mem_range_rev'
 | 
			
		||||
  - 'for_each_migratetype_order'
 | 
			
		||||
  - 'for_each_msi_entry'
 | 
			
		||||
  - 'for_each_msi_entry_safe'
 | 
			
		||||
  - 'for_each_net'
 | 
			
		||||
  - 'for_each_net_continue_reverse'
 | 
			
		||||
  - 'for_each_netdev'
 | 
			
		||||
  - 'for_each_netdev_continue'
 | 
			
		||||
  - 'for_each_netdev_continue_rcu'
 | 
			
		||||
  - 'for_each_netdev_continue_reverse'
 | 
			
		||||
  - 'for_each_netdev_feature'
 | 
			
		||||
  - 'for_each_netdev_in_bond_rcu'
 | 
			
		||||
  - 'for_each_netdev_rcu'
 | 
			
		||||
  - 'for_each_netdev_reverse'
 | 
			
		||||
  - 'for_each_netdev_safe'
 | 
			
		||||
  - 'for_each_net_rcu'
 | 
			
		||||
  - 'for_each_new_connector_in_state'
 | 
			
		||||
  - 'for_each_new_crtc_in_state'
 | 
			
		||||
  - 'for_each_new_mst_mgr_in_state'
 | 
			
		||||
  - 'for_each_new_plane_in_state'
 | 
			
		||||
  - 'for_each_new_private_obj_in_state'
 | 
			
		||||
  - 'for_each_node'
 | 
			
		||||
  - 'for_each_node_by_name'
 | 
			
		||||
  - 'for_each_node_by_type'
 | 
			
		||||
  - 'for_each_node_mask'
 | 
			
		||||
  - 'for_each_node_state'
 | 
			
		||||
  - 'for_each_node_with_cpus'
 | 
			
		||||
  - 'for_each_node_with_property'
 | 
			
		||||
  - 'for_each_of_allnodes'
 | 
			
		||||
  - 'for_each_of_allnodes_from'
 | 
			
		||||
  - 'for_each_of_cpu_node'
 | 
			
		||||
  - 'for_each_of_pci_range'
 | 
			
		||||
  - 'for_each_old_connector_in_state'
 | 
			
		||||
  - 'for_each_old_crtc_in_state'
 | 
			
		||||
  - 'for_each_old_mst_mgr_in_state'
 | 
			
		||||
  - 'for_each_oldnew_connector_in_state'
 | 
			
		||||
  - 'for_each_oldnew_crtc_in_state'
 | 
			
		||||
  - 'for_each_oldnew_mst_mgr_in_state'
 | 
			
		||||
  - 'for_each_oldnew_plane_in_state'
 | 
			
		||||
  - 'for_each_oldnew_plane_in_state_reverse'
 | 
			
		||||
  - 'for_each_oldnew_private_obj_in_state'
 | 
			
		||||
  - 'for_each_old_plane_in_state'
 | 
			
		||||
  - 'for_each_old_private_obj_in_state'
 | 
			
		||||
  - 'for_each_online_cpu'
 | 
			
		||||
  - 'for_each_online_node'
 | 
			
		||||
  - 'for_each_online_pgdat'
 | 
			
		||||
  - 'for_each_pci_bridge'
 | 
			
		||||
  - 'for_each_pci_dev'
 | 
			
		||||
  - 'for_each_pci_msi_entry'
 | 
			
		||||
  - 'for_each_populated_zone'
 | 
			
		||||
  - 'for_each_possible_cpu'
 | 
			
		||||
  - 'for_each_present_cpu'
 | 
			
		||||
  - 'for_each_prime_number'
 | 
			
		||||
  - 'for_each_prime_number_from'
 | 
			
		||||
  - 'for_each_process'
 | 
			
		||||
  - 'for_each_process_thread'
 | 
			
		||||
  - 'for_each_property_of_node'
 | 
			
		||||
  - 'for_each_registered_fb'
 | 
			
		||||
  - 'for_each_reserved_mem_region'
 | 
			
		||||
  - 'for_each_rtd_codec_dai'
 | 
			
		||||
  - 'for_each_rtd_codec_dai_rollback'
 | 
			
		||||
  - 'for_each_rtd_components'
 | 
			
		||||
  - 'for_each_set_bit'
 | 
			
		||||
  - 'for_each_set_bit_from'
 | 
			
		||||
  - 'for_each_set_clump8'
 | 
			
		||||
  - 'for_each_sg'
 | 
			
		||||
  - 'for_each_sg_dma_page'
 | 
			
		||||
  - 'for_each_sg_page'
 | 
			
		||||
  - 'for_each_sibling_event'
 | 
			
		||||
  - 'for_each_subelement'
 | 
			
		||||
  - 'for_each_subelement_extid'
 | 
			
		||||
  - 'for_each_subelement_id'
 | 
			
		||||
  - '__for_each_thread'
 | 
			
		||||
  - 'for_each_thread'
 | 
			
		||||
  - 'for_each_wakeup_source'
 | 
			
		||||
  - 'for_each_zone'
 | 
			
		||||
  - 'for_each_zone_zonelist'
 | 
			
		||||
  - 'for_each_zone_zonelist_nodemask'
 | 
			
		||||
  - 'fwnode_for_each_available_child_node'
 | 
			
		||||
  - 'fwnode_for_each_child_node'
 | 
			
		||||
  - 'fwnode_graph_for_each_endpoint'
 | 
			
		||||
  - 'gadget_for_each_ep'
 | 
			
		||||
  - 'genradix_for_each'
 | 
			
		||||
  - 'genradix_for_each_from'
 | 
			
		||||
  - 'hash_for_each'
 | 
			
		||||
  - 'hash_for_each_possible'
 | 
			
		||||
  - 'hash_for_each_possible_rcu'
 | 
			
		||||
  - 'hash_for_each_possible_rcu_notrace'
 | 
			
		||||
  - 'hash_for_each_possible_safe'
 | 
			
		||||
  - 'hash_for_each_rcu'
 | 
			
		||||
  - 'hash_for_each_safe'
 | 
			
		||||
  - 'hctx_for_each_ctx'
 | 
			
		||||
  - 'hlist_bl_for_each_entry'
 | 
			
		||||
  - 'hlist_bl_for_each_entry_rcu'
 | 
			
		||||
  - 'hlist_bl_for_each_entry_safe'
 | 
			
		||||
  - 'hlist_for_each'
 | 
			
		||||
  - 'hlist_for_each_entry'
 | 
			
		||||
  - 'hlist_for_each_entry_continue'
 | 
			
		||||
  - 'hlist_for_each_entry_continue_rcu'
 | 
			
		||||
  - 'hlist_for_each_entry_continue_rcu_bh'
 | 
			
		||||
  - 'hlist_for_each_entry_from'
 | 
			
		||||
  - 'hlist_for_each_entry_from_rcu'
 | 
			
		||||
  - 'hlist_for_each_entry_rcu'
 | 
			
		||||
  - 'hlist_for_each_entry_rcu_bh'
 | 
			
		||||
  - 'hlist_for_each_entry_rcu_notrace'
 | 
			
		||||
  - 'hlist_for_each_entry_safe'
 | 
			
		||||
  - '__hlist_for_each_rcu'
 | 
			
		||||
  - 'hlist_for_each_safe'
 | 
			
		||||
  - 'hlist_nulls_for_each_entry'
 | 
			
		||||
  - 'hlist_nulls_for_each_entry_from'
 | 
			
		||||
  - 'hlist_nulls_for_each_entry_rcu'
 | 
			
		||||
  - 'hlist_nulls_for_each_entry_safe'
 | 
			
		||||
  - 'i3c_bus_for_each_i2cdev'
 | 
			
		||||
  - 'i3c_bus_for_each_i3cdev'
 | 
			
		||||
  - 'ide_host_for_each_port'
 | 
			
		||||
  - 'ide_port_for_each_dev'
 | 
			
		||||
  - 'ide_port_for_each_present_dev'
 | 
			
		||||
  - 'idr_for_each_entry'
 | 
			
		||||
  - 'idr_for_each_entry_continue'
 | 
			
		||||
  - 'idr_for_each_entry_continue_ul'
 | 
			
		||||
  - 'idr_for_each_entry_ul'
 | 
			
		||||
  - 'in_dev_for_each_ifa_rcu'
 | 
			
		||||
  - 'in_dev_for_each_ifa_rtnl'
 | 
			
		||||
  - 'inet_bind_bucket_for_each'
 | 
			
		||||
  - 'inet_lhash2_for_each_icsk_rcu'
 | 
			
		||||
  - 'key_for_each'
 | 
			
		||||
  - 'key_for_each_safe'
 | 
			
		||||
  - 'klp_for_each_func'
 | 
			
		||||
  - 'klp_for_each_func_safe'
 | 
			
		||||
  - 'klp_for_each_func_static'
 | 
			
		||||
  - 'klp_for_each_object'
 | 
			
		||||
  - 'klp_for_each_object_safe'
 | 
			
		||||
  - 'klp_for_each_object_static'
 | 
			
		||||
  - 'kvm_for_each_memslot'
 | 
			
		||||
  - 'kvm_for_each_vcpu'
 | 
			
		||||
  - 'list_for_each'
 | 
			
		||||
  - 'list_for_each_codec'
 | 
			
		||||
  - 'list_for_each_codec_safe'
 | 
			
		||||
  - 'list_for_each_continue'
 | 
			
		||||
  - 'list_for_each_entry'
 | 
			
		||||
  - 'list_for_each_entry_continue'
 | 
			
		||||
  - 'list_for_each_entry_continue_rcu'
 | 
			
		||||
  - 'list_for_each_entry_continue_reverse'
 | 
			
		||||
  - 'list_for_each_entry_from'
 | 
			
		||||
  - 'list_for_each_entry_from_rcu'
 | 
			
		||||
  - 'list_for_each_entry_from_reverse'
 | 
			
		||||
  - 'list_for_each_entry_lockless'
 | 
			
		||||
  - 'list_for_each_entry_rcu'
 | 
			
		||||
  - 'list_for_each_entry_reverse'
 | 
			
		||||
  - 'list_for_each_entry_safe'
 | 
			
		||||
  - 'list_for_each_entry_safe_continue'
 | 
			
		||||
  - 'list_for_each_entry_safe_from'
 | 
			
		||||
  - 'list_for_each_entry_safe_reverse'
 | 
			
		||||
  - 'list_for_each_prev'
 | 
			
		||||
  - 'list_for_each_prev_safe'
 | 
			
		||||
  - 'list_for_each_safe'
 | 
			
		||||
  - 'llist_for_each'
 | 
			
		||||
  - 'llist_for_each_entry'
 | 
			
		||||
  - 'llist_for_each_entry_safe'
 | 
			
		||||
  - 'llist_for_each_safe'
 | 
			
		||||
  - 'mci_for_each_dimm'
 | 
			
		||||
  - 'media_device_for_each_entity'
 | 
			
		||||
  - 'media_device_for_each_intf'
 | 
			
		||||
  - 'media_device_for_each_link'
 | 
			
		||||
  - 'media_device_for_each_pad'
 | 
			
		||||
  - 'nanddev_io_for_each_page'
 | 
			
		||||
  - 'netdev_for_each_lower_dev'
 | 
			
		||||
  - 'netdev_for_each_lower_private'
 | 
			
		||||
  - 'netdev_for_each_lower_private_rcu'
 | 
			
		||||
  - 'netdev_for_each_mc_addr'
 | 
			
		||||
  - 'netdev_for_each_uc_addr'
 | 
			
		||||
  - 'netdev_for_each_upper_dev_rcu'
 | 
			
		||||
  - 'netdev_hw_addr_list_for_each'
 | 
			
		||||
  - 'nft_rule_for_each_expr'
 | 
			
		||||
  - 'nla_for_each_attr'
 | 
			
		||||
  - 'nla_for_each_nested'
 | 
			
		||||
  - 'nlmsg_for_each_attr'
 | 
			
		||||
  - 'nlmsg_for_each_msg'
 | 
			
		||||
  - 'nr_neigh_for_each'
 | 
			
		||||
  - 'nr_neigh_for_each_safe'
 | 
			
		||||
  - 'nr_node_for_each'
 | 
			
		||||
  - 'nr_node_for_each_safe'
 | 
			
		||||
  - 'of_for_each_phandle'
 | 
			
		||||
  - 'of_property_for_each_string'
 | 
			
		||||
  - 'of_property_for_each_u32'
 | 
			
		||||
  - 'pci_bus_for_each_resource'
 | 
			
		||||
  - 'ping_portaddr_for_each_entry'
 | 
			
		||||
  - 'plist_for_each'
 | 
			
		||||
  - 'plist_for_each_continue'
 | 
			
		||||
  - 'plist_for_each_entry'
 | 
			
		||||
  - 'plist_for_each_entry_continue'
 | 
			
		||||
  - 'plist_for_each_entry_safe'
 | 
			
		||||
  - 'plist_for_each_safe'
 | 
			
		||||
  - 'pnp_for_each_card'
 | 
			
		||||
  - 'pnp_for_each_dev'
 | 
			
		||||
  - 'protocol_for_each_card'
 | 
			
		||||
  - 'protocol_for_each_dev'
 | 
			
		||||
  - 'queue_for_each_hw_ctx'
 | 
			
		||||
  - 'radix_tree_for_each_slot'
 | 
			
		||||
  - 'radix_tree_for_each_tagged'
 | 
			
		||||
  - 'rbtree_postorder_for_each_entry_safe'
 | 
			
		||||
  - 'rdma_for_each_block'
 | 
			
		||||
  - 'rdma_for_each_port'
 | 
			
		||||
  - 'resource_list_for_each_entry'
 | 
			
		||||
  - 'resource_list_for_each_entry_safe'
 | 
			
		||||
  - 'rhl_for_each_entry_rcu'
 | 
			
		||||
  - 'rhl_for_each_rcu'
 | 
			
		||||
  - 'rht_for_each'
 | 
			
		||||
  - 'rht_for_each_entry'
 | 
			
		||||
  - 'rht_for_each_entry_from'
 | 
			
		||||
  - 'rht_for_each_entry_rcu'
 | 
			
		||||
  - 'rht_for_each_entry_rcu_from'
 | 
			
		||||
  - 'rht_for_each_entry_safe'
 | 
			
		||||
  - 'rht_for_each_from'
 | 
			
		||||
  - 'rht_for_each_rcu'
 | 
			
		||||
  - 'rht_for_each_rcu_from'
 | 
			
		||||
  - '__rq_for_each_bio'
 | 
			
		||||
  - 'rq_for_each_bvec'
 | 
			
		||||
  - 'rq_for_each_segment'
 | 
			
		||||
  - 'scsi_for_each_prot_sg'
 | 
			
		||||
  - 'scsi_for_each_sg'
 | 
			
		||||
  - 'sctp_for_each_hentry'
 | 
			
		||||
  - 'sctp_skb_for_each'
 | 
			
		||||
  - 'shdma_for_each_chan'
 | 
			
		||||
  - '__shost_for_each_device'
 | 
			
		||||
  - 'shost_for_each_device'
 | 
			
		||||
  - 'sk_for_each'
 | 
			
		||||
  - 'sk_for_each_bound'
 | 
			
		||||
  - 'sk_for_each_entry_offset_rcu'
 | 
			
		||||
  - 'sk_for_each_from'
 | 
			
		||||
  - 'sk_for_each_rcu'
 | 
			
		||||
  - 'sk_for_each_safe'
 | 
			
		||||
  - 'sk_nulls_for_each'
 | 
			
		||||
  - 'sk_nulls_for_each_from'
 | 
			
		||||
  - 'sk_nulls_for_each_rcu'
 | 
			
		||||
  - 'snd_array_for_each'
 | 
			
		||||
  - 'snd_pcm_group_for_each_entry'
 | 
			
		||||
  - 'snd_soc_dapm_widget_for_each_path'
 | 
			
		||||
  - 'snd_soc_dapm_widget_for_each_path_safe'
 | 
			
		||||
  - 'snd_soc_dapm_widget_for_each_sink_path'
 | 
			
		||||
  - 'snd_soc_dapm_widget_for_each_source_path'
 | 
			
		||||
  - 'tb_property_for_each'
 | 
			
		||||
  - 'tcf_exts_for_each_action'
 | 
			
		||||
  - 'udp_portaddr_for_each_entry'
 | 
			
		||||
  - 'udp_portaddr_for_each_entry_rcu'
 | 
			
		||||
  - 'usb_hub_for_each_child'
 | 
			
		||||
  - 'v4l2_device_for_each_subdev'
 | 
			
		||||
  - 'v4l2_m2m_for_each_dst_buf'
 | 
			
		||||
  - 'v4l2_m2m_for_each_dst_buf_safe'
 | 
			
		||||
  - 'v4l2_m2m_for_each_src_buf'
 | 
			
		||||
  - 'v4l2_m2m_for_each_src_buf_safe'
 | 
			
		||||
  - 'virtio_device_for_each_vq'
 | 
			
		||||
  - 'xa_for_each'
 | 
			
		||||
  - 'xa_for_each_marked'
 | 
			
		||||
  - 'xa_for_each_range'
 | 
			
		||||
  - 'xa_for_each_start'
 | 
			
		||||
  - 'xas_for_each'
 | 
			
		||||
  - 'xas_for_each_conflict'
 | 
			
		||||
  - 'xas_for_each_marked'
 | 
			
		||||
  - 'xbc_array_for_each_value'
 | 
			
		||||
  - 'xbc_for_each_key_value'
 | 
			
		||||
  - 'xbc_node_for_each_array_value'
 | 
			
		||||
  - 'xbc_node_for_each_child'
 | 
			
		||||
  - 'xbc_node_for_each_key_value'
 | 
			
		||||
  - 'zorro_for_each_dev'
 | 
			
		||||
 | 
			
		||||
#IncludeBlocks: Preserve # Unknown to clang-format-5.0
 | 
			
		||||
IncludeCategories:
 | 
			
		||||
  - Regex: '.*'
 | 
			
		||||
    Priority: 1
 | 
			
		||||
IncludeIsMainRegex: '(Test)?$'
 | 
			
		||||
IndentCaseLabels: false
 | 
			
		||||
#IndentPPDirectives: None # Unknown to clang-format-5.0
 | 
			
		||||
IndentWidth: 8
 | 
			
		||||
IndentWrappedFunctionNames: false
 | 
			
		||||
JavaScriptQuotes: Leave
 | 
			
		||||
JavaScriptWrapImports: true
 | 
			
		||||
KeepEmptyLinesAtTheStartOfBlocks: false
 | 
			
		||||
MacroBlockBegin: ''
 | 
			
		||||
MacroBlockEnd: ''
 | 
			
		||||
MaxEmptyLinesToKeep: 1
 | 
			
		||||
NamespaceIndentation: Inner
 | 
			
		||||
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
 | 
			
		||||
ObjCBlockIndentWidth: 8
 | 
			
		||||
ObjCSpaceAfterProperty: true
 | 
			
		||||
ObjCSpaceBeforeProtocolList: true
 | 
			
		||||
 | 
			
		||||
# Taken from git's rules
 | 
			
		||||
#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
 | 
			
		||||
PenaltyBreakBeforeFirstCallParameter: 30
 | 
			
		||||
PenaltyBreakComment: 10
 | 
			
		||||
PenaltyBreakFirstLessLess: 0
 | 
			
		||||
PenaltyBreakString: 10
 | 
			
		||||
PenaltyExcessCharacter: 100
 | 
			
		||||
PenaltyReturnTypeOnItsOwnLine: 60
 | 
			
		||||
 | 
			
		||||
PointerAlignment: Right
 | 
			
		||||
ReflowComments: false
 | 
			
		||||
SortIncludes: false
 | 
			
		||||
#SortUsingDeclarations: false # Unknown to clang-format-4.0
 | 
			
		||||
SpaceAfterCStyleCast: false
 | 
			
		||||
SpaceAfterTemplateKeyword: true
 | 
			
		||||
SpaceBeforeAssignmentOperators: true
 | 
			
		||||
#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
 | 
			
		||||
#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
 | 
			
		||||
SpaceBeforeParens: ControlStatements
 | 
			
		||||
#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
 | 
			
		||||
SpaceInEmptyParentheses: false
 | 
			
		||||
SpacesBeforeTrailingComments: 1
 | 
			
		||||
SpacesInAngles: false
 | 
			
		||||
SpacesInContainerLiterals: false
 | 
			
		||||
SpacesInCStyleCastParentheses: false
 | 
			
		||||
SpacesInParentheses: false
 | 
			
		||||
SpacesInSquareBrackets: false
 | 
			
		||||
Standard: Cpp03
 | 
			
		||||
TabWidth: 8
 | 
			
		||||
UseTab: Always
 | 
			
		||||
...
 | 
			
		||||
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -5,6 +5,7 @@
 | 
			
		||||
Transceiver52M/osmo-trx-uhd
 | 
			
		||||
Transceiver52M/osmo-trx-usrp1
 | 
			
		||||
Transceiver52M/osmo-trx-lms
 | 
			
		||||
Transceiver52M/osmo-trx-ipc
 | 
			
		||||
 | 
			
		||||
# tests
 | 
			
		||||
tests/CommonLibs/BitVectorTest
 | 
			
		||||
@@ -19,6 +20,7 @@ tests/CommonLibs/VectorTest
 | 
			
		||||
tests/CommonLibs/PRBSTest
 | 
			
		||||
tests/Transceiver52M/convolve_test
 | 
			
		||||
tests/Transceiver52M/LMSDeviceTest
 | 
			
		||||
Transceiver52M/device/ipc/ipc-driver-test
 | 
			
		||||
 | 
			
		||||
# automake/autoconf
 | 
			
		||||
*.in
 | 
			
		||||
@@ -63,3 +65,8 @@ 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								COPYING
									
									
									
									
									
								
							@@ -666,7 +666,7 @@ For more information on this, and how to apply and follow the GNU AGPL, see
 | 
			
		||||
=========================================================================
 | 
			
		||||
 | 
			
		||||
This marks the end of the AGPLv3 text.  The following text is appended to the
 | 
			
		||||
same file for convience but constituting a distinct document, not part of the
 | 
			
		||||
same file for convenience but constituting a distinct document, not part of the
 | 
			
		||||
actual AGPL text and not part of an attempt to create a deriviative work based
 | 
			
		||||
on the AGPLv3 text.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ using namespace std;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  Apply a Galois polymonial to a binary seqeunce.
 | 
			
		||||
  Apply a Galois polymonial to a binary sequence.
 | 
			
		||||
  @param val The input sequence.
 | 
			
		||||
  @param poly The polynomial.
 | 
			
		||||
  @param order The order of the polynomial.
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@
 | 
			
		||||
// (pat) The elements in the queue are type T*, and
 | 
			
		||||
// the Fifo class implements the underlying queue.
 | 
			
		||||
// The default is class PointerFIFO, which does not place any restrictions on the type of T,
 | 
			
		||||
// and is implemented by allocating auxilliary structures for the queue,
 | 
			
		||||
// and is implemented by allocating auxiliary structures for the queue,
 | 
			
		||||
// or SingleLinkedList, which implements the queue using an internal pointer in type T,
 | 
			
		||||
// which must implement the functional interface of class SingleLinkListNode,
 | 
			
		||||
// namely: functions T*next() and void setNext(T*).
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
* SPDX-License-Identifier: AGPL-3.0+
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under multiple licenses; see the COPYING file in
 | 
			
		||||
* the main directory for licensing information for this specific distribuion.
 | 
			
		||||
* the main directory for licensing information for this specific distribution.
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
			
		||||
* See the COPYING file in the main directory for details.
 | 
			
		||||
 
 | 
			
		||||
@@ -48,9 +48,11 @@ Log::~Log()
 | 
			
		||||
	int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
 | 
			
		||||
	const char *fmt = neednl ? "%s\n" : "%s";
 | 
			
		||||
 | 
			
		||||
	log_mutex_lock_canceldisable(&old_state);
 | 
			
		||||
	/* print related function called inside a C++ destructor, use pthread_setcancelstate() APIs.
 | 
			
		||||
	   See osmo-trx commit 86be40b4eb762d5c12e8e3f7388ca9f254e77b36 for more information */
 | 
			
		||||
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
 | 
			
		||||
	LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str());
 | 
			
		||||
	log_mutex_unlock_canceldisable(old_state);
 | 
			
		||||
	pthread_setcancelstate(old_state, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ostringstream& Log::get()
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,9 @@ extern "C" {
 | 
			
		||||
#define LOGLV(category, level) \
 | 
			
		||||
	Log(category, level, __BASE_FILE__, __LINE__).get() <<  "[tid=" << pthread_self() << "] "
 | 
			
		||||
 | 
			
		||||
#define LOGSRC(category, level, file, line) \
 | 
			
		||||
	Log(category, level, file, line).get() <<  "[tid=" << pthread_self() << "] "
 | 
			
		||||
 | 
			
		||||
#define LOGCHAN(chan, category, level) \
 | 
			
		||||
	Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() <<  "[tid=" << pthread_self() << "][chan=" << chan << "] "
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -186,7 +186,7 @@ class Thread {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** Send cancelation to thread */
 | 
			
		||||
	/** Send cancellation to thread */
 | 
			
		||||
	void cancel() { pthread_cancel(mThread); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -84,7 +84,7 @@ class Timeval {
 | 
			
		||||
	uint32_t usec() const { return mTimespec.tv_nsec / 1000; }
 | 
			
		||||
	uint32_t nsec() const { return mTimespec.tv_nsec; }
 | 
			
		||||
 | 
			
		||||
	/** Return differnce from other (other-self), in ms. */
 | 
			
		||||
	/** Return difference from other (other-self), in ms. */
 | 
			
		||||
	long delta(const Timeval& other) const;
 | 
			
		||||
 | 
			
		||||
	/** Elapsed time in ms. */
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
// We cant use Logger.h in this file...
 | 
			
		||||
// We can't 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 copyable type class, optimize:
 | 
			
		||||
		/*TODO if not non-trivially copiable type class, optimize:
 | 
			
		||||
		memcpy(dst,mStart,span*sizeof(T)); */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,17 @@
 | 
			
		||||
 * See the COPYING file in the main directory for details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#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 <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
@@ -35,21 +45,39 @@ static const struct log_info_cat default_categories[] = {
 | 
			
		||||
		.color = NULL,
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DTRXCLK] = {
 | 
			
		||||
			.name = "DTRXCLK",
 | 
			
		||||
			.description = "TRX Master Clock",
 | 
			
		||||
			.color = NULL,
 | 
			
		||||
			.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DTRXCTRL] = {
 | 
			
		||||
			.name = "DTRXCTRL",
 | 
			
		||||
			.description = "TRX CTRL interface",
 | 
			
		||||
			.color = "\033[1;33m",
 | 
			
		||||
			.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DTRXDDL] = {
 | 
			
		||||
			.name = "DTRXDDL",
 | 
			
		||||
			.description = "TRX Data interface Downlink",
 | 
			
		||||
			.color = NULL,
 | 
			
		||||
			.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DTRXDUL] = {
 | 
			
		||||
			.name = "DTRXDUL",
 | 
			
		||||
			.description = "TRX CTRL interface Uplink",
 | 
			
		||||
			.color = NULL,
 | 
			
		||||
			.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DDEV] = {
 | 
			
		||||
		.name = "DDEV",
 | 
			
		||||
		.description = "Device/Driver specific code",
 | 
			
		||||
		.color = NULL,
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_INFO,
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DLMS] = {
 | 
			
		||||
		.name = "DLMS",
 | 
			
		||||
		.description = "Logging from within LimeSuite itself",
 | 
			
		||||
	[DDEVDRV] = {
 | 
			
		||||
		.name = "DDEVDRV",
 | 
			
		||||
		.description = "Logging from external device driver library implementing lower level specifics",
 | 
			
		||||
		.color = NULL,
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
@@ -60,48 +88,14 @@ const struct log_info log_info = {
 | 
			
		||||
	.num_cat = ARRAY_SIZE(default_categories),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pthread_mutex_t log_mutex;
 | 
			
		||||
 | 
			
		||||
bool log_mutex_init() {
 | 
			
		||||
	int rc;
 | 
			
		||||
	pthread_mutexattr_t attr;
 | 
			
		||||
 | 
			
		||||
	if ((rc = pthread_mutexattr_init(&attr))) {
 | 
			
		||||
		fprintf(stderr, "pthread_mutexattr_init() failed: %d\n", rc);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if ((rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE))) {
 | 
			
		||||
		fprintf(stderr, "pthread_mutexattr_settype() failed: %d\n", rc);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if ((rc = pthread_mutex_init(&log_mutex, &attr))) {
 | 
			
		||||
		fprintf(stderr, "pthread_mutex_init() failed: %d\n", rc);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if ((rc = pthread_mutexattr_destroy(&attr))) {
 | 
			
		||||
		fprintf(stderr, "pthread_mutexattr_destroy() failed: %d\n", rc);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
	/* FIXME: do we need to call pthread_mutex_destroy() during process exit? */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* If called inside a C++ destructor, use log_mutex_(un)lock_canceldisable() APIs instead.
 | 
			
		||||
   See osmo-trx commit 86be40b4eb762d5c12e8e3f7388ca9f254e77b36 for more information */
 | 
			
		||||
void log_mutex_lock() {
 | 
			
		||||
	OSMO_ASSERT(!pthread_mutex_lock(&log_mutex));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void log_mutex_unlock() {
 | 
			
		||||
	OSMO_ASSERT(!pthread_mutex_unlock(&log_mutex));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void log_mutex_lock_canceldisable(int *st) {
 | 
			
		||||
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, st);
 | 
			
		||||
	log_mutex_lock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void log_mutex_unlock_canceldisable(int st) {
 | 
			
		||||
	log_mutex_unlock();
 | 
			
		||||
	pthread_setcancelstate(st, NULL);
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
@@ -10,26 +10,20 @@ extern const struct log_info log_info;
 | 
			
		||||
/* Debug Areas of the code */
 | 
			
		||||
enum {
 | 
			
		||||
	DMAIN,
 | 
			
		||||
	DTRXCLK,
 | 
			
		||||
	DTRXCTRL,
 | 
			
		||||
	DTRXDDL,
 | 
			
		||||
	DTRXDUL,
 | 
			
		||||
	DDEV,
 | 
			
		||||
	DLMS,
 | 
			
		||||
	DDEVDRV,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
pid_t my_gettid(void);
 | 
			
		||||
 | 
			
		||||
#define CLOGC(category, level, fmt, args...) do { \
 | 
			
		||||
	log_mutex_lock(); \
 | 
			
		||||
	LOGP(category, level, "[tid=%lu] " fmt, pthread_self(), ##args);  \
 | 
			
		||||
	log_mutex_unlock(); \
 | 
			
		||||
	LOGP(category, level, "[tid=%ld] " fmt, (long int) my_gettid(), ##args);  \
 | 
			
		||||
} while(0)
 | 
			
		||||
 | 
			
		||||
#define CLOGCHAN(chan, category, level, fmt, args...) do { \
 | 
			
		||||
	log_mutex_lock(); \
 | 
			
		||||
	LOGP(category, level, "[tid=%lu][chan=%lu] " fmt, pthread_self(), chan, ##args);  \
 | 
			
		||||
	log_mutex_unlock(); \
 | 
			
		||||
	LOGP(category, level, "[tid=%ld][chan=%zu] " fmt, (long int) my_gettid(), chan, ##args);  \
 | 
			
		||||
} while(0)
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@
 | 
			
		||||
 * That signal is processed here in device_sig_cb, where a copy of the "struct
 | 
			
		||||
 * device_counters" structure is held and the main thread is instructed through
 | 
			
		||||
 * a timerfd to update rate_ctr APIs against this copy. All this is done inside
 | 
			
		||||
 * a mutex to avoid different race conditons (between Rx andTx threads, and
 | 
			
		||||
 * a mutex to avoid different race conditions (between Rx andTx threads, and
 | 
			
		||||
 * between Rx/Tx and main thread). For the same reason, callers of signal
 | 
			
		||||
 * <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> (device_sig_cb), that is Rx/Tx threads,
 | 
			
		||||
 * must do so with PTHREAD_CANCEL_DISABLE, in order to avoid possible deadlocks
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/logging.h>
 | 
			
		||||
#include <osmocom/vty/vty.h>
 | 
			
		||||
#include <osmocom/vty/misc.h>
 | 
			
		||||
 | 
			
		||||
@@ -41,22 +42,32 @@
 | 
			
		||||
 | 
			
		||||
static struct trx_ctx* g_trx_ctx;
 | 
			
		||||
 | 
			
		||||
static const struct value_string clock_ref_names[] = {
 | 
			
		||||
const struct value_string clock_ref_names[] = {
 | 
			
		||||
	{ REF_INTERNAL,	"internal" },
 | 
			
		||||
	{ REF_EXTERNAL,	"external" },
 | 
			
		||||
	{ REF_GPS,	"gpsdo" },
 | 
			
		||||
	{ 0,		NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct value_string filler_names[] = {
 | 
			
		||||
	{ FILLER_DUMMY,		"Dummy bursts" },
 | 
			
		||||
	{ FILLER_ZERO,		"Disabled" },
 | 
			
		||||
	{ FILLER_NORM_RAND,	"Normal bursts with random payload" },
 | 
			
		||||
	{ FILLER_EDGE_RAND,	"EDGE bursts with random payload" },
 | 
			
		||||
	{ FILLER_ACCESS_RAND,	"Access bursts with random payload" },
 | 
			
		||||
const struct value_string filler_names[] = {
 | 
			
		||||
	{ FILLER_DUMMY,		"Dummy bursts (C0 only)" },
 | 
			
		||||
	{ FILLER_ZERO,		"Empty bursts" },
 | 
			
		||||
	{ FILLER_NORM_RAND,	"GMSK Normal Bursts with random payload" },
 | 
			
		||||
	{ FILLER_EDGE_RAND,	"8-PSK Normal Bursts with random payload" },
 | 
			
		||||
	{ FILLER_ACCESS_RAND,	"Access Bursts with random payload" },
 | 
			
		||||
	{ 0,			NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct value_string filler_types[] = {
 | 
			
		||||
	{ FILLER_DUMMY,		"dummy" },
 | 
			
		||||
	{ FILLER_ZERO,		"zero" },
 | 
			
		||||
	{ FILLER_NORM_RAND,	"random-nb-gmsk" },
 | 
			
		||||
	{ FILLER_EDGE_RAND,	"random-nb-8psk" },
 | 
			
		||||
	{ FILLER_ACCESS_RAND,	"random-ab" },
 | 
			
		||||
	{ 0,			NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct trx_ctx *trx_from_vty(struct vty *v)
 | 
			
		||||
{
 | 
			
		||||
        /* It can't hurt to force callers to continue to pass the vty instance
 | 
			
		||||
@@ -172,57 +183,10 @@ DEFUN(cfg_rx_sps, cfg_rx_sps_cmd,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_test_rtsc, cfg_test_rtsc_cmd,
 | 
			
		||||
	"test rtsc <0-7>",
 | 
			
		||||
	"Set the Random Normal Burst test mode with TSC\n"
 | 
			
		||||
	"TSC\n")
 | 
			
		||||
{
 | 
			
		||||
	struct trx_ctx *trx = trx_from_vty(vty);
 | 
			
		||||
 | 
			
		||||
	if (trx->cfg.rach_delay_set) {
 | 
			
		||||
		vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	trx->cfg.rtsc_set = true;
 | 
			
		||||
	trx->cfg.rtsc = atoi(argv[0]);
 | 
			
		||||
	if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */
 | 
			
		||||
		trx->cfg.filler = FILLER_NORM_RAND;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_test_rach_delay, cfg_test_rach_delay_cmd,
 | 
			
		||||
	"test rach-delay <0-68>",
 | 
			
		||||
	"Set the Random Access Burst test mode with delay\n"
 | 
			
		||||
	"RACH delay\n")
 | 
			
		||||
{
 | 
			
		||||
	struct trx_ctx *trx = trx_from_vty(vty);
 | 
			
		||||
 | 
			
		||||
	if (trx->cfg.rtsc_set) {
 | 
			
		||||
		vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s",
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (trx->cfg.egprs) {
 | 
			
		||||
		vty_out(vty, "rach-delay and egprs options are mutual-exclusive%s",
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	trx->cfg.rach_delay_set = true;
 | 
			
		||||
	trx->cfg.rach_delay = atoi(argv[0]);
 | 
			
		||||
	trx->cfg.filler = FILLER_ACCESS_RAND;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_clock_ref, cfg_clock_ref_cmd,
 | 
			
		||||
	"clock-ref (internal|external|gpsdo)",
 | 
			
		||||
	"Set the Reference Clock\n"
 | 
			
		||||
	"Enable internal referece (default)\n"
 | 
			
		||||
	"Enable internal reference (default)\n"
 | 
			
		||||
	"Enable external 10 MHz reference\n"
 | 
			
		||||
	"Enable GPSDO reference\n")
 | 
			
		||||
{
 | 
			
		||||
@@ -305,7 +269,6 @@ DEFUN(cfg_egprs, cfg_egprs_cmd,
 | 
			
		||||
		trx->cfg.egprs = false;
 | 
			
		||||
	} else if (strcmp("enable", argv[0]) == 0) {
 | 
			
		||||
		trx->cfg.egprs = true;
 | 
			
		||||
		trx->cfg.filler = FILLER_EDGE_RAND;
 | 
			
		||||
	} else {
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
@@ -352,14 +315,57 @@ DEFUN(cfg_stack_size, cfg_stack_size_cmd,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_filler, cfg_filler_cmd,
 | 
			
		||||
	"filler dummy",
 | 
			
		||||
	"Enable C0 filler table\n"
 | 
			
		||||
	"Dummy method\n")
 | 
			
		||||
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")
 | 
			
		||||
{
 | 
			
		||||
	struct trx_ctx *trx = trx_from_vty(vty);
 | 
			
		||||
 | 
			
		||||
	trx->cfg.filler = FILLER_DUMMY;
 | 
			
		||||
	trx->cfg.rtsc = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_test_rach_delay, cfg_filler_rach_delay_cmd,
 | 
			
		||||
	"filler access-burst-delay <0-68>",
 | 
			
		||||
	"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]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
@@ -547,10 +553,6 @@ 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);
 | 
			
		||||
@@ -563,6 +565,12 @@ 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, " ");
 | 
			
		||||
@@ -590,11 +598,9 @@ static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx)
 | 
			
		||||
	vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " Test Mode: TSC: %u (%s)%s", trx->cfg.rtsc,
 | 
			
		||||
		trx->cfg.rtsc_set ? "Enabled" : "Disabled", VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " Test Mode: RACH Delay: %u (%s)%s", trx->cfg.rach_delay,
 | 
			
		||||
		trx->cfg.rach_delay_set ? "Enabled" : "Disabled", VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " C0 Filler Table: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " Filler Burst Type: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " Filler Burst TSC: %u%s", trx->cfg.rtsc, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " Filler Burst RACH Delay: %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " Clock Reference: %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " Multi-Carrier: %s%s", trx->cfg.multi_arfcn ? "Enabled" : "Disabled", VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " Tuning offset: %f%s", trx->cfg.offset, VTY_NEWLINE);
 | 
			
		||||
@@ -663,6 +669,7 @@ static int trx_vty_go_parent(struct vty *vty)
 | 
			
		||||
static const char trx_copyright[] =
 | 
			
		||||
	"Copyright (C) 2007-2014 Free Software Foundation, Inc.\r\n"
 | 
			
		||||
	"Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>\r\n"
 | 
			
		||||
	"Copyright (C) 2013-2019 Fairwaves, Inc.\r\n"
 | 
			
		||||
	"Copyright (C) 2015 Ettus Research LLC\r\n"
 | 
			
		||||
	"Copyright (C) 2017-2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
 | 
			
		||||
	"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
 | 
			
		||||
@@ -705,8 +712,6 @@ 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);
 | 
			
		||||
@@ -715,7 +720,9 @@ 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_cmd);
 | 
			
		||||
	install_element(TRX_NODE, &cfg_filler_type_cmd);
 | 
			
		||||
	install_element(TRX_NODE, &cfg_filler_tsc_cmd);
 | 
			
		||||
	install_element(TRX_NODE, &cfg_filler_rach_delay_cmd);
 | 
			
		||||
	install_element(TRX_NODE, &cfg_ctr_error_threshold_cmd);
 | 
			
		||||
	install_element(TRX_NODE, &cfg_no_ctr_error_threshold_cmd);
 | 
			
		||||
	install_element(TRX_NODE, &cfg_stack_size_cmd);
 | 
			
		||||
@@ -725,5 +732,7 @@ int trx_vty_init(struct trx_ctx* trx)
 | 
			
		||||
	install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
 | 
			
		||||
	install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
 | 
			
		||||
 | 
			
		||||
	logging_vty_add_deprecated_subsys(g_trx_ctx, "lms");
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@
 | 
			
		||||
#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
 | 
			
		||||
@@ -51,9 +53,7 @@ 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;
 | 
			
		||||
 
 | 
			
		||||
@@ -33,12 +33,16 @@ SUBDIRS = \
 | 
			
		||||
	GSM \
 | 
			
		||||
	Transceiver52M \
 | 
			
		||||
	contrib \
 | 
			
		||||
	tests
 | 
			
		||||
	tests \
 | 
			
		||||
	utils
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	LEGAL \
 | 
			
		||||
	COPYING \
 | 
			
		||||
	README.md
 | 
			
		||||
	README.md \
 | 
			
		||||
	contrib/osmo-trx.spec.in \
 | 
			
		||||
	debian \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
 | 
			
		||||
	--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
 | 
			
		||||
 
 | 
			
		||||
@@ -98,7 +98,7 @@ bool Channelizer::rotate(const float *in, size_t len)
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Setup channelizer paramaters */
 | 
			
		||||
/* Setup channelizer parameters */
 | 
			
		||||
Channelizer::Channelizer(size_t m, size_t blockLen, size_t hLen)
 | 
			
		||||
	: ChannelizerBase(m, blockLen, hLen)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -225,7 +225,7 @@ bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Setup channelizer paramaters
 | 
			
		||||
 * Setup channelizer parameters
 | 
			
		||||
 */
 | 
			
		||||
ChannelizerBase::ChannelizerBase(size_t m, size_t blockLen, size_t hLen)
 | 
			
		||||
	: subFilters(NULL), hInputs(NULL), hOutputs(NULL), hist(NULL),
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ protected:
 | 
			
		||||
	/* Buffer length validity checking */
 | 
			
		||||
	bool checkLen(size_t innerLen, size_t outerLen);
 | 
			
		||||
public:
 | 
			
		||||
	/* Initilize channelizer/synthesis filter internals */
 | 
			
		||||
	/* Initialize channelizer/synthesis filter internals */
 | 
			
		||||
	bool init();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ unlike the built-in complex<> templates, these inline most operations for speed
 | 
			
		||||
/*
 | 
			
		||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
			
		||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
 | 
			
		||||
*
 | 
			
		||||
* This use of this software may be subject to additional restrictions.
 | 
			
		||||
* See the LEGAL file in the main directory for details.
 | 
			
		||||
 
 | 
			
		||||
@@ -104,3 +104,11 @@ osmo_trx_lms_LDADD = \
 | 
			
		||||
	$(LMS_LIBS)
 | 
			
		||||
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 \
 | 
			
		||||
	$(COMMON_LDADD) \
 | 
			
		||||
	$(UHD_LIBS)
 | 
			
		||||
osmo_trx_ipc_CPPFLAGS  = $(AM_CPPFLAGS) $(UHD_CFLAGS)
 | 
			
		||||
 
 | 
			
		||||
@@ -35,12 +35,12 @@ public:
 | 
			
		||||
	Resampler(size_t p, size_t q, size_t filt_len = 16);
 | 
			
		||||
	~Resampler();
 | 
			
		||||
 | 
			
		||||
	/* Initilize resampler filterbank.
 | 
			
		||||
	/* Initialize resampler filterbank.
 | 
			
		||||
	 *   @param bw bandwidth factor on filter generation (pre-window)
 | 
			
		||||
	 *   @return false on error, zero otherwise
 | 
			
		||||
	 *
 | 
			
		||||
	 * Automatic setting is to compute the filter to prevent aliasing with
 | 
			
		||||
	 * a Blackman-Harris window. Adjustment is made through a bandwith
 | 
			
		||||
	 * a Blackman-Harris window. Adjustment is made through a bandwidth
 | 
			
		||||
	 * factor to shift the cutoff and/or the constituent filter lengths.
 | 
			
		||||
	 * Calculation of specific rolloff factors or 3-dB cutoff points is
 | 
			
		||||
	 * left as an excersize for the reader.
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +45,8 @@ extern "C" {
 | 
			
		||||
 | 
			
		||||
using namespace GSM;
 | 
			
		||||
 | 
			
		||||
Transceiver *transceiver;
 | 
			
		||||
 | 
			
		||||
#define USB_LATENCY_INTRVL		10,0
 | 
			
		||||
 | 
			
		||||
/* Number of running values use in noise average */
 | 
			
		||||
@@ -151,20 +153,30 @@ Transceiver::~Transceiver()
 | 
			
		||||
    close(mClockSocket);
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < mChans; i++) {
 | 
			
		||||
    if (mControlServiceLoopThreads[i]) {
 | 
			
		||||
      mControlServiceLoopThreads[i]->cancel();
 | 
			
		||||
      mControlServiceLoopThreads[i]->join();
 | 
			
		||||
      delete mControlServiceLoopThreads[i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mTxPriorityQueues[i].clear();
 | 
			
		||||
    if (mCtrlSockets[i] >= 0)
 | 
			
		||||
      close(mCtrlSockets[i]);
 | 
			
		||||
    if (mDataSockets[i] >= 0)
 | 
			
		||||
      close(mDataSockets[i]);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Transceiver::ctrl_sock_cb(struct osmo_fd *bfd, unsigned int flags)
 | 
			
		||||
{
 | 
			
		||||
  int rc = 0;
 | 
			
		||||
  int chan = static_cast<int>(reinterpret_cast<uintptr_t>(bfd->data));
 | 
			
		||||
 | 
			
		||||
  if (flags & OSMO_FD_READ)
 | 
			
		||||
    rc = transceiver->ctrl_sock_handle_rx(chan);
 | 
			
		||||
  if (rc < 0)
 | 
			
		||||
    osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
 | 
			
		||||
 | 
			
		||||
  if (flags & OSMO_FD_WRITE)
 | 
			
		||||
    rc = transceiver->ctrl_sock_write(chan);
 | 
			
		||||
  if (rc < 0)
 | 
			
		||||
    osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
 | 
			
		||||
 | 
			
		||||
  return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize transceiver
 | 
			
		||||
 *
 | 
			
		||||
@@ -180,12 +192,12 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
 | 
			
		||||
  int d_srcport, d_dstport, c_srcport, c_dstport;
 | 
			
		||||
 | 
			
		||||
  if (!mChans) {
 | 
			
		||||
    LOG(ALERT) << "No channels assigned";
 | 
			
		||||
    LOG(FATAL) << "No channels assigned";
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!sigProcLibSetup()) {
 | 
			
		||||
    LOG(ALERT) << "Failed to initialize signal processing library";
 | 
			
		||||
    LOG(FATAL) << "Failed to initialize signal processing library";
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -193,8 +205,7 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
 | 
			
		||||
  mEdge = edge;
 | 
			
		||||
 | 
			
		||||
  mDataSockets.resize(mChans, -1);
 | 
			
		||||
  mCtrlSockets.resize(mChans, -1);
 | 
			
		||||
  mControlServiceLoopThreads.resize(mChans);
 | 
			
		||||
  mCtrlSockets.resize(mChans);
 | 
			
		||||
  mTxPriorityQueueServiceLoopThreads.resize(mChans);
 | 
			
		||||
  mRxServiceLoopThreads.resize(mChans);
 | 
			
		||||
 | 
			
		||||
@@ -216,24 +227,34 @@ 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;
 | 
			
		||||
 | 
			
		||||
    mCtrlSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
 | 
			
		||||
    rv = osmo_sock_init2_ofd(&mCtrlSockets[i].conn_bfd, 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 (mCtrlSockets[i] < 0)
 | 
			
		||||
    if (rv < 0)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    mCtrlSockets[i].conn_bfd.cb = ctrl_sock_cb;
 | 
			
		||||
    mCtrlSockets[i].conn_bfd.data = reinterpret_cast<void*>(i);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
 | 
			
		||||
                                      mLocalAddr.c_str(), d_srcport,
 | 
			
		||||
                                      mRemoteAddr.c_str(), d_dstport,
 | 
			
		||||
				      OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
 | 
			
		||||
    if (mCtrlSockets[i] < 0)
 | 
			
		||||
    if (mDataSockets[i] < 0)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    if (i && filler == FILLER_DUMMY)
 | 
			
		||||
      filler = FILLER_ZERO;
 | 
			
		||||
 | 
			
		||||
    mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Randomize the central clock */
 | 
			
		||||
@@ -243,21 +264,6 @@ bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
 | 
			
		||||
  mLastClockUpdateTime = startTime;
 | 
			
		||||
  mLatencyUpdateTime = startTime;
 | 
			
		||||
 | 
			
		||||
  /* Start control threads */
 | 
			
		||||
  for (size_t i = 0; i < mChans; i++) {
 | 
			
		||||
    TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
 | 
			
		||||
    params->trx = this;
 | 
			
		||||
    params->num = i;
 | 
			
		||||
    mControlServiceLoopThreads[i] = new Thread(stackSize);
 | 
			
		||||
    mControlServiceLoopThreads[i]->start((void * (*)(void*))
 | 
			
		||||
                                 ControlServiceLoopAdapter, (void*) params);
 | 
			
		||||
 | 
			
		||||
    if (i && filler == FILLER_DUMMY)
 | 
			
		||||
      filler = FILLER_ZERO;
 | 
			
		||||
 | 
			
		||||
    mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -285,7 +291,7 @@ bool Transceiver::start()
 | 
			
		||||
  mLatencyUpdateTime = time;
 | 
			
		||||
 | 
			
		||||
  if (!mRadioInterface->start()) {
 | 
			
		||||
    LOG(ALERT) << "Device failed to start";
 | 
			
		||||
    LOG(FATAL) << "Device failed to start";
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -371,12 +377,12 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
 | 
			
		||||
  radioVector *radio_burst;
 | 
			
		||||
 | 
			
		||||
  if (chan >= mTxPriorityQueues.size()) {
 | 
			
		||||
    LOG(ALERT) << "Invalid channel " << chan;
 | 
			
		||||
    LOGCHAN(chan, DTRXDDL, FATAL) << "Invalid channel";
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (wTime.TN() > 7) {
 | 
			
		||||
    LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
 | 
			
		||||
    LOGCHAN(chan, DTRXDDL, FATAL) << "Received burst with invalid slot " << wTime.TN();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -419,7 +425,7 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
 | 
			
		||||
    state = &mStates[i];
 | 
			
		||||
 | 
			
		||||
    while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
 | 
			
		||||
      LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
 | 
			
		||||
      LOGCHAN(i, DTRXDDL, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
 | 
			
		||||
                  << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
 | 
			
		||||
      if (state->mRetrans)
 | 
			
		||||
        updateFillerTable(i, burst);
 | 
			
		||||
@@ -606,7 +612,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, DMAIN, ERROR) << "ReceiveFIFO->read() returned no burst";
 | 
			
		||||
    LOGCHAN(chan, DTRXDUL, ERROR) << "ReceiveFIFO->read() returned no burst";
 | 
			
		||||
    return -EIO;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -651,7 +657,7 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (max_i < 0) {
 | 
			
		||||
    LOG(ALERT) << "Received empty burst";
 | 
			
		||||
    LOGCHAN(chan, DTRXDUL, FATAL) << "Received empty burst";
 | 
			
		||||
    goto ret_idle;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -678,9 +684,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)
 | 
			
		||||
      LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
 | 
			
		||||
      LOGCHAN(chan, DTRXDUL, NOTICE) << "Clipping detected on received RACH or Normal Burst";
 | 
			
		||||
    else if (rc != SIGERR_NONE)
 | 
			
		||||
      LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
 | 
			
		||||
      LOGCHAN(chan, DTRXDUL, NOTICE) << "Unhandled RACH or Normal Burst detection error";
 | 
			
		||||
    goto ret_idle;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -719,8 +725,6 @@ void Transceiver::reset()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define MAX_PACKET_LENGTH 100
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Matches a buffer with a command.
 | 
			
		||||
 * @param  buf    a buffer to look command in
 | 
			
		||||
@@ -750,27 +754,77 @@ static bool match_cmd(char *buf,
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Transceiver::driveControl(size_t chan)
 | 
			
		||||
void Transceiver::ctrl_sock_send(ctrl_msg& m, int chan)
 | 
			
		||||
{
 | 
			
		||||
  char buffer[MAX_PACKET_LENGTH + 1];
 | 
			
		||||
  char response[MAX_PACKET_LENGTH + 1];
 | 
			
		||||
  ctrl_sock_state& s = mCtrlSockets[chan];
 | 
			
		||||
  struct osmo_fd *conn_bfd = &s.conn_bfd;
 | 
			
		||||
 | 
			
		||||
  s.txmsgqueue.push_back(m);
 | 
			
		||||
  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 *command, *params;
 | 
			
		||||
  int msgLen;
 | 
			
		||||
  ctrl_sock_state& s = mCtrlSockets[chan];
 | 
			
		||||
 | 
			
		||||
  /* Attempt to read from control socket */
 | 
			
		||||
  msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
 | 
			
		||||
  msgLen = read(s.conn_bfd.fd, buffer, sizeof(cmd_received.data)-1);
 | 
			
		||||
  if (msgLen < 0 && errno == EAGAIN)
 | 
			
		||||
      return 0; /* Try again later */
 | 
			
		||||
  if (msgLen <= 0) {
 | 
			
		||||
    LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
 | 
			
		||||
    return false;
 | 
			
		||||
    LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets read(" << s.conn_bfd.fd << ") failed: " << msgLen;
 | 
			
		||||
    return -EIO;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /* Zero-terminate received string */
 | 
			
		||||
  buffer[msgLen] = '\0';
 | 
			
		||||
 | 
			
		||||
  /* Verify a command signature */
 | 
			
		||||
  if (strncmp(buffer, "CMD ", 4)) {
 | 
			
		||||
    LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
 | 
			
		||||
    return false;
 | 
			
		||||
    LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
 | 
			
		||||
    return -EIO;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Set command pointer */
 | 
			
		||||
@@ -794,7 +848,7 @@ bool Transceiver::driveControl(size_t chan)
 | 
			
		||||
    unsigned ts = 0, ss = 0;
 | 
			
		||||
    sscanf(params, "%u %u", &ts, &ss);
 | 
			
		||||
    if (ts > 7 || ss > 7) {
 | 
			
		||||
      sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
 | 
			
		||||
      sprintf(response, "RSP HANDOVER 1 %u %u", ts, ss);
 | 
			
		||||
    } else {
 | 
			
		||||
      mHandover[ts][ss] = true;
 | 
			
		||||
      sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
 | 
			
		||||
@@ -854,7 +908,7 @@ bool Transceiver::driveControl(size_t chan)
 | 
			
		||||
    sscanf(params, "%d", &freqKhz);
 | 
			
		||||
    mRxFreq = freqKhz * 1e3;
 | 
			
		||||
    if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
 | 
			
		||||
       LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
 | 
			
		||||
       LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune";
 | 
			
		||||
       sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
@@ -865,7 +919,7 @@ bool Transceiver::driveControl(size_t chan)
 | 
			
		||||
    sscanf(params, "%d", &freqKhz);
 | 
			
		||||
    mTxFreq = freqKhz * 1e3;
 | 
			
		||||
    if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
 | 
			
		||||
       LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
 | 
			
		||||
       LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune";
 | 
			
		||||
       sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
@@ -887,9 +941,9 @@ bool Transceiver::driveControl(size_t chan)
 | 
			
		||||
    int  timeslot;
 | 
			
		||||
    sscanf(params, "%d %d", ×lot, &corrCode);
 | 
			
		||||
    if ((timeslot < 0) || (timeslot > 7)) {
 | 
			
		||||
      LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
 | 
			
		||||
      LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
 | 
			
		||||
      sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
 | 
			
		||||
      return true;
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
    mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
 | 
			
		||||
    setModulus(timeslot, chan);
 | 
			
		||||
@@ -909,24 +963,20 @@ bool Transceiver::driveControl(size_t chan)
 | 
			
		||||
      sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
 | 
			
		||||
    }
 | 
			
		||||
  } else if (match_cmd(command, "_SETBURSTTODISKMASK", ¶ms)) {
 | 
			
		||||
    // debug command! may change or disapear without notice
 | 
			
		||||
    // debug command! may change or disappear without notice
 | 
			
		||||
    // set a mask which bursts to dump to disk
 | 
			
		||||
    int mask;
 | 
			
		||||
    sscanf(params, "%d", &mask);
 | 
			
		||||
    mWriteBurstToDiskMask = mask;
 | 
			
		||||
    sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
 | 
			
		||||
  } else {
 | 
			
		||||
    LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
 | 
			
		||||
    LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus command " << command << " on control interface.";
 | 
			
		||||
    sprintf(response,"RSP ERR 1");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
 | 
			
		||||
  msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
 | 
			
		||||
  if (msgLen <= 0) {
 | 
			
		||||
    LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
  transceiver->ctrl_sock_send(cmd_to_send, chan);
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Transceiver::driveTxPriorityQueue(size_t chan)
 | 
			
		||||
@@ -940,7 +990,7 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
 | 
			
		||||
  // check data socket
 | 
			
		||||
  msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
 | 
			
		||||
  if (msgLen <= 0) {
 | 
			
		||||
    LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
 | 
			
		||||
    LOGCHAN(chan, DTRXDDL, NOTICE) << "mDataSockets read(" << mDataSockets[chan] << ") failed: " << msgLen;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -950,13 +1000,13 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
 | 
			
		||||
      break;
 | 
			
		||||
    case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */
 | 
			
		||||
      if (mSPSTx != 4) {
 | 
			
		||||
        LOG(ERR) << "EDGE burst received but SPS is set to " << mSPSTx;
 | 
			
		||||
        LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << mSPSTx;
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      burstLen = EDGE_BURST_NBITS;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      LOG(ERR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
 | 
			
		||||
      LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -972,13 +1022,12 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
 | 
			
		||||
  case 1:
 | 
			
		||||
    break;
 | 
			
		||||
  default:
 | 
			
		||||
    LOG(ERR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
 | 
			
		||||
    LOGCHAN(chan, DTRXDDL, ERROR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LOG(DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version) << "): "
 | 
			
		||||
             << "fn=" << fn << ", tn=" << unsigned(dl->common.tn) << ", "
 | 
			
		||||
             << "burst_len=" << burstLen;
 | 
			
		||||
  LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version)
 | 
			
		||||
    << "): fn=" << fn << ", tn=" << unsigned(dl->common.tn) << ", burst_len=" << burstLen;
 | 
			
		||||
 | 
			
		||||
  BitVector newBurst(burstLen);
 | 
			
		||||
  BitVector::iterator itr = newBurst.begin();
 | 
			
		||||
@@ -1004,6 +1053,8 @@ bool Transceiver::driveReceiveRadio()
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
 | 
			
		||||
    if (mForceClockInterface)
 | 
			
		||||
      LOGC(DTRXCLK, NOTICE) << "Sending CLOCK indications";
 | 
			
		||||
    mForceClockInterface = false;
 | 
			
		||||
    return writeClockInterface();
 | 
			
		||||
  }
 | 
			
		||||
@@ -1020,7 +1071,7 @@ void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
 | 
			
		||||
    else os << "-";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LOGCHAN(chan, DMAIN, DEBUG) << std::fixed << std::right
 | 
			
		||||
  LOGCHAN(chan, DTRXDUL, 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"
 | 
			
		||||
@@ -1038,7 +1089,7 @@ bool Transceiver::driveReceiveFIFO(size_t chan)
 | 
			
		||||
 | 
			
		||||
  if ((rc = pullRadioVector(chan, &bi)) < 0) {
 | 
			
		||||
    if (rc == -ENOENT) { /* timeslot off, continue processing */
 | 
			
		||||
      LOGCHAN(chan, DMAIN, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
 | 
			
		||||
      LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false; /* other errors: we want to stop the process */
 | 
			
		||||
@@ -1075,7 +1126,7 @@ void Transceiver::driveTxFIFO()
 | 
			
		||||
 | 
			
		||||
  if (mOn) {
 | 
			
		||||
    //radioClock->wait(); // wait until clock updates
 | 
			
		||||
    LOG(DEBUG) << "radio clock " << radioClock->get();
 | 
			
		||||
    LOGC(DTRXCLK, DEBUG) << "radio clock " << radioClock->get();
 | 
			
		||||
    while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
 | 
			
		||||
      // if underrun, then we're not providing bursts to radio/USRP fast
 | 
			
		||||
      //   enough.  Need to increase latency by one GSM frame.
 | 
			
		||||
@@ -1084,8 +1135,9 @@ void Transceiver::driveTxFIFO()
 | 
			
		||||
          // only update latency at the defined frame interval
 | 
			
		||||
          if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
 | 
			
		||||
            mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
 | 
			
		||||
            LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
 | 
			
		||||
                      << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
 | 
			
		||||
            LOGC(DTRXCLK, INFO) << "new latency: " << mTransmitLatency << " (underrun "
 | 
			
		||||
                                << radioClock->get() << " vs "
 | 
			
		||||
                                << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
 | 
			
		||||
            mLatencyUpdateTime = radioClock->get();
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
@@ -1095,7 +1147,7 @@ void Transceiver::driveTxFIFO()
 | 
			
		||||
          if (mTransmitLatency > mRadioInterface->minLatency()) {
 | 
			
		||||
              if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
 | 
			
		||||
              mTransmitLatency.decTN();
 | 
			
		||||
              LOG(INFO) << "reduced latency: " << mTransmitLatency;
 | 
			
		||||
              LOGC(DTRXCLK, INFO) << "reduced latency: " << mTransmitLatency;
 | 
			
		||||
              mLatencyUpdateTime = radioClock->get();
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
@@ -1119,11 +1171,11 @@ bool Transceiver::writeClockInterface()
 | 
			
		||||
  // FIXME -- This should be adaptive.
 | 
			
		||||
  sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
 | 
			
		||||
 | 
			
		||||
  LOG(INFO) << "ClockInterface: sending " << command;
 | 
			
		||||
  LOGC(DTRXCLK, INFO) << "sending " << command;
 | 
			
		||||
 | 
			
		||||
  msgLen = write(mClockSocket, command, strlen(command) + 1);
 | 
			
		||||
  if (msgLen <= 0) {
 | 
			
		||||
    LOG(ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
 | 
			
		||||
    LOGC(DTRXCLK, ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -1144,7 +1196,7 @@ void *RxUpperLoopAdapter(TrxChanThParams *params)
 | 
			
		||||
 | 
			
		||||
  while (1) {
 | 
			
		||||
    if (!trx->driveReceiveFIFO(num)) {
 | 
			
		||||
      LOGCHAN(num, DMAIN, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
 | 
			
		||||
      LOGCHAN(num, DTRXDUL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
 | 
			
		||||
      osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1159,7 +1211,7 @@ void *RxLowerLoopAdapter(Transceiver *transceiver)
 | 
			
		||||
 | 
			
		||||
  while (1) {
 | 
			
		||||
    if (!transceiver->driveReceiveRadio()) {
 | 
			
		||||
      LOG(FATAL) << "Something went wrong in thread RxLower, requesting stop";
 | 
			
		||||
      LOGC(DTRXDUL, FATAL) << "Something went wrong in thread RxLower, requesting stop";
 | 
			
		||||
      osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1179,28 +1231,6 @@ void *TxLowerLoopAdapter(Transceiver *transceiver)
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *ControlServiceLoopAdapter(TrxChanThParams *params)
 | 
			
		||||
{
 | 
			
		||||
  char thread_name[16];
 | 
			
		||||
  Transceiver *trx = params->trx;
 | 
			
		||||
  size_t num = params->num;
 | 
			
		||||
 | 
			
		||||
  free(params);
 | 
			
		||||
 | 
			
		||||
  snprintf(thread_name, 16, "CtrlService%zu", num);
 | 
			
		||||
  set_selfthread_name(thread_name);
 | 
			
		||||
 | 
			
		||||
  while (1) {
 | 
			
		||||
    if (!trx->driveControl(num)) {
 | 
			
		||||
      LOGCHAN(num, DTRXCTRL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
 | 
			
		||||
      osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    pthread_testcancel();
 | 
			
		||||
  }
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *TxUpperLoopAdapter(TrxChanThParams *params)
 | 
			
		||||
{
 | 
			
		||||
  char thread_name[16];
 | 
			
		||||
@@ -1214,7 +1244,7 @@ void *TxUpperLoopAdapter(TrxChanThParams *params)
 | 
			
		||||
 | 
			
		||||
  while (1) {
 | 
			
		||||
    if (!trx->driveTxPriorityQueue(num)) {
 | 
			
		||||
      LOGCHAN(num, DMAIN, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
 | 
			
		||||
      LOGCHAN(num, DTRXDDL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
 | 
			
		||||
      osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -33,11 +33,14 @@
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/signal.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include "config_defs.h"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Transceiver;
 | 
			
		||||
 | 
			
		||||
extern Transceiver *transceiver;
 | 
			
		||||
 | 
			
		||||
/** Channel descriptor for transceiver object and channel number pair */
 | 
			
		||||
struct TrxChanThParams {
 | 
			
		||||
	Transceiver *trx;
 | 
			
		||||
@@ -142,12 +145,33 @@ 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<int> mCtrlSockets;  ///< socket for writing/reading control commands from GSM core
 | 
			
		||||
  std::vector<ctrl_sock_state> mCtrlSockets;  ///< socket for writing/reading control commands from GSM core
 | 
			
		||||
  int mClockSocket;               ///< socket for writing clock updates to GSM core
 | 
			
		||||
 | 
			
		||||
  std::vector<VectorQueue> mTxPriorityQueues;   ///< priority queue of transmit bursts received from GSM core
 | 
			
		||||
@@ -156,7 +180,6 @@ private:
 | 
			
		||||
  std::vector<Thread *> mRxServiceLoopThreads;  ///< thread to pull bursts into receive FIFO
 | 
			
		||||
  Thread *mRxLowerLoopThread;                   ///< thread to pull bursts into receive FIFO
 | 
			
		||||
  Thread *mTxLowerLoopThread;                   ///< thread to push bursts into transmit FIFO
 | 
			
		||||
  std::vector<Thread *> mControlServiceLoopThreads;         ///< thread to process control messages from GSM core
 | 
			
		||||
  std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
 | 
			
		||||
 | 
			
		||||
  GSM::Time mTransmitLatency;             ///< latency between basestation clock and transmit deadline clock
 | 
			
		||||
@@ -193,6 +216,12 @@ private:
 | 
			
		||||
  /** 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;
 | 
			
		||||
@@ -216,7 +245,7 @@ private:
 | 
			
		||||
  bool start();
 | 
			
		||||
  void stop();
 | 
			
		||||
 | 
			
		||||
  /** Protect destructor accessable stop call */
 | 
			
		||||
  /** Protect destructor accessible stop call */
 | 
			
		||||
  Mutex mLock;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
@@ -229,9 +258,6 @@ protected:
 | 
			
		||||
  /** drive transmission of GSM bursts */
 | 
			
		||||
  void driveTxFIFO();
 | 
			
		||||
 | 
			
		||||
  /** drive handling of control messages from GSM core */
 | 
			
		||||
  bool driveControl(size_t chan);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    drive modulation and sorting of GSM bursts from GSM core
 | 
			
		||||
    @return true if a burst was transferred successfully
 | 
			
		||||
@@ -242,7 +268,6 @@ protected:
 | 
			
		||||
  friend void *TxUpperLoopAdapter(TrxChanThParams *params);
 | 
			
		||||
  friend void *RxLowerLoopAdapter(Transceiver *transceiver);
 | 
			
		||||
  friend void *TxLowerLoopAdapter(Transceiver *transceiver);
 | 
			
		||||
  friend void *ControlServiceLoopAdapter(TrxChanThParams *params);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  void reset();
 | 
			
		||||
@@ -256,8 +281,5 @@ void *RxUpperLoopAdapter(TrxChanThParams *params);
 | 
			
		||||
void *RxLowerLoopAdapter(Transceiver *transceiver);
 | 
			
		||||
void *TxLowerLoopAdapter(Transceiver *transceiver);
 | 
			
		||||
 | 
			
		||||
/** control message handler thread loop */
 | 
			
		||||
void *ControlServiceLoopAdapter(TrxChanThParams *params);
 | 
			
		||||
 | 
			
		||||
/** transmit queueing thread loop */
 | 
			
		||||
void *TxUpperLoopAdapter(TrxChanThParams *params);
 | 
			
		||||
 
 | 
			
		||||
@@ -58,7 +58,7 @@ static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* API: Initalize convolve module */
 | 
			
		||||
/* API: Initialize convolve module */
 | 
			
		||||
void convolve_init(void)
 | 
			
		||||
{
 | 
			
		||||
	/* Stub */
 | 
			
		||||
 
 | 
			
		||||
@@ -103,7 +103,7 @@ void free_fft(struct fft_hdl *hdl)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! \brief Run multiple DFT operations with the initialized plan
 | 
			
		||||
 *  \param[in] hdl handle to an intitialized fft struct
 | 
			
		||||
 *  \param[in] hdl handle to an initialized fft struct
 | 
			
		||||
 *
 | 
			
		||||
 * Input and output buffers are configured with init_fft().
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Architecture dependant function pointers */
 | 
			
		||||
/* Architecture dependent function pointers */
 | 
			
		||||
struct convert_cpu_context {
 | 
			
		||||
	void (*convert_si16_ps_16n) (float *, const short *, int);
 | 
			
		||||
	void (*convert_si16_ps) (float *, const short *, int);
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Architecture dependant function pointers */
 | 
			
		||||
/* Architecture dependent function pointers */
 | 
			
		||||
struct convolve_cpu_context {
 | 
			
		||||
	void (*conv_cmplx_4n) (const float *, int, const float *, int, float *,
 | 
			
		||||
			       int, int, int);
 | 
			
		||||
@@ -66,7 +66,7 @@ int _base_convolve_complex(const float *x, int x_len,
 | 
			
		||||
int bounds_check(int x_len, int h_len, int y_len,
 | 
			
		||||
		 int start, int len);
 | 
			
		||||
 | 
			
		||||
/* API: Initalize convolve module */
 | 
			
		||||
/* API: Initialize convolve module */
 | 
			
		||||
void convolve_init(void)
 | 
			
		||||
{
 | 
			
		||||
	c.conv_cmplx_4n = (void *)_base_convolve_complex;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,10 @@ include $(top_srcdir)/Makefile.common
 | 
			
		||||
 | 
			
		||||
SUBDIRS = common
 | 
			
		||||
 | 
			
		||||
#if DEVICE_IPC
 | 
			
		||||
SUBDIRS += ipc
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
if DEVICE_USRP1
 | 
			
		||||
SUBDIRS += usrp1
 | 
			
		||||
endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
			
		||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
 | 
			
		||||
*
 | 
			
		||||
* This use of this software may be subject to additional restrictions.
 | 
			
		||||
* See the LEGAL file in the main directory for details.
 | 
			
		||||
@@ -77,23 +77,20 @@ class RadioDevice {
 | 
			
		||||
	@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
 | 
			
		||||
	@param timestamp The timestamp of the first samples to be read
 | 
			
		||||
	@param underrun Set if radio does not have data to transmit, e.g. data not being sent fast enough
 | 
			
		||||
	@param RSSI The received signal strength of the read result
 | 
			
		||||
	@return The number of samples actually read
 | 
			
		||||
  */
 | 
			
		||||
  virtual int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
                          TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0,
 | 
			
		||||
                          unsigned *RSSI = 0) = 0;
 | 
			
		||||
                          TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0) = 0;
 | 
			
		||||
  /**
 | 
			
		||||
        Write samples to the radio.
 | 
			
		||||
        @param buf Contains the data to be written.
 | 
			
		||||
        @param len number of samples to write.
 | 
			
		||||
        @param underrun Set if radio does not have data to transmit, e.g. data not being sent fast enough
 | 
			
		||||
        @param timestamp The timestamp of the first sample of the data buffer.
 | 
			
		||||
        @param isControl Set if data is a control packet, e.g. a ping command
 | 
			
		||||
        @return The number of samples actually written
 | 
			
		||||
  */
 | 
			
		||||
  virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
 | 
			
		||||
                           TIMESTAMP timestamp, bool isControl = false) = 0;
 | 
			
		||||
                           TIMESTAMP timestamp) = 0;
 | 
			
		||||
 | 
			
		||||
  /** Update the alignment between the read and write timestamps */
 | 
			
		||||
  virtual bool updateAlignment(TIMESTAMP timestamp)=0;
 | 
			
		||||
 
 | 
			
		||||
@@ -154,7 +154,7 @@ std::string smpl_buf::str_status(TIMESTAMP timestamp) const
 | 
			
		||||
	return ost.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string smpl_buf::str_code(ssize_t code)
 | 
			
		||||
std::string smpl_buf::str_code(int code)
 | 
			
		||||
{
 | 
			
		||||
	switch (code) {
 | 
			
		||||
	case ERROR_TIMESTAMP:
 | 
			
		||||
@@ -166,6 +166,8 @@ std::string smpl_buf::str_code(ssize_t code)
 | 
			
		||||
	case ERROR_OVERFLOW:
 | 
			
		||||
		return "Sample buffer: Overrun";
 | 
			
		||||
	default:
 | 
			
		||||
		return "Sample buffer: Unknown error";
 | 
			
		||||
		std::stringstream ss;
 | 
			
		||||
		ss << "Sample buffer: Unknown error " << code;
 | 
			
		||||
		return ss.str();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@
 | 
			
		||||
/*
 | 
			
		||||
    Sample Buffer - Allows reading and writing of timed samples using osmo-trx
 | 
			
		||||
                    timestamps. Time conversions are handled
 | 
			
		||||
                    internally or accessable through the static convert calls.
 | 
			
		||||
                    internally or accessible through the static convert calls.
 | 
			
		||||
*/
 | 
			
		||||
class smpl_buf {
 | 
			
		||||
public:
 | 
			
		||||
@@ -68,7 +68,7 @@ public:
 | 
			
		||||
	    @param code an error code
 | 
			
		||||
	    @return a formatted error string
 | 
			
		||||
	*/
 | 
			
		||||
	static std::string str_code(ssize_t code);
 | 
			
		||||
	static std::string str_code(int code);
 | 
			
		||||
 | 
			
		||||
	enum err_code {
 | 
			
		||||
		ERROR_TIMESTAMP = -1,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1131
									
								
								Transceiver52M/device/ipc/IPCDevice.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1131
									
								
								Transceiver52M/device/ipc/IPCDevice.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										235
									
								
								Transceiver52M/device/ipc/IPCDevice.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								Transceiver52M/device/ipc/IPCDevice.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,235 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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_
 | 
			
		||||
							
								
								
									
										29
									
								
								Transceiver52M/device/ipc/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								Transceiver52M/device/ipc/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										467
									
								
								Transceiver52M/device/ipc/ipc-driver-test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										467
									
								
								Transceiver52M/device/ipc/ipc-driver-test.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,467 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								Transceiver52M/device/ipc/ipc-driver-test.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								Transceiver52M/device/ipc/ipc-driver-test.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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);
 | 
			
		||||
							
								
								
									
										254
									
								
								Transceiver52M/device/ipc/ipc_chan.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								Transceiver52M/device/ipc/ipc_chan.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,254 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
 | 
			
		||||
*
 | 
			
		||||
* SPDX-License-Identifier: 0BSD
 | 
			
		||||
*
 | 
			
		||||
    Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
 | 
			
		||||
    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 | 
			
		||||
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 | 
			
		||||
    INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 | 
			
		||||
    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 | 
			
		||||
    PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <sys/stat.h> /* For mode constants */
 | 
			
		||||
#include <fcntl.h> /* For O_* constants */
 | 
			
		||||
 | 
			
		||||
#include <debug.h>
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
 | 
			
		||||
#include "shm.h"
 | 
			
		||||
#include "ipc-driver-test.h"
 | 
			
		||||
#include "ipc_chan.h"
 | 
			
		||||
#include "ipc_sock.h"
 | 
			
		||||
 | 
			
		||||
static int ipc_chan_rx(uint8_t msg_type, struct ipc_sk_chan_if *ipc_prim, uint8_t chan_nr)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	switch (msg_type) {
 | 
			
		||||
	case IPC_IF_MSG_START_REQ:
 | 
			
		||||
		rc = ipc_rx_chan_start_req(&ipc_prim->u.start_req, chan_nr);
 | 
			
		||||
		break;
 | 
			
		||||
	case IPC_IF_MSG_STOP_REQ:
 | 
			
		||||
		rc = ipc_rx_chan_stop_req(&ipc_prim->u.stop_req, chan_nr);
 | 
			
		||||
		break;
 | 
			
		||||
	case IPC_IF_MSG_SETGAIN_REQ:
 | 
			
		||||
		rc = ipc_rx_chan_setgain_req(&ipc_prim->u.set_gain_req, chan_nr);
 | 
			
		||||
		break;
 | 
			
		||||
	case IPC_IF_MSG_SETFREQ_REQ:
 | 
			
		||||
		rc = ipc_rx_chan_setfreq_req(&ipc_prim->u.set_freq_req, chan_nr);
 | 
			
		||||
		break;
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								Transceiver52M/device/ipc/ipc_chan.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								Transceiver52M/device/ipc/ipc_chan.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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
 | 
			
		||||
							
								
								
									
										204
									
								
								Transceiver52M/device/ipc/ipc_shm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								Transceiver52M/device/ipc/ipc_shm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,204 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								Transceiver52M/device/ipc/ipc_shm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								Transceiver52M/device/ipc/ipc_shm.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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
 | 
			
		||||
							
								
								
									
										271
									
								
								Transceiver52M/device/ipc/ipc_sock.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										271
									
								
								Transceiver52M/device/ipc/ipc_sock.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,271 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								Transceiver52M/device/ipc/ipc_sock.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Transceiver52M/device/ipc/ipc_sock.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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
 | 
			
		||||
							
								
								
									
										165
									
								
								Transceiver52M/device/ipc/shm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								Transceiver52M/device/ipc/shm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,165 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										224
									
								
								Transceiver52M/device/ipc/shm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								Transceiver52M/device/ipc/shm.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,224 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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));
 | 
			
		||||
							
								
								
									
										243
									
								
								Transceiver52M/device/ipc/uhdwrap.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								Transceiver52M/device/ipc/uhdwrap.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,243 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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, ×tamp, 1);
 | 
			
		||||
		if (len < 0)
 | 
			
		||||
			return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return d->writeSamples(d->wrap_tx_buf_ptrs, len, underrun, timestamp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" double uhdwrap_set_freq(void *dev, double f, size_t chan, bool for_tx)
 | 
			
		||||
{
 | 
			
		||||
	uhd_wrap *d = (uhd_wrap *)dev;
 | 
			
		||||
	if (for_tx)
 | 
			
		||||
		return d->setTxFreq(f, chan);
 | 
			
		||||
	else
 | 
			
		||||
		return d->setRxFreq(f, chan);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" double uhdwrap_set_gain(void *dev, double f, size_t chan, bool for_tx)
 | 
			
		||||
{
 | 
			
		||||
	uhd_wrap *d = (uhd_wrap *)dev;
 | 
			
		||||
	if (for_tx)
 | 
			
		||||
		return d->setTxGain(f, chan);
 | 
			
		||||
	else
 | 
			
		||||
		return d->setRxGain(f, chan);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" 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++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								Transceiver52M/device/ipc/uhdwrap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								Transceiver52M/device/ipc/uhdwrap.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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
 | 
			
		||||
@@ -20,6 +20,10 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#include "trx_vty.h"
 | 
			
		||||
#include "Logger.h"
 | 
			
		||||
#include "Threads.h"
 | 
			
		||||
#include "LMSDevice.h"
 | 
			
		||||
@@ -39,17 +43,74 @@ 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 */
 | 
			
		||||
 | 
			
		||||
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
 | 
			
		||||
 | 
			
		||||
/* Device Name Prefixes as presented by LimeSuite API LMS_GetDeviceInfo(): */
 | 
			
		||||
#define LMS_DEV_SDR_USB_PREFIX_NAME "LimeSDR-USB"
 | 
			
		||||
#define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini"
 | 
			
		||||
#define LMS_DEV_NET_MICRO_PREFIX_NAME "LimeNET-Micro"
 | 
			
		||||
 | 
			
		||||
/* Device parameter descriptor */
 | 
			
		||||
struct dev_desc {
 | 
			
		||||
	/* Does LimeSuite allow switching the clock source for this device?
 | 
			
		||||
	 * LimeSDR-Mini does not have switches but needs soldering to select
 | 
			
		||||
	 * external/internal clock. Any call to LMS_SetClockFreq() will fail.
 | 
			
		||||
	 */
 | 
			
		||||
	bool clock_src_switchable;
 | 
			
		||||
	/* Does LimeSuite allow using REF_INTERNAL for this device?
 | 
			
		||||
	 * LimeNET-Micro does not like selecting internal clock
 | 
			
		||||
	 */
 | 
			
		||||
	bool clock_src_int_usable;
 | 
			
		||||
	/* 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,
 | 
			
		||||
		     const std::vector<std::string>& tx_paths,
 | 
			
		||||
		     const std::vector<std::string>& rx_paths):
 | 
			
		||||
	RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths),
 | 
			
		||||
	m_lms_dev(NULL)
 | 
			
		||||
	RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
 | 
			
		||||
	m_lms_dev(NULL), started(false), m_dev_type(LMS_DEV_UNKNOWN)
 | 
			
		||||
{
 | 
			
		||||
	LOGC(DDEV, INFO) << "creating LMS device...";
 | 
			
		||||
 | 
			
		||||
@@ -59,6 +120,11 @@ LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t c
 | 
			
		||||
	tx_gains.resize(chans);
 | 
			
		||||
 | 
			
		||||
	rx_buffers.resize(chans);
 | 
			
		||||
 | 
			
		||||
	/* Set up per-channel Rx timestamp based Ring buffers */
 | 
			
		||||
	for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
			
		||||
		rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LMSDevice::~LMSDevice()
 | 
			
		||||
@@ -93,7 +159,7 @@ static void lms_log_callback(int lvl, const char *msg)
 | 
			
		||||
	if ((unsigned int) lvl >= ARRAY_SIZE(lvl_map))
 | 
			
		||||
		lvl = ARRAY_SIZE(lvl_map)-1;
 | 
			
		||||
 | 
			
		||||
	LOGLV(DLMS, lvl_map[lvl]) << msg;
 | 
			
		||||
	LOGLV(DDEVDRV, lvl_map[lvl]) << msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void print_range(const char* name, lms_range_t *range)
 | 
			
		||||
@@ -110,7 +176,7 @@ static void print_range(const char* name, lms_range_t *range)
 | 
			
		||||
int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::string &args)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i, j;
 | 
			
		||||
	vector<string> filters;
 | 
			
		||||
	std::vector<string> filters;
 | 
			
		||||
 | 
			
		||||
	filters = comma_delimited_to_vector(args.c_str());
 | 
			
		||||
 | 
			
		||||
@@ -134,11 +200,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..";
 | 
			
		||||
 | 
			
		||||
@@ -175,19 +241,20 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
 | 
			
		||||
	delete [] info_list;
 | 
			
		||||
 | 
			
		||||
	device_info = LMS_GetDeviceInfo(m_lms_dev);
 | 
			
		||||
	m_dev_type = parse_dev_type(m_lms_dev);
 | 
			
		||||
	dev_desc = dev_param_map.at(m_dev_type);
 | 
			
		||||
 | 
			
		||||
	if ((ref != REF_EXTERNAL) && (ref != REF_INTERNAL)){
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Invalid reference type";
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* if reference clock is external setup must happen _before_ calling LMS_Init */
 | 
			
		||||
	/* FIXME make external reference frequency configurable */
 | 
			
		||||
	/* if reference clock is external, setup must happen _before_ calling LMS_Init */
 | 
			
		||||
	if (ref == REF_EXTERNAL) {
 | 
			
		||||
		LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
 | 
			
		||||
		/* Assume an external 10 MHz reference clock */
 | 
			
		||||
		if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, 10000000.0) < 0)
 | 
			
		||||
		/* FIXME: Assume an external 10 MHz reference clock. make
 | 
			
		||||
		   external reference frequency configurable */
 | 
			
		||||
		if (!do_clock_src_freq(REF_EXTERNAL, 10000000.0))
 | 
			
		||||
			goto out_close;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -197,22 +264,13 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* LimeSDR-Mini does not have switches but needs soldering to select external/internal clock */
 | 
			
		||||
	/* LimeNET-Micro also does not like selecting internal clock*/
 | 
			
		||||
	/* also set device specific maximum tx levels selected by phasenoise measurements*/
 | 
			
		||||
	if (strncmp(device_info->deviceName,"LimeSDR-USB",11) == 0){
 | 
			
		||||
		/* if reference clock is internal setup must happen _after_ calling LMS_Init */
 | 
			
		||||
		/* according to lms using LMS_CLOCK_EXTREF with a frequency <= 0 is the correct way to set clock to internal reference*/
 | 
			
		||||
		if (ref == REF_INTERNAL) {
 | 
			
		||||
			LOGC(DDEV, INFO) << "Setting Internal clock reference";
 | 
			
		||||
			if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, -1) < 0)
 | 
			
		||||
				goto out_close;
 | 
			
		||||
		}
 | 
			
		||||
		maxTxGainClamp = 73.0;
 | 
			
		||||
	} else if (strncmp(device_info->deviceName,"LimeSDR-Mini",12) == 0)
 | 
			
		||||
		maxTxGainClamp = 66.0;
 | 
			
		||||
	else
 | 
			
		||||
		maxTxGainClamp = 71.0; /* "LimeNET-Micro", etc FIXME pciE based LMS boards?*/
 | 
			
		||||
	/* if reference clock is internal, setup must happen _after_ calling LMS_Init */
 | 
			
		||||
	if (ref == REF_INTERNAL) {
 | 
			
		||||
		LOGC(DDEV, INFO) << "Setting Internal clock reference";
 | 
			
		||||
		/* Internal freq param is not used */
 | 
			
		||||
		if (!do_clock_src_freq(REF_INTERNAL, 0))
 | 
			
		||||
			goto out_close;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* enable all used channels */
 | 
			
		||||
	for (i=0; i<chans; i++) {
 | 
			
		||||
@@ -227,16 +285,22 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	print_range("Sample Rate", &range_sr);
 | 
			
		||||
 | 
			
		||||
	LOGC(DDEV, INFO) << "Setting sample rate to " << GSMRATE*tx_sps << " " << tx_sps;
 | 
			
		||||
	if (LMS_SetSampleRate(m_lms_dev, GSMRATE*tx_sps, 32) < 0)
 | 
			
		||||
	if (iface == MULTI_ARFCN)
 | 
			
		||||
		sr_host = dev_desc.rate_multiarfcn * tx_sps;
 | 
			
		||||
	else
 | 
			
		||||
		sr_host = dev_desc.rate * tx_sps;
 | 
			
		||||
	LOGC(DDEV, INFO) << "Setting sample rate to " << sr_host << " " << tx_sps;
 | 
			
		||||
	if (LMS_SetSampleRate(m_lms_dev, sr_host, 32) < 0)
 | 
			
		||||
		goto out_close;
 | 
			
		||||
 | 
			
		||||
	if (LMS_GetSampleRate(m_lms_dev, LMS_CH_RX, 0, &sr_host, &sr_rf))
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	LOGC(DDEV, INFO) << "Sample Rate: Host=" << sr_host << " RF=" << sr_rf;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: make this device/model dependent, like UHDDevice:dev_param_map! */
 | 
			
		||||
	ts_offset = static_cast<TIMESTAMP>(8.9e-5 * GSMRATE * tx_sps); /* time * sample_rate */
 | 
			
		||||
	if (iface == MULTI_ARFCN)
 | 
			
		||||
		ts_offset = static_cast<TIMESTAMP>(dev_desc.ts_offset_coef_multiarfcn * sr_host);
 | 
			
		||||
	else
 | 
			
		||||
		ts_offset = static_cast<TIMESTAMP>(dev_desc.ts_offset_coef * sr_host);
 | 
			
		||||
 | 
			
		||||
	/* configure antennas */
 | 
			
		||||
	if (!set_antennas()) {
 | 
			
		||||
@@ -244,13 +308,7 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Set up per-channel Rx timestamp based Ring buffers */
 | 
			
		||||
	for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
			
		||||
		rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
 | 
			
		||||
 | 
			
		||||
	started = false;
 | 
			
		||||
 | 
			
		||||
	return NORMAL;
 | 
			
		||||
	return iface == MULTI_ARFCN ? MULTI_ARFCN : NORMAL;
 | 
			
		||||
 | 
			
		||||
out_close:
 | 
			
		||||
	LOGC(DDEV, FATAL) << "Error in LMS open, closing: " << LMS_GetLastErrorMessage();
 | 
			
		||||
@@ -344,6 +402,43 @@ bool LMSDevice::stop()
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
 | 
			
		||||
{
 | 
			
		||||
	struct dev_desc dev_desc = dev_param_map.at(m_dev_type);
 | 
			
		||||
	size_t lms_clk_id;
 | 
			
		||||
 | 
			
		||||
	switch (ref) {
 | 
			
		||||
	case REF_EXTERNAL:
 | 
			
		||||
		lms_clk_id = LMS_CLOCK_EXTREF;
 | 
			
		||||
		break;
 | 
			
		||||
	case REF_INTERNAL:
 | 
			
		||||
		if (!dev_desc.clock_src_int_usable) {
 | 
			
		||||
			LOGC(DDEV, ERROR) << "Device type " << dev_desc.name_prefix
 | 
			
		||||
					  << " doesn't support internal reference clock";
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		/* According to lms using LMS_CLOCK_EXTREF with a
 | 
			
		||||
		   frequency <= 0 is the correct way to set clock to
 | 
			
		||||
		   internal reference */
 | 
			
		||||
		lms_clk_id = LMS_CLOCK_EXTREF;
 | 
			
		||||
		freq = -1;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Invalid reference type " << get_value_string(clock_ref_names, ref);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (dev_desc.clock_src_switchable) {
 | 
			
		||||
		if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0)
 | 
			
		||||
			return false;
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGC(DDEV, INFO) << "Device type " << dev_desc.name_prefix
 | 
			
		||||
				 << " doesn't support switching clock source through SW";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* do rx/tx calibration - depends on gain, freq and bw */
 | 
			
		||||
bool LMSDevice::do_calib(size_t chan)
 | 
			
		||||
{
 | 
			
		||||
@@ -385,7 +480,7 @@ bool LMSDevice::do_filters(size_t chan)
 | 
			
		||||
 | 
			
		||||
double LMSDevice::maxTxGain()
 | 
			
		||||
{
 | 
			
		||||
	return maxTxGainClamp;
 | 
			
		||||
	return dev_param_map.at(m_dev_type).max_tx_gain;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double LMSDevice::minTxGain()
 | 
			
		||||
@@ -624,7 +719,7 @@ void LMSDevice::update_stream_stats_rx(size_t chan, bool *overrun)
 | 
			
		||||
	m_ctr[chan].rx_overruns += status.overrun;
 | 
			
		||||
 | 
			
		||||
	/* Dropped packets in Rx are counted when gaps in Rx timestamps are
 | 
			
		||||
	   detected (likely because buffer oveflow in hardware). Value count
 | 
			
		||||
	   detected (likely because buffer overflow in hardware). Value count
 | 
			
		||||
	   since the last call to LMS_GetStreamStatus(stream). */
 | 
			
		||||
	if (status.droppedPackets) {
 | 
			
		||||
		changed = true;
 | 
			
		||||
@@ -644,7 +739,7 @@ void LMSDevice::update_stream_stats_rx(size_t chan, bool *overrun)
 | 
			
		||||
 | 
			
		||||
// NOTE: Assumes sequential reads
 | 
			
		||||
int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
 | 
			
		||||
			   TIMESTAMP timestamp, bool * underrun, unsigned *RSSI)
 | 
			
		||||
			   TIMESTAMP timestamp, bool * underrun)
 | 
			
		||||
{
 | 
			
		||||
	int rc, num_smpls, expect_smpls;
 | 
			
		||||
	ssize_t avail_smpls;
 | 
			
		||||
@@ -711,8 +806,9 @@ int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
 | 
			
		||||
	for (size_t i = 0; i < rx_buffers.size(); i++) {
 | 
			
		||||
		rc = rx_buffers[i]->read(bufs[i], len, timestamp);
 | 
			
		||||
		if ((rc < 0) || (rc != len)) {
 | 
			
		||||
			LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
 | 
			
		||||
			LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
 | 
			
		||||
			LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc) << ". "
 | 
			
		||||
						<< rx_buffers[i]->str_status(timestamp)
 | 
			
		||||
						<< ", (len=" << len << ")";
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -762,8 +858,7 @@ void LMSDevice::update_stream_stats_tx(size_t chan, bool *underrun)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
 | 
			
		||||
			    bool * underrun, unsigned long long timestamp,
 | 
			
		||||
			    bool isControl)
 | 
			
		||||
			    bool * underrun, unsigned long long timestamp)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
@@ -772,11 +867,6 @@ int LMSDevice::writeSamples(std::vector < short *>&bufs, int len,
 | 
			
		||||
	tx_metadata.waitForTimestamp = true;
 | 
			
		||||
	tx_metadata.timestamp = timestamp - ts_offset;	/* Shift Tx time by offset */
 | 
			
		||||
 | 
			
		||||
	if (isControl) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Control packets not supported";
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bufs.size() != chans) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Invalid channel combination " << bufs.size();
 | 
			
		||||
		return -1;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
* SPDX-License-Identifier: AGPL-3.0+
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under multiple licenses; see the COPYING file in
 | 
			
		||||
* the main directory for licensing information for this specific distribuion.
 | 
			
		||||
* the main directory for licensing information for this specific distribution.
 | 
			
		||||
*
 | 
			
		||||
* This use of this software may be subject to additional restrictions.
 | 
			
		||||
* See the LEGAL file in the main directory for details.
 | 
			
		||||
@@ -41,6 +41,13 @@
 | 
			
		||||
 * 	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 {
 | 
			
		||||
 | 
			
		||||
@@ -59,7 +66,8 @@ private:
 | 
			
		||||
	TIMESTAMP ts_initial, ts_offset;
 | 
			
		||||
 | 
			
		||||
	std::vector<double> tx_gains, rx_gains;
 | 
			
		||||
	double maxTxGainClamp;
 | 
			
		||||
 | 
			
		||||
	enum lms_dev_type m_dev_type;
 | 
			
		||||
 | 
			
		||||
	bool do_calib(size_t chan);
 | 
			
		||||
	bool do_filters(size_t chan);
 | 
			
		||||
@@ -68,11 +76,12 @@ 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 chans, double lo_offset,
 | 
			
		||||
	LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
			
		||||
		  const std::vector<std::string>& tx_paths,
 | 
			
		||||
		  const std::vector<std::string>& rx_paths);
 | 
			
		||||
	~LMSDevice();
 | 
			
		||||
@@ -97,24 +106,21 @@ public:
 | 
			
		||||
	@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
 | 
			
		||||
	@param timestamp The timestamp of the first samples to be read
 | 
			
		||||
	@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
 | 
			
		||||
	@param RSSI The received signal strength of the read result
 | 
			
		||||
	@return The number of samples actually read
 | 
			
		||||
	*/
 | 
			
		||||
	int readSamples(std::vector < short *>&buf, int len, bool * overrun,
 | 
			
		||||
			TIMESTAMP timestamp = 0xffffffff, bool * underrun =
 | 
			
		||||
			NULL, unsigned *RSSI = NULL);
 | 
			
		||||
			NULL);
 | 
			
		||||
	/**
 | 
			
		||||
	Write samples to the LMS.
 | 
			
		||||
	@param buf Contains the data to be written.
 | 
			
		||||
	@param len number of samples to write.
 | 
			
		||||
	@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
 | 
			
		||||
	@param timestamp The timestamp of the first sample of the data buffer.
 | 
			
		||||
	@param isControl Set if data is a control packet, e.g. a ping command
 | 
			
		||||
	@return The number of samples actually written
 | 
			
		||||
	*/
 | 
			
		||||
	int writeSamples(std::vector < short *>&bufs, int len, bool * underrun,
 | 
			
		||||
			 TIMESTAMP timestamp = 0xffffffff, bool isControl =
 | 
			
		||||
			 false);
 | 
			
		||||
			 TIMESTAMP timestamp = 0xffffffff);
 | 
			
		||||
 | 
			
		||||
	/** Update the alignment between the read and write timestamps */
 | 
			
		||||
	bool updateAlignment(TIMESTAMP timestamp);
 | 
			
		||||
 
 | 
			
		||||
@@ -33,11 +33,12 @@
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef USE_UHD_3_11
 | 
			
		||||
#ifdef USE_UHD_3_11
 | 
			
		||||
#include <uhd/utils/log_add.hpp>
 | 
			
		||||
#include <uhd/utils/thread.hpp>
 | 
			
		||||
#else
 | 
			
		||||
#include <uhd/utils/msg.hpp>
 | 
			
		||||
#include <uhd/utils/thread_priority.hpp>
 | 
			
		||||
#else
 | 
			
		||||
#include <uhd/utils/thread.hpp>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define USRP_TX_AMPL     0.3
 | 
			
		||||
@@ -134,23 +135,52 @@ void *async_event_loop(uhd_device *dev)
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef USE_UHD_3_11
 | 
			
		||||
#ifdef USE_UHD_3_11
 | 
			
		||||
static void uhd_log_handler(const uhd::log::logging_info &info)
 | 
			
		||||
{
 | 
			
		||||
	int level;
 | 
			
		||||
 | 
			
		||||
	switch (info.verbosity)
 | 
			
		||||
	{
 | 
			
		||||
	case uhd::log::trace:
 | 
			
		||||
	case uhd::log::debug:
 | 
			
		||||
		level = LOGL_DEBUG;
 | 
			
		||||
		break;
 | 
			
		||||
	case uhd::log::info:
 | 
			
		||||
		level = LOGL_INFO;
 | 
			
		||||
		break;
 | 
			
		||||
	case uhd::log::warning:
 | 
			
		||||
		level = LOGL_NOTICE;
 | 
			
		||||
		break;
 | 
			
		||||
	case uhd::log::error:
 | 
			
		||||
		level = LOGL_ERROR;
 | 
			
		||||
		break;
 | 
			
		||||
	case uhd::log::fatal:
 | 
			
		||||
		level = LOGL_FATAL;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		level = LOGL_NOTICE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGSRC(DDEVDRV, level, info.file.c_str(), info.line) << "[" << info.component << "] " << info.message;
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
/*
 | 
			
		||||
    Catch and drop underrun 'U' and overrun 'O' messages from stdout
 | 
			
		||||
    since we already report using the logging facility. Direct
 | 
			
		||||
    everything else appropriately.
 | 
			
		||||
 */
 | 
			
		||||
void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
 | 
			
		||||
static void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
 | 
			
		||||
{
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case uhd::msg::status:
 | 
			
		||||
		LOGC(DDEV, INFO) << msg;
 | 
			
		||||
		LOGC(DDEVDRV, INFO) << msg;
 | 
			
		||||
		break;
 | 
			
		||||
	case uhd::msg::warning:
 | 
			
		||||
		LOGC(DDEV, WARNING) << msg;
 | 
			
		||||
		LOGC(DDEVDRV, NOTICE) << msg;
 | 
			
		||||
		break;
 | 
			
		||||
	case uhd::msg::error:
 | 
			
		||||
		LOGC(DDEV, ERROR) << msg;
 | 
			
		||||
		LOGC(DDEVDRV, ERROR) << msg;
 | 
			
		||||
		break;
 | 
			
		||||
	case uhd::msg::fastpath:
 | 
			
		||||
		break;
 | 
			
		||||
@@ -159,10 +189,10 @@ 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 chans, double lo_offset,
 | 
			
		||||
		       InterfaceType iface, size_t chan_num, double lo_offset,
 | 
			
		||||
		       const std::vector<std::string>& tx_paths,
 | 
			
		||||
		       const std::vector<std::string>& rx_paths)
 | 
			
		||||
	: RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths),
 | 
			
		||||
	: RadioDevice(tx_sps, rx_sps, iface, chan_num, 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),
 | 
			
		||||
@@ -418,6 +448,16 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
{
 | 
			
		||||
	const char *refstr;
 | 
			
		||||
 | 
			
		||||
	/* Register msg handler. Different APIs depending on UHD version */
 | 
			
		||||
#ifdef USE_UHD_3_11
 | 
			
		||||
	uhd::log::add_logger("OsmoTRX", &uhd_log_handler);
 | 
			
		||||
	uhd::log::set_log_level(uhd::log::debug);
 | 
			
		||||
	uhd::log::set_console_level(uhd::log::off);
 | 
			
		||||
	uhd::log::set_logger_level("OsmoTRX", uhd::log::debug);
 | 
			
		||||
#else
 | 
			
		||||
	uhd::msg::register_handler(&uhd_msg_handler);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// Find UHD devices
 | 
			
		||||
	uhd::device_addr_t addr(args);
 | 
			
		||||
	uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
 | 
			
		||||
@@ -526,7 +566,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
	init_gains();
 | 
			
		||||
 | 
			
		||||
	// Print configuration
 | 
			
		||||
	LOGC(DDEV, INFO) << "\n" << usrp_dev->get_pp_string();
 | 
			
		||||
	LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
 | 
			
		||||
 | 
			
		||||
	if (iface == MULTI_ARFCN)
 | 
			
		||||
		return MULTI_ARFCN;
 | 
			
		||||
@@ -604,10 +644,6 @@ bool uhd_device::start()
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifndef USE_UHD_3_11
 | 
			
		||||
	// Register msg handler
 | 
			
		||||
	uhd::msg::register_handler(&uhd_msg_handler);
 | 
			
		||||
#endif
 | 
			
		||||
	// Start asynchronous event (underrun check) loop
 | 
			
		||||
	async_event_thrd = new Thread();
 | 
			
		||||
	async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
 | 
			
		||||
@@ -685,7 +721,7 @@ int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
			    TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
 | 
			
		||||
			    TIMESTAMP timestamp, bool *underrun)
 | 
			
		||||
{
 | 
			
		||||
	ssize_t rc;
 | 
			
		||||
	uhd::time_spec_t ts;
 | 
			
		||||
@@ -742,7 +778,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
		for (size_t i = 0; i < rx_buffers.size(); i++) {
 | 
			
		||||
			rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
 | 
			
		||||
						  num_smpls,
 | 
			
		||||
						  metadata.time_spec.to_ticks(rx_rate));
 | 
			
		||||
						  ts.to_ticks(rx_rate));
 | 
			
		||||
 | 
			
		||||
			// Continue on local overrun, exit on other errors
 | 
			
		||||
			if ((rc < 0)) {
 | 
			
		||||
@@ -768,7 +804,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
 | 
			
		||||
			     unsigned long long timestamp,bool isControl)
 | 
			
		||||
			     unsigned long long timestamp)
 | 
			
		||||
{
 | 
			
		||||
	uhd::tx_metadata_t metadata;
 | 
			
		||||
	metadata.has_time_spec = true;
 | 
			
		||||
@@ -778,12 +814,6 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
 | 
			
		||||
 | 
			
		||||
	*underrun = false;
 | 
			
		||||
 | 
			
		||||
	// No control packets
 | 
			
		||||
	if (isControl) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Control packets not supported";
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bufs.size() != chans) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
 | 
			
		||||
		return -1;
 | 
			
		||||
@@ -883,15 +913,18 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
 | 
			
		||||
	std::vector<double> freqs;
 | 
			
		||||
	uhd::tune_result_t tres;
 | 
			
		||||
	uhd::tune_request_t treq = select_freq(freq, chan, tx);
 | 
			
		||||
	std::string str_dir;
 | 
			
		||||
 | 
			
		||||
	if (tx) {
 | 
			
		||||
		tres = usrp_dev->set_tx_freq(treq, chan);
 | 
			
		||||
		tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
 | 
			
		||||
		str_dir = "Tx";
 | 
			
		||||
	} else {
 | 
			
		||||
		tres = usrp_dev->set_rx_freq(treq, chan);
 | 
			
		||||
		rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
 | 
			
		||||
		str_dir = "Rx";
 | 
			
		||||
	}
 | 
			
		||||
	LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl;
 | 
			
		||||
	LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
 | 
			
		||||
 | 
			
		||||
	if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
 | 
			
		||||
		return true;
 | 
			
		||||
@@ -911,7 +944,7 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
 | 
			
		||||
			rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl;
 | 
			
		||||
		LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
@@ -1169,6 +1202,7 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata)
 | 
			
		||||
	return ost.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef IPCMAGIC
 | 
			
		||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
 | 
			
		||||
			       InterfaceType iface, size_t chans, double lo_offset,
 | 
			
		||||
			       const std::vector<std::string>& tx_paths,
 | 
			
		||||
@@ -1176,3 +1210,4 @@ RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
 | 
			
		||||
{
 | 
			
		||||
	return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -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 chans, double offset,
 | 
			
		||||
		   size_t chan_num, double offset,
 | 
			
		||||
		   const std::vector<std::string>& tx_paths,
 | 
			
		||||
		   const std::vector<std::string>& rx_paths);
 | 
			
		||||
	~uhd_device();
 | 
			
		||||
@@ -74,10 +74,10 @@ public:
 | 
			
		||||
	enum TxWindowType getWindowType() { return tx_window; }
 | 
			
		||||
 | 
			
		||||
	int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
			TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
 | 
			
		||||
			TIMESTAMP timestamp, bool *underrun);
 | 
			
		||||
 | 
			
		||||
	int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
 | 
			
		||||
			 TIMESTAMP timestamp, bool isControl);
 | 
			
		||||
			 TIMESTAMP timestamp);
 | 
			
		||||
 | 
			
		||||
	bool updateAlignment(TIMESTAMP timestamp);
 | 
			
		||||
 | 
			
		||||
@@ -127,7 +127,7 @@ public:
 | 
			
		||||
		ERROR_UNHANDLED = -4,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
protected:
 | 
			
		||||
	uhd::usrp::multi_usrp::sptr usrp_dev;
 | 
			
		||||
	uhd::tx_streamer::sptr tx_stream;
 | 
			
		||||
	uhd::rx_streamer::sptr rx_stream;
 | 
			
		||||
 
 | 
			
		||||
@@ -61,10 +61,10 @@ const dboardConfigType dboardConfig = TXA_RXB;
 | 
			
		||||
const double USRPDevice::masterClockRate = 52.0e6;
 | 
			
		||||
 | 
			
		||||
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
 | 
			
		||||
		       size_t chans, double lo_offset,
 | 
			
		||||
		       size_t chan_num, double lo_offset,
 | 
			
		||||
		       const std::vector<std::string>& tx_paths,
 | 
			
		||||
		       const std::vector<std::string>& rx_paths):
 | 
			
		||||
		RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths)
 | 
			
		||||
		RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths)
 | 
			
		||||
{
 | 
			
		||||
  LOGC(DDEV, INFO) << "creating USRP device...";
 | 
			
		||||
 | 
			
		||||
@@ -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, unsigned *RSSI)
 | 
			
		||||
                            TIMESTAMP timestamp, bool *underrun)
 | 
			
		||||
{
 | 
			
		||||
#ifndef SWLOOPBACK
 | 
			
		||||
  if (!m_uRx)
 | 
			
		||||
@@ -433,8 +433,10 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
	*underrun = true;
 | 
			
		||||
	LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface";
 | 
			
		||||
      }
 | 
			
		||||
      if (RSSI) *RSSI = (word0 >> 21) & 0x3f;
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
      /* FIXME: Do something with this ? */
 | 
			
		||||
      unsigned RSSI = (word0 >> 21) & 0x3f;
 | 
			
		||||
#endif
 | 
			
		||||
      if (!isAligned) continue;
 | 
			
		||||
 | 
			
		||||
      unsigned cursorStart = pktTimestamp - timeStart + dataStart;
 | 
			
		||||
@@ -513,9 +515,8 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
 | 
			
		||||
                             bool *underrun, unsigned long long timestamp,
 | 
			
		||||
                             bool isControl)
 | 
			
		||||
int USRPDevice::writeSamplesControl(std::vector<short *> &bufs, int len,
 | 
			
		||||
                             bool *underrun, unsigned long long timestamp, bool isControl)
 | 
			
		||||
{
 | 
			
		||||
  writeLock.lock();
 | 
			
		||||
 | 
			
		||||
@@ -569,6 +570,12 @@ int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
 | 
			
		||||
                             bool *underrun, unsigned long long timestamp)
 | 
			
		||||
{
 | 
			
		||||
  return writeSamplesControl(bufs, len, underrun, timestamp, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
 | 
			
		||||
{
 | 
			
		||||
#ifndef SWLOOPBACK
 | 
			
		||||
@@ -578,7 +585,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
 | 
			
		||||
  bool tmpUnderrun;
 | 
			
		||||
 | 
			
		||||
  std::vector<short *> buf(1, data);
 | 
			
		||||
  if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
 | 
			
		||||
  if (writeSamplesControl(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
 | 
			
		||||
    pingTimestamp = timestamp;
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
* SPDX-License-Identifier: AGPL-3.0+
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under multiple licenses; see the COPYING file in
 | 
			
		||||
* the main directory for licensing information for this specific distribuion.
 | 
			
		||||
* the main directory for licensing information for this specific distribution.
 | 
			
		||||
*
 | 
			
		||||
* This use of this software may be subject to additional restrictions.
 | 
			
		||||
* See the LEGAL file in the main directory for details.
 | 
			
		||||
@@ -82,6 +82,9 @@ 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;
 | 
			
		||||
@@ -95,7 +98,7 @@ private:
 | 
			
		||||
 public:
 | 
			
		||||
 | 
			
		||||
  /** Object constructor */
 | 
			
		||||
  USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
 | 
			
		||||
  USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
			
		||||
		const std::vector<std::string>& tx_paths,
 | 
			
		||||
		const std::vector<std::string>& rx_paths);
 | 
			
		||||
 | 
			
		||||
@@ -117,23 +120,20 @@ private:
 | 
			
		||||
	@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
 | 
			
		||||
	@param timestamp The timestamp of the first samples to be read
 | 
			
		||||
	@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
 | 
			
		||||
	@param RSSI The received signal strength of the read result
 | 
			
		||||
	@return The number of samples actually read
 | 
			
		||||
  */
 | 
			
		||||
  int readSamples(std::vector<short *> &buf, int len, bool *overrun,
 | 
			
		||||
                  TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
 | 
			
		||||
                  unsigned *RSSI = NULL);
 | 
			
		||||
                  TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL);
 | 
			
		||||
  /**
 | 
			
		||||
        Write samples to the USRP.
 | 
			
		||||
        @param buf Contains the data to be written.
 | 
			
		||||
        @param len number of samples to write.
 | 
			
		||||
        @param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
 | 
			
		||||
        @param timestamp The timestamp of the first sample of the data buffer.
 | 
			
		||||
        @param isControl Set if data is a control packet, e.g. a ping command
 | 
			
		||||
        @return The number of samples actually written
 | 
			
		||||
  */
 | 
			
		||||
  int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
 | 
			
		||||
                   TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
 | 
			
		||||
                   TIMESTAMP timestamp = 0xffffffff);
 | 
			
		||||
 | 
			
		||||
  /** Update the alignment between the read and write timestamps */
 | 
			
		||||
  bool updateAlignment(TIMESTAMP timestamp);
 | 
			
		||||
 
 | 
			
		||||
@@ -79,7 +79,6 @@ static struct ctrl_handle *g_ctrlh;
 | 
			
		||||
 | 
			
		||||
static RadioDevice *usrp;
 | 
			
		||||
static RadioInterface *radio;
 | 
			
		||||
static Transceiver *transceiver;
 | 
			
		||||
 | 
			
		||||
/* Create radio interface
 | 
			
		||||
 *     The interface consists of sample rate changes, frequency shifts,
 | 
			
		||||
@@ -237,7 +236,7 @@ static void setup_signal_handlers()
 | 
			
		||||
		exit(EXIT_FAILURE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osmo_fd_setup(&signal_ofd, sfd, BSC_FD_READ, signalfd_callback, NULL, 0);
 | 
			
		||||
	osmo_fd_setup(&signal_ofd, sfd, OSMO_FD_READ, signalfd_callback, NULL, 0);
 | 
			
		||||
	if (osmo_fd_register(&signal_ofd) < 0) {
 | 
			
		||||
		fprintf(stderr, "osmo_fd_register() failed.\n");
 | 
			
		||||
		exit(EXIT_FAILURE);
 | 
			
		||||
@@ -334,14 +333,12 @@ 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;
 | 
			
		||||
@@ -383,6 +380,11 @@ static void handle_options(int argc, char **argv, struct trx_ctx* trx)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (argc > optind) {
 | 
			
		||||
		LOG(ERROR) << "Unsupported positional arguments on command line";
 | 
			
		||||
		goto bad_config;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Cmd line option specific validation & setup */
 | 
			
		||||
 | 
			
		||||
	if (trx->cfg.num_chans > TRX_CHAN_MAX) {
 | 
			
		||||
@@ -459,7 +461,9 @@ static void print_config(struct trx_ctx *trx)
 | 
			
		||||
	ost << "   EDGE support............ " << trx->cfg.egprs << std::endl;
 | 
			
		||||
	ost << "   Extended RACH support... " << trx->cfg.ext_rach << std::endl;
 | 
			
		||||
	ost << "   Reference............... " << trx->cfg.clock_ref << std::endl;
 | 
			
		||||
	ost << "   C0 Filler Table......... " << trx->cfg.filler << std::endl;
 | 
			
		||||
	ost << "   Filler Burst Type....... " << get_value_string(filler_names, trx->cfg.filler) << std::endl;
 | 
			
		||||
	ost << "   Filler Burst TSC........ " << trx->cfg.rtsc << std::endl;
 | 
			
		||||
	ost << "   Filler Burst RACH Delay. " << trx->cfg.rach_delay << std::endl;
 | 
			
		||||
	ost << "   Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl;
 | 
			
		||||
	ost << "   Tuning offset........... " << trx->cfg.offset << std::endl;
 | 
			
		||||
	ost << "   RSSI to dBm offset...... " << trx->cfg.rssi_offset << std::endl;
 | 
			
		||||
@@ -576,20 +580,17 @@ 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();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 appeneded to each burst */
 | 
			
		||||
	/* +2: Historically (OpenBTS times), two extra non-used bytes are sent appended to each burst */
 | 
			
		||||
	char buf[sizeof(struct trxd_hdr_v0) + bi->nbits + 2];
 | 
			
		||||
	struct trxd_hdr_v0* pkt = (struct trxd_hdr_v0*)buf;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
			
		||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
 | 
			
		||||
*
 | 
			
		||||
* This use of this software may be subject to additional restrictions.
 | 
			
		||||
* See the LEGAL file in the main directory for details.
 | 
			
		||||
@@ -79,7 +79,7 @@ public:
 | 
			
		||||
  bool start();
 | 
			
		||||
  bool stop();
 | 
			
		||||
 | 
			
		||||
  /** intialization */
 | 
			
		||||
  /** initialization */
 | 
			
		||||
  virtual bool init(int type);
 | 
			
		||||
  virtual void close();
 | 
			
		||||
 | 
			
		||||
@@ -156,16 +156,24 @@ 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;
 | 
			
		||||
 
 | 
			
		||||
@@ -73,6 +73,8 @@ void RadioInterfaceMulti::close()
 | 
			
		||||
	powerScaling.resize(0);
 | 
			
		||||
	history.resize(0);
 | 
			
		||||
	active.resize(0);
 | 
			
		||||
	rx_freq_state.resize(0);
 | 
			
		||||
	tx_freq_state.resize(0);
 | 
			
		||||
 | 
			
		||||
	RadioInterface::close();
 | 
			
		||||
}
 | 
			
		||||
@@ -148,6 +150,8 @@ 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;
 | 
			
		||||
@@ -362,42 +366,67 @@ static bool fltcmp(double a, double b)
 | 
			
		||||
	return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RadioInterfaceMulti::verify_arfcn_consistency(double freq, size_t chan, bool tx)
 | 
			
		||||
{
 | 
			
		||||
	double freq_i;
 | 
			
		||||
	std::string str_dir = tx ? "Tx" : "Rx";
 | 
			
		||||
	std::vector<struct freq_cfg_state> &v = tx ? tx_freq_state : rx_freq_state;
 | 
			
		||||
 | 
			
		||||
	for (size_t i = 0; i < mChans; i++) {
 | 
			
		||||
		if (i == chan)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!v[i].set)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		freq_i = v[i].freq_hz + (double) ((int)chan - (int)i) * MCBTS_SPACING;
 | 
			
		||||
		if (!fltcmp(freq, freq_i)) {
 | 
			
		||||
			LOGCHAN(chan, DMAIN, ERROR)
 | 
			
		||||
				<< "Setting " << str_dir << " frequency " << freq
 | 
			
		||||
				<< " is incompatible: already configured channel "
 | 
			
		||||
				<< i << " uses frequency " << v[i].freq_hz
 | 
			
		||||
				<< " (expected " << freq_i << ")";
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	v[chan].set = true;
 | 
			
		||||
	v[chan].freq_hz = freq;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
 | 
			
		||||
{
 | 
			
		||||
  if (chan >= mChans)
 | 
			
		||||
    return false;
 | 
			
		||||
	double shift;
 | 
			
		||||
 | 
			
		||||
  double shift = (double) getFreqShift(mChans);
 | 
			
		||||
	if (chan >= mChans)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
  if (!chan)
 | 
			
		||||
    return mDevice->setTxFreq(freq + shift * MCBTS_SPACING);
 | 
			
		||||
	if (!verify_arfcn_consistency(freq, chan, true))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
  double center = mDevice->getTxFreq();
 | 
			
		||||
  if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
 | 
			
		||||
    LOG(NOTICE) << "Channel " << chan << " RF Tx frequency offset is "
 | 
			
		||||
                << freq / 1e6 << " MHz";
 | 
			
		||||
  }
 | 
			
		||||
	if (chan == 0) {
 | 
			
		||||
		shift = (double) getFreqShift(mChans);
 | 
			
		||||
		return mDevice->setTxFreq(freq + shift * MCBTS_SPACING);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
 | 
			
		||||
{
 | 
			
		||||
  if (chan >= mChans)
 | 
			
		||||
    return false;
 | 
			
		||||
	double shift;
 | 
			
		||||
 | 
			
		||||
  double shift = (double) getFreqShift(mChans);
 | 
			
		||||
	if (chan >= mChans)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
  if (!chan)
 | 
			
		||||
    return mDevice->setRxFreq(freq + shift * MCBTS_SPACING);
 | 
			
		||||
	if (!verify_arfcn_consistency(freq, chan, false))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
  double center = mDevice->getRxFreq();
 | 
			
		||||
  if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
 | 
			
		||||
    LOG(NOTICE) << "Channel " << chan << " RF Rx frequency offset is "
 | 
			
		||||
                << freq / 1e6 << " MHz";
 | 
			
		||||
  }
 | 
			
		||||
	if (chan == 0) {
 | 
			
		||||
		shift = (double) getFreqShift(mChans);
 | 
			
		||||
		return mDevice->setRxFreq(freq + shift * MCBTS_SPACING);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double RadioInterfaceMulti::setRxGain(double db, size_t chan)
 | 
			
		||||
 
 | 
			
		||||
@@ -345,7 +345,7 @@ static signalVector *convolve(const signalVector *x, const signalVector *h,
 | 
			
		||||
    _x = x;
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * Four convovle types:
 | 
			
		||||
   * Four convolve types:
 | 
			
		||||
   *   1. Complex-Real (aligned)
 | 
			
		||||
   *   2. Complex-Complex (aligned)
 | 
			
		||||
   *   3. Complex-Real (!aligned)
 | 
			
		||||
@@ -723,7 +723,7 @@ static signalVector *mapEdgeSymbols(const BitVector &bits)
 | 
			
		||||
 *
 | 
			
		||||
 * Delay the EDGE downlink bursts by one symbol in order to match GMSK pulse
 | 
			
		||||
 * shaping group delay. The difference in group delay arises from the dual
 | 
			
		||||
 * pulse filter combination of the GMSK Laurent represenation whereas 8-PSK
 | 
			
		||||
 * pulse filter combination of the GMSK Laurent representation whereas 8-PSK
 | 
			
		||||
 * uses a single pulse linear filter.
 | 
			
		||||
 */
 | 
			
		||||
static signalVector *shapeEdgeBurst(const signalVector &symbols)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
			
		||||
*
 | 
			
		||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
			
		||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
 | 
			
		||||
*
 | 
			
		||||
* This use of this software may be subject to additional restrictions.
 | 
			
		||||
* See the LEGAL file in the main directory for details.
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ void signalVector::operator=(const signalVector& vector)
 | 
			
		||||
	complex *src = vector.mData;
 | 
			
		||||
	for (i = 0; i < size(); i++, src++, dst++)
 | 
			
		||||
		*dst = *src;
 | 
			
		||||
	/* TODO: optimize for non non-trivially copyable types: */
 | 
			
		||||
	/* TODO: optimize for non non-trivially copiable types: */
 | 
			
		||||
	/*memcpy(mData, vector.mData, bytes()); */
 | 
			
		||||
	mStart = mData + vector.getStart();
 | 
			
		||||
}
 | 
			
		||||
@@ -70,7 +70,7 @@ size_t signalVector::updateHistory()
 | 
			
		||||
	complex *src = mStart + this->size() - num;
 | 
			
		||||
	for (i = 0; i < num; i++, src++, dst++)
 | 
			
		||||
		*dst = *src;
 | 
			
		||||
	/* TODO: optimize for non non-trivially copyable types: */
 | 
			
		||||
	/* TODO: optimize for non non-trivially copiable types: */
 | 
			
		||||
	/*memmove(mData, mStart + this->size() - num, num * sizeof(complex)); */
 | 
			
		||||
 | 
			
		||||
	return num;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								configure.ac
									
									
									
									
									
								
							@@ -51,6 +51,11 @@ AC_PROG_INSTALL
 | 
			
		||||
AC_PATH_PROG([RM_PROG], [rm])
 | 
			
		||||
AC_LANG([C++])
 | 
			
		||||
 | 
			
		||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
 | 
			
		||||
AS_CASE(["$LD"],[*clang*],
 | 
			
		||||
  [AS_CASE(["${host_os}"],
 | 
			
		||||
     [*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
 | 
			
		||||
 | 
			
		||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
 | 
			
		||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
 | 
			
		||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
 | 
			
		||||
@@ -65,7 +70,7 @@ AC_PROG_LIBTOOL
 | 
			
		||||
 | 
			
		||||
dnl Checks for header files.
 | 
			
		||||
AC_HEADER_STDC
 | 
			
		||||
dnl This is required for GnuRadio includes to understand endianess correctly:
 | 
			
		||||
dnl This is required for GnuRadio includes to understand endianness correctly:
 | 
			
		||||
AC_CHECK_HEADERS([byteswap.h])
 | 
			
		||||
 | 
			
		||||
dnl Checks for typedefs, structures, and compiler characteristics.
 | 
			
		||||
@@ -75,9 +80,19 @@ AC_TYPE_SIZE_T
 | 
			
		||||
AC_HEADER_TIME
 | 
			
		||||
AC_C_BIGENDIAN
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.12.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.12.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.12.0)
 | 
			
		||||
# 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)
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(sanitize,
 | 
			
		||||
	[AS_HELP_STRING(
 | 
			
		||||
@@ -125,6 +140,11 @@ AC_ARG_WITH(lms, [
 | 
			
		||||
        [enable LimeSuite based transceiver])
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
AC_ARG_WITH(ipc, [
 | 
			
		||||
    AS_HELP_STRING([--with-ipc],
 | 
			
		||||
        [enable IPC])
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
AC_ARG_WITH(singledb, [
 | 
			
		||||
    AS_HELP_STRING([--with-singledb],
 | 
			
		||||
        [enable single daughterboard use on USRP1])
 | 
			
		||||
@@ -226,6 +246,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(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
 | 
			
		||||
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
 | 
			
		||||
 | 
			
		||||
@@ -310,9 +331,11 @@ AC_CONFIG_FILES([\
 | 
			
		||||
    Transceiver52M/device/uhd/Makefile \
 | 
			
		||||
    Transceiver52M/device/usrp1/Makefile \
 | 
			
		||||
    Transceiver52M/device/lms/Makefile \
 | 
			
		||||
    Transceiver52M/device/ipc/Makefile \
 | 
			
		||||
    tests/Makefile \
 | 
			
		||||
    tests/CommonLibs/Makefile \
 | 
			
		||||
    tests/Transceiver52M/Makefile \
 | 
			
		||||
    utils/Makefile \
 | 
			
		||||
    doc/Makefile \
 | 
			
		||||
    doc/examples/Makefile \
 | 
			
		||||
    contrib/Makefile \
 | 
			
		||||
@@ -320,4 +343,5 @@ AC_CONFIG_FILES([\
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
AC_OUTPUT(
 | 
			
		||||
	doc/manuals/Makefile)
 | 
			
		||||
	doc/manuals/Makefile
 | 
			
		||||
	contrib/osmo-trx.spec)
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ substr() { [ -z "${2##*$1*}" ]; }
 | 
			
		||||
mychroot_nocwd() {
 | 
			
		||||
        # LC_ALL + LANGUAGE set to avoid lots of print errors due to locale not being set inside container
 | 
			
		||||
        # PATH is needed to be able to reach binaries like ldconfig without logging in to root, which adds the paths to PATH.
 | 
			
		||||
        # PROOT_NO_SECCOMP is requried due to proot bug #106
 | 
			
		||||
        # PROOT_NO_SECCOMP is required due to proot bug #106
 | 
			
		||||
        LC_ALL=C LANGUAGE=C PATH="$PATH:/usr/sbin:/sbin" PROOT_NO_SECCOMP=1 proot -r "$ROOTFS" -w / -b /proc --root-id -q qemu-arm-static "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -23,15 +23,8 @@ mychroot() {
 | 
			
		||||
        mychroot_nocwd -w / "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
base="$PWD"
 | 
			
		||||
deps="$base/deps"
 | 
			
		||||
inst="$deps/install"
 | 
			
		||||
export deps inst
 | 
			
		||||
 | 
			
		||||
if [ -z "${INSIDE_CHROOT}" ]; then
 | 
			
		||||
 | 
			
		||||
        osmo-clean-workspace.sh
 | 
			
		||||
 | 
			
		||||
        # Only use ARM chroot if host is not ARM and the target is ARM:
 | 
			
		||||
        if ! $(substr "arm" "$(uname -m)") && [ "x${INSTR}" = "x--with-neon" -o "x${INSTR}" = "x--with-neon-vfpv4" ]; then
 | 
			
		||||
 | 
			
		||||
@@ -69,6 +62,20 @@ if [ -z "${INSIDE_CHROOT}" ]; then
 | 
			
		||||
        fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
set -ex
 | 
			
		||||
 | 
			
		||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
 | 
			
		||||
	echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
 | 
			
		||||
	exit 2
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
base="$PWD"
 | 
			
		||||
deps="$base/deps"
 | 
			
		||||
inst="$deps/install"
 | 
			
		||||
export deps inst
 | 
			
		||||
 | 
			
		||||
osmo-clean-workspace.sh
 | 
			
		||||
 | 
			
		||||
mkdir "$deps" || true
 | 
			
		||||
 | 
			
		||||
osmo-build-dep.sh libosmocore "" "--enable-sanitize --disable-doxygen --disable-pcsc"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										241
									
								
								contrib/osmo-trx.spec.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								contrib/osmo-trx.spec.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,241 @@
 | 
			
		||||
#
 | 
			
		||||
# 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
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
  osmo-trx-lms.service \
 | 
			
		||||
  osmo-trx-uhd.service \
 | 
			
		||||
  osmo-trx-usrp1.service
 | 
			
		||||
  osmo-trx-usrp1.service \
 | 
			
		||||
  osmo-trx-ipc.service
 | 
			
		||||
 | 
			
		||||
if HAVE_SYSTEMD
 | 
			
		||||
SYSTEMD_SERVICES =
 | 
			
		||||
@@ -18,5 +19,9 @@ if DEVICE_LMS
 | 
			
		||||
SYSTEMD_SERVICES += osmo-trx-lms.service
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if DEVICE_IPC
 | 
			
		||||
SYSTEMD_SERVICES += osmo-trx-ipc.service
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
systemdsystemunit_DATA = $(SYSTEMD_SERVICES)
 | 
			
		||||
endif # HAVE_SYSTEMD
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								contrib/systemd/osmo-trx-ipc.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								contrib/systemd/osmo-trx-ipc.service
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Osmocom SDR BTS L1 Transceiver (IPC Backend)
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
Restart=always
 | 
			
		||||
ExecStart=/usr/bin/osmo-trx-ipc -C /etc/osmocom/osmo-trx-ipc.cfg
 | 
			
		||||
RestartSec=2
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
							
								
								
									
										52
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										52
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,55 @@
 | 
			
		||||
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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -14,7 +14,7 @@ Build-Depends: debhelper (>= 9),
 | 
			
		||||
               libtalloc-dev,
 | 
			
		||||
               libusrp-dev,
 | 
			
		||||
               liblimesuite-dev,
 | 
			
		||||
               libosmocore-dev (>= 0.12.0),
 | 
			
		||||
               libosmocore-dev (>= 1.3.0),
 | 
			
		||||
               osmo-gsm-manuals-dev
 | 
			
		||||
Standards-Version: 3.9.6
 | 
			
		||||
Vcs-Browser: http://cgit.osmocom.org/osmo-trx
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/patches/build-for-debian8.patch
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/patches/build-for-debian8.patch
									
									
									
									
										vendored
									
									
								
							@@ -7,7 +7,7 @@ index 8ff59f0..126c16a 100644
 | 
			
		||||
                libtalloc-dev,
 | 
			
		||||
                libusrp-dev,
 | 
			
		||||
-               liblimesuite-dev,
 | 
			
		||||
                libosmocore-dev (>= 0.12.0),
 | 
			
		||||
                libosmocore-dev (>= 1.3.0),
 | 
			
		||||
                osmo-gsm-manuals-dev
 | 
			
		||||
 Standards-Version: 3.9.6
 | 
			
		||||
@@ -30,7 +29,7 @@ Package: osmo-trx-dbg
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ log stderr
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 1
 | 
			
		||||
 logging print file basename
 | 
			
		||||
 logging level set-all info
 | 
			
		||||
 logging level set-all notice
 | 
			
		||||
!
 | 
			
		||||
line vty
 | 
			
		||||
 no login
 | 
			
		||||
@@ -12,7 +12,6 @@ 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
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ log stderr
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 1
 | 
			
		||||
 logging print file basename
 | 
			
		||||
 logging level set-all info
 | 
			
		||||
 logging level set-all notice
 | 
			
		||||
!
 | 
			
		||||
line vty
 | 
			
		||||
 no login
 | 
			
		||||
@@ -12,7 +12,6 @@ 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
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ log stderr
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 1
 | 
			
		||||
 logging print file basename
 | 
			
		||||
 logging level set-all info
 | 
			
		||||
 logging level set-all notice
 | 
			
		||||
!
 | 
			
		||||
line vty
 | 
			
		||||
 no login
 | 
			
		||||
@@ -12,7 +12,6 @@ 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
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ log stderr
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 1
 | 
			
		||||
 logging print file basename
 | 
			
		||||
 logging level set-all info
 | 
			
		||||
 logging level set-all notice
 | 
			
		||||
!
 | 
			
		||||
line vty
 | 
			
		||||
 no login
 | 
			
		||||
@@ -12,8 +12,10 @@ 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
 | 
			
		||||
 
 | 
			
		||||
@@ -46,15 +46,16 @@ Multi-ARFCN support is available since osmo-trx release `0.2.0`, and it was
 | 
			
		||||
added specifically in commit `76764278169d252980853251daeb9f1ba0c246e1`.
 | 
			
		||||
 | 
			
		||||
This feature is useful for instance if you want to run more than 1 TRX with an
 | 
			
		||||
Ettus B200 device, or 2 TRX with an Ettus B210 device, since they support only 1
 | 
			
		||||
and 2 physical RF channels respectively. No device from other providers or even
 | 
			
		||||
other devices than B200 and B210 from Ettus are known to support this feature.
 | 
			
		||||
Ettus B200 device, or more than 2 TRXs with an Ettus B210 device, since they
 | 
			
		||||
support only 1 and 2 physical RF channels respectively. No device from other
 | 
			
		||||
providers or even other devices than B200 and B210 from Ettus are known to
 | 
			
		||||
support this feature.
 | 
			
		||||
 | 
			
		||||
With multi-ARFCN enabled, ARFCN spacing is fixed at 800 kHz or 4 GSM channels.
 | 
			
		||||
So if TRX-0 is set to ARFCN 51, TRX-1 _must_ be set to 55, and so on. Up to
 | 
			
		||||
three ARFCN's is supported for multi-TRX.
 | 
			
		||||
 | 
			
		||||
From BTS and BSC point of view, supporting multiple TRX through multi-ARFCN
 | 
			
		||||
From BTS and BSC point of view, supporting multiple TRXs through multi-ARFCN
 | 
			
		||||
feature in OsmoTRX doesn't make any difference from a regular multi-TRX setup,
 | 
			
		||||
leaving apart of course the mentioned ARFCN limitations explained above and as a
 | 
			
		||||
consequence physical installation and operational differences.
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
 | 
			
		||||
This section specifies the format of USB packets used for in-band data
 | 
			
		||||
transmission and signaling on the USRP1. All packets are 512-byte long, and are
 | 
			
		||||
transfered using USB "bulk" transfers.
 | 
			
		||||
transferred using USB "bulk" transfers.
 | 
			
		||||
 | 
			
		||||
IN packets are sent towards the host. OUT packets are sent away from the host.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ B200 family and Fairwaves UmTRX family, and used to be the default backend used
 | 
			
		||||
for legacy @osmo-trx@ binary when per-backend binaries didn't exist yet.
 | 
			
		||||
 | 
			
		||||
Any device providing generic support for UHD should theoretically be able to be
 | 
			
		||||
run through this backend without much effort, but pracitcal experience showed
 | 
			
		||||
run through this backend without much effort, but practical experience showed
 | 
			
		||||
that some devices don't play well with it, such as the LimeSDR family of
 | 
			
		||||
devices, which showed far better results when using its native interface.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,10 +18,11 @@
 | 
			
		||||
        <param name='terminal' doc='Write to terminal' />
 | 
			
		||||
      </params>
 | 
			
		||||
    </command>
 | 
			
		||||
    <command id='write file'>
 | 
			
		||||
    <command id='write file [PATH]'>
 | 
			
		||||
      <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'>
 | 
			
		||||
@@ -96,12 +97,6 @@
 | 
			
		||||
        <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' />
 | 
			
		||||
@@ -193,14 +188,17 @@
 | 
			
		||||
        <param name='MASK' doc='List of logging categories to log, e.g. 'abc:mno:xyz'. Available log categories depend on the specific application, refer to the 'logging level' command. Optionally add individual log levels like 'abc,1:mno,3:xyz,5', 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|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)'>
 | 
			
		||||
    <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)'>
 | 
			
		||||
      <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='lms' doc='Logging from within LimeSuite itself' />
 | 
			
		||||
        <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' />
 | 
			
		||||
@@ -259,6 +257,43 @@
 | 
			
		||||
        <param name='force-all' doc='Release any globally forced log level set with 'logging level force-all <level>'' />
 | 
			
		||||
      </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' />
 | 
			
		||||
@@ -272,6 +307,12 @@
 | 
			
		||||
        <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' />
 | 
			
		||||
@@ -415,12 +456,6 @@
 | 
			
		||||
        <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' />
 | 
			
		||||
@@ -512,14 +547,17 @@
 | 
			
		||||
        <param name='MASK' doc='List of logging categories to log, e.g. 'abc:mno:xyz'. Available log categories depend on the specific application, refer to the 'logging level' command. Optionally add individual log levels like 'abc,1:mno,3:xyz,5', 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|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)'>
 | 
			
		||||
    <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)'>
 | 
			
		||||
      <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='lms' doc='Logging from within LimeSuite itself' />
 | 
			
		||||
        <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' />
 | 
			
		||||
@@ -578,6 +616,43 @@
 | 
			
		||||
        <param name='force-all' doc='Release any globally forced log level set with 'logging level force-all <level>'' />
 | 
			
		||||
      </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' />
 | 
			
		||||
@@ -591,6 +666,12 @@
 | 
			
		||||
        <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' />
 | 
			
		||||
@@ -772,16 +853,6 @@
 | 
			
		||||
        <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' />
 | 
			
		||||
@@ -861,6 +932,16 @@
 | 
			
		||||
        <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' />
 | 
			
		||||
@@ -973,14 +1054,17 @@
 | 
			
		||||
        <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|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)'>
 | 
			
		||||
    <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)'>
 | 
			
		||||
      <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='lms' doc='Logging from within LimeSuite itself' />
 | 
			
		||||
        <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' />
 | 
			
		||||
@@ -1179,24 +1263,10 @@
 | 
			
		||||
        <param name='4' doc='(null)' />
 | 
			
		||||
      </params>
 | 
			
		||||
    </command>
 | 
			
		||||
    <command id='test rtsc <0-7>'>
 | 
			
		||||
      <params>
 | 
			
		||||
        <param name='test' doc='Set the Random Normal Burst test mode with TSC' />
 | 
			
		||||
        <param name='rtsc' doc='TSC' />
 | 
			
		||||
        <param name='<0-7>' doc='(null)' />
 | 
			
		||||
      </params>
 | 
			
		||||
    </command>
 | 
			
		||||
    <command id='test rach-delay <0-68>'>
 | 
			
		||||
      <params>
 | 
			
		||||
        <param name='test' doc='Set the Random Access Burst test mode with delay' />
 | 
			
		||||
        <param name='rach-delay' doc='RACH delay' />
 | 
			
		||||
        <param name='<0-68>' 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 referece (default)' />
 | 
			
		||||
        <param name='internal' doc='Enable internal reference (default)' />
 | 
			
		||||
        <param name='external' doc='Enable external 10 MHz reference' />
 | 
			
		||||
        <param name='gpsdo' doc='Enable GPSDO reference' />
 | 
			
		||||
      </params>
 | 
			
		||||
@@ -1247,10 +1317,29 @@
 | 
			
		||||
        <param name='<1-32>' doc='Real time priority' />
 | 
			
		||||
      </params>
 | 
			
		||||
    </command>
 | 
			
		||||
    <command id='filler dummy'>
 | 
			
		||||
    <command id='filler type (zero|dummy|random-nb-gmsk|random-nb-8psk|random-ab)'>
 | 
			
		||||
      <params>
 | 
			
		||||
        <param name='filler' doc='Enable C0 filler table' />
 | 
			
		||||
        <param name='dummy' doc='Dummy method' />
 | 
			
		||||
        <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'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 'filler tsc' 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 'filler tsc' 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 'filler access-burst-delay' to introduce artificial delay.' />
 | 
			
		||||
      </params>
 | 
			
		||||
    </command>
 | 
			
		||||
    <command id='filler tsc <0-7>'>
 | 
			
		||||
      <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 'random-nb-gmsk' and 'random-nb-8psk' filler types. (default=0)' />
 | 
			
		||||
        <param name='<0-7>' doc='TSC' />
 | 
			
		||||
      </params>
 | 
			
		||||
    </command>
 | 
			
		||||
    <command id='filler access-burst-delay <0-68>'>
 | 
			
		||||
      <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 'random-ab' filler type. (default=0)' />
 | 
			
		||||
        <param name='<0-68>' doc='RACH delay in symbols' />
 | 
			
		||||
      </params>
 | 
			
		||||
    </command>
 | 
			
		||||
    <command id='ctr-error-threshold (rx_overruns|tx_underruns|rx_drop_events|rx_drop_samples|tx_drop_events|tx_drop_samples) <0-65535> (per-second|per-minute|per-hour|per-day)'>
 | 
			
		||||
 
 | 
			
		||||
@@ -146,9 +146,9 @@ struct test_vec
 | 
			
		||||
	float *h;
 | 
			
		||||
	float *y;
 | 
			
		||||
 | 
			
		||||
	int x_len;	/* Theses are in # of _floats_ ! */
 | 
			
		||||
	int h_len;	/* Theses are in # of _floats_ ! */
 | 
			
		||||
	int y_len;	/* Theses are in # of _floats_ ! */
 | 
			
		||||
	int x_len;	/* These are in # of _floats_ ! */
 | 
			
		||||
	int h_len;	/* These are in # of _floats_ ! */
 | 
			
		||||
	int y_len;	/* These are in # of _floats_ ! */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Reset test vectors */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								utils/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								utils/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
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)
 | 
			
		||||
							
								
								
									
										386
									
								
								utils/prbs-tool.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										386
									
								
								utils/prbs-tool.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,386 @@
 | 
			
		||||
/* 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;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user