mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-11-02 13:03:33 +00:00
Compare commits
27 Commits
laforge/rt
...
2022q2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a02faff1ac | ||
|
|
ce055d5ac4 | ||
|
|
452f2ba5bd | ||
|
|
0b6faa4521 | ||
|
|
1c69fb0f14 | ||
|
|
069dd16b67 | ||
|
|
e144275757 | ||
|
|
e0058b7207 | ||
|
|
bb3ccdea1a | ||
|
|
2799ff9bb9 | ||
|
|
fed5feafd3 | ||
|
|
1de5ed6f97 | ||
|
|
80751d850b | ||
|
|
2c40164ff0 | ||
|
|
e308202285 | ||
|
|
b4a067c9fb | ||
|
|
8029b12146 | ||
|
|
ebb05c1f90 | ||
|
|
bc3f3b40fe | ||
|
|
fae09f4562 | ||
|
|
7c0fe31697 | ||
|
|
98aef217c6 | ||
|
|
e03e34f8bb | ||
|
|
ee6958c9a8 | ||
|
|
fbcf4a6f6c | ||
|
|
58dc88bc31 | ||
|
|
aac84edc9d |
445
.clang-format
445
.clang-format
@@ -64,451 +64,6 @@ 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_bvec_all'
|
||||
- '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'
|
||||
- 'displayid_iter_for_each'
|
||||
- 'dma_fence_chain_for_each'
|
||||
- 'do_for_each_ftrace_op'
|
||||
- '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_crtc_reverse'
|
||||
- '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_acpi_dev_match'
|
||||
- 'for_each_active_dev_scope'
|
||||
- 'for_each_active_drhd_unit'
|
||||
- 'for_each_active_iommu'
|
||||
- 'for_each_aggr_pgid'
|
||||
- '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_dapms'
|
||||
- 'for_each_card_pre_auxs'
|
||||
- 'for_each_card_prelinks'
|
||||
- 'for_each_card_rtds'
|
||||
- 'for_each_card_rtds_safe'
|
||||
- 'for_each_card_widgets'
|
||||
- 'for_each_card_widgets_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_dapm_widgets'
|
||||
- 'for_each_dev_addr'
|
||||
- 'for_each_dev_scope'
|
||||
- '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_dtpm_table'
|
||||
- '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_cpus'
|
||||
- 'for_each_link_platforms'
|
||||
- 'for_each_lru'
|
||||
- 'for_each_matching_node'
|
||||
- 'for_each_matching_node_and_match'
|
||||
- 'for_each_member'
|
||||
- 'for_each_memcg_cache_index'
|
||||
- 'for_each_mem_pfn_range'
|
||||
- '__for_each_mem_range'
|
||||
- 'for_each_mem_range'
|
||||
- '__for_each_mem_range_rev'
|
||||
- 'for_each_mem_range_rev'
|
||||
- 'for_each_mem_region'
|
||||
- 'for_each_migratetype_order'
|
||||
- 'for_each_msi_entry'
|
||||
- 'for_each_msi_entry_safe'
|
||||
- 'for_each_msi_vector'
|
||||
- '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_nonreserved_multicast_dest_pgid'
|
||||
- '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_pcm_streams'
|
||||
- 'for_each_physmem_range'
|
||||
- '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_prop_codec_conf'
|
||||
- 'for_each_prop_dai_codec'
|
||||
- 'for_each_prop_dai_cpu'
|
||||
- 'for_each_prop_dlc_codecs'
|
||||
- 'for_each_prop_dlc_cpus'
|
||||
- 'for_each_prop_dlc_platforms'
|
||||
- 'for_each_property_of_node'
|
||||
- 'for_each_registered_fb'
|
||||
- 'for_each_requested_gpio'
|
||||
- 'for_each_requested_gpio_in_range'
|
||||
- 'for_each_reserved_mem_range'
|
||||
- 'for_each_reserved_mem_region'
|
||||
- 'for_each_rtd_codec_dais'
|
||||
- 'for_each_rtd_components'
|
||||
- 'for_each_rtd_cpu_dais'
|
||||
- 'for_each_rtd_dais'
|
||||
- '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_sgtable_dma_page'
|
||||
- 'for_each_sgtable_dma_sg'
|
||||
- 'for_each_sgtable_page'
|
||||
- 'for_each_sgtable_sg'
|
||||
- 'for_each_sibling_event'
|
||||
- 'for_each_subelement'
|
||||
- 'for_each_subelement_extid'
|
||||
- 'for_each_subelement_id'
|
||||
- '__for_each_thread'
|
||||
- 'for_each_thread'
|
||||
- 'for_each_unicast_dest_pgid'
|
||||
- 'for_each_vsi'
|
||||
- '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_entry_srcu'
|
||||
- '__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'
|
||||
- 'kunit_suite_for_each_test_case'
|
||||
- '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_entry_srcu'
|
||||
- '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'
|
||||
- 'pcl_for_each_chunk'
|
||||
- 'pcl_for_each_segment'
|
||||
- 'pcm_for_each_format'
|
||||
- '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'
|
||||
- 'rb_for_each'
|
||||
- 'rbtree_postorder_for_each_entry_safe'
|
||||
- 'rdma_for_each_block'
|
||||
- 'rdma_for_each_port'
|
||||
- 'rdma_umem_for_each_dma_block'
|
||||
- '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'
|
||||
- 'while_for_each_ftrace_op'
|
||||
- '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'
|
||||
- 'for_each_line'
|
||||
- 'for_each_non_empty_line'
|
||||
|
||||
#IncludeBlocks: Preserve # Unknown to clang-format-5.0
|
||||
IncludeCategories:
|
||||
- Regex: '.*'
|
||||
|
||||
@@ -24,5 +24,4 @@
|
||||
# If any interfaces have been removed or changed since the last public release, a=0.
|
||||
#
|
||||
#library what description / commit summary line
|
||||
update dependency to libosmocore > 1.5.1 for our use of osmo_sock_set_dscp()
|
||||
libosmo-mgcp-client struct mgcp_client_conf ABI breackage
|
||||
libosmogsm >1.6.0 use of iuup.h (libosmocore.git Ib21cee2e30bf83dff4e167f79541796007af9845)
|
||||
|
||||
12
configure.ac
12
configure.ac
@@ -44,13 +44,13 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DLSYM)
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.2.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.2.0)
|
||||
|
||||
CFLAGS="$CFLAGS -pthread"
|
||||
LDFLAGS="$LDFLAGS -pthread"
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
"""
|
||||
|
||||
|
||||
@@ -30,29 +30,29 @@ BuildRequires: pkgconfig >= 0.20
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmocoding) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmotrau) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmocoding) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 1.2.0
|
||||
BuildRequires: pkgconfig(libosmotrau) >= 1.2.0
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
|
||||
|
||||
%package -n libosmo-mgcp-client8
|
||||
%package -n libosmo-mgcp-client9
|
||||
Summary: Osmocom's Media Gateway Control Protocol client library
|
||||
Group: System/Libraries
|
||||
|
||||
%description -n libosmo-mgcp-client8
|
||||
%description -n libosmo-mgcp-client9
|
||||
Osmocom's Media Gateway Control Protocol client library.
|
||||
|
||||
%package -n libosmo-mgcp-client-devel
|
||||
Summary: Development files for Osmocom's Media Gateway Control Protocol client library
|
||||
Group: Development/Libraries/C and C++
|
||||
Requires: libosmo-mgcp-client8 = %{version}
|
||||
Requires: libosmo-mgcp-client9 = %{version}
|
||||
|
||||
%description -n libosmo-mgcp-client-devel
|
||||
Osmocom's Media Gateway Control Protocol client librarary.
|
||||
@@ -90,8 +90,8 @@ find %{buildroot} -type f -name "*.la" -delete -print
|
||||
%check
|
||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
|
||||
%post -n libosmo-mgcp-client8 -p /sbin/ldconfig
|
||||
%postun -n libosmo-mgcp-client8 -p /sbin/ldconfig
|
||||
%post -n libosmo-mgcp-client9 -p /sbin/ldconfig
|
||||
%postun -n libosmo-mgcp-client9 -p /sbin/ldconfig
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%preun
|
||||
@@ -119,8 +119,8 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg
|
||||
|
||||
%files -n libosmo-mgcp-client8
|
||||
%{_libdir}/libosmo-mgcp-client.so.8*
|
||||
%files -n libosmo-mgcp-client9
|
||||
%{_libdir}/libosmo-mgcp-client.so.9*
|
||||
|
||||
%files -n libosmo-mgcp-client-devel
|
||||
%{_libdir}/libosmo-mgcp-client.so
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
CFLAGS=-g -Wall $(shell pkg-config --cflags libosmocore libosmoctrl libosmo-netif liburing)
|
||||
LIBS=-lpthread $(shell pkg-config --libs libosmocore libosmoctrl libosmo-netif liburing)
|
||||
|
||||
rtp-load-gen: rtp-load-gen.o rtp_provider.o rtp_provider_static.o ctrl_if.o
|
||||
$(CC) -o $@ $^ $(LIBS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $^
|
||||
|
||||
clean:
|
||||
@rm rtp-load-gen *.o
|
||||
@@ -1,214 +0,0 @@
|
||||
/* CTRL interface of rtpsource program
|
||||
*
|
||||
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "rtp_provider.h"
|
||||
|
||||
static struct rtpsim_connection *find_connection_by_cname(const char *cname)
|
||||
{
|
||||
struct rtpsim_connection *rtpc;
|
||||
struct rtpsim_instance *ri;
|
||||
|
||||
pthread_rwlock_rdlock(&g_rtpsim->rwlock);
|
||||
llist_for_each_entry(ri, &g_rtpsim->instances, list) {
|
||||
rtpc = rtpsim_conn_find(ri, cname);
|
||||
if (rtpc) {
|
||||
pthread_rwlock_unlock(&g_rtpsim->rwlock);
|
||||
return rtpc;
|
||||
}
|
||||
}
|
||||
pthread_rwlock_unlock(&g_rtpsim->rwlock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct rtpsim_connection *create_connection(const char *cname, enum codec_type codec)
|
||||
{
|
||||
struct rtpsim_connection *rtpc;
|
||||
struct rtpsim_instance *ri;
|
||||
|
||||
pthread_rwlock_rdlock(&g_rtpsim->rwlock);
|
||||
llist_for_each_entry(ri, &g_rtpsim->instances, list) {
|
||||
rtpc = rtpsim_conn_reserve(ri, cname, codec);
|
||||
if (rtpc) {
|
||||
pthread_rwlock_unlock(&g_rtpsim->rwlock);
|
||||
return rtpc;
|
||||
}
|
||||
}
|
||||
pthread_rwlock_unlock(&g_rtpsim->rwlock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int connect_connection(struct rtpsim_connection *rtpc, const char *remote_host,
|
||||
uint16_t remote_port, uint8_t pt)
|
||||
{
|
||||
int rc;
|
||||
|
||||
osmo_sockaddr_str_from_str(&rtpc->cfg.remote, remote_host, remote_port);
|
||||
rtpc->cfg.pt = pt;
|
||||
|
||||
rc = rtpsim_conn_connect(rtpc);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = rtpsim_conn_start(rtpc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int delete_connection(struct rtpsim_connection *rtpc)
|
||||
{
|
||||
rtpsim_conn_stop(rtpc);
|
||||
rtpsim_conn_unreserve(rtpc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_WO_NOVRF(rtp_create, "rtp_create");
|
||||
static int set_rtp_create(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct rtpsim_connection *conn;
|
||||
const char *cname, *codec_str;
|
||||
char *tmp, *saveptr;
|
||||
enum codec_type codec;
|
||||
|
||||
tmp = talloc_strdup(cmd, cmd->value);
|
||||
OSMO_ASSERT(tmp);
|
||||
|
||||
cname = strtok_r(tmp, ",", &saveptr);
|
||||
codec_str = strtok_r(NULL, ",", &saveptr);
|
||||
|
||||
if (!cname || !codec_str) {
|
||||
cmd->reply = "Format is cname,codec";
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (find_connection_by_cname(cname)) {
|
||||
cmd->reply = "Connection already exists for cname";
|
||||
goto error;
|
||||
}
|
||||
|
||||
codec = get_string_value(codec_type_names, codec_str);
|
||||
if (codec < 0) {
|
||||
cmd->reply = "Invalid codec name (try GSM_FR, GSM_EFR etc.)";
|
||||
goto error;
|
||||
}
|
||||
|
||||
conn = create_connection(cname, codec);
|
||||
if (!conn) {
|
||||
cmd->reply = "Error creating RTP connection";
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Respond */
|
||||
cmd->reply = talloc_asprintf(cmd, "%s,%s,%d", conn->cname, conn->cfg.local.ip, conn->cfg.local.port);
|
||||
talloc_free(tmp);
|
||||
return CTRL_CMD_REPLY;
|
||||
|
||||
error:
|
||||
talloc_free(tmp);
|
||||
return CTRL_CMD_ERROR;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_WO_NOVRF(rtp_connect, "rtp_connect");
|
||||
static int set_rtp_connect(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct rtpsim_connection *conn;
|
||||
const char *cname, *remote_host, *remote_port, *pt;
|
||||
char *tmp, *saveptr;
|
||||
int rc;
|
||||
|
||||
tmp = talloc_strdup(cmd, cmd->value);
|
||||
OSMO_ASSERT(tmp);
|
||||
|
||||
/* FIXME: parse command */
|
||||
cname = strtok_r(tmp, ",", &saveptr);
|
||||
remote_host = strtok_r(NULL, ",", &saveptr);
|
||||
remote_port = strtok_r(NULL, ",", &saveptr);
|
||||
pt = strtok_r(NULL, ",", &saveptr);
|
||||
|
||||
if (!cname || !remote_host || !remote_port || !pt) {
|
||||
cmd->reply = "Format is cname,remote_host,remote_port,pt";
|
||||
talloc_free(tmp);
|
||||
return CTRL_CMD_ERROR;
|
||||
}
|
||||
|
||||
conn = find_connection_by_cname(cname);
|
||||
if (!conn) {
|
||||
cmd->reply = "Error finding RTP connection for connect";
|
||||
talloc_free(tmp);
|
||||
return CTRL_CMD_ERROR;
|
||||
}
|
||||
|
||||
rc = connect_connection(conn, remote_host, atoi(remote_port), atoi(pt));
|
||||
if (rc < 0) {
|
||||
cmd->reply = "Error binding RTP connection";
|
||||
talloc_free(tmp);
|
||||
return CTRL_CMD_ERROR;
|
||||
}
|
||||
|
||||
/* Respond */
|
||||
talloc_free(tmp);
|
||||
cmd->reply = talloc_asprintf(cmd, "%s,%s,%d,%d", conn->cname, conn->cfg.remote.ip,
|
||||
conn->cfg.remote.port, conn->cfg.pt);
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_WO_NOVRF(rtp_delete, "rtp_delete");
|
||||
static int set_rtp_delete(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct rtpsim_connection *conn;
|
||||
const char *cname = cmd->value;
|
||||
|
||||
conn = find_connection_by_cname(cname);
|
||||
if (!conn) {
|
||||
cmd->reply = "Error finding RTP connection for delete";
|
||||
return CTRL_CMD_ERROR;
|
||||
}
|
||||
cmd->reply = talloc_asprintf(cmd, "%s", conn->cname);
|
||||
|
||||
delete_connection(conn);
|
||||
|
||||
/* Respond */
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int rtpsource_ctrl_cmds_install(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rtp_create);
|
||||
if (rc)
|
||||
goto end;
|
||||
|
||||
rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rtp_connect);
|
||||
if (rc)
|
||||
goto end;
|
||||
|
||||
rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rtp_delete);
|
||||
if (rc)
|
||||
goto end;
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include <liburing.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
|
||||
struct rtp_provider_instance;
|
||||
|
||||
/* configuration of one RTP connection/socket */
|
||||
struct rtpsim_connection_cfg {
|
||||
struct osmo_sockaddr_str local;
|
||||
struct osmo_sockaddr_str remote;
|
||||
uint8_t pt;
|
||||
uint32_t ssrc;
|
||||
uint32_t duration;
|
||||
};
|
||||
|
||||
/* TX side state of RTP connection/socket */
|
||||
struct rtpsim_connection_tx {
|
||||
bool enabled;
|
||||
uint32_t timestamp;
|
||||
uint16_t seq;
|
||||
|
||||
struct rtp_provider_instance *rtp_prov_inst;
|
||||
|
||||
/* transmit buffer for outgoing messages */
|
||||
uint8_t *buf;
|
||||
/* used part of buffer */
|
||||
size_t buf_len;
|
||||
};
|
||||
|
||||
/* RX side state of RTP connection/socket */
|
||||
struct rtpsim_connection_rx {
|
||||
bool enabled;
|
||||
/* receive buffer for incoming messages */
|
||||
uint8_t *buf;
|
||||
/* used length of buffer */
|
||||
size_t buf_len;
|
||||
};
|
||||
|
||||
struct rtpsim_instance;
|
||||
|
||||
/* One RTP connection/socket */
|
||||
struct rtpsim_connection {
|
||||
/* index in rtp_instance.connections */
|
||||
unsigned int idx;
|
||||
/* back-pointer */
|
||||
struct rtpsim_instance *inst;
|
||||
|
||||
struct rtpsim_connection_cfg cfg;
|
||||
struct rtpsim_connection_tx tx;
|
||||
struct rtpsim_connection_rx rx;
|
||||
struct rate_ctr_group *ctrg;
|
||||
|
||||
/* socket file descriptor */
|
||||
int fd;
|
||||
char *cname;
|
||||
};
|
||||
|
||||
struct rtpsim_instance_cfg {
|
||||
int num;
|
||||
uint16_t base_port;
|
||||
unsigned int num_flows;
|
||||
};
|
||||
|
||||
/* one instance of the RTP simulator; typically one per worker thread */
|
||||
struct rtpsim_instance {
|
||||
/* element in application global list of instances */
|
||||
struct llist_head list;
|
||||
struct rtpsim_instance_cfg cfg;
|
||||
/* per-instance io_uring */
|
||||
struct io_uring ring;
|
||||
/* per-instance timerfd */
|
||||
int timerfd;
|
||||
/* counter group of per-instance counters */
|
||||
struct rate_ctr_group *ctrg;
|
||||
|
||||
struct rtpsim_connection **connections;
|
||||
/* size of 'connections' in number of pointers */
|
||||
unsigned int connections_size;
|
||||
};
|
||||
|
||||
struct rtpsim_global {
|
||||
/* global list of instances */
|
||||
struct llist_head instances;
|
||||
pthread_rwlock_t rwlock;
|
||||
|
||||
struct ctrl_handle *ctrl;
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
DMAIN,
|
||||
};
|
||||
|
||||
enum codec_type;
|
||||
|
||||
extern struct rtpsim_global *g_rtpsim;
|
||||
|
||||
struct rtpsim_connection *rtpsim_conn_find(struct rtpsim_instance *ri, const char *cname);
|
||||
struct rtpsim_connection *rtpsim_conn_reserve(struct rtpsim_instance *ri, const char *cname, enum codec_type codec);
|
||||
int rtpsim_conn_start(struct rtpsim_connection *rtpc);
|
||||
void rtpsim_conn_stop(struct rtpsim_connection *rtpc);
|
||||
void rtpsim_conn_unreserve(struct rtpsim_connection *rtpc);
|
||||
int rtpsim_conn_connect(struct rtpsim_connection *rtpc);
|
||||
|
||||
int rtpsource_ctrl_cmds_install(void);
|
||||
@@ -1,560 +0,0 @@
|
||||
#include <liburing.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/timerfd.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/netif/rtp.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "rtp_provider.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* use a separate rx-completion thread: submit from main, reap from completion */
|
||||
//#define USE_CQ_THREAD
|
||||
|
||||
/* use registered files: Doesn't seem to work with sockets? */
|
||||
//#define USE_REGISTERED_FILES
|
||||
|
||||
/* use registered buffers (mapped once into kernel, rather than at every write */
|
||||
#define USE_REGISTERED_BUFFERS
|
||||
|
||||
/* number of sockets/flows to create */
|
||||
#define NUM_FLOWS 4096
|
||||
|
||||
/* number of workers to spawn. Each worker will get an equal share of NR_FLOWS to work on */
|
||||
#define NR_WORKERS 4
|
||||
|
||||
/* size of rx/tx buffer for one RTP frame */
|
||||
#define BUF_SIZE 256
|
||||
|
||||
#define NUM_FLOWS_PER_WORKER (NUM_FLOWS/NR_WORKERS)
|
||||
|
||||
#define TX_BUF_IDX 0
|
||||
#define RX_BUF_IDX 1
|
||||
|
||||
struct rtpsim_global *g_rtpsim;
|
||||
|
||||
enum rtpsim_conn_ctr {
|
||||
RTP_CONN_CTR_TX_PKTS,
|
||||
RTP_CONN_CTR_TX_BYTES,
|
||||
RTP_CONN_CTR_RX_PKTS,
|
||||
RTP_CONN_CTR_RX_BYTES,
|
||||
RTP_CONN_CTR_RX_INVALID,
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc rtpsim_conn_ctrs[] = {
|
||||
[RTP_CONN_CTR_TX_PKTS] = { "tx_pkts:total", "Transmitted packets" },
|
||||
[RTP_CONN_CTR_TX_BYTES] = { "tx_bytes:total", "Transmitted bytes" },
|
||||
[RTP_CONN_CTR_RX_PKTS] = { "rx_pkts:total", "Received packets (total)" },
|
||||
[RTP_CONN_CTR_RX_BYTES] = { "rx_bytes:total", "Transmitted bytes" },
|
||||
[RTP_CONN_CTR_RX_INVALID] = { "rx_pkts:invalid", "Received packets (invalidl)" },
|
||||
};
|
||||
static const struct rate_ctr_group_desc rtpsim_conn_ctrg_desc = {
|
||||
.group_name_prefix = "rtpsim_conn",
|
||||
.group_description = "RTP Simulator Connection",
|
||||
.class_id = 0,
|
||||
.num_ctr = ARRAY_SIZE(rtpsim_conn_ctrs),
|
||||
.ctr_desc = rtpsim_conn_ctrs,
|
||||
};
|
||||
|
||||
enum rtpsim_ctr {
|
||||
RTP_INST_TIMERS_TOTAL,
|
||||
RTP_INST_TIMERS_LATE,
|
||||
};
|
||||
static const struct rate_ctr_desc rtpsim_ctrs[] = {
|
||||
[RTP_INST_TIMERS_TOTAL] = { "timers:total", "Timers expiring (total)" },
|
||||
[RTP_INST_TIMERS_LATE] = { "timers:late", "Timers expiring (late)" },
|
||||
};
|
||||
static const struct rate_ctr_group_desc rtpsim_ctrg_desc = {
|
||||
.group_name_prefix = "rtpsim",
|
||||
.group_description = "RTP Simulator Instance",
|
||||
.class_id = 0,
|
||||
.num_ctr = ARRAY_SIZE(rtpsim_ctrs),
|
||||
.ctr_desc = rtpsim_ctrs,
|
||||
};
|
||||
|
||||
struct rtpsim_instance *rtpsim_instance_init(void *ctx, const struct rtpsim_instance_cfg *rmp)
|
||||
{
|
||||
struct rtpsim_instance *ri = talloc_zero(ctx, struct rtpsim_instance);
|
||||
int rc;
|
||||
|
||||
if (!ri)
|
||||
return NULL;
|
||||
ri->connections_size = NUM_FLOWS_PER_WORKER;
|
||||
ri->connections = talloc_zero_size(ri, sizeof(struct rtpsim_connection *)*ri->connections_size);
|
||||
if (!ri->connections) {
|
||||
talloc_free(ri);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ri->cfg = *rmp;
|
||||
rc = io_uring_queue_init(NUM_FLOWS_PER_WORKER*2, &ri->ring, 0);
|
||||
if (rc < 0) {
|
||||
talloc_free(ri);
|
||||
return NULL;
|
||||
}
|
||||
ri->ctrg = rate_ctr_group_alloc(ri, &rtpsim_ctrg_desc, rmp->num);
|
||||
OSMO_ASSERT(ri->ctrg);
|
||||
return ri;
|
||||
}
|
||||
|
||||
static int rtpsim_instance_conn_add(struct rtpsim_instance *ri, struct rtpsim_connection *rtpc)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ri->connections_size; i++) {
|
||||
if (ri->connections[i] == NULL) {
|
||||
ri->connections[i] = rtpc;
|
||||
rtpc->idx = i;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static struct rtpsim_connection *
|
||||
rtpsim_conn_open_bind(struct rtpsim_instance *ri, const struct rtpsim_connection_cfg *rcfg)
|
||||
{
|
||||
struct rtpsim_connection *rtpc = talloc_zero(ri, struct rtpsim_connection);
|
||||
struct osmo_sockaddr sa_local;
|
||||
int rc;
|
||||
|
||||
if (!rtpc)
|
||||
return NULL;
|
||||
|
||||
rtpc->inst = ri;
|
||||
rtpc->cfg = *rcfg;
|
||||
|
||||
osmo_sockaddr_str_to_sockaddr(&rtpc->cfg.local, &sa_local.u.sas);
|
||||
|
||||
rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &sa_local, NULL, OSMO_SOCK_F_BIND);
|
||||
if (rc < 0) {
|
||||
talloc_free(rtpc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rtpc->fd = rc;
|
||||
rtpc->ctrg = rate_ctr_group_alloc(rtpc, &rtpsim_conn_ctrg_desc, rtpc->cfg.local.port);
|
||||
OSMO_ASSERT(rtpc->ctrg);
|
||||
#ifndef USE_REGISTERED_BUFFERS
|
||||
rtpc->tx.buf = talloc_zero_size(rtpc, BUF_SIZE);
|
||||
rtpc->rx.buf = talloc_zero_size(rtpc, BUF_SIZE);
|
||||
#endif
|
||||
OSMO_ASSERT(rtpsim_instance_conn_add(ri, rtpc) >= 0);
|
||||
|
||||
return rtpc;
|
||||
}
|
||||
|
||||
/* find a connection for given cname (may be NULL to find unused connection) */
|
||||
struct rtpsim_connection *rtpsim_conn_find(struct rtpsim_instance *ri, const char *cname)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ri->connections_size; i++) {
|
||||
struct rtpsim_connection *rtpc = ri->connections[i];
|
||||
if (!rtpc)
|
||||
continue;
|
||||
if (!rtpc->cname) {
|
||||
if (!cname)
|
||||
return rtpc;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(rtpc->cname, cname))
|
||||
return rtpc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* reserve a connection; associates cname with it */
|
||||
struct rtpsim_connection *rtpsim_conn_reserve(struct rtpsim_instance *ri, const char *cname,
|
||||
enum codec_type codec)
|
||||
{
|
||||
struct rtpsim_connection *rtpc;
|
||||
const struct rtp_provider *rtp_prov;
|
||||
|
||||
rtp_prov = rtp_provider_find("static"); // TODO: configurable */
|
||||
OSMO_ASSERT(rtp_prov);
|
||||
|
||||
rtpc = rtpsim_conn_find(ri, NULL);
|
||||
if (!rtpc)
|
||||
return NULL;
|
||||
|
||||
/* this is called from main thread, we cannot use per-thread talloc contexts
|
||||
* such as ri or rtpc */
|
||||
rtpc->cname = talloc_strdup(g_rtpsim, cname);
|
||||
rtpc->tx.rtp_prov_inst = rtp_provider_instance_alloc(g_rtpsim, rtp_prov, codec);
|
||||
OSMO_ASSERT(rtpc->tx.rtp_prov_inst);
|
||||
|
||||
/* re-start from zero transmit sequence number */
|
||||
rtpc->tx.seq = 0;
|
||||
|
||||
return rtpc;
|
||||
}
|
||||
|
||||
int rtpsim_conn_start(struct rtpsim_connection *rtpc)
|
||||
{
|
||||
rtpc->tx.enabled = true;
|
||||
rtpc->rx.enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rtpsim_conn_stop(struct rtpsim_connection *rtpc)
|
||||
{
|
||||
/* disable Rx and Tx */
|
||||
rtpc->tx.enabled = false;
|
||||
rtpc->rx.enabled = false;
|
||||
}
|
||||
|
||||
/* unreserve a connection; stops all rx/tx and removes cname */
|
||||
void rtpsim_conn_unreserve(struct rtpsim_connection *rtpc)
|
||||
{
|
||||
rtp_provider_instance_free(rtpc->tx.rtp_prov_inst);
|
||||
rtpc->tx.rtp_prov_inst = NULL;
|
||||
|
||||
talloc_free(rtpc->cname);
|
||||
rtpc->cname = NULL;
|
||||
}
|
||||
|
||||
/* connect a RTP connection to its remote peer (as in rtpc->cfg.remote) */
|
||||
int rtpsim_conn_connect(struct rtpsim_connection *rtpc)
|
||||
{
|
||||
struct osmo_sockaddr sa_remote;
|
||||
int rc;
|
||||
|
||||
osmo_sockaddr_str_to_sockaddr(&rtpc->cfg.remote, &sa_remote.u.sas);
|
||||
rc = connect(rtpc->fd, &sa_remote.u.sa, sizeof(struct osmo_sockaddr));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* transmit one RTP frame for given connection */
|
||||
static int rtpsim_conn_tx_frame(struct rtpsim_connection *rtpc)
|
||||
{
|
||||
struct rtp_hdr *rtph = (struct rtp_hdr *) rtpc->tx.buf;
|
||||
struct io_uring_sqe *sqe;
|
||||
uint8_t *payload;
|
||||
int rc;
|
||||
|
||||
rtph->version = RTP_VERSION;
|
||||
rtph->padding = 0;
|
||||
rtph->extension = 0;
|
||||
rtph->csrc_count = 0;
|
||||
rtph->marker = 0;
|
||||
rtph->payload_type = rtpc->cfg.pt;
|
||||
rtph->sequence = htons(rtpc->tx.seq++);
|
||||
rtph->timestamp = htonl(rtpc->tx.timestamp);
|
||||
rtpc->tx.timestamp += rtpc->cfg.duration;
|
||||
rtph->ssrc = htonl(rtpc->cfg.ssrc);
|
||||
payload = rtpc->tx.buf + sizeof(*rtph);
|
||||
/* add payload data */
|
||||
|
||||
rc = rtp_provider_instance_gen_frame(rtpc->tx.rtp_prov_inst, payload, BUF_SIZE-sizeof(*rtph));
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
rtpc->tx.buf_len = sizeof(*rtph) + rc;
|
||||
|
||||
sqe = io_uring_get_sqe(&rtpc->inst->ring);
|
||||
OSMO_ASSERT(sqe);
|
||||
sqe->user_data = rtpc->idx;
|
||||
#ifdef USE_REGISTERED_FILES
|
||||
io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
|
||||
#ifdef USE_REGISTERED_BUFFERS
|
||||
io_uring_prep_write_fixed(sqe, rtpc->idx, rtpc->tx.buf, rtpc->tx.buf_len, 0, TX_BUF_IDX);
|
||||
#else
|
||||
io_uring_prep_write(sqe, rtpc->idx, rtpc->tx.buf, rtpc->tx.buf_len, 0);
|
||||
#endif
|
||||
#else /* REGISTERED FILES */
|
||||
#ifdef USE_REGISTERED_BUFFERS
|
||||
io_uring_prep_write_fixed(sqe, rtpc->fd, rtpc->tx.buf, rtpc->tx.buf_len, 0, TX_BUF_IDX);
|
||||
#else
|
||||
io_uring_prep_write(sqe, rtpc->fd, rtpc->tx.buf, rtpc->tx.buf_len, 0);
|
||||
#endif
|
||||
#endif /* REGISTERED_FILES */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* submit RX buffer for a RTP frame on given connection */
|
||||
static int rtpsim_conn_rx_prep(struct rtpsim_connection *rtpc)
|
||||
{
|
||||
struct io_uring_sqe *sqe;
|
||||
|
||||
sqe = io_uring_get_sqe(&rtpc->inst->ring);
|
||||
OSMO_ASSERT(sqe);
|
||||
sqe->user_data = 0x8000000 | rtpc->idx;
|
||||
#ifdef USE_REGISTERED_FILES
|
||||
io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
|
||||
/* FIXME */
|
||||
#else /* REGISTERED FILES */
|
||||
#ifdef USE_REGISTERED_BUFFERS
|
||||
io_uring_prep_read_fixed(sqe, rtpc->fd, rtpc->rx.buf, BUF_SIZE, 0, RX_BUF_IDX);
|
||||
#else
|
||||
io_uring_prep_read(sqe, rtpc->fd, rtpc->rx.buf, BUF_SIZE, 0);
|
||||
#endif
|
||||
#endif /* REGISTERED_FILES */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* process one completion entry */
|
||||
static void handle_completion(struct rtpsim_instance *ri, struct io_uring_cqe *cqe)
|
||||
{
|
||||
struct rtpsim_connection *rtpc = ri->connections[cqe->user_data & 0x7fffffff];
|
||||
OSMO_ASSERT(rtpc);
|
||||
|
||||
if (cqe->user_data & 0x80000000) {
|
||||
/* read */
|
||||
rate_ctr_inc(&rtpc->ctrg->ctr[RTP_CONN_CTR_RX_PKTS]);
|
||||
rate_ctr_add(&rtpc->ctrg->ctr[RTP_CONN_CTR_RX_BYTES], cqe->res);
|
||||
OSMO_ASSERT(cqe->res >= 0);
|
||||
} else {
|
||||
/* write */
|
||||
rate_ctr_inc(&rtpc->ctrg->ctr[RTP_CONN_CTR_TX_PKTS]);
|
||||
rate_ctr_add(&rtpc->ctrg->ctr[RTP_CONN_CTR_TX_BYTES], cqe->res);
|
||||
OSMO_ASSERT(cqe->res == sizeof(struct rtp_hdr) + 33);
|
||||
}
|
||||
io_uring_cqe_seen(&ri->ring, cqe);
|
||||
}
|
||||
|
||||
#ifdef USE_CQ_THREAD
|
||||
/* 'main' function for separate completion queue reaping thread */
|
||||
static void *reap_completion(void *_ri)
|
||||
{
|
||||
struct rtpsim_instance *ri = _ri;
|
||||
while (1) {
|
||||
struct io_uring_cqe *cqe;
|
||||
int rc;
|
||||
|
||||
rc = io_uring_wait_cqe(&ri->ring, &cqe);
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
handle_completion(ri, cqe);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
extern int osmo_ctx_init(const char *id);
|
||||
|
||||
static void rtpsim_main(const struct rtpsim_instance_cfg *rmp)
|
||||
{
|
||||
struct rtpsim_instance *ri;
|
||||
struct rtpsim_connection *rtpc;
|
||||
int i, rc;
|
||||
|
||||
char namebuf[32];
|
||||
snprintf(namebuf, sizeof(namebuf), "rtpsim_worker%d", rmp->num);
|
||||
osmo_ctx_init(namebuf);
|
||||
|
||||
ri = rtpsim_instance_init(OTC_GLOBAL, rmp);
|
||||
OSMO_ASSERT(ri);
|
||||
|
||||
pthread_rwlock_wrlock(&g_rtpsim->rwlock);
|
||||
llist_add_tail(&ri->list, &g_rtpsim->instances);
|
||||
pthread_rwlock_unlock(&g_rtpsim->rwlock);
|
||||
|
||||
/* create desired number of sockets */
|
||||
printf("binding sockets\n");
|
||||
for (i = 0; i < rmp->num_flows; i++) {
|
||||
struct rtpsim_connection *rtpc;
|
||||
struct rtpsim_connection_cfg rcfg = {};
|
||||
rcfg.local = (struct osmo_sockaddr_str) {
|
||||
.af = AF_INET,
|
||||
.ip = "127.0.0.1",
|
||||
.port = rmp->base_port + 2*i,
|
||||
};
|
||||
rcfg.remote = (struct osmo_sockaddr_str) {
|
||||
.af = AF_INET,
|
||||
.ip = "127.0.0.1",
|
||||
.port = rmp->base_port + 2*i,
|
||||
};
|
||||
rcfg.pt = 3;
|
||||
rcfg.ssrc = 0x80000000 + rmp->base_port + i;
|
||||
rcfg.duration = 160; /* 8000 Hz sampling rate / 50 Hz RTP rate */
|
||||
|
||||
rtpc = rtpsim_conn_open_bind(ri, &rcfg);
|
||||
OSMO_ASSERT(rtpc);
|
||||
}
|
||||
|
||||
#if 1
|
||||
/* HACK */
|
||||
printf("connecting sockets\n");
|
||||
for (i = 0; i < rmp->num_flows; i++) {
|
||||
char namebuf[32];
|
||||
snprintf(namebuf, sizeof(namebuf), "conn%d", i);
|
||||
struct rtpsim_connection *rtpc = rtpsim_conn_reserve(ri, namebuf, CODEC_GSM_FR);
|
||||
OSMO_ASSERT(rtpc);
|
||||
OSMO_ASSERT(rtpsim_conn_connect(rtpc) == 0);
|
||||
OSMO_ASSERT(rtpsim_conn_start(rtpc) == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_REGISTERED_FILES
|
||||
/* register all our file descriptors; seems to fail on 5.8.x ? */
|
||||
int fds[NUM_FLOWS_PER_WORKER];
|
||||
for (i = 0; i < ri->connections_size; i++) {
|
||||
if (!rtpc) {
|
||||
fds[i] = -1;
|
||||
continue;
|
||||
}
|
||||
rtpc = ri->connections[i];
|
||||
fds[i] = rtpc->fd;
|
||||
}
|
||||
printf("Registering %d files\n", i);
|
||||
rc = io_uring_register_files(&ri->ring, fds, i);
|
||||
printf("rc = %d: %s\n", rc, strerror(-rc));
|
||||
OSMO_ASSERT(rc == 0);
|
||||
#endif
|
||||
|
||||
#ifdef USE_REGISTERED_BUFFERS
|
||||
/* register two large buffers for Rx and Tx; assign per-connection
|
||||
* buffers within those two registered buffers */
|
||||
void *largebuf_tx = talloc_zero_size(ri, rmp->num_flows * BUF_SIZE);
|
||||
void *largebuf_rx = talloc_zero_size(ri, rmp->num_flows * BUF_SIZE);
|
||||
struct iovec iov[2] = {
|
||||
[TX_BUF_IDX] = {
|
||||
.iov_base = largebuf_tx,
|
||||
.iov_len = rmp->num_flows * BUF_SIZE,
|
||||
},
|
||||
[RX_BUF_IDX] = {
|
||||
.iov_base = largebuf_rx,
|
||||
.iov_len = rmp->num_flows * BUF_SIZE,
|
||||
},
|
||||
};
|
||||
printf("Registering buffers for %d sockets\n", i);
|
||||
rc = io_uring_register_buffers(&ri->ring, iov, ARRAY_SIZE(iov));
|
||||
printf("rc = %d: %s\n", rc, strerror(-rc));
|
||||
OSMO_ASSERT(rc == 0);
|
||||
for (i = 0; i < ri->connections_size; i++) {
|
||||
rtpc = ri->connections[i];
|
||||
if (!rtpc)
|
||||
continue;
|
||||
rtpc->tx.buf = largebuf_tx + (i * BUF_SIZE);
|
||||
rtpc->rx.buf = largebuf_rx + (i * BUF_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_CQ_THREAD
|
||||
/* start a separate completion thread instead of handling completions in-line */
|
||||
pthread_t complete;
|
||||
rc = pthread_create(&complete, NULL, reap_completion, ri);
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
#endif
|
||||
|
||||
/* start timerfd every 20ms */
|
||||
ri->timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
|
||||
OSMO_ASSERT(ri->timerfd >= 0);
|
||||
struct itimerspec its = (struct itimerspec) {
|
||||
.it_interval = { 0, 20*1000*1000 },
|
||||
.it_value = { 0, 20*1000*1000 },
|
||||
};
|
||||
rc = timerfd_settime(ri->timerfd, 0, &its, NULL);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
/* start transmitting */
|
||||
|
||||
while (1) {
|
||||
/* the assumption here is that every flow wants to write 50
|
||||
* packets per second, so we try try to submit one write for each
|
||||
* flow every 20ms */
|
||||
unsigned int submit_granularity = rmp->num_flows/50;
|
||||
/* number of 20ms timer expirations */
|
||||
uint64_t num_exp;
|
||||
unsigned int t;
|
||||
|
||||
if (submit_granularity <= 0)
|
||||
submit_granularity = 1;
|
||||
|
||||
/* read from timerfd to pace the 20ms inter packet interval */
|
||||
rc = read(ri->timerfd, &num_exp, sizeof(num_exp));
|
||||
OSMO_ASSERT(rc == sizeof(num_exp));
|
||||
|
||||
rate_ctr_add(&ri->ctrg->ctr[RTP_INST_TIMERS_TOTAL], num_exp);
|
||||
if (num_exp != 1) {
|
||||
fputc('X', stdout);
|
||||
rate_ctr_add(&ri->ctrg->ctr[RTP_INST_TIMERS_LATE], num_exp-1);
|
||||
} else {
|
||||
fputc('.', stdout);
|
||||
}
|
||||
fflush(stdout);
|
||||
|
||||
for (t = 0; t < num_exp; t++) {
|
||||
for (i = 0; i < ri->connections_size; i++) {
|
||||
rtpc = ri->connections[i];
|
||||
if (!rtpc)
|
||||
continue;
|
||||
if (rtpc->tx.enabled)
|
||||
rtpsim_conn_tx_frame(rtpc);
|
||||
if (rtpc->rx.enabled)
|
||||
rtpsim_conn_rx_prep(rtpc);
|
||||
if ((i % submit_granularity) == 0) {
|
||||
int pending = io_uring_submit(&ri->ring);
|
||||
|
||||
#ifndef USE_CQ_THREAD
|
||||
for (int j = 0; j < pending; j++) {
|
||||
struct io_uring_cqe *cqe;
|
||||
int rc;
|
||||
|
||||
rc = io_uring_wait_cqe(&ri->ring, &cqe);
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
handle_completion(ri, cqe);
|
||||
}
|
||||
#endif /* USE_CQ_THREAD */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void *rtpsim_worker_thread(void *_rmp)
|
||||
{
|
||||
rtpsim_main((struct rtpsim_instance_cfg *)_rmp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
pthread_t worker[NR_WORKERS];
|
||||
struct rtpsim_instance_cfg rmp[NR_WORKERS];
|
||||
int i, rc;
|
||||
|
||||
g_rtpsim = talloc_zero(NULL, struct rtpsim_global);
|
||||
OSMO_ASSERT(g_rtpsim);
|
||||
INIT_LLIST_HEAD(&g_rtpsim->instances);
|
||||
pthread_rwlock_init(&g_rtpsim->rwlock, NULL);
|
||||
|
||||
osmo_init_logging2(g_rtpsim, NULL);
|
||||
// osmo_stats_init(g_rtpsim);
|
||||
|
||||
/* Create worker threads */
|
||||
for (i = 0; i < NR_WORKERS; i++) {
|
||||
int rc;
|
||||
rmp[i].num = i;
|
||||
rmp[i].num_flows = NUM_FLOWS_PER_WORKER;
|
||||
rmp[i].base_port = 10000 + i * (2 * rmp[i].num_flows);
|
||||
rc = pthread_create(&worker[i], NULL, rtpsim_worker_thread, &rmp[i]);
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
}
|
||||
|
||||
/* CTRL interface */
|
||||
//g_rtpsim->ctrl = ctrl_interface_setup_dynip(g_rss, ctrl_vty_get_bind_addr(), 11111, NULL);
|
||||
g_rtpsim->ctrl = ctrl_interface_setup_dynip(g_rtpsim, "127.0.0.1", 11111, NULL);
|
||||
OSMO_ASSERT(g_rtpsim->ctrl);
|
||||
rc = rtpsource_ctrl_cmds_install();
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
while (1) {
|
||||
osmo_select_main(0);
|
||||
}
|
||||
|
||||
for (i = 0; i < NR_WORKERS; i++) {
|
||||
pthread_join(worker[i], NULL);
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include "rtp_provider.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
static LLIST_HEAD(g_providers);
|
||||
|
||||
const struct value_string codec_type_names[] = {
|
||||
{ CODEC_ULAW, "ULAW" },
|
||||
{ CODEC_ALAW, "ALAW" },
|
||||
{ CODEC_GSM_FR, "GSM_FR" },
|
||||
{ CODEC_GSM_EFR, "GSM_EFR" },
|
||||
{ CODEC_GSM_HR, "GSM_HR" },
|
||||
{ CODEC_AMR_4_75, "AMR_4_75" },
|
||||
{ CODEC_AMR_5_15, "AMR_5_15" },
|
||||
{ CODEC_AMR_5_90, "AMR_5_90" },
|
||||
{ CODEC_AMR_6_70, "AMR_6_70" },
|
||||
{ CODEC_AMR_7_40, "AMR_7_40" },
|
||||
{ CODEC_AMR_7_95, "AMR_7_95" },
|
||||
{ CODEC_AMR_10_2, "AMR_10_2" },
|
||||
{ CODEC_AMR_12_2, "AMR_12_2" },
|
||||
{ CODEC_AMR_SID, "AMR_SID" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
void rtp_provider_register(struct rtp_provider *prov)
|
||||
{
|
||||
llist_add_tail(&prov->list, &g_providers);
|
||||
}
|
||||
|
||||
const struct rtp_provider *rtp_provider_find(const char *name)
|
||||
{
|
||||
struct rtp_provider *p;
|
||||
llist_for_each_entry(p, &g_providers, list) {
|
||||
if (!strcmp(name, p->name))
|
||||
return p;
|
||||
}
|
||||
LOGP(DMAIN, LOGL_ERROR, "Couldn't find RTP provider '%s'\n", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct rtp_provider_instance *
|
||||
rtp_provider_instance_alloc(void *ctx, const struct rtp_provider *provider, enum codec_type codec)
|
||||
{
|
||||
struct rtp_provider_instance *pi;
|
||||
|
||||
pi = talloc_zero(ctx, struct rtp_provider_instance);
|
||||
if (!pi)
|
||||
return NULL;
|
||||
|
||||
pi->provider = provider;
|
||||
pi->codec = codec;
|
||||
|
||||
return pi;
|
||||
}
|
||||
|
||||
void rtp_provider_instance_free(struct rtp_provider_instance *pi)
|
||||
{
|
||||
llist_del(&pi->list);
|
||||
talloc_free(pi);
|
||||
}
|
||||
|
||||
int rtp_provider_instance_gen_frame(struct rtp_provider_instance *pi, uint8_t *out, size_t out_size)
|
||||
{
|
||||
OSMO_ASSERT(pi->provider);
|
||||
return pi->provider->rtp_gen(pi, out, out_size);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
enum codec_type {
|
||||
CODEC_ULAW,
|
||||
CODEC_ALAW,
|
||||
CODEC_GSM_FR,
|
||||
CODEC_GSM_EFR,
|
||||
CODEC_GSM_HR,
|
||||
CODEC_AMR_4_75,
|
||||
CODEC_AMR_5_15,
|
||||
CODEC_AMR_5_90,
|
||||
CODEC_AMR_6_70,
|
||||
CODEC_AMR_7_40,
|
||||
CODEC_AMR_7_95,
|
||||
CODEC_AMR_10_2,
|
||||
CODEC_AMR_12_2,
|
||||
CODEC_AMR_SID,
|
||||
_NUM_CODECS
|
||||
};
|
||||
|
||||
extern const struct value_string codec_type_names[];
|
||||
|
||||
struct rtp_provider_instance;
|
||||
|
||||
struct rtp_provider {
|
||||
/* global list of RTP providers */
|
||||
struct llist_head list;
|
||||
const char *name;
|
||||
|
||||
/* create/initialie a RTP provider with specified argument string */
|
||||
int (*setup)(struct rtp_provider_instance *inst, const char *arg);
|
||||
|
||||
/* generate the next RTP packet; return length in octests or negative on error */
|
||||
int (*rtp_gen)(struct rtp_provider_instance *inst, uint8_t *out, size_t out_size);
|
||||
};
|
||||
|
||||
struct rtp_provider_instance {
|
||||
/* entry in global list of RTP provider instances */
|
||||
struct llist_head list;
|
||||
/* pointer to provider of which we are an instance */
|
||||
const struct rtp_provider *provider;
|
||||
/* codec payload we are to generate */
|
||||
enum codec_type codec;
|
||||
|
||||
/* private user data */
|
||||
void *priv;
|
||||
};
|
||||
|
||||
void rtp_provider_register(struct rtp_provider *prov);
|
||||
const struct rtp_provider *rtp_provider_find(const char *name);
|
||||
|
||||
struct rtp_provider_instance *rtp_provider_instance_alloc(void *ctx, const struct rtp_provider *provider, enum codec_type codec);
|
||||
void rtp_provider_instance_free(struct rtp_provider_instance *pi);
|
||||
int rtp_provider_instance_gen_frame(struct rtp_provider_instance *pi, uint8_t *out, size_t out_size);
|
||||
@@ -1,108 +0,0 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <osmocom/codec/codec.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include "rtp_provider.h"
|
||||
#include "internal.h"
|
||||
|
||||
static struct rtp_provider static_provider;
|
||||
|
||||
static const uint8_t len4codec[_NUM_CODECS] = {
|
||||
[CODEC_ULAW] = 160,
|
||||
[CODEC_ALAW] = 160,
|
||||
[CODEC_GSM_FR] = GSM_FR_BYTES,
|
||||
[CODEC_GSM_EFR] = GSM_EFR_BYTES,
|
||||
[CODEC_GSM_HR] = GSM_HR_BYTES,
|
||||
[CODEC_AMR_4_75] = 12,
|
||||
[CODEC_AMR_5_15] = 13,
|
||||
[CODEC_AMR_5_90] = 15,
|
||||
[CODEC_AMR_6_70] = 17,
|
||||
[CODEC_AMR_7_40] = 19,
|
||||
[CODEC_AMR_7_95] = 20,
|
||||
[CODEC_AMR_10_2] = 26,
|
||||
[CODEC_AMR_12_2] = 31,
|
||||
[CODEC_AMR_SID] = 5,
|
||||
};
|
||||
|
||||
/* generate a static / fixed RTP payload of matching codec/mode */
|
||||
static int rtp_gen_static(struct rtp_provider_instance *pi, uint8_t *out, size_t out_size)
|
||||
{
|
||||
uint8_t len;
|
||||
|
||||
OSMO_ASSERT(pi->provider == &static_provider);
|
||||
|
||||
len = len4codec[pi->codec];
|
||||
if (out_size < len) {
|
||||
LOGP(DMAIN, LOGL_ERROR, "out_size %zu < %u\n", out_size, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(out, 0, len);
|
||||
|
||||
switch (pi->codec) {
|
||||
case CODEC_ULAW:
|
||||
case CODEC_ALAW:
|
||||
break;
|
||||
case CODEC_GSM_FR:
|
||||
out[0] = (out[0] & 0x0f) | 0xD0; /* mask in first four bit for FR */
|
||||
break;
|
||||
case CODEC_GSM_EFR:
|
||||
out[0] = (out[0] & 0x0f) | 0xC0; /* mask in first four bit for EFR */
|
||||
break;
|
||||
case CODEC_GSM_HR:
|
||||
break;
|
||||
case CODEC_AMR_4_75:
|
||||
out[0] = 0 << 4;
|
||||
out[1] = 0 << 3;
|
||||
break;
|
||||
case CODEC_AMR_5_15:
|
||||
out[0] = 1 << 4;
|
||||
out[1] = 1 << 3;
|
||||
break;
|
||||
case CODEC_AMR_5_90:
|
||||
out[0] = 2 << 4;
|
||||
out[1] = 2 << 3;
|
||||
break;
|
||||
case CODEC_AMR_6_70:
|
||||
out[0] = 3 << 4;
|
||||
out[1] = 3 << 3;
|
||||
break;
|
||||
case CODEC_AMR_7_40:
|
||||
out[0] = 4 << 4;
|
||||
out[1] = 4 << 3;
|
||||
break;
|
||||
case CODEC_AMR_7_95:
|
||||
out[0] = 5 << 4;
|
||||
out[1] = 5 << 3;
|
||||
break;
|
||||
case CODEC_AMR_10_2:
|
||||
out[0] = 6 << 4;
|
||||
out[1] = 6 << 3;
|
||||
break;
|
||||
case CODEC_AMR_12_2:
|
||||
out[0] = 7 << 4;
|
||||
out[1] = 7 << 3;
|
||||
break;
|
||||
case CODEC_AMR_SID:
|
||||
out[0] = 2 << 4; /* CMR: 5.90 */
|
||||
out[0] = 8 << 3;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static struct rtp_provider static_provider = {
|
||||
.name = "static",
|
||||
.rtp_gen = &rtp_gen_static,
|
||||
};
|
||||
|
||||
static void __attribute__((constructor)) rtp_provider_static_constr(void)
|
||||
{
|
||||
rtp_provider_register(&static_provider);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
CFLAGS:= -O2 -g -Wall $(shell pkg-config --cflags libosmocore libosmotrau)
|
||||
LIBS:= $(shell pkg-config --libs libosmocore libosmotrau)
|
||||
|
||||
all: osmo-simcom2rtp
|
||||
|
||||
osmo-simcom2rtp: g711.o g711_table.o simcom2rtp.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $^
|
||||
|
||||
@@ -1,313 +0,0 @@
|
||||
/*
|
||||
* This source code is a product of Sun Microsystems, Inc. and is provided
|
||||
* for unrestricted use. Users may copy or modify this source code without
|
||||
* charge.
|
||||
*
|
||||
* SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
|
||||
* THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
|
||||
*
|
||||
* Sun source code is provided with no support and without any obligation on
|
||||
* the part of Sun Microsystems, Inc. to assist in its use, correction,
|
||||
* modification or enhancement.
|
||||
*
|
||||
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
|
||||
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
|
||||
* OR ANY PART THEREOF.
|
||||
*
|
||||
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
|
||||
* or profits or other special, indirect and consequential damages, even if
|
||||
* Sun has been advised of the possibility of such damages.
|
||||
*
|
||||
* Sun Microsystems, Inc.
|
||||
* 2550 Garcia Avenue
|
||||
* Mountain View, California 94043
|
||||
*/
|
||||
/*
|
||||
* December 30, 1994:
|
||||
* Functions linear2alaw, linear2ulaw have been updated to correctly
|
||||
* convert unquantized 16 bit values.
|
||||
* Tables for direct u- to A-law and A- to u-law conversions have been
|
||||
* corrected.
|
||||
* Borge Lindberg, Center for PersonKommunikation, Aalborg University.
|
||||
* bli@cpk.auc.dk
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* Downloaded from comp.speech site in Cambridge.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "g711.h"
|
||||
|
||||
/*
|
||||
* g711.c
|
||||
*
|
||||
* u-law, A-law and linear PCM conversions.
|
||||
* Source: http://www.speech.kth.se/cost250/refsys/latest/src/g711.c
|
||||
*/
|
||||
#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
|
||||
#define QUANT_MASK (0xf) /* Quantization field mask. */
|
||||
#define NSEGS (8) /* Number of A-law segments. */
|
||||
#define SEG_SHIFT (4) /* Left shift for segment number. */
|
||||
#define SEG_MASK (0x70) /* Segment field mask. */
|
||||
|
||||
static short seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
|
||||
0x1FF, 0x3FF, 0x7FF, 0xFFF};
|
||||
static short seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
|
||||
0x3FF, 0x7FF, 0xFFF, 0x1FFF};
|
||||
|
||||
/* copy from CCITT G.711 specifications */
|
||||
unsigned char _u2a[128] = { /* u- to A-law conversions */
|
||||
1, 1, 2, 2, 3, 3, 4, 4,
|
||||
5, 5, 6, 6, 7, 7, 8, 8,
|
||||
9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 27, 29, 31, 33, 34, 35, 36,
|
||||
37, 38, 39, 40, 41, 42, 43, 44,
|
||||
46, 48, 49, 50, 51, 52, 53, 54,
|
||||
55, 56, 57, 58, 59, 60, 61, 62,
|
||||
64, 65, 66, 67, 68, 69, 70, 71,
|
||||
72, 73, 74, 75, 76, 77, 78, 79,
|
||||
/* corrected:
|
||||
81, 82, 83, 84, 85, 86, 87, 88,
|
||||
should be: */
|
||||
80, 82, 83, 84, 85, 86, 87, 88,
|
||||
89, 90, 91, 92, 93, 94, 95, 96,
|
||||
97, 98, 99, 100, 101, 102, 103, 104,
|
||||
105, 106, 107, 108, 109, 110, 111, 112,
|
||||
113, 114, 115, 116, 117, 118, 119, 120,
|
||||
121, 122, 123, 124, 125, 126, 127, 128};
|
||||
|
||||
unsigned char _a2u[128] = { /* A- to u-law conversions */
|
||||
1, 3, 5, 7, 9, 11, 13, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 32, 33, 33, 34, 34, 35, 35,
|
||||
36, 37, 38, 39, 40, 41, 42, 43,
|
||||
44, 45, 46, 47, 48, 48, 49, 49,
|
||||
50, 51, 52, 53, 54, 55, 56, 57,
|
||||
58, 59, 60, 61, 62, 63, 64, 64,
|
||||
65, 66, 67, 68, 69, 70, 71, 72,
|
||||
/* corrected:
|
||||
73, 74, 75, 76, 77, 78, 79, 79,
|
||||
should be: */
|
||||
73, 74, 75, 76, 77, 78, 79, 80,
|
||||
80, 81, 82, 83, 84, 85, 86, 87,
|
||||
88, 89, 90, 91, 92, 93, 94, 95,
|
||||
96, 97, 98, 99, 100, 101, 102, 103,
|
||||
104, 105, 106, 107, 108, 109, 110, 111,
|
||||
112, 113, 114, 115, 116, 117, 118, 119,
|
||||
120, 121, 122, 123, 124, 125, 126, 127};
|
||||
|
||||
static short search(
|
||||
short val,
|
||||
short *table,
|
||||
short size)
|
||||
{
|
||||
short i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (val <= *table++)
|
||||
return (i);
|
||||
}
|
||||
return (size);
|
||||
}
|
||||
|
||||
/*
|
||||
* linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
|
||||
*
|
||||
* linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
|
||||
*
|
||||
* Linear Input Code Compressed Code
|
||||
* ------------------------ ---------------
|
||||
* 0000000wxyza 000wxyz
|
||||
* 0000001wxyza 001wxyz
|
||||
* 000001wxyzab 010wxyz
|
||||
* 00001wxyzabc 011wxyz
|
||||
* 0001wxyzabcd 100wxyz
|
||||
* 001wxyzabcde 101wxyz
|
||||
* 01wxyzabcdef 110wxyz
|
||||
* 1wxyzabcdefg 111wxyz
|
||||
*
|
||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
||||
*/
|
||||
unsigned char
|
||||
linear2alaw(short pcm_val) /* 2's complement (16-bit range) */
|
||||
{
|
||||
short mask;
|
||||
short seg;
|
||||
unsigned char aval;
|
||||
|
||||
pcm_val = pcm_val >> 3;
|
||||
|
||||
if (pcm_val >= 0) {
|
||||
mask = 0xD5; /* sign (7th) bit = 1 */
|
||||
} else {
|
||||
mask = 0x55; /* sign bit = 0 */
|
||||
pcm_val = -pcm_val - 1;
|
||||
}
|
||||
|
||||
/* Convert the scaled magnitude to segment number. */
|
||||
seg = search(pcm_val, seg_aend, 8);
|
||||
|
||||
/* Combine the sign, segment, and quantization bits. */
|
||||
|
||||
if (seg >= 8) /* out of range, return maximum value. */
|
||||
return (unsigned char) (0x7F ^ mask);
|
||||
else {
|
||||
aval = (unsigned char) seg << SEG_SHIFT;
|
||||
if (seg < 2)
|
||||
aval |= (pcm_val >> 1) & QUANT_MASK;
|
||||
else
|
||||
aval |= (pcm_val >> seg) & QUANT_MASK;
|
||||
return (aval ^ mask);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* alaw2linear() - Convert an A-law value to 16-bit linear PCM
|
||||
*
|
||||
*/
|
||||
short
|
||||
alaw2linear(
|
||||
unsigned char a_val)
|
||||
{
|
||||
short t;
|
||||
short seg;
|
||||
|
||||
a_val ^= 0x55;
|
||||
|
||||
t = (a_val & QUANT_MASK) << 4;
|
||||
seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
|
||||
switch (seg) {
|
||||
case 0:
|
||||
t += 8;
|
||||
break;
|
||||
case 1:
|
||||
t += 0x108;
|
||||
break;
|
||||
default:
|
||||
t += 0x108;
|
||||
t <<= seg - 1;
|
||||
}
|
||||
return ((a_val & SIGN_BIT) ? t : -t);
|
||||
}
|
||||
|
||||
#define BIAS (0x84) /* Bias for linear code. */
|
||||
#define CLIP 8159
|
||||
|
||||
/*
|
||||
* linear2ulaw() - Convert a linear PCM value to u-law
|
||||
*
|
||||
* In order to simplify the encoding process, the original linear magnitude
|
||||
* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
|
||||
* (33 - 8191). The result can be seen in the following encoding table:
|
||||
*
|
||||
* Biased Linear Input Code Compressed Code
|
||||
* ------------------------ ---------------
|
||||
* 00000001wxyza 000wxyz
|
||||
* 0000001wxyzab 001wxyz
|
||||
* 000001wxyzabc 010wxyz
|
||||
* 00001wxyzabcd 011wxyz
|
||||
* 0001wxyzabcde 100wxyz
|
||||
* 001wxyzabcdef 101wxyz
|
||||
* 01wxyzabcdefg 110wxyz
|
||||
* 1wxyzabcdefgh 111wxyz
|
||||
*
|
||||
* Each biased linear code has a leading 1 which identifies the segment
|
||||
* number. The value of the segment number is equal to 7 minus the number
|
||||
* of leading 0's. The quantization interval is directly available as the
|
||||
* four bits wxyz. * The trailing bits (a - h) are ignored.
|
||||
*
|
||||
* Ordinarily the complement of the resulting code word is used for
|
||||
* transmission, and so the code word is complemented before it is returned.
|
||||
*
|
||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
||||
*/
|
||||
unsigned char
|
||||
linear2ulaw(
|
||||
short pcm_val) /* 2's complement (16-bit range) */
|
||||
{
|
||||
short mask;
|
||||
short seg;
|
||||
unsigned char uval;
|
||||
|
||||
/* Get the sign and the magnitude of the value. */
|
||||
pcm_val = pcm_val >> 2;
|
||||
if (pcm_val < 0) {
|
||||
pcm_val = -pcm_val;
|
||||
mask = 0x7F;
|
||||
} else {
|
||||
mask = 0xFF;
|
||||
}
|
||||
if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */
|
||||
pcm_val += (BIAS >> 2);
|
||||
|
||||
/* Convert the scaled magnitude to segment number. */
|
||||
seg = search(pcm_val, seg_uend, 8);
|
||||
|
||||
/*
|
||||
* Combine the sign, segment, quantization bits;
|
||||
* and complement the code word.
|
||||
*/
|
||||
if (seg >= 8) /* out of range, return maximum value. */
|
||||
return (unsigned char) (0x7F ^ mask);
|
||||
else {
|
||||
uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
|
||||
return (uval ^ mask);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* ulaw2linear() - Convert a u-law value to 16-bit linear PCM
|
||||
*
|
||||
* First, a biased linear code is derived from the code word. An unbiased
|
||||
* output can then be obtained by subtracting 33 from the biased code.
|
||||
*
|
||||
* Note that this function expects to be passed the complement of the
|
||||
* original code word. This is in keeping with ISDN conventions.
|
||||
*/
|
||||
short
|
||||
ulaw2linear(
|
||||
unsigned char u_val)
|
||||
{
|
||||
short t;
|
||||
|
||||
/* Complement to obtain normal u-law value. */
|
||||
u_val = ~u_val;
|
||||
|
||||
/*
|
||||
* Extract and bias the quantization bits. Then
|
||||
* shift up by the segment number and subtract out the bias.
|
||||
*/
|
||||
t = ((u_val & QUANT_MASK) << 3) + BIAS;
|
||||
t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
|
||||
|
||||
return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
|
||||
}
|
||||
|
||||
/* A-law to u-law conversion */
|
||||
unsigned char
|
||||
alaw2ulaw(
|
||||
unsigned char aval)
|
||||
{
|
||||
aval &= 0xff;
|
||||
return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
|
||||
(0x7F ^ _a2u[aval ^ 0x55]));
|
||||
}
|
||||
|
||||
/* u-law to A-law conversion */
|
||||
unsigned char
|
||||
ulaw2alaw(
|
||||
unsigned char uval)
|
||||
{
|
||||
uval &= 0xff;
|
||||
return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
|
||||
(0x55 ^ (_u2a[0x7F ^ uval] - 1)));
|
||||
}
|
||||
|
||||
/* ---------- end of g711.c ----------------------------------------------------- */
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* g711.h
|
||||
*
|
||||
* u-law, A-law and linear PCM conversions.
|
||||
* Source: http://www.speech.kth.se/cost250/refsys/latest/src/g711.h
|
||||
*/
|
||||
|
||||
#ifndef _G711_H_
|
||||
#define _G711_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
unsigned char linear2alaw(short pcm_val);
|
||||
short alaw2linear(unsigned char a_val);
|
||||
unsigned char linear2ulaw(short pcm_val);
|
||||
short ulaw2linear(unsigned char u_val);
|
||||
unsigned char alaw2ulaw(unsigned char aval);
|
||||
unsigned char ulaw2alaw(unsigned char uval);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _G711_H_ */
|
||||
@@ -1,102 +0,0 @@
|
||||
#ifndef G711_TABLE_H
|
||||
#define G711_TABLE_H
|
||||
|
||||
#include "g711.h"
|
||||
|
||||
/* 16384 entries per table (16 bit) */
|
||||
unsigned char linear_to_alaw[65536];
|
||||
unsigned char linear_to_ulaw[65536];
|
||||
|
||||
/* 16384 entries per table (8 bit) */
|
||||
unsigned short alaw_to_linear[256];
|
||||
unsigned short ulaw_to_linear[256];
|
||||
|
||||
static void build_linear_to_xlaw_table(unsigned char *linear_to_xlaw,
|
||||
unsigned char (*linear2xlaw)(short))
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<65536;i++){
|
||||
linear_to_xlaw[i] = linear2xlaw((short) i);
|
||||
}
|
||||
}
|
||||
|
||||
static void build_xlaw_to_linear_table(unsigned short *xlaw_to_linear,
|
||||
short (*xlaw2linear)(unsigned char))
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<256;i++){
|
||||
xlaw_to_linear[i] = (unsigned short) xlaw2linear(i);
|
||||
}
|
||||
}
|
||||
|
||||
static void pcm16_to_xlaw(unsigned char *linear_to_xlaw, int src_length, const char *src_samples, char *dst_samples)
|
||||
{
|
||||
int i;
|
||||
const unsigned short *s_samples;
|
||||
|
||||
s_samples = (const unsigned short *)src_samples;
|
||||
|
||||
for (i=0; i < src_length / 2; i++)
|
||||
{
|
||||
dst_samples[i] = linear_to_xlaw[s_samples[i]];
|
||||
}
|
||||
}
|
||||
|
||||
static void xlaw_to_pcm16(unsigned short *xlaw_to_linear, int src_length, const char *src_samples, char *dst_samples)
|
||||
{
|
||||
int i;
|
||||
unsigned char *s_samples;
|
||||
unsigned short *d_samples;
|
||||
|
||||
s_samples = (unsigned char *) src_samples;
|
||||
d_samples = (unsigned short *)dst_samples;
|
||||
|
||||
for (i=0; i < src_length; i++)
|
||||
{
|
||||
d_samples[i] = xlaw_to_linear[s_samples[i]];
|
||||
}
|
||||
}
|
||||
|
||||
void pcm16_to_alaw(int src_length, const char *src_samples, char *dst_samples)
|
||||
{
|
||||
pcm16_to_xlaw(linear_to_alaw, src_length, src_samples, dst_samples);
|
||||
}
|
||||
|
||||
void pcm16_to_ulaw(int src_length, const char *src_samples, char *dst_samples)
|
||||
{
|
||||
pcm16_to_xlaw(linear_to_ulaw, src_length, src_samples, dst_samples);
|
||||
}
|
||||
|
||||
void alaw_to_pcm16(int src_length, const char *src_samples, char *dst_samples)
|
||||
{
|
||||
xlaw_to_pcm16(alaw_to_linear, src_length, src_samples, dst_samples);
|
||||
}
|
||||
|
||||
void ulaw_to_pcm16(int src_length, const char *src_samples, char *dst_samples)
|
||||
{
|
||||
xlaw_to_pcm16(ulaw_to_linear, src_length, src_samples, dst_samples);
|
||||
}
|
||||
|
||||
void pcm16_alaw_tableinit()
|
||||
{
|
||||
build_linear_to_xlaw_table(linear_to_alaw, linear2alaw);
|
||||
}
|
||||
|
||||
void pcm16_ulaw_tableinit()
|
||||
{
|
||||
build_linear_to_xlaw_table(linear_to_ulaw, linear2ulaw);
|
||||
}
|
||||
|
||||
void alaw_pcm16_tableinit()
|
||||
{
|
||||
build_xlaw_to_linear_table(alaw_to_linear, alaw2linear);
|
||||
}
|
||||
|
||||
void ulaw_pcm16_tableinit()
|
||||
{
|
||||
build_xlaw_to_linear_table(ulaw_to_linear, ulaw2linear);
|
||||
}
|
||||
|
||||
#endif // G711_TABLE_H
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef G711_TABLE_H
|
||||
#define G711_TABLE_H
|
||||
|
||||
void pcm16_to_alaw(int length, const char *src_samples, char *dst_samples);
|
||||
void pcm16_to_ulaw(int length, const char *src_samples, char *dst_samples);
|
||||
void alaw_to_pcm16(int length, const char *src_samples, char *dst_samples);
|
||||
void ulaw_to_pcm16(int length, const char *src_samples, char *dst_samples);
|
||||
|
||||
void pcm16_alaw_tableinit();
|
||||
void pcm16_ulaw_tableinit();
|
||||
void alaw_pcm16_tableinit();
|
||||
void ulaw_pcm16_tableinit();
|
||||
|
||||
#endif // G711_TABLE_H
|
||||
@@ -1,218 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/serial.h>
|
||||
|
||||
#include <osmocom/trau/osmo_ortp.h>
|
||||
|
||||
#include "g711.h"
|
||||
|
||||
#define RTP_PT_PCMU 0
|
||||
#define RTP_PT_PCMA 8
|
||||
|
||||
struct modem_state {
|
||||
struct osmo_fd data_fd;
|
||||
struct osmo_rtp_socket *rtp;
|
||||
/* queue of linear PCM audio in RTP -> modem direction */
|
||||
struct llist_head rtp2modem;
|
||||
/* message buffer used if samples insufficient for next RTP frame were received */
|
||||
struct msgb *modem2rtp;
|
||||
};
|
||||
|
||||
static void *g_tall_ctx;
|
||||
|
||||
|
||||
/* call-back on received RTP data */
|
||||
static void ortp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *payload,
|
||||
unsigned int payload_len, uint16_t seq_nr, uint32_t timestamp, bool marker)
|
||||
{
|
||||
/* we received a RTP frame */
|
||||
struct modem_state *ms = rs->priv;
|
||||
struct msgb *msg = msgb_alloc(payload_len*2, "RTP Rx");
|
||||
unsigned int i;
|
||||
int16_t *out;
|
||||
|
||||
OSMO_ASSERT(msg);
|
||||
|
||||
out = (int16_t *) msgb_put(msg, payload_len*2);
|
||||
|
||||
if (payload_len != 160) {
|
||||
fprintf(stderr, "RTP payload length %d != 160, dropping\n", payload_len);
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* convert from Alaw to linear PCM (160 -> 320 bytes) */
|
||||
for (i = 0; i < payload_len; i++)
|
||||
out[i] = alaw2linear(payload[i]);
|
||||
|
||||
/* append to the write queue */
|
||||
msgb_enqueue(&ms->rtp2modem, msg);
|
||||
ms->data_fd.when |= OSMO_FD_WRITE;
|
||||
}
|
||||
|
||||
static void modem2rtp(struct modem_state *ms, const uint8_t *data, unsigned int len)
|
||||
{
|
||||
const int16_t *data16 = (const int16_t *)data;
|
||||
unsigned int samples = len / 2;
|
||||
unsigned int offset = 0;
|
||||
unsigned int i;
|
||||
|
||||
/* samples are always 16bit, we cannot read half a sample */
|
||||
OSMO_ASSERT((len & 1) == 0);
|
||||
|
||||
/* first complete any pending incomplete RTP frame */
|
||||
if (ms->modem2rtp) {
|
||||
struct msgb *msg = ms->modem2rtp;
|
||||
unsigned int missing_samples = 160 - msgb_length(msg);
|
||||
for (i = 0; i < missing_samples; i++) {
|
||||
if (i >= samples)
|
||||
break;
|
||||
msgb_put_u8(msg, linear2alaw(data16[i]));
|
||||
}
|
||||
offset = i;
|
||||
if (msgb_length(msg) == 160) {
|
||||
osmo_rtp_send_frame_ext(ms->rtp, msgb_data(msg), msgb_length(msg), 160, false);
|
||||
msgb_free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* then send as many RTP frames as we have samples */
|
||||
for (offset = offset; offset + 160 <= samples; offset += 160) {
|
||||
uint8_t buf[160];
|
||||
for (i = 0; i < sizeof(buf); i++)
|
||||
buf[i] = linear2alaw(data16[offset + i]);
|
||||
osmo_rtp_send_frame_ext(ms->rtp, buf, sizeof(buf), 160, false);
|
||||
}
|
||||
|
||||
/* store remainder in msgb */
|
||||
if (offset < samples) {
|
||||
struct msgb *msg = msgb_alloc_c(ms, 160, "modem2rtp");
|
||||
OSMO_ASSERT(msg);
|
||||
OSMO_ASSERT(len - offset < 160);
|
||||
for (i = 0; i < len - offset; i++)
|
||||
msgb_put_u8(msg, linear2alaw(data16[offset + i]));
|
||||
ms->modem2rtp = msg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* call back on file descriptor events of the modem DATA ttyUSB device */
|
||||
static int modem_data_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct modem_state *ms = ofd->data;
|
||||
int rc;
|
||||
|
||||
if (what & OSMO_FD_READ) {
|
||||
/* SIM5360 USB AUDIO Application Note v1.01 states 1600 bytes every 100ms */
|
||||
uint8_t rx_buf[1600];
|
||||
rc = read(ofd->fd, rx_buf, sizeof(rx_buf));
|
||||
OSMO_ASSERT(rc > 0);
|
||||
modem2rtp(ms, rx_buf, rc);
|
||||
}
|
||||
|
||||
if (what & OSMO_FD_WRITE) {
|
||||
struct msgb *msg = msgb_dequeue(&ms->rtp2modem);
|
||||
if (!msg)
|
||||
ofd->when &= ~OSMO_FD_WRITE;
|
||||
else {
|
||||
/* SIM5300 USB AUDIO Application Note v1.01 states 640 bytes every 40ms;
|
||||
* we simply write every RTP frame individually (320 bytes every 20ms) */
|
||||
rc = write(ofd->fd, msgb_data(msg), msgb_length(msg));
|
||||
if (rc != msgb_length(msg))
|
||||
fprintf(stderr, "Short write: %d < %u\n", rc, msgb_length(msg));
|
||||
msgb_free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int modem_data_open(struct modem_state *ms, const char *basepath)
|
||||
{
|
||||
char fname[PATH_MAX+1];
|
||||
int fd;
|
||||
|
||||
/* the assumption is that the caller provides something like
|
||||
* "/dev/serial/by-path/pci-0000:00:14.0-usb-0:2:1" */
|
||||
snprintf(fname, sizeof(fname), "%s.0-port0", basepath);
|
||||
|
||||
fd = osmo_serial_init(fname, 921600);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "failed to open device '%s': %s\n", fname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
osmo_fd_setup(&ms->data_fd, fd, OSMO_FD_READ, modem_data_fd_cb, ms, 0);
|
||||
osmo_fd_register(&ms->data_fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct modem_state *modem_create(void *ctx)
|
||||
{
|
||||
struct modem_state *ms = talloc_zero(ctx, struct modem_state);
|
||||
int rc;
|
||||
|
||||
INIT_LLIST_HEAD(&ms->rtp2modem);
|
||||
|
||||
ms->rtp = osmo_rtp_socket_create(ms, 0);
|
||||
OSMO_ASSERT(ms->rtp);
|
||||
osmo_rtp_socket_set_pt(ms->rtp, RTP_PT_PCMA);
|
||||
ms->rtp->priv = ms;
|
||||
ms->rtp->rx_cb = ortp_rx_cb;
|
||||
|
||||
rc = osmo_rtp_socket_bind(ms->rtp, "0.0.0.0", 1111);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
rc = osmo_rtp_socket_connect(ms->rtp, "127.0.0.1", 2222);
|
||||
//rc = osmo_rtp_socket_autoconnect(ms->rtp);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
osmo_rtp_set_source_desc(ms->rtp, "cname", "simcom2rtp", NULL, NULL, NULL,
|
||||
"osmo-simcom2rtp", NULL);
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
talloc_enable_null_tracking();
|
||||
g_tall_ctx = talloc_named_const(NULL, 1, "simcom2rtp");
|
||||
|
||||
msgb_talloc_ctx_init(g_tall_ctx, 0);
|
||||
osmo_init_logging2(g_tall_ctx, NULL);
|
||||
osmo_fsm_log_timeouts(true);
|
||||
osmo_fsm_log_addr(true);
|
||||
//osmo_stats_init(g_tall_ctx);
|
||||
osmo_rtp_init(g_tall_ctx);
|
||||
|
||||
struct modem_state *ms = modem_create(g_tall_ctx);
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(ms);
|
||||
rc = modem_data_open(ms, "/dev/serial/by-path/pci-0000:00:14.0-usb-0:2:1");
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
while (1) {
|
||||
osmo_select_main(0);
|
||||
}
|
||||
|
||||
}
|
||||
114
debian/changelog
vendored
114
debian/changelog
vendored
@@ -1,3 +1,117 @@
|
||||
osmo-mgw (1.9.0) unstable; urgency=medium
|
||||
|
||||
[ Harald Welte ]
|
||||
* TOS bits != DSCP
|
||||
* manual: don't define fig-bsc twice
|
||||
* switch from osmo_sock_set_dscp() to OSMO_SOCK_F_DSCP()
|
||||
* mgw: Add support for setting socket priority from VTY
|
||||
* manual: Include QoS chapter and add osmo-mgw specific example
|
||||
|
||||
[ Keith ]
|
||||
* Add vty command 'show mgcp active'
|
||||
* Log some useful messages at ERROR/INFO instead of DEBUG
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* send DLCX only once
|
||||
* tweak termination DLCX log msg
|
||||
* add osmo_mgcpc_ep_ci_get_remote_rtp_info()
|
||||
* check_rtp_destin(): clarify log msg
|
||||
* mgcp_client_fsm delete: set mgcp_client as ctx, not NULL
|
||||
* mgcp_client_endpoint_fsm: on term, still let conns wait for DLCX OK
|
||||
* mgcp_client_fsm: add missing log_subsys
|
||||
* mgcp_client: add logging on received MGCP messages
|
||||
|
||||
[ Philipp Maier ]
|
||||
* mgcp_network: fix implicit address loopback
|
||||
* mgcp_common, mgcp_udp_send: make parameter buf const
|
||||
* mgcp_network: refactor MGCP_DUMMY_LOAD
|
||||
* mgcp_client: drop nunnecessary else statement
|
||||
* mgcp_client: fix error handling in mgcp message generation
|
||||
* mgcp_protocol: fix loop that sends dummy RTP packets
|
||||
* mgw_main: fix loop that resets all endpoints
|
||||
* mgcp_trunk: drop ws line
|
||||
* mgcp_ratectr: drop ws line
|
||||
* mgcp_ratectr: fix sourcecode formatting
|
||||
* mgcp_trunk: add value string for trunk type.
|
||||
* mgcp_ratectr: refactor rate counter and set group name
|
||||
* mgcp_protocol: forward declare mgcp_endpoint
|
||||
* mgcp_endp: make wildcarded detection separate
|
||||
* mgcp_protocol: refactor request handler array
|
||||
* mgcp_trunk: use unsigned int instead of int as trunk_nr
|
||||
* mgcp_protocol: refactor MGCP request handling
|
||||
* mgcp_ratectr: add stats items to monitor trunk usage
|
||||
* mgcp_msg: add trunk parameter to mgcp_check_param for logging
|
||||
* mgcp_protocol: refactor function create_response_with_sdp
|
||||
* mgcp_protocol: add support for wildcarded DLCX
|
||||
* remove struct member wildcarded_req from struct mgcp_endpoint
|
||||
* mgcp_lient: remove unsubstantial FIXME note
|
||||
* mgcp_protocol: assert endp when it becomes mandatory
|
||||
* mgcp_trunk: check MGW domain name earlier
|
||||
* mgcp_ratectr: add stats items to monitor trunk usage
|
||||
* mgcp_client: fix typo Initalize -> Initialize
|
||||
* mgcp_protocol: get rid of policy_cb and change_cb
|
||||
* mgcp_ratectr: do not set talloc destructor on library allocated item
|
||||
* mgcp_client: allow to reset endpoints on startup
|
||||
* mgcp_client_vty: remove unnecessary checks
|
||||
* mgcp_client: do not print (null) when address is ANY
|
||||
* mgcp_client: refactor function init_socket
|
||||
* libosmo-mgcp-client: extend the mgcp_client for MGW pooling
|
||||
* mgcp_client_vty: add missing docstrings
|
||||
* mgcp_client_vty: add OSMO_ASSERT on pool parameter
|
||||
* mgcp_client_vty: fix docstrings for mgw-pool
|
||||
* mgcp_client_vty: cosmetic: doc string should terminated with \n
|
||||
* mgcp_client_vty: fixing docstring
|
||||
* mgcp_ratectr: remove unusued rate counters
|
||||
* mgcp_client: fix typo in doxygen comment
|
||||
* mgcp_client: add MGW name as logging context
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Use new stat item/ctr getter APIs
|
||||
* constify arg in addr_is_any()
|
||||
* Define patch_ssrc as bool type
|
||||
* constify some function arg pointers
|
||||
* mgcp_network.c: Reorder some functions in file
|
||||
* mgcp_send_dummy: Check RTP destination is available before attempt tx
|
||||
* mgw: rx CRCX: Avoid sending dummy rtp if remote address not provided
|
||||
* Fail rx MDCX sendrecv with invalid remote addr
|
||||
* Take into account Marker bit when patching RTP stream
|
||||
* Use DLMGCP instead of DLGLOBAL in log lines
|
||||
* Fix attribute parsing on gcc 11.1.0
|
||||
|
||||
[ neels ]
|
||||
* Revert "mgcp_ratectr: add stats items to monitor trunk usage"
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* contrib/jenkins: Use ASAN for osmo-mgw
|
||||
|
||||
[ Oliver Smith ]
|
||||
* mgcp_client_vty: add missing NO_STR
|
||||
* Revert "Turn some compiler warnings into errors"
|
||||
|
||||
[ Eric ]
|
||||
* add vscode stuff to gitignore
|
||||
* configure.ac: fix maybe-uninitialized for clang
|
||||
* fix missing includes and forward declarations
|
||||
* rename do_retransmission
|
||||
* mgcp_sdp: fix potential leak
|
||||
* stats: make sanitizers happy
|
||||
* libosmo-mgcp: cleanup audio codex alloc
|
||||
* libosmo-mgcp: atomic rate counter group indexes
|
||||
* libosmo-mgcp: do not use the default msgb talloc context
|
||||
* globally lock the portrange when trying to grab a port to prep for multithreading
|
||||
* embed strings into structs
|
||||
* adjust talloc context
|
||||
* endp: do not cache cfg pointer
|
||||
* add modified .clang-format
|
||||
* rename strip_epname and find_specific_endpoint and make them available
|
||||
* fix mgcp_conn_free_all
|
||||
* fix up some docstrings that lost their dot
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* libosmo-mgcp: use OSMO_STRLCPY_ARRAY in mgcp_codec_add()
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 16:59:21 +0100
|
||||
|
||||
osmo-mgw (1.8.1) unstable; urgency=medium
|
||||
|
||||
* attempt to fix RPM spec file after recent soversion bump
|
||||
|
||||
10
debian/control
vendored
10
debian/control
vendored
@@ -6,10 +6,10 @@ Build-Depends: debhelper (>=9),
|
||||
dh-autoreconf,
|
||||
pkg-config,
|
||||
autotools-dev,
|
||||
libosmocore-dev (>= 1.5.0),
|
||||
libosmocore-dev (>= 1.6.0),
|
||||
libosmo-netif-dev (>= 1.1.0),
|
||||
libosmo-abis-dev (>= 1.1.0),
|
||||
osmo-gsm-manuals-dev (>= 1.1.0)
|
||||
libosmo-abis-dev (>= 1.2.0),
|
||||
osmo-gsm-manuals-dev (>= 1.2.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
|
||||
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
|
||||
@@ -21,7 +21,7 @@ Multi-Arch: foreign
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
|
||||
Package: libosmo-mgcp-client8
|
||||
Package: libosmo-mgcp-client9
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
@@ -33,7 +33,7 @@ Package: libosmo-mgcp-client-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-mgcp-client8 (= ${binary:Version}), ${misc:Depends}
|
||||
Depends: libosmo-mgcp-client9 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
|
||||
|
||||
Package: osmo-mgw-doc
|
||||
|
||||
@@ -91,6 +91,11 @@ the IP related aspects is nearly identical to the configuration of the virtual
|
||||
trunk. However, it is important that the user assigns one of the E1 line numbers
|
||||
that were configured under the e1_input node.
|
||||
|
||||
NOTE: The endpoint name that is used on MGCP level will include the trunk number,
|
||||
not the E1 line number. For simplicity (and compatibility with OsmoBSC) it is
|
||||
recommended to use equal numbers for trunk and E1 line. However, if required any
|
||||
E1 line can be mapped flexible on any trunk as long as the mapping is bijective.
|
||||
|
||||
.Example: A typical configuration with one E1 trunk
|
||||
----
|
||||
e1_input
|
||||
|
||||
@@ -107,8 +107,8 @@ We are planning to add further endpoint types for:
|
||||
|
||||
You can find the OsmoMGW issue tracker and wiki online at
|
||||
|
||||
- https://osmocom.org/projects/osmomgw
|
||||
- https://osmocom.org/projects/osmomgw/wiki
|
||||
- https://osmocom.org/projects/osmo-mgw
|
||||
- https://osmocom.org/projects/osmo-mgw/wiki
|
||||
|
||||
RFC 3435 for MGCP is located at
|
||||
|
||||
|
||||
@@ -13,4 +13,5 @@ noinst_HEADERS = \
|
||||
mgcp_e1.h \
|
||||
mgcp_network.h \
|
||||
mgcp_protocol.h \
|
||||
mgcp_iuup.h \
|
||||
$(NULL)
|
||||
|
||||
@@ -17,3 +17,4 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
|
||||
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
|
||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
|
||||
const char *subtype_name, unsigned int match_nr);
|
||||
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/gsm/iuup.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LOGPCONN(conn, cat, level, fmt, args...) \
|
||||
@@ -47,6 +48,7 @@ enum mgcp_conn_rtp_type {
|
||||
MGCP_RTP_DEFAULT = 0,
|
||||
MGCP_OSMUX_BSC,
|
||||
MGCP_OSMUX_BSC_NAT,
|
||||
MGCP_RTP_IUUP,
|
||||
};
|
||||
|
||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||
@@ -93,6 +95,14 @@ struct mgcp_conn_rtp {
|
||||
} stats;
|
||||
} osmux;
|
||||
|
||||
struct {
|
||||
struct osmo_iuup_instance *iui;
|
||||
bool active_init; /* true: Send IuUP Init */
|
||||
int rfci_id_no_data; /* RFCI Id for RFCI NO_DATA (-1 if not available) */
|
||||
bool configured;
|
||||
struct osmo_iuup_rnl_prim *init_ind;
|
||||
} iuup;
|
||||
|
||||
struct rate_ctr_group *rate_ctr_group;
|
||||
};
|
||||
|
||||
@@ -176,6 +186,12 @@ static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
|
||||
return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
|
||||
}
|
||||
|
||||
/* Was conn configured to handle Osmux? */
|
||||
static inline bool mgcp_conn_rtp_is_iuup(const struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
return conn->type == MGCP_RTP_IUUP;
|
||||
}
|
||||
|
||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
enum mgcp_conn_type type, char *name);
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
|
||||
|
||||
33
include/osmocom/mgcp/mgcp_iuup.h
Normal file
33
include/osmocom/mgcp/mgcp_iuup.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* IuUP connection functionalitites */
|
||||
|
||||
/*
|
||||
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Pau Espin Pedrol
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
struct mgcp_conn_rtp;
|
||||
|
||||
int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp);
|
||||
void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp);
|
||||
int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg);
|
||||
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg);
|
||||
int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp);
|
||||
@@ -70,8 +70,6 @@ struct mgcp_rtp_state {
|
||||
* data is just re-used) */
|
||||
uint16_t alt_rtp_tx_sequence;
|
||||
uint32_t alt_rtp_tx_ssrc;
|
||||
|
||||
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
|
||||
};
|
||||
|
||||
struct mgcp_rtp_codec {
|
||||
@@ -110,8 +108,8 @@ struct mgcp_rtp_end {
|
||||
uint32_t packet_duration_ms;
|
||||
int maximum_packet_time; /* -1: not set */
|
||||
char *fmtp_extra;
|
||||
/* are we transmitting packets (1) or dropping (0) outbound packets */
|
||||
int output_enabled;
|
||||
/* are we transmitting packets (true) or dropping (false) outbound packets */
|
||||
bool output_enabled;
|
||||
/* FIXME: This parameter can be set + printed, but is nowhere used! */
|
||||
int force_output_ptime;
|
||||
|
||||
@@ -177,3 +175,8 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
|
||||
const uint16_t seq, const int32_t transit,
|
||||
const uint32_t ssrc, const bool marker_bit);
|
||||
|
||||
void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
|
||||
int id, int inc);
|
||||
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
|
||||
uint32_t mgcp_get_current_ts(unsigned codec_rate);
|
||||
|
||||
52
include/osmocom/mgcp/mgcp_threads_queue.h
Normal file
52
include/osmocom/mgcp/mgcp_threads_queue.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Eric Wild
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct spsc {
|
||||
atomic_uint readptr;
|
||||
atomic_uint writeptr;
|
||||
|
||||
int efd_r, efd_w; /* eventfds used to block/notify readers/writers */
|
||||
|
||||
int count;
|
||||
int size_per_buf;
|
||||
|
||||
void *buf; /* buffer size count*size_per_buf */
|
||||
uintptr_t data[0]; /* count sized array of pointers to size_per_buf chunks in buf array*/
|
||||
};
|
||||
|
||||
struct qchan {
|
||||
struct spsc *a;
|
||||
struct spsc *b;
|
||||
};
|
||||
|
||||
bool spsc_push(struct spsc *q, void *elem);
|
||||
bool spsc_pop(struct spsc *q, void *elem);
|
||||
ssize_t spsc_prep_pop(struct spsc *q);
|
||||
int spsc_get_a_rdfd(struct qchan *q);
|
||||
|
||||
struct qchan spsc_chan_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf);
|
||||
struct qchan spsc_chan_init_ex(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr_a,
|
||||
bool blockw_a, bool blockr_b, bool blockw_b);
|
||||
void spsc_chan_close(struct qchan *q);
|
||||
@@ -49,6 +49,7 @@ enum mgcp_codecs {
|
||||
CODEC_GSMHR_8000_1 = 111,
|
||||
CODEC_AMR_8000_1 = 112,
|
||||
CODEC_AMRWB_16000_1 = 113,
|
||||
CODEC_IUFP = 96,
|
||||
};
|
||||
/* Note: when new codec types are added, the corresponding value strings
|
||||
* in mgcp_client.c (codec_table) must be updated as well. Enumerations
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check whether the given FLAG works with the current language's compiler
|
||||
# or gives an error. (Warnings, however, are ignored)
|
||||
#
|
||||
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
|
||||
# success/failure.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the current language's default
|
||||
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
|
||||
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
|
||||
# force the compiler to issue an error when a bad flag is given.
|
||||
#
|
||||
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
|
||||
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 4
|
||||
|
||||
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
|
||||
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
|
||||
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
|
||||
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
|
||||
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
|
||||
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
|
||||
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
|
||||
[AS_VAR_SET(CACHEVAR,[yes])],
|
||||
[AS_VAR_SET(CACHEVAR,[no])])
|
||||
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
|
||||
AS_VAR_IF(CACHEVAR,yes,
|
||||
[m4_default([$2], :)],
|
||||
[m4_default([$3], :)])
|
||||
AS_VAR_POPDEF([CACHEVAR])dnl
|
||||
])dnl AX_CHECK_COMPILE_FLAGS
|
||||
@@ -21,7 +21,7 @@ AM_LDFLAGS = \
|
||||
|
||||
# This is not at all related to the release version, but a range of supported
|
||||
# API versions. Read TODO_RELEASE in the source tree's root!
|
||||
MGCP_CLIENT_LIBVERSION=8:0:0
|
||||
MGCP_CLIENT_LIBVERSION=9:0:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp-client.la \
|
||||
|
||||
@@ -59,6 +59,7 @@ const struct value_string osmo_mgcpc_codec_names[] = {
|
||||
{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
|
||||
{ CODEC_AMR_8000_1, "AMR/8000/1" },
|
||||
{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
|
||||
{ CODEC_IUFP, "VND.3GPP.IUFP/16000" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
|
||||
@@ -48,4 +48,5 @@ libosmo_mgcp_a_SOURCES = \
|
||||
mgcp_ctrl.c \
|
||||
mgcp_ratectr.c \
|
||||
mgcp_e1.c \
|
||||
mgcp_iuup.c \
|
||||
$(NULL)
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
static inline int val_seg(int val)
|
||||
|
||||
@@ -355,7 +355,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
|
||||
*
|
||||
* https://tools.ietf.org/html/rfc4867
|
||||
*/
|
||||
static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
|
||||
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
if (!codec->param_present)
|
||||
return false;
|
||||
@@ -378,10 +378,10 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c
|
||||
return false;
|
||||
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
|
||||
return false;
|
||||
if (!strcmp(codec_a->subtype_name, "AMR")) {
|
||||
if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Note: AMR allows to set the RTP payload format to octet-aligned or bandwith-efficient (octet-aligned=0)
|
||||
* via SDP. This difference concerns payload format only, but not the actual codec. It is not a difference
|
||||
* within the meaning of this function. */
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -417,7 +417,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
|
||||
if (!codec_src)
|
||||
return -EINVAL;
|
||||
|
||||
/* Use the codec infrmation from the source and try to find the
|
||||
/* Use the codec information from the source and try to find the
|
||||
* equivalent of it on the destination side */
|
||||
codecs_assigned = rtp_dst->codecs_assigned;
|
||||
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_iuup.h>
|
||||
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
@@ -108,7 +110,7 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
|
||||
/* Set default values */
|
||||
end->frames_per_packet = 0; /* unknown */
|
||||
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
|
||||
end->output_enabled = 0;
|
||||
end->output_enabled = false;
|
||||
end->maximum_packet_time = -1;
|
||||
|
||||
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
|
||||
@@ -129,6 +131,8 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
if (mgcp_conn_rtp_is_osmux(conn_rtp))
|
||||
conn_osmux_disable(conn_rtp);
|
||||
if (mgcp_conn_rtp_is_iuup(conn_rtp))
|
||||
mgcp_conn_iuup_cleanup(conn_rtp);
|
||||
mgcp_free_rtp_port(&conn_rtp->end);
|
||||
rate_ctr_group_free(conn_rtp->rate_ctr_group);
|
||||
mgcp_codec_reset_all(conn_rtp);
|
||||
@@ -325,6 +329,11 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
|
||||
|
||||
/*! free all connections at once.
|
||||
* \param[in] endp associated endpoint */
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(no_sanitize)
|
||||
__attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */
|
||||
#endif
|
||||
#endif
|
||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
@@ -338,6 +338,7 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
|
||||
trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
|
||||
if (!trunk) {
|
||||
LOGP(DE1, LOGL_ERROR, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -359,6 +360,9 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
|
||||
|
||||
/* Trigger sending of pending E1 traffic */
|
||||
e1_send(ts, trunk);
|
||||
|
||||
/* e1inp_rx_ts() does not free() msgb */
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
|
||||
|
||||
@@ -224,9 +224,9 @@ static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
|
||||
}
|
||||
|
||||
/*! Find an endpoint of a trunk specified by its name.
|
||||
* \param[in] epname endpoint name to check
|
||||
* \param[in] trunk mgcp_trunk that might have this endpoint
|
||||
* \returns NULL if no ep found, else endpoint */
|
||||
* \param[in] epname endpoint name to check.
|
||||
* \param[in] trunk mgcp_trunk that might have this endpoint.
|
||||
* \returns NULL if no ep found, else endpoint. */
|
||||
struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
|
||||
const struct mgcp_trunk *trunk)
|
||||
{
|
||||
|
||||
745
src/libosmo-mgcp/mgcp_iuup.c
Normal file
745
src/libosmo-mgcp/mgcp_iuup.c
Normal file
@@ -0,0 +1,745 @@
|
||||
/*
|
||||
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All rights not specifically granted under this license are reserved.
|
||||
*
|
||||
* Author: Pau Espin Pedrol
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/byteswap.h>
|
||||
|
||||
#include <osmocom/gsm/iuup.h>
|
||||
|
||||
#include <osmocom/netif/rtp.h>
|
||||
#include <osmocom/netif/amr.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_iuup.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_network.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
|
||||
#define MGW_IUUP_MSGB_SIZE 4096
|
||||
|
||||
static const struct osmo_iuup_rnl_config def_configure_req = {
|
||||
.transparent = false,
|
||||
.active = true,
|
||||
.supported_versions_mask = 0x0003,
|
||||
.num_rfci = 0,
|
||||
.num_subflows = 0,
|
||||
.IPTIs_present = false,
|
||||
.t_init = { .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT },
|
||||
.t_ta = { .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT },
|
||||
.t_rc = { .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT },
|
||||
};
|
||||
|
||||
/* Find a destination connection. */
|
||||
static struct mgcp_conn *_find_dst_conn(struct mgcp_conn *conn)
|
||||
{
|
||||
/* NOTE: This code path runs every time an RTP packet is received. The
|
||||
* function mgcp_find_dst_conn() we use to determine the detination
|
||||
* connection will iterate the connection list inside the endpoint.
|
||||
* Since list iterations are quite costly, we will figure out the
|
||||
* destination only once and use the optional private data pointer of
|
||||
* the connection to cache the destination connection pointer. */
|
||||
|
||||
struct mgcp_conn *conn_dst;
|
||||
if (!conn->priv) {
|
||||
conn_dst = mgcp_find_dst_conn(conn);
|
||||
conn->priv = conn_dst;
|
||||
} else {
|
||||
conn_dst = (struct mgcp_conn *)conn->priv;
|
||||
}
|
||||
return conn_dst;
|
||||
}
|
||||
|
||||
/* Find RFCI containing all 0 sizes, -1 if not found. irp is an Initialization.ind prim */
|
||||
static int _find_rfci_no_data(struct osmo_iuup_rnl_prim *irp)
|
||||
{
|
||||
int i;
|
||||
uint8_t rfci_cnt = 0;
|
||||
/* Find RFCI containing NO_DATA: */
|
||||
for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
|
||||
struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i];
|
||||
int j;
|
||||
bool is_no_data;
|
||||
if (!rfci->used)
|
||||
continue;
|
||||
rfci_cnt++;
|
||||
|
||||
is_no_data = true;
|
||||
for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++) {
|
||||
if (rfci->subflow_sizes[j]) {
|
||||
is_no_data = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_no_data)
|
||||
return rfci->id;
|
||||
|
||||
/* early loop termination: */
|
||||
if (rfci_cnt == irp->u.status.u.initialization.num_subflows)
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Lookup RFCI to use for specific AMR codec type. -1 if none found */
|
||||
static int8_t _conn_iuup_amr_ft_2_rfci(struct mgcp_conn_rtp *conn_rtp, uint8_t ft)
|
||||
{
|
||||
int8_t i;
|
||||
uint8_t rfci_cnt = 0;
|
||||
unsigned match_bytes = (unsigned)osmo_amr_bytes(ft);
|
||||
struct osmo_iuup_rnl_prim *irp = conn_rtp->iuup.init_ind;
|
||||
OSMO_ASSERT(irp);
|
||||
|
||||
/* TODO: cache this somehow */
|
||||
for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
|
||||
struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i];
|
||||
int j;
|
||||
unsigned num_bits;
|
||||
if (!rfci->used)
|
||||
continue;
|
||||
rfci_cnt++;
|
||||
|
||||
num_bits = 0;
|
||||
for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++)
|
||||
num_bits += rfci->subflow_sizes[j];
|
||||
if (match_bytes == (num_bits + 7)/8)
|
||||
return rfci->id;
|
||||
|
||||
/* early loop termination: */
|
||||
if (rfci_cnt == irp->u.status.u.initialization.num_subflows)
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Helper function to configure IuUP layer FSM as Init-Passive, based on default config */
|
||||
static int _conn_iuup_configure_as_passive(struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
struct osmo_iuup_rnl_prim *irp;
|
||||
int rc;
|
||||
|
||||
conn_rtp->iuup.active_init = false;
|
||||
|
||||
/* Tx CONFIG.req */
|
||||
irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
|
||||
irp->u.config = def_configure_req;
|
||||
irp->u.config.active = conn_rtp->iuup.active_init;
|
||||
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) == 0)
|
||||
conn_rtp->iuup.configured = true;
|
||||
else
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Helper function to configure IuUP layer FSM as Init-Active, based on received
|
||||
* RNL Status-Init primitive from the sister IuUP connection we will bridge to. */
|
||||
static int _conn_iuup_configure_as_active(struct mgcp_conn_rtp *conn_rtp, struct osmo_iuup_rnl_prim *init_ind)
|
||||
{
|
||||
struct osmo_iuup_rnl_prim *irp = init_ind;
|
||||
struct osmo_iuup_rnl_prim *irp2;
|
||||
struct msgb *msg;
|
||||
bool prev_output_enabled;
|
||||
int rc;
|
||||
|
||||
conn_rtp->iuup.active_init = true;
|
||||
|
||||
/* Find RFCI containing NO_DATA: */
|
||||
conn_rtp->iuup.rfci_id_no_data = _find_rfci_no_data(init_ind);
|
||||
|
||||
/* Copy over the rfci_id_no_data, since we reuse the same subflow set: */
|
||||
msg = msgb_copy_c(conn_rtp->conn, irp->oph.msg, "iuup-init-copy");
|
||||
conn_rtp->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg);
|
||||
conn_rtp->iuup.init_ind->oph.msg = msg;
|
||||
|
||||
/* Tx CONFIG.req */
|
||||
irp2 = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
|
||||
irp2->u.config.transparent = false;
|
||||
irp2->u.config.active = conn_rtp->iuup.active_init;
|
||||
irp2->u.config.data_pdu_type = irp->u.status.u.initialization.data_pdu_type;
|
||||
irp2->u.config.supported_versions_mask = def_configure_req.supported_versions_mask;
|
||||
irp2->u.config.num_rfci = irp->u.status.u.initialization.num_rfci;
|
||||
irp2->u.config.num_subflows = irp->u.status.u.initialization.num_subflows;
|
||||
irp2->u.config.IPTIs_present = irp->u.status.u.initialization.IPTIs_present;
|
||||
memcpy(irp2->u.config.rfci, irp->u.status.u.initialization.rfci, sizeof(irp2->u.config.rfci));
|
||||
irp2->u.config.t_init = def_configure_req.t_init;
|
||||
irp2->u.config.t_ta = def_configure_req.t_ta;
|
||||
irp2->u.config.t_rc = def_configure_req.t_rc;
|
||||
|
||||
/* We need to force allowance of RTP containing Init-ACK back: */
|
||||
prev_output_enabled = conn_rtp->end.output_enabled;
|
||||
conn_rtp->end.output_enabled = true;
|
||||
|
||||
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp2)) == 0)
|
||||
conn_rtp->iuup.configured = true;
|
||||
else
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n");
|
||||
|
||||
conn_rtp->end.output_enabled = prev_output_enabled;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Helper function to push an RTP+IuUP pkt up to the IuUP layer FSM through the
|
||||
* TNL primitive interface. */
|
||||
static int _conn_iuup_rtp_pl_up(struct mgcp_conn_rtp *conn_rtp, struct msgb *msg)
|
||||
{
|
||||
/* Send RTP payload (IuUP) up the stack: */
|
||||
struct osmo_iuup_tnl_prim *itp;
|
||||
int rc;
|
||||
|
||||
msg->l2h = msgb_data(msg) + sizeof(struct rtp_hdr);
|
||||
|
||||
itp = osmo_iuup_tnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, MGW_IUUP_MSGB_SIZE);
|
||||
itp->oph.msg->l2h = msgb_put(itp->oph.msg, msgb_l2len(msg));
|
||||
memcpy(itp->oph.msg->l2h, msgb_l2(msg), msgb_l2len(msg));
|
||||
if ((rc = osmo_iuup_tnl_prim_up(conn_rtp->iuup.iui, itp)) != 0) {
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed passing IuUP-Init to IuUP layer\n");
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int check_rtp_iuup(const struct mgcp_conn_rtp *conn_rtp, struct msgb *msg)
|
||||
{
|
||||
size_t min_size = sizeof(struct rtp_hdr);
|
||||
/* Check there's at least 2 bytes of RTP payload (IuUP header). This is
|
||||
** mainly to avoid 0-byte payload copy cases */
|
||||
if (msgb_length(msg) < sizeof(struct rtp_hdr) + 2) {
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "RTP-IuUP packet too short (%u < %zu)\n",
|
||||
msgb_length(msg), min_size);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an IuUP sister
|
||||
* conn in the endpoint. The function takes ownsership of the irp */
|
||||
static int bridge_iuup_to_iuup_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* If we are not configured and we received bridged data, it means
|
||||
* conn_rtp_src is already configured and INITed, and we can infer
|
||||
* conn_rtp_src is Init-passive (RNC side), so conn_rtp_dst needs to be
|
||||
* configured as INIT-active: */
|
||||
if (!conn_rtp_dst->iuup.configured) {
|
||||
OSMO_ASSERT(conn_rtp_src->iuup.init_ind);
|
||||
rc = _conn_iuup_configure_as_active(conn_rtp_dst, conn_rtp_src->iuup.init_ind);
|
||||
if (rc < 0) {
|
||||
msgb_free(irp->oph.msg);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* We simply forward the msg, without freeing it: */
|
||||
talloc_steal(conn_rtp_dst->conn, irp->oph.msg);
|
||||
irp->oph.operation = PRIM_OP_REQUEST;
|
||||
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp_dst->iuup.iui, irp)) != 0)
|
||||
LOG_CONN_RTP(conn_rtp_dst, LOGL_ERROR, "Failed Tx data down to IuUP layer\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an RTP (no IuUP)
|
||||
* sister conn in the endpoint. The function takes ownsership of the irp */
|
||||
static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp)
|
||||
{
|
||||
/* FIXME: We probably need transcoding here?! Or at least look up AMR modes and translate to related RFCI */
|
||||
uint8_t frame_nr = irp->u.data.frame_nr;
|
||||
uint8_t fqc = irp->u.data.fqc;
|
||||
struct msgb *msg = irp->oph.msg;
|
||||
ssize_t amr_length = 0;
|
||||
int ft;
|
||||
uint8_t *amr_data;
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
struct amr_hdr *amr_hdr;
|
||||
int rc;
|
||||
|
||||
ft = osmo_amr_bytes_to_ft(msgb_l3len(msg));
|
||||
if (ft < 0) {
|
||||
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
|
||||
"Unknown AMR format for size %u\n", msgb_l3len(msg));
|
||||
msgb_free(msg);
|
||||
return ft;
|
||||
}
|
||||
msgb_pull_to_l3(msg);
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR: ft %d, len %d\n", ft, msgb_l3len(msg));
|
||||
|
||||
if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.codec)) {
|
||||
amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr));
|
||||
amr_hdr->cmr = 15; /* no change */
|
||||
amr_hdr->f = 0;
|
||||
amr_hdr->q = !fqc;
|
||||
amr_hdr->ft = ft & 0xff;
|
||||
amr_hdr->pad1 = 0;
|
||||
amr_hdr->pad2 = 0;
|
||||
} else {
|
||||
OSMO_ASSERT(msgb_tailroom(msg) >= 2);
|
||||
msgb_put(msg, 2);
|
||||
osmo_amr_iuup_to_bwe(msgb_data(msg), msgb_length(msg) - 2, msgb_length(msg) + 2);
|
||||
/* fill bwe header */
|
||||
amr_data = msgb_data(msg);
|
||||
/* CMR no change | follow bit | ft (3 of 4 bits) */
|
||||
amr_data[0] = 15 << 4 | (0 << 3) | (ft >> 1);
|
||||
amr_data[1] |= ((ft & 0x1) << 7) | (((!fqc) & 0x1) << 6);
|
||||
amr_length = (osmo_amr_bits(ft) + 10 + 7) / 8;
|
||||
msgb_trim(msg, amr_length);
|
||||
}
|
||||
rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(*rtp_hdr));
|
||||
*rtp_hdr = (struct rtp_hdr){
|
||||
.csrc_count = 0,
|
||||
.extension = 0,
|
||||
.padding = 0,
|
||||
.version = 0,
|
||||
.payload_type = conn_rtp_dst->end.codec->payload_type,
|
||||
.marker = 0,
|
||||
.sequence = frame_nr,
|
||||
.timestamp = 0,
|
||||
.ssrc = 0
|
||||
};
|
||||
|
||||
rc = mgcp_send(conn_rtp_dst->conn->endp, true, NULL, msg, conn_rtp_src, conn_rtp_dst);
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Handle RNL Data primitive received from the IuUP layer FSM: Bridge it to the
|
||||
* sister connection in the endpoint: */
|
||||
static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
|
||||
{
|
||||
struct mgcp_conn *conn_dst;
|
||||
struct mgcp_conn_rtp *conn_rtp_dst;
|
||||
int rc;
|
||||
|
||||
conn_dst = _find_dst_conn(conn_rtp_src->conn);
|
||||
|
||||
/* There is no destination conn, stop here */
|
||||
if (!conn_dst) {
|
||||
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_DEBUG,
|
||||
"no connection to forward an incoming IuUP payload to\n");
|
||||
rc = -1;
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
/* The destination conn is not an RTP/IuUP connection */
|
||||
if (conn_dst->type != MGCP_CONN_TYPE_RTP) {
|
||||
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
|
||||
"unable to find suitable destination conn\n");
|
||||
rc = -1;
|
||||
goto free_ret;
|
||||
}
|
||||
conn_rtp_dst = &conn_dst->u.rtp;
|
||||
|
||||
switch (conn_rtp_dst->type) {
|
||||
case MGCP_RTP_IUUP:
|
||||
return bridge_iuup_to_iuup_peer(conn_rtp_src, conn_rtp_dst, irp);
|
||||
case MGCP_RTP_DEFAULT:
|
||||
return bridge_iuup_to_rtp_peer(conn_rtp_src, conn_rtp_dst, irp);
|
||||
case MGCP_OSMUX_BSC:
|
||||
case MGCP_OSMUX_BSC_NAT:
|
||||
default:
|
||||
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
|
||||
"Forward of IuUP payload to RTP connection type %u not supported!\n",
|
||||
conn_rtp_dst->type);
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
free_ret:
|
||||
msgb_free(irp->oph.msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Handle RNL Status-Init primitive received from the IuUP layer FSM.
|
||||
* Potentially configure sister conn as IuUP Init-Active: */
|
||||
static int _conn_iuup_rx_rnl_status_init(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
|
||||
{
|
||||
struct mgcp_conn *conn_dst;
|
||||
struct mgcp_conn_rtp *conn_rtp_dst;
|
||||
int rc = 0;
|
||||
struct msgb *msg;
|
||||
|
||||
if (conn_rtp_src->iuup.init_ind) {
|
||||
/* We received more than one IuUP Initialization. It's probably
|
||||
* a retransmission, so simply ignore it (lower layers take care
|
||||
* of ACKing it). */
|
||||
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_INFO,
|
||||
"Ignoring potential IuUP Initialization retrans\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
msg = msgb_copy_c(conn_rtp_src->conn, irp->oph.msg, "iuup-init-copy");
|
||||
conn_rtp_src->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg);
|
||||
conn_rtp_src->iuup.init_ind->oph.msg = msg;
|
||||
|
||||
/* Find RFCI containing NO_DATA: */
|
||||
conn_rtp_src->iuup.rfci_id_no_data = _find_rfci_no_data(irp);
|
||||
|
||||
conn_dst = _find_dst_conn(conn_rtp_src->conn);
|
||||
/* If not yet there, peer will potentially be IuUP-Initialized later
|
||||
* when we attempt to bridge audio towards it. See bridge_iuup_to_iuup_peer() */
|
||||
if (!conn_dst)
|
||||
return 0;
|
||||
conn_rtp_dst = &conn_dst->u.rtp;
|
||||
if (!mgcp_conn_rtp_is_iuup(conn_rtp_dst))
|
||||
return 0; /* Nothing to do */
|
||||
|
||||
/* We received IuUP parameters on the peer (RNC), Init actively this conn (against CN): */
|
||||
if (!conn_rtp_dst->iuup.configured)
|
||||
rc = _conn_iuup_configure_as_active(conn_rtp_dst, irp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Handle RNL Status primitives received from the IuUP layer FSM: */
|
||||
static int _conn_iuup_rx_rnl_status(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (irp->u.status.procedure) {
|
||||
case IUUP_PROC_INIT:
|
||||
rc = _conn_iuup_rx_rnl_status_init(conn_rtp_src, irp);
|
||||
break;
|
||||
case IUUP_PROC_RATE_CTRL:
|
||||
case IUUP_PROC_TIME_ALIGN:
|
||||
case IUUP_PROC_ERR_EVENT:
|
||||
default:
|
||||
LOG_CONN_RTP(conn_rtp_src, LOGL_ERROR,
|
||||
"Received IuUP RNL STATUS procedure type %u not handled\n",
|
||||
irp->u.status.procedure);
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Received RNL primitive from the IuUP layer FSM containing IuUP Status or
|
||||
* data. Continue pushing it up the stack, either IuUP Status or Data: */
|
||||
static int _conn_iuup_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
||||
{
|
||||
struct mgcp_conn_rtp *conn_rtp_src = ctx;
|
||||
struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
|
||||
struct msgb *msg = oph->msg;
|
||||
int rc;
|
||||
|
||||
switch (OSMO_PRIM_HDR(&irp->oph)) {
|
||||
case OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION):
|
||||
/* we pass ownsership of msg here: */
|
||||
rc = _conn_iuup_rx_rnl_data(conn_rtp_src, irp);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION):
|
||||
rc = _conn_iuup_rx_rnl_status(conn_rtp_src, irp);
|
||||
msgb_free(msg);
|
||||
break;
|
||||
default:
|
||||
msgb_free(msg);
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Send |RTP+IuUP| data down the stack of the specified destination connection.
|
||||
* \param[in] endp associated endpoint (for configuration, logging).
|
||||
* \param[in] buf buffer that contains the |RTP+IuUP| data.
|
||||
* \param[in] len length of the buffer that contains the |RTP+IuUP| data.
|
||||
* \param[in] conn_src associated source connection.
|
||||
* \param[in] conn_dst associated destination connection.
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
|
||||
struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
|
||||
{
|
||||
/*! When no destination connection is available (e.g. when only one
|
||||
* connection in loopback mode exists), then the source connection
|
||||
* shall be specified as destination connection */
|
||||
|
||||
struct mgcp_rtp_end *rtp_end;
|
||||
struct mgcp_rtp_state *rtp_state;
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg);
|
||||
int buflen = msgb_length(msg);
|
||||
char *dest_name;
|
||||
int len;
|
||||
|
||||
OSMO_ASSERT(conn_src);
|
||||
OSMO_ASSERT(conn_dst);
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering IuUP packet...\n");
|
||||
|
||||
/* Note: In case of loopback configuration, both, the source and the
|
||||
* destination will point to the same connection. */
|
||||
rtp_end = &conn_dst->end;
|
||||
rtp_state = &conn_src->state;
|
||||
dest_name = conn_dst->conn->name;
|
||||
|
||||
/* Ensure we have an alternative SSRC in case we need it, see also
|
||||
* gen_rtp_header() */
|
||||
if (rtp_state->alt_rtp_tx_ssrc == 0)
|
||||
rtp_state->alt_rtp_tx_ssrc = rand();
|
||||
|
||||
if (!rtp_end->output_enabled) {
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_DROPPED_PACKETS_CTR, 1);
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"output disabled, drop to %s %s "
|
||||
"rtp_port:%u rtcp_port:%u\n",
|
||||
dest_name,
|
||||
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
|
||||
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Specs say, in IuUP, the RTP seqnum and timestamp should actually be
|
||||
* ignored by the receiver, but still it's useful for debug purposes
|
||||
* to set it. Moreover, it seems ip.access nano3g produces much worse
|
||||
* audio output on the air side if timestamp is not set properly. */
|
||||
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
|
||||
hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence);
|
||||
hdr->ssrc = rtp_state->alt_rtp_tx_ssrc;
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"process/send IuUP to %s %s rtp_port:%u rtcp_port:%u\n",
|
||||
dest_name, osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
|
||||
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port));
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
|
||||
msg);
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
|
||||
(char *)hdr, buflen);
|
||||
|
||||
if (len <= 0)
|
||||
return len;
|
||||
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_PACKETS_TX_CTR, 1);
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Received TNL primitive from IuUP layer FSM, transmit it further down to the
|
||||
* socket towards destination peer. */
|
||||
static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
||||
{
|
||||
struct mgcp_conn_rtp *conn_rtp_dst = ctx;
|
||||
struct mgcp_conn *conn_dst = conn_rtp_dst->conn;
|
||||
struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
|
||||
struct mgcp_conn *conn_src;
|
||||
struct msgb *msg;
|
||||
struct rtp_hdr *rtph;
|
||||
|
||||
OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
|
||||
|
||||
msg = oph->msg;
|
||||
talloc_steal(conn_rtp_dst->conn, msg);
|
||||
|
||||
msgb_pull_to_l2(msg);
|
||||
rtph = (struct rtp_hdr *)msgb_push(msg, sizeof(*rtph));
|
||||
/* TODO: fill rtph properly: */
|
||||
*rtph = (struct rtp_hdr){
|
||||
.csrc_count = 0,
|
||||
.extension = 0,
|
||||
.padding = 0,
|
||||
.version = 2,
|
||||
.payload_type = conn_rtp_dst->end.codec->payload_type,
|
||||
.marker = 0,
|
||||
.sequence = 0,
|
||||
.timestamp = 0,
|
||||
.ssrc = 0
|
||||
};
|
||||
|
||||
/* The destination of the destination conn is the source conn, right? */
|
||||
conn_src = _find_dst_conn(conn_dst);
|
||||
if (!conn_src) {
|
||||
LOG_CONN_RTP(conn_rtp_dst, LOGL_NOTICE,
|
||||
"Couldn't find source conn for IuUP dst conn\n");
|
||||
/* If there's no sister connection we are either still
|
||||
* initializing (so we want to send back Init (ACK)), or we are
|
||||
* probably in loopback mode anyway, so use dst as src. */
|
||||
conn_src = conn_dst;
|
||||
}
|
||||
|
||||
return mgcp_send_iuup(conn_dst->endp, msg, &conn_src->u.rtp, conn_rtp_dst);
|
||||
}
|
||||
|
||||
/* Used to upgrade a regular RTP connection (MGCP_RTP_DEFAULT) to become a IuUP
|
||||
* connection (MGCP_RTP_IUUP) */
|
||||
int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
conn_rtp->type = MGCP_RTP_IUUP;
|
||||
conn_rtp->iuup.iui = osmo_iuup_instance_alloc(conn_rtp->conn, conn_rtp->conn->id);
|
||||
OSMO_ASSERT(conn_rtp->iuup.iui);
|
||||
osmo_iuup_instance_set_user_prim_cb(conn_rtp->iuup.iui, _conn_iuup_user_prim_cb, conn_rtp);
|
||||
osmo_iuup_instance_set_transport_prim_cb(conn_rtp->iuup.iui, _conn_iuup_transport_prim_cb, conn_rtp);
|
||||
conn_rtp->iuup.rfci_id_no_data = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cleanup specific IuUP connection (MGCP_RTP_IUUP) state, allocated by mgcp_conn_iuup_init() */
|
||||
void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
osmo_iuup_instance_free(conn_rtp->iuup.iui);
|
||||
conn_rtp->iuup.iui = NULL;
|
||||
}
|
||||
|
||||
/* Received RTP+IuUP pkt from socket of conn_rtp_src, build a TNL primitive to
|
||||
* push it further up the stack to the IuUP layer FSM to handle and/or bridge it */
|
||||
int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg)
|
||||
{
|
||||
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
|
||||
struct mgcp_conn_rtp *conn_rtp_src = mc->conn_src;
|
||||
int rc = 0;
|
||||
bool force_output_enabled = false;
|
||||
bool prev_output_enabled;
|
||||
struct osmo_sockaddr prev_rem_addr;
|
||||
uint16_t prev_rem_rtp_port;
|
||||
|
||||
OSMO_ASSERT(mgcp_conn_rtp_is_iuup(conn_rtp_src));
|
||||
|
||||
if ((rc = check_rtp_iuup(conn_rtp_src, msg)) < 0)
|
||||
goto free_ret;
|
||||
|
||||
if (!conn_rtp_src->iuup.configured) {
|
||||
/* We received the first message without sending any, the peer is the active side (RNC). */
|
||||
rc = _conn_iuup_configure_as_passive(conn_rtp_src);
|
||||
if (rc < 0)
|
||||
goto free_ret;
|
||||
/* We need to force allowance of RTP containing Init-ACK back: */
|
||||
prev_output_enabled = conn_rtp_src->end.output_enabled;
|
||||
conn_rtp_src->end.output_enabled = true;
|
||||
force_output_enabled = true;
|
||||
/* Fill in the peer address so that we can send Init-ACK back: */
|
||||
prev_rem_addr = conn_rtp_src->end.addr;
|
||||
prev_rem_rtp_port = conn_rtp_src->end.rtp_port;
|
||||
conn_rtp_src->end.addr = *mc->from_addr;
|
||||
conn_rtp_src->end.rtp_port = htons(osmo_sockaddr_port(&mc->from_addr->u.sa));
|
||||
}
|
||||
|
||||
rc = _conn_iuup_rtp_pl_up(conn_rtp_src, msg);
|
||||
|
||||
if (force_output_enabled) {
|
||||
conn_rtp_src->end.output_enabled = prev_output_enabled;
|
||||
conn_rtp_src->end.addr = prev_rem_addr;
|
||||
conn_rtp_src->end.rtp_port = prev_rem_rtp_port;
|
||||
}
|
||||
|
||||
return rc;
|
||||
free_ret:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Build IuUP RNL Data primitive from msg containing an incoming RTP pkt from
|
||||
* peer and send it down the IuUP layer towards the destination as IuUP/RTP: */
|
||||
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg)
|
||||
{
|
||||
struct osmo_iuup_rnl_prim *irp;
|
||||
struct rtp_hdr *rtph;
|
||||
int rc = -1;
|
||||
int iuup_length = 0;
|
||||
int8_t rfci;
|
||||
|
||||
/* Tx RNL-DATA.req */
|
||||
rtph = (struct rtp_hdr *)msgb_data(msg);
|
||||
msgb_pull(msg, sizeof(*rtph));
|
||||
|
||||
/* FIXME: validate amr packets */
|
||||
irp = osmo_iuup_rnl_prim_alloc(conn_dest_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
|
||||
irp->u.data.frame_nr = htons(rtph->sequence) % 16;
|
||||
|
||||
/* TODO: CMR handling & multiple frames handling */
|
||||
|
||||
if (strcmp(conn_src_rtp->end.codec->subtype_name, "AMR") != 0) {
|
||||
LOG_CONN_RTP(conn_src_rtp, LOGL_ERROR,
|
||||
"Bridge RTP=>IuUP: Bridging src codec %s to IuUP AMR not supported\n",
|
||||
conn_src_rtp->end.codec->subtype_name);
|
||||
goto free_ret;
|
||||
}
|
||||
if (mgcp_codec_amr_is_octet_aligned(conn_src_rtp->end.codec)) {
|
||||
struct amr_hdr *amr_hdr = (struct amr_hdr *) msgb_data(msg);
|
||||
if (msgb_length(msg) < (sizeof(*amr_hdr))) {
|
||||
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
|
||||
"Bridge RTP=>IuUP: too short for AMR OA hdr (%u)\n", msgb_length(msg));
|
||||
goto free_ret;
|
||||
}
|
||||
if (amr_hdr->ft >= AMR_FT_MAX) {
|
||||
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR OA ft=%u\n", amr_hdr->ft);
|
||||
goto free_ret;
|
||||
}
|
||||
if ((rfci = _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, amr_hdr->ft)) < 0) {
|
||||
LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR OA ft=%u\n", amr_hdr->ft);
|
||||
goto free_ret;
|
||||
}
|
||||
irp->u.data.fqc = amr_hdr->q;
|
||||
irp->u.data.rfci = rfci;
|
||||
msgb_pull(msg, 2);
|
||||
} else {
|
||||
uint8_t *amr_bwe_hdr = (uint8_t *) msgb_data(msg);
|
||||
int8_t ft;
|
||||
if (msgb_length(msg) < 2) {
|
||||
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
|
||||
"Bridge RTP=>IuUP: too short for AMR BE hdr (%u)\n", msgb_length(msg));
|
||||
goto free_ret;
|
||||
}
|
||||
ft = ((amr_bwe_hdr[0] & 0x07) << 1) | ((amr_bwe_hdr[1] & 0x80) >> 7);
|
||||
if (ft >= AMR_FT_MAX) {
|
||||
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR BE ft=%u\n", ft);
|
||||
goto free_ret;
|
||||
}
|
||||
if ((rfci = _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, ft)) < 0) {
|
||||
LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR BE ft=%u\n", ft);
|
||||
goto free_ret;
|
||||
}
|
||||
irp->u.data.fqc = ((amr_bwe_hdr[1] & 0x40) >> 6);
|
||||
irp->u.data.rfci = rfci;
|
||||
rc = iuup_length = osmo_amr_bwe_to_iuup(msgb_data(msg), msgb_length(msg));
|
||||
if (rc < 0) {
|
||||
LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed convert the RTP/AMR to IuUP payload\n");
|
||||
return rc;
|
||||
}
|
||||
msgb_trim(msg, iuup_length);
|
||||
}
|
||||
|
||||
irp->oph.msg->l3h = msgb_put(irp->oph.msg, msgb_length(msg));
|
||||
memcpy(irp->oph.msg->l3h, msgb_data(msg), msgb_length(msg));
|
||||
if ((rc = osmo_iuup_rnl_prim_down(conn_dest_rtp->iuup.iui, irp)) != 0)
|
||||
LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed Tx RTP payload down the IuUP layer\n");
|
||||
return rc;
|
||||
|
||||
free_ret:
|
||||
msgb_free(irp->oph.msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Build IuUP RNL Data primitive from msg containing dummy content and send it
|
||||
* down the IuUP layer towards the destination as IuUP/RTP: */
|
||||
int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
struct osmo_iuup_rnl_prim *irp;
|
||||
int rc;
|
||||
|
||||
if (conn_rtp->iuup.rfci_id_no_data == -1) {
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_NOTICE, "No RFCI NO_DATA found, unable to send dummy packet\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
|
||||
irp->u.data.frame_nr = 0;
|
||||
irp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
|
||||
irp->u.data.rfci = conn_rtp->iuup.rfci_id_no_data;
|
||||
irp->oph.msg->l3h = irp->oph.msg->tail;
|
||||
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) != 0) {
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed Tx RTP dummy payload down the IuUP layer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -110,8 +110,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
|
||||
/* Special handling for RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
conn->u.rtp.end.output_enabled =
|
||||
conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
|
||||
conn->u.rtp.end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
|
||||
}
|
||||
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
|
||||
@@ -121,7 +120,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
|
||||
/* Special handling für RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n",
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
|
||||
conn->u.rtp.end.output_enabled);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/codec/codec.h>
|
||||
#include <osmocom/mgcp/mgcp_e1.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_iuup.h>
|
||||
|
||||
#define RTP_SEQ_MOD (1 << 16)
|
||||
#define RTP_MAX_DROPOUT 3000
|
||||
@@ -59,7 +59,7 @@ enum rtp_proto {
|
||||
MGCP_PROTO_RTCP,
|
||||
};
|
||||
|
||||
static void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
|
||||
void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
|
||||
int id, int inc)
|
||||
{
|
||||
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
|
||||
@@ -156,7 +156,7 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
|
||||
/* This does not need to be a precision timestamp and
|
||||
* is allowed to wrap quite fast. The returned value is
|
||||
* 1/codec_rate seconds. */
|
||||
static uint32_t get_current_ts(unsigned codec_rate)
|
||||
uint32_t mgcp_get_current_ts(unsigned codec_rate)
|
||||
{
|
||||
struct timespec tp;
|
||||
uint64_t ret;
|
||||
@@ -529,7 +529,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
|
||||
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
|
||||
seq = ntohs(rtp_hdr->sequence);
|
||||
timestamp = ntohl(rtp_hdr->timestamp);
|
||||
arrival_time = get_current_ts(rtp_end->codec->rate);
|
||||
arrival_time = mgcp_get_current_ts(rtp_end->codec->rate);
|
||||
ssrc = ntohl(rtp_hdr->ssrc);
|
||||
marker_bit = !!rtp_hdr->marker;
|
||||
transit = arrival_time - timestamp;
|
||||
@@ -789,7 +789,7 @@ static int amr_oa_check(char *data, int len)
|
||||
|
||||
/* Forward data to a debug tap. This is debug function that is intended for
|
||||
* debugging the voice traffic with tools like gstreamer */
|
||||
static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
|
||||
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
@@ -815,7 +815,7 @@ static void gen_rtp_header(struct msgb *msg, struct mgcp_rtp_end *rtp_end,
|
||||
|
||||
hdr->version = 2;
|
||||
hdr->payload_type = rtp_end->codec->payload_type;
|
||||
hdr->timestamp = osmo_htonl(get_current_ts(rtp_end->codec->rate));
|
||||
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
|
||||
hdr->sequence = osmo_htons(state->alt_rtp_tx_sequence);
|
||||
hdr->ssrc = state->alt_rtp_tx_ssrc;
|
||||
}
|
||||
@@ -975,7 +975,7 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
|
||||
* the length is because we currently handle IUUP packets as RTP
|
||||
* packets, so they must pass this check, if we weould be more
|
||||
* strict here, we would possibly break 3G. (see also FIXME note
|
||||
* below */
|
||||
* below.*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1013,6 +1013,19 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
|
||||
"endpoint type is MGCP_OSMUX_BSC_NAT, "
|
||||
"using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
|
||||
return osmux_xfrm_to_osmux((char*)msgb_data(msg), msgb_length(msg), conn_dst);
|
||||
case MGCP_RTP_IUUP:
|
||||
if (proto == MGCP_PROTO_RTP) {
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"endpoint type is MGCP_RTP_IUUP, "
|
||||
"using mgcp_conn_iuup_send_rtp() to forward data over IuUP\n");
|
||||
return mgcp_conn_iuup_send_rtp(conn_src, conn_dst, msg);
|
||||
}
|
||||
/* RTCP: we forward as usual for regular RTP connection */
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"endpoint type is MGCP_RTP_IUUP and proto!=MGCP_PROTO_RTP, "
|
||||
"using mgcp_send() to forward data directly\n");
|
||||
return mgcp_send(endp, false,
|
||||
mc->from_addr, msg, conn_src, conn_dst);
|
||||
}
|
||||
|
||||
/* If the data has not been handled/forwarded until here, it will
|
||||
@@ -1073,8 +1086,11 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
if (check_rtp_destin(conn) != 0)
|
||||
goto failed;
|
||||
|
||||
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
|
||||
conn->end.rtp_port, rtp_dummy_payload, sizeof(rtp_dummy_payload));
|
||||
if (mgcp_conn_rtp_is_iuup(conn))
|
||||
rc = mgcp_conn_iuup_send_dummy(conn);
|
||||
else
|
||||
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr, conn->end.rtp_port,
|
||||
rtp_dummy_payload, sizeof(rtp_dummy_payload));
|
||||
|
||||
if (rc == -1)
|
||||
goto failed;
|
||||
@@ -1138,7 +1154,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
* course unable to patch the payload type. A situation like this
|
||||
* should not occur if transcoding is consequently avoided. Until
|
||||
* we have transcoding support in osmo-mgw we can not resolve this. */
|
||||
if (is_rtp) {
|
||||
if (is_rtp && conn_dst->type != MGCP_RTP_IUUP) {
|
||||
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
@@ -1185,7 +1201,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
mgcp_patch_and_count(endp, rtp_state, rtp_end,
|
||||
addr, msg);
|
||||
|
||||
if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
|
||||
if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) {
|
||||
/* the iuup code will correctly transform to the correct AMR mode */
|
||||
} else if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
|
||||
rc = amr_oa_bwe_convert(endp, msg,
|
||||
conn_dst->end.codec->param.amr_octet_aligned);
|
||||
if (rc < 0) {
|
||||
@@ -1211,27 +1229,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
);
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
|
||||
forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
|
||||
msg);
|
||||
|
||||
/* FIXME: HACK HACK HACK. See OS#2459.
|
||||
* The ip.access nano3G needs the first RTP payload's first two bytes to read hex
|
||||
* 'e400', or it will reject the RAB assignment. It seems to not harm other femto
|
||||
* cells (as long as we patch only the first RTP payload in each stream).
|
||||
*/
|
||||
if (!rtp_state->patched_first_rtp_payload
|
||||
&& conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
|
||||
uint8_t *data = msgb_data(msg) + 12;
|
||||
if (data[0] == 0xe0) {
|
||||
data[0] = 0xe4;
|
||||
data[1] = 0x00;
|
||||
rtp_state->patched_first_rtp_payload = true;
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"Patching over first two bytes"
|
||||
" to fake an IuUP Initialization Ack\n");
|
||||
}
|
||||
}
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
|
||||
(char *)msgb_data(msg), msgb_length(msg));
|
||||
|
||||
@@ -1291,6 +1291,9 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
* destination connection is known the RTP packet is sent via
|
||||
* the destination connection. */
|
||||
|
||||
/* If source is IuUP, we need to handle state, forward it through specific bridge path: */
|
||||
if (mgcp_conn_rtp_is_iuup(conn_src) && mc->proto == MGCP_PROTO_RTP)
|
||||
return mgcp_conn_iuup_dispatch_rtp(msg);
|
||||
|
||||
/* Check if the connection is in loopback mode, if yes, just send the
|
||||
* incoming data back to the origin */
|
||||
@@ -1468,7 +1471,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
msgb_put(msg, ret);
|
||||
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
|
||||
proto == MGCP_PROTO_RTP ? "RTP" : "RTPC",
|
||||
proto == MGCP_PROTO_RTP ? "RTP" : "RTCP",
|
||||
msgb_length(msg), osmo_sockaddr_ntop(&addr.u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&addr.u.sa));
|
||||
|
||||
@@ -1506,7 +1509,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data(fd->fd, &conn_src->tap_in, msg);
|
||||
forward_data_tap(fd->fd, &conn_src->tap_in, msg);
|
||||
|
||||
rc = rx_rtp(msg);
|
||||
|
||||
@@ -1515,6 +1518,7 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Note: This function is able to handle RTP and RTCP */
|
||||
static int rx_rtp(struct msgb *msg)
|
||||
{
|
||||
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
|
||||
@@ -1531,7 +1535,8 @@ static int rx_rtp(struct msgb *msg)
|
||||
* framing mode (octet-aligned vs. bandwith-efficient is explicitly
|
||||
* define, then we check if the incoming payload matches that
|
||||
* expectation. */
|
||||
if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
|
||||
if (mc->proto == MGCP_PROTO_RTP &&
|
||||
amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
|
||||
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
|
||||
if (oa < 0)
|
||||
return -1;
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_iuup.h>
|
||||
|
||||
/* Contains the last successfully resolved endpoint name. This variable is used
|
||||
* for the unit-tests to verify that the endpoint was correctly resolved. */
|
||||
@@ -97,10 +98,6 @@ struct mgcp_request {
|
||||
/* function pointer to the request handler */
|
||||
struct msgb *(*handle_request)(struct mgcp_request_data *data);
|
||||
|
||||
/* true if the request requires an endpoint, false if only a trunk
|
||||
* is sufficient. (corner cases, e.g. wildcarded DLCX) */
|
||||
bool require_endp;
|
||||
|
||||
/* a human readable name that describes the request */
|
||||
char *debug_name;
|
||||
};
|
||||
@@ -112,32 +109,34 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *data);
|
||||
static struct msgb *handle_rsip(struct mgcp_request_data *data);
|
||||
static struct msgb *handle_noti_req(struct mgcp_request_data *data);
|
||||
static const struct mgcp_request mgcp_requests[] = {
|
||||
{ .name = "AUEP",
|
||||
.handle_request = handle_audit_endpoint,
|
||||
.debug_name = "AuditEndpoint",
|
||||
.require_endp = true },
|
||||
{ .name = "CRCX",
|
||||
.handle_request = handle_create_con,
|
||||
.debug_name = "CreateConnection",
|
||||
.require_endp = true },
|
||||
{ .name = "DLCX",
|
||||
.handle_request = handle_delete_con,
|
||||
.debug_name = "DeleteConnection",
|
||||
.require_endp = false },
|
||||
{ .name = "MDCX",
|
||||
.handle_request = handle_modify_con,
|
||||
.debug_name = "ModifiyConnection",
|
||||
.require_endp = true },
|
||||
{ .name = "RQNT",
|
||||
.handle_request = handle_noti_req,
|
||||
.debug_name = "NotificationRequest",
|
||||
.require_endp = true },
|
||||
{ .name = "AUEP", .handle_request = handle_audit_endpoint, .debug_name = "AuditEndpoint" },
|
||||
{
|
||||
.name = "CRCX",
|
||||
.handle_request = handle_create_con,
|
||||
.debug_name = "CreateConnection",
|
||||
},
|
||||
{
|
||||
.name = "DLCX",
|
||||
.handle_request = handle_delete_con,
|
||||
.debug_name = "DeleteConnection",
|
||||
},
|
||||
{
|
||||
.name = "MDCX",
|
||||
.handle_request = handle_modify_con,
|
||||
.debug_name = "ModifiyConnection",
|
||||
},
|
||||
{
|
||||
.name = "RQNT",
|
||||
.handle_request = handle_noti_req,
|
||||
.debug_name = "NotificationRequest",
|
||||
},
|
||||
|
||||
/* SPEC extension */
|
||||
{ .name = "RSIP",
|
||||
.handle_request = handle_rsip,
|
||||
.debug_name = "ReSetInProgress",
|
||||
.require_endp = true },
|
||||
{
|
||||
.name = "RSIP",
|
||||
.handle_request = handle_rsip,
|
||||
.debug_name = "ReSetInProgress",
|
||||
},
|
||||
};
|
||||
|
||||
/* Initalize transcoder */
|
||||
@@ -149,7 +148,13 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst = conn;
|
||||
struct mgcp_conn *_conn;
|
||||
|
||||
if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
|
||||
switch (conn->type) {
|
||||
case MGCP_RTP_DEFAULT:
|
||||
case MGCP_OSMUX_BSC:
|
||||
case MGCP_OSMUX_BSC_NAT:
|
||||
case MGCP_RTP_IUUP:
|
||||
break;
|
||||
default:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
|
||||
return 0;
|
||||
@@ -173,12 +178,15 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
|
||||
}
|
||||
|
||||
/* Helper function to allocate some memory for responses and retransmissions */
|
||||
static struct msgb *mgcp_msgb_alloc(void)
|
||||
static struct msgb *mgcp_msgb_alloc(void *ctx)
|
||||
{
|
||||
struct msgb *msg;
|
||||
msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
|
||||
if (!msg)
|
||||
msg = msgb_alloc_headroom_c(ctx, 4096, 128, "MGCP msg");
|
||||
|
||||
if (!msg) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
@@ -186,7 +194,7 @@ static struct msgb *mgcp_msgb_alloc(void)
|
||||
/* Helper function for do_retransmission() and create_resp() */
|
||||
static struct msgb *create_retransmission_response(const struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct msgb *msg = mgcp_msgb_alloc();
|
||||
struct msgb *msg = mgcp_msgb_alloc(endp->trunk);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
@@ -196,15 +204,14 @@ static struct msgb *create_retransmission_response(const struct mgcp_endpoint *e
|
||||
return msg;
|
||||
}
|
||||
|
||||
static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
|
||||
const char *txt, const char *msg,
|
||||
const char *trans, const char *param,
|
||||
const char *sdp)
|
||||
static struct msgb *create_resp(void *msgctx, struct mgcp_endpoint *endp, int code, const char *txt, const char *msg,
|
||||
const char *trans, const char *param, const char *sdp)
|
||||
{
|
||||
int len;
|
||||
struct msgb *res;
|
||||
|
||||
res = mgcp_msgb_alloc();
|
||||
OSMO_ASSERT(msgctx != 0);
|
||||
res = mgcp_msgb_alloc(msgctx);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
@@ -236,26 +243,22 @@ static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
|
||||
int code, const char *msg,
|
||||
const char *trans,
|
||||
const char *param)
|
||||
static struct msgb *create_ok_resp_with_param(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
|
||||
const char *trans, const char *param)
|
||||
{
|
||||
return create_resp(endp, code, " OK", msg, trans, param, NULL);
|
||||
return create_resp(msgctx, endp, code, " OK", msg, trans, param, NULL);
|
||||
}
|
||||
|
||||
static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
|
||||
int code, const char *msg,
|
||||
static struct msgb *create_ok_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
|
||||
const char *trans)
|
||||
{
|
||||
return create_ok_resp_with_param(endp, code, msg, trans, NULL);
|
||||
return create_ok_resp_with_param(msgctx, endp, code, msg, trans, NULL);
|
||||
}
|
||||
|
||||
static struct msgb *create_err_response(struct mgcp_endpoint *endp,
|
||||
int code, const char *msg,
|
||||
static struct msgb *create_err_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
|
||||
const char *trans)
|
||||
{
|
||||
return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
|
||||
return create_resp(msgctx, endp, code, " FAIL", msg, trans, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Format MGCP response string (with SDP attached) */
|
||||
@@ -278,7 +281,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
|
||||
int rc;
|
||||
struct msgb *result;
|
||||
|
||||
sdp = msgb_alloc_headroom(4096, 128, "sdp record");
|
||||
sdp = msgb_alloc_headroom_c(endp->trunk, 4096, 128, "sdp record");
|
||||
if (!sdp)
|
||||
return NULL;
|
||||
|
||||
@@ -309,7 +312,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
|
||||
rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
|
||||
if (rc < 0)
|
||||
goto error;
|
||||
result = create_resp(endp, 200, " OK", msg, trans_id, NULL, (char*) sdp->data);
|
||||
result = create_resp(endp->trunk, endp, 200, " OK", msg, trans_id, NULL, (char *)sdp->data);
|
||||
msgb_free(sdp);
|
||||
return result;
|
||||
error:
|
||||
@@ -376,7 +379,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
if (rc < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
|
||||
return create_err_response(NULL, -rc, rq.name, "000000");
|
||||
return create_err_response(cfg, NULL, -rc, rq.name, "000000");
|
||||
}
|
||||
|
||||
/* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
|
||||
@@ -397,7 +400,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
if (!rq.trunk) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
|
||||
rq.name, pdata.epname);
|
||||
return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
|
||||
return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
|
||||
}
|
||||
} else {
|
||||
/* If the endpoint name suggests that the request refers to a specific endpoint, then the
|
||||
@@ -405,7 +408,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
|
||||
pdata.epname, -rq.mgcp_cause);
|
||||
return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
|
||||
return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
|
||||
}
|
||||
} else {
|
||||
osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
|
||||
@@ -422,15 +425,6 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
/* Find an appropriate handler for the current request and execute it */
|
||||
for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
|
||||
if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
|
||||
/* Check if the request requires and endpoint, if yes, check if we have it, otherwise don't
|
||||
* execute the request handler. */
|
||||
if (mgcp_requests[i].require_endp && !rq.endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"%s: the request handler \"%s\" requires an endpoint resource for \"%s\", which is not available -- abort\n",
|
||||
rq.name, mgcp_requests[i].debug_name, pdata.epname);
|
||||
return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
|
||||
}
|
||||
|
||||
/* Execute request handler */
|
||||
if (rq.endp)
|
||||
LOGP(DLMGCP, LOGL_INFO,
|
||||
@@ -461,7 +455,12 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
|
||||
{
|
||||
LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
|
||||
return create_ok_response(rq->endp, 200, "AUEP", rq->pdata->trans);
|
||||
if (!rq->endp || !mgcp_endp_avail(rq->endp)) {
|
||||
LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR, "AUEP: selected endpoint not available!\n");
|
||||
return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdata->trans);
|
||||
}
|
||||
|
||||
return create_ok_response(rq->trunk, rq->endp, 200, "AUEP", rq->pdata->trans);
|
||||
}
|
||||
|
||||
/* Try to find a free port by attempting to bind on it. Also handle the
|
||||
@@ -855,11 +854,18 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
|
||||
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
|
||||
|
||||
/* we must have a free ep */
|
||||
if (!endp) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "CRCX: no free endpoints available!\n");
|
||||
return create_err_response(rq->trunk, NULL, 403, "CRCX", pdata->trans);
|
||||
}
|
||||
|
||||
if (!mgcp_endp_avail(endp)) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: selected endpoint not available!\n");
|
||||
return create_err_response(NULL, 501, "CRCX", pdata->trans);
|
||||
return create_err_response(rq->trunk, NULL, 501, "CRCX", pdata->trans);
|
||||
}
|
||||
|
||||
/* parse CallID C: and LocalParameters L: */
|
||||
@@ -879,7 +885,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
|
||||
* together with a CRCX, the MGW will assign the
|
||||
* connection identifier by itself on CRCX */
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
|
||||
return create_err_response(NULL, 523, "CRCX", pdata->trans);
|
||||
return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans);
|
||||
break;
|
||||
case 'M':
|
||||
mode = (const char *)line + 3;
|
||||
@@ -905,7 +911,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: unhandled option: '%c'/%d\n", *line, *line);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
|
||||
return create_err_response(NULL, 539, "CRCX", pdata->trans);
|
||||
return create_err_response(rq->trunk, NULL, 539, "CRCX", pdata->trans);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -916,14 +922,14 @@ mgcp_header_done:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: insufficient parameters, missing callid\n");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
|
||||
return create_err_response(endp, 516, "CRCX", pdata->trans);
|
||||
return create_err_response(endp, endp, 516, "CRCX", pdata->trans);
|
||||
}
|
||||
|
||||
if (!mode) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: insufficient parameters, missing mode\n");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
|
||||
return create_err_response(endp, 517, "CRCX", pdata->trans);
|
||||
return create_err_response(endp, endp, 517, "CRCX", pdata->trans);
|
||||
}
|
||||
|
||||
/* Check if we are able to accept the creation of another connection */
|
||||
@@ -940,7 +946,7 @@ mgcp_header_done:
|
||||
/* There is no more room for a connection, leave
|
||||
* everything as it is and return with an error */
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
|
||||
return create_err_response(endp, 540, "CRCX", pdata->trans);
|
||||
return create_err_response(endp, endp, 540, "CRCX", pdata->trans);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -958,7 +964,7 @@ mgcp_header_done:
|
||||
/* This is not our call, leave everything as it is and
|
||||
* return with an error. */
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
|
||||
return create_err_response(endp, 400, "CRCX", pdata->trans);
|
||||
return create_err_response(endp, endp, 400, "CRCX", pdata->trans);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -969,7 +975,7 @@ mgcp_header_done:
|
||||
rc = mgcp_endp_claim(endp, callid);
|
||||
if (rc != 0) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
|
||||
return create_err_response(endp, 502, "CRCX", pdata->trans);
|
||||
return create_err_response(endp, endp, 502, "CRCX", pdata->trans);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1029,6 +1035,11 @@ mgcp_header_done:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
|
||||
goto error2;
|
||||
}
|
||||
/* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
|
||||
/* TODO: "codec" probably needs to be moved from endp to conn */
|
||||
if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) {
|
||||
rc = mgcp_conn_iuup_init(conn);
|
||||
}
|
||||
|
||||
conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
|
||||
trunk->audio_fmtp_extra);
|
||||
@@ -1088,7 +1099,7 @@ error2:
|
||||
mgcp_endp_release(endp);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: unable to create connection\n");
|
||||
return create_err_response(endp, error_code, "CRCX", pdata->trans);
|
||||
return create_err_response(endp, endp, error_code, "CRCX", pdata->trans);
|
||||
}
|
||||
|
||||
/* MDCX command handler, processes the received command */
|
||||
@@ -1112,26 +1123,24 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
|
||||
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
|
||||
|
||||
if (!mgcp_endp_avail(endp)) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: selected endpoint not available!\n");
|
||||
return create_err_response(NULL, 501, "MDCX", pdata->trans);
|
||||
}
|
||||
|
||||
/* Prohibit wildcarded requests */
|
||||
if (rq->wildcarded) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: wildcarded endpoint names not supported.\n");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
|
||||
return create_err_response(endp, 507, "MDCX", pdata->trans);
|
||||
return create_err_response(rq->trunk, endp, 507, "MDCX", pdata->trans);
|
||||
}
|
||||
|
||||
if (!endp || !mgcp_endp_avail(endp)) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "MDCX: selected endpoint not available!\n");
|
||||
return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans);
|
||||
}
|
||||
if (llist_count(&endp->conns) <= 0) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint is not holding a connection.\n");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
|
||||
return create_err_response(endp, 400, "MDCX", pdata->trans);
|
||||
return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
|
||||
}
|
||||
|
||||
for_each_line(line, pdata->save) {
|
||||
@@ -1181,7 +1190,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
|
||||
"MDCX: Unhandled MGCP option: '%c'/%d\n",
|
||||
line[0], line[0]);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
|
||||
return create_err_response(NULL, 539, "MDCX", pdata->trans);
|
||||
return create_err_response(rq->trunk, NULL, 539, "MDCX", pdata->trans);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1191,13 +1200,13 @@ mgcp_header_done:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
|
||||
return create_err_response(endp, 515, "MDCX", pdata->trans);
|
||||
return create_err_response(endp, endp, 515, "MDCX", pdata->trans);
|
||||
}
|
||||
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
if (!conn) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
|
||||
return create_err_response(endp, 400, "MDCX", pdata->trans);
|
||||
return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
|
||||
}
|
||||
|
||||
mgcp_conn_watchdog_kick(conn->conn);
|
||||
@@ -1304,7 +1313,7 @@ mgcp_header_done:
|
||||
mgcp_endp_update(endp);
|
||||
return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
|
||||
error3:
|
||||
return create_err_response(endp, error_code, "MDCX", pdata->trans);
|
||||
return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
|
||||
|
||||
out_silent:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
|
||||
@@ -1335,14 +1344,27 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"DLCX: selected endpoint not available!\n");
|
||||
return create_err_response(NULL, 501, "DLCX", pdata->trans);
|
||||
return create_err_response(rq->trunk, NULL, 501, "DLCX", pdata->trans);
|
||||
}
|
||||
|
||||
if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"DLCX: endpoint is not holding a connection.\n");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
|
||||
return create_err_response(endp, 515, "DLCX", pdata->trans);
|
||||
return create_err_response(endp, endp, 515, "DLCX", pdata->trans);
|
||||
}
|
||||
|
||||
/* Handle wildcarded DLCX that refers to the whole trunk. This means
|
||||
* that we walk over all endpoints on the trunk in order to drop all
|
||||
* connections on the trunk. (see also RFC3435 Annex F.7) */
|
||||
if (rq->wildcarded) {
|
||||
int num_conns = 0;
|
||||
for (i = 0; i < trunk->number_endpoints; i++) {
|
||||
num_conns += llist_count(&trunk->endpoints[i]->conns);
|
||||
mgcp_endp_release(trunk->endpoints[i]);
|
||||
}
|
||||
rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
|
||||
return create_ok_response(trunk, NULL, 200, "DLCX", pdata->trans);
|
||||
}
|
||||
|
||||
for_each_line(line, pdata->save) {
|
||||
@@ -1357,7 +1379,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
|
||||
"cannot handle requests with call-id (C) without endpoint -- abort!");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
|
||||
return create_err_response(NULL, 539, "DLCX", pdata->trans);
|
||||
return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
|
||||
}
|
||||
|
||||
if (mgcp_verify_call_id(endp, line + 3) != 0) {
|
||||
@@ -1373,7 +1395,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
|
||||
"cannot handle requests with conn-id (I) without endpoint -- abort!");
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
|
||||
return create_err_response(NULL, 539, "DLCX", pdata->trans);
|
||||
return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
|
||||
}
|
||||
|
||||
conn_id = (const char *)line + 3;
|
||||
@@ -1389,24 +1411,11 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
|
||||
line[0], line[0]);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
|
||||
return create_err_response(NULL, 539, "DLCX", pdata->trans);
|
||||
return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle wildcarded DLCX that refers to the whole trunk. This means
|
||||
* that we walk over all endpoints on the trunk in order to drop all
|
||||
* connections on the trunk. (see also RFC3435 Annex F.7) */
|
||||
if (rq->wildcarded) {
|
||||
int num_conns = 0;
|
||||
for (i = 0; i < trunk->number_endpoints; i++) {
|
||||
num_conns += llist_count(&trunk->endpoints[i]->conns);
|
||||
mgcp_endp_release(trunk->endpoints[i]);
|
||||
}
|
||||
rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
|
||||
return create_ok_response(NULL, 200, "DLCX", pdata->trans);
|
||||
}
|
||||
|
||||
/* The logic does not permit to go past this point without having the
|
||||
* the endp pointer populated. */
|
||||
OSMO_ASSERT(endp);
|
||||
@@ -1429,7 +1438,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
/* Note: In this case we do not return any statistics,
|
||||
* as we assume that the client is not interested in
|
||||
* this case. */
|
||||
return create_ok_response(endp, 200, "DLCX", pdata->trans);
|
||||
return create_ok_response(endp, endp, 200, "DLCX", pdata->trans);
|
||||
}
|
||||
|
||||
/* Find the connection */
|
||||
@@ -1458,10 +1467,10 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
|
||||
if (silent)
|
||||
goto out_silent;
|
||||
return create_ok_resp_with_param(endp, 250, "DLCX", pdata->trans, stats);
|
||||
return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);
|
||||
|
||||
error3:
|
||||
return create_err_response(endp, error_code, "DLCX", pdata->trans);
|
||||
return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
|
||||
|
||||
out_silent:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
|
||||
@@ -1516,14 +1525,13 @@ static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
|
||||
|
||||
/* we didn't see a signal request with a tone */
|
||||
if (tone == CHAR_MAX)
|
||||
return create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans);
|
||||
return create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans);
|
||||
|
||||
if (rq->pdata->cfg->rqnt_cb)
|
||||
res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
|
||||
|
||||
return res == 0 ?
|
||||
create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans) :
|
||||
create_err_response(rq->endp, res, "RQNT", rq->pdata->trans);
|
||||
return res == 0 ? create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans) :
|
||||
create_err_response(rq->endp, rq->endp, res, "RQNT", rq->pdata->trans);
|
||||
}
|
||||
|
||||
/* Connection keepalive timer, will take care that dummy packets are send
|
||||
|
||||
@@ -98,7 +98,7 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
||||
/* Helper function to update codec map information with additional data from
|
||||
* SDP, called from: mgcp_parse_sdp_data() */
|
||||
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
int payload, const char *audio_name)
|
||||
int payload_type, const char *audio_name)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -110,7 +110,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
/* Note: We can only update payload codecs that already exist
|
||||
* in our codec list. If we get an unexpected payload type,
|
||||
* we just drop it */
|
||||
if (codecs[i].payload_type != payload)
|
||||
if (codecs[i].payload_type != payload_type)
|
||||
continue;
|
||||
|
||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
||||
@@ -127,7 +127,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload_type,
|
||||
audio_name);
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
void *tmp_ctx = talloc_new(NULL);
|
||||
struct mgcp_rtp_end *rtp;
|
||||
|
||||
int payload;
|
||||
int payload_type;
|
||||
int ptime, ptime2 = 0;
|
||||
char audio_name[64];
|
||||
int port, rc;
|
||||
@@ -355,8 +355,8 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
/* skip these SDP attributes */
|
||||
break;
|
||||
case 'a':
|
||||
if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
|
||||
codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
|
||||
if (sscanf(line, "a=rtpmap:%d %63s", &payload_type, audio_name) == 2) {
|
||||
codecs_update(tmp_ctx, codecs, codecs_used, payload_type, audio_name);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
207
src/libosmo-mgcp/mgcp_threads_queue.c
Normal file
207
src/libosmo-mgcp/mgcp_threads_queue.c
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Eric Wild
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <talloc.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_threads_queue.h>
|
||||
|
||||
/*
|
||||
classic lamport circular lockfree spsc queue:
|
||||
every "side" only writes its own ptr, but may read the other sides ptr
|
||||
|
||||
notify reader using eventfd as soon as element is added, reader then reads until
|
||||
read fails
|
||||
-> reader pops in a loop until FALSE and might get spurious events because it
|
||||
read before it was notified, which is fine
|
||||
-> writing pushes *the same data* in a loop until TRUE, blocks
|
||||
|
||||
shutting this down requires
|
||||
1) to stop reading and pushing
|
||||
2) ONE side to take care of the eventfds
|
||||
*/
|
||||
|
||||
static struct spsc *spsc_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr, bool blockw)
|
||||
{
|
||||
struct spsc *q = talloc_zero_size(talloc_ctx, sizeof(struct spsc) + sizeof(uintptr_t) * count);
|
||||
atomic_init(&q->readptr, 0);
|
||||
atomic_init(&q->writeptr, 0);
|
||||
q->efd_r = eventfd(0, blockr ? 0 : EFD_NONBLOCK);
|
||||
q->efd_w = eventfd(1, blockw ? 0 : EFD_NONBLOCK);
|
||||
q->count = count;
|
||||
q->size_per_buf = size_per_buf;
|
||||
q->buf = talloc_zero_size(q, size_per_buf * count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
q->data[i] = (uintptr_t)q->buf + i * size_per_buf;
|
||||
return q;
|
||||
}
|
||||
|
||||
static void spsc_deinit(struct spsc *q)
|
||||
{
|
||||
talloc_free(q->buf);
|
||||
close(q->efd_r);
|
||||
close(q->efd_w);
|
||||
talloc_free(q);
|
||||
}
|
||||
|
||||
static ssize_t spsc_check_r(struct spsc *q)
|
||||
{
|
||||
uint64_t efdr;
|
||||
return read(q->efd_r, &efdr, sizeof(uint64_t));
|
||||
}
|
||||
static ssize_t spsc_check_w(struct spsc *q)
|
||||
{
|
||||
uint64_t efdr;
|
||||
return read(q->efd_w, &efdr, sizeof(uint64_t));
|
||||
}
|
||||
static void spsc_notify_r(struct spsc *q)
|
||||
{
|
||||
uint64_t efdu = 1;
|
||||
write(q->efd_r, &efdu, sizeof(uint64_t));
|
||||
}
|
||||
static void spsc_notify_w(struct spsc *q)
|
||||
{
|
||||
uint64_t efdu = 1;
|
||||
write(q->efd_w, &efdu, sizeof(uint64_t));
|
||||
}
|
||||
|
||||
/*! Adds element to the queue by copying the data.
|
||||
* \param[in] q queue.
|
||||
* \param[in] elem input buffer, must match the originally configured queue buffer size!.
|
||||
* \returns true if queue was not full and element was successfully pushed */
|
||||
bool spsc_push(struct spsc *q, void *elem)
|
||||
{
|
||||
size_t cur_wp, cur_rp;
|
||||
cur_wp = atomic_load_explicit(&q->writeptr, memory_order_relaxed);
|
||||
cur_rp = atomic_load_explicit(&q->readptr, memory_order_acquire);
|
||||
if ((cur_wp + 1) % q->count == cur_rp) {
|
||||
spsc_check_w(q); /* blocks, ensures next (!) call succeeds */
|
||||
return false;
|
||||
}
|
||||
memcpy((void *)q->data[cur_wp], elem, q->size_per_buf);
|
||||
atomic_store_explicit(&q->writeptr, (cur_wp + 1) % q->count, memory_order_release);
|
||||
spsc_notify_r(q); /* fine after release */
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! Reads the read-fd of the queue, which, depending on settings passed on queue creation, blocks.
|
||||
* This function can be used to deliberately wait for a non-empty queue on the read side.
|
||||
* \param[in] q queue.
|
||||
* \returns result of reading the fd. */
|
||||
ssize_t spsc_prep_pop(struct spsc *q)
|
||||
{
|
||||
return spsc_check_r(q);
|
||||
}
|
||||
|
||||
/*! Removes element from the queue by copying the data.
|
||||
* \param[in] q queue.
|
||||
* \param[in] elem output buffer, must match the originally configured queue buffer size!.
|
||||
* \returns true if queue was not empty and element was successfully removed */
|
||||
bool spsc_pop(struct spsc *q, void *elem)
|
||||
{
|
||||
size_t cur_wp, cur_rp;
|
||||
cur_wp = atomic_load_explicit(&q->writeptr, memory_order_acquire);
|
||||
cur_rp = atomic_load_explicit(&q->readptr, memory_order_relaxed);
|
||||
|
||||
if (cur_wp == cur_rp) /* blocks via prep_pop */
|
||||
return false;
|
||||
memcpy(elem, (void *)q->data[cur_rp], q->size_per_buf);
|
||||
atomic_store_explicit(&q->readptr, (cur_rp + 1) % q->count, memory_order_release);
|
||||
spsc_notify_w(q);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! Creates a bidirectional queue channel that consists of two queues, one in each direction,
|
||||
* commonly referred to as a and b side.
|
||||
* \param[in] talloc_ctx allocation context.
|
||||
* \param[in] count number of buffers per queue.
|
||||
* \param[in] size_per_buf size of buffers per queue.
|
||||
* \param[in] blockr_a should reading the a-side read fd block?.
|
||||
* \param[in] blockw_a should reading the a-side write fd block?.
|
||||
* \param[in] blockr_b should reading the b-side read fd block?.
|
||||
* \param[in] blockw_b should reading the b-side write fd block?.
|
||||
* \returns queue channel */
|
||||
struct qchan spsc_chan_init_ex(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr_a,
|
||||
bool blockw_a, bool blockr_b, bool blockw_b)
|
||||
{
|
||||
struct qchan q;
|
||||
q.a = spsc_init(talloc_ctx, count, size_per_buf, blockr_a, blockw_a);
|
||||
q.b = spsc_init(talloc_ctx, count, size_per_buf, blockr_b, blockw_b);
|
||||
return q;
|
||||
}
|
||||
|
||||
/*! Creates a bidirectional queue channel that consists of two queues, one in each direction,
|
||||
* commonly referred to as a and b side.
|
||||
* \param[in] talloc_ctx allocation context.
|
||||
* \param[in] count number of buffers per queue.
|
||||
* \param[in] size_per_buf size of buffers per queue.
|
||||
* \returns queue channel */
|
||||
struct qchan spsc_chan_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf)
|
||||
{
|
||||
return spsc_chan_init_ex(talloc_ctx, count, size_per_buf, false, true, false, true);
|
||||
}
|
||||
|
||||
/*! Closes a bidirectional queue channel.
|
||||
* \param[in] q queue */
|
||||
void spsc_chan_close(struct qchan *q)
|
||||
{
|
||||
spsc_deinit(q->a);
|
||||
spsc_deinit(q->b);
|
||||
free(q);
|
||||
}
|
||||
|
||||
/*! Gets queue channel read/write fd for a/b side according to function name.
|
||||
* \param[in] q queue channel.
|
||||
* \returns fd */
|
||||
int spsc_get_a_rdfd(struct qchan *q)
|
||||
{
|
||||
return q->a->efd_r;
|
||||
}
|
||||
/*! Gets queue channel read/write fd for a/b side according to function name.
|
||||
* \param[in] q queue channel.
|
||||
* \returns fd */
|
||||
int spsc_get_b_rdfd(struct qchan *q)
|
||||
{
|
||||
return q->b->efd_r;
|
||||
}
|
||||
/*! Gets queue channel read/write fd for a/b side according to function name.
|
||||
* \param[in] q queue channel.
|
||||
* \returns fd */
|
||||
int spsc_get_a_wrfd(struct qchan *q)
|
||||
{
|
||||
return q->a->efd_w;
|
||||
}
|
||||
/*! Gets queue channel read/write fd for a/b side according to function name.
|
||||
* \param[in] q queue channel.
|
||||
* \returns fd */
|
||||
int spsc_get_b_wrfd(struct qchan *q)
|
||||
{
|
||||
return q->b->efd_w;
|
||||
}
|
||||
@@ -24,7 +24,7 @@ EXTRA_DIST = \
|
||||
mgcp_test.ok \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
check_PROGRAMS = \
|
||||
mgcp_test \
|
||||
$(NULL)
|
||||
|
||||
|
||||
@@ -868,7 +868,7 @@ static void test_messages(void)
|
||||
printf("Connection mode not set\n");
|
||||
|
||||
OSMO_ASSERT(conn->end.output_enabled
|
||||
== (conn->conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0));
|
||||
== !!(conn->conn->mode & MGCP_CONN_SEND_ONLY));
|
||||
|
||||
conn->conn->mode |= CONN_UNMODIFIED;
|
||||
|
||||
@@ -1896,16 +1896,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
|
||||
.codecs = {
|
||||
{
|
||||
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
|
||||
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
|
||||
},
|
||||
{
|
||||
{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
|
||||
{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
|
||||
},
|
||||
},
|
||||
.expect = {
|
||||
{ .payload_type_map = {111, 121}, },
|
||||
{ .payload_type_map = {112, 122} },
|
||||
{ .payload_type_map = {111, 122}, },
|
||||
{ .end = true },
|
||||
},
|
||||
},
|
||||
@@ -1914,15 +1911,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
|
||||
.codecs = {
|
||||
{
|
||||
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
|
||||
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
|
||||
},
|
||||
{
|
||||
{ 122, "AMR/8000", &amr_param_octet_aligned_unset, },
|
||||
},
|
||||
},
|
||||
.expect = {
|
||||
{ .payload_type_map = {111, -EINVAL}, },
|
||||
{ .payload_type_map = {112, 122} },
|
||||
{ .payload_type_map = {111, 122}, },
|
||||
{ .end = true },
|
||||
},
|
||||
},
|
||||
@@ -1931,15 +1926,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
|
||||
.codecs = {
|
||||
{
|
||||
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
|
||||
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
|
||||
},
|
||||
{
|
||||
{ 122, "AMR/8000", NULL, },
|
||||
},
|
||||
},
|
||||
.expect = {
|
||||
{ .payload_type_map = {111, -EINVAL}, },
|
||||
{ .payload_type_map = {112, 122} },
|
||||
{ .payload_type_map = {111, 122}, },
|
||||
{ .end = true },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1347,32 +1347,24 @@ Testing mgcp_codec_pt_translate()
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
|
||||
#4: conn1 has no codecs
|
||||
#4: conn1 has no codecs
|
||||
- add codecs on conn0:
|
||||
1: 0 PCMU/8000/1 -> rc=0
|
||||
2: 111 GSM-HR-08/8000/1 -> rc=0
|
||||
- add codecs on conn1:
|
||||
(none)
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
|
||||
0: 112 AMR/8000/1 octet-aligned=1 -> rc=0
|
||||
1: 0 PCMU/8000/1 -> rc=0
|
||||
2: 111 GSM-HR-08/8000/1 -> rc=0
|
||||
- add codecs on conn1:
|
||||
(none)
|
||||
- add codecs on conn0:
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
|
||||
- add codecs on conn1:
|
||||
0: 122 AMR/8000 octet-aligned=0 -> rc=0
|
||||
1: 121 AMR/8000 octet-aligned=1 -> rc=0
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
|
||||
#5: test AMR with differing octet-aligned settings
|
||||
- add codecs on conn0:
|
||||
0: 111 AMR/8000 octet-aligned=1 -> rc=0
|
||||
- add codecs on conn1:
|
||||
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
|
||||
0: 122 AMR/8000 octet-aligned=0 -> rc=0
|
||||
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 122
|
||||
0: 111 AMR/8000 octet-aligned=1 -> rc=0
|
||||
1: 112 AMR/8000 octet-aligned=0 -> rc=0
|
||||
- add codecs on conn1:
|
||||
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 111
|
||||
#6: test AMR with missing octet-aligned settings (defaults to 0)
|
||||
- add codecs on conn0:
|
||||
0: 111 AMR/8000 octet-aligned=1 -> rc=0
|
||||
- add codecs on conn1:
|
||||
|
||||
@@ -22,7 +22,7 @@ EXTRA_DIST = \
|
||||
mgcp_client_test.err \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
check_PROGRAMS = \
|
||||
mgcp_client_test \
|
||||
$(NULL)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user