From bc8c3a602bceaba0e6d34a1ba8b776b56b00d766 Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Mon, 27 Aug 2018 15:08:14 +1000 Subject: Public push of AVIRT. Follow readme for building in/out of tree for Ubuntu/AGL/etc. Signed-off-by: James O'Shannessy --- .clang-format | 428 +++++++++++++++++++++++++++++++++++++++++ .gitignore | 10 +- .vscode/extensions.json | 8 + .vscode/settings.json | 8 + .vscode/tasks.json | 73 +++++++ Kconfig | 20 ++ LICENSE | 201 ++++++++++++++++++++ Makefile | 16 ++ README.md | 47 +++++ alsa-pcm.c | 381 +++++++++++++++++++++++++++++++++++++ alsa.c | 255 +++++++++++++++++++++++++ alsa.h | 131 +++++++++++++ core.c | 495 ++++++++++++++++++++++++++++++++++++++++++++++++ core.h | 64 +++++++ dummy/Kconfig | 12 ++ dummy/Makefile | 6 + dummy/dummy.c | 278 +++++++++++++++++++++++++++ loadDrivers.sh | 18 ++ make-agl.sh | 12 ++ unload.sh | 9 + xds-project.conf | 4 + 21 files changed, 2472 insertions(+), 4 deletions(-) create mode 100644 .clang-format create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 Kconfig create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 alsa-pcm.c create mode 100755 alsa.c create mode 100755 alsa.h create mode 100644 core.c create mode 100644 core.h create mode 100644 dummy/Kconfig create mode 100644 dummy/Makefile create mode 100644 dummy/dummy.c create mode 100755 loadDrivers.sh create mode 100755 make-agl.sh create mode 100755 unload.sh create mode 100644 xds-project.conf diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..4b283db --- /dev/null +++ b/.clang-format @@ -0,0 +1,428 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# clang-format configuration file. Intended for clang-format >= 4. +# +# For more information, see: +# +# Documentation/process/clang-format.rst +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + #AfterExternBlock: false # Unknown to clang-format-5.0 + BeforeCatch: false + BeforeElse: false + IndentBraces: false + #SplitEmptyFunction: true # Unknown to clang-format-4.0 + #SplitEmptyRecord: true # Unknown to clang-format-4.0 + #SplitEmptyNamespace: true # Unknown to clang-format-4.0 +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +#CompactNamespaces: false # Unknown to clang-format-4.0 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +#FixNamespaceComments: false # Unknown to clang-format-4.0 + +# Taken from: +# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \ +# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ +# | sort | uniq +ForEachMacros: + - 'apei_estatus_for_each_section' + - 'ata_for_each_dev' + - 'ata_for_each_link' + - 'ax25_for_each' + - 'ax25_uid_for_each' + - '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' + - 'blkg_for_each_descendant_post' + - 'blkg_for_each_descendant_pre' + - 'blk_queue_for_each_rl' + - 'bond_for_each_slave' + - 'bond_for_each_slave_rcu' + - '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' + - 'drm_atomic_crtc_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane_state' + - 'drm_for_each_connector_iter' + - 'drm_for_each_crtc' + - 'drm_for_each_encoder' + - 'drm_for_each_encoder_mask' + - 'drm_for_each_fb' + - 'drm_for_each_legacy_plane' + - 'drm_for_each_plane' + - 'drm_for_each_plane_mask' + - 'drm_mm_for_each_hole' + - 'drm_mm_for_each_node' + - 'drm_mm_for_each_node_in_range' + - 'drm_mm_for_each_node_safe' + - 'for_each_active_drhd_unit' + - 'for_each_active_iommu' + - 'for_each_available_child_of_node' + - 'for_each_bio' + - 'for_each_board_func_rsrc' + - 'for_each_bvec' + - 'for_each_child_of_node' + - 'for_each_clear_bit' + - 'for_each_clear_bit_from' + - 'for_each_cmsghdr' + - 'for_each_compatible_node' + - 'for_each_console' + - 'for_each_cpu' + - 'for_each_cpu_and' + - 'for_each_cpu_not' + - 'for_each_cpu_wrap' + - 'for_each_dev_addr' + - 'for_each_dma_cap_mask' + - 'for_each_drhd_unit' + - 'for_each_dss_dev' + - 'for_each_efi_memory_desc' + - 'for_each_efi_memory_desc_in_map' + - 'for_each_endpoint_of_node' + - 'for_each_evictable_lru' + - 'for_each_fib6_node_rt_rcu' + - 'for_each_fib6_walker_rt' + - '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_lru' + - 'for_each_matching_node' + - 'for_each_matching_node_and_match' + - 'for_each_memblock' + - 'for_each_memblock_type' + - 'for_each_memcg_cache_index' + - 'for_each_mem_pfn_range' + - 'for_each_mem_range' + - 'for_each_mem_range_rev' + - 'for_each_migratetype_order' + - 'for_each_msi_entry' + - 'for_each_net' + - 'for_each_netdev' + - 'for_each_netdev_continue' + - 'for_each_netdev_continue_rcu' + - '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_plane_in_state' + - 'for_each_new_private_obj_in_state' + - 'for_each_node' + - 'for_each_node_by_name' + - 'for_each_node_by_type' + - 'for_each_node_mask' + - 'for_each_node_state' + - 'for_each_node_with_cpus' + - 'for_each_node_with_property' + - 'for_each_of_allnodes' + - 'for_each_of_allnodes_from' + - 'for_each_of_pci_range' + - 'for_each_old_connector_in_state' + - 'for_each_old_crtc_in_state' + - 'for_each_oldnew_connector_in_state' + - 'for_each_oldnew_crtc_in_state' + - 'for_each_oldnew_plane_in_state' + - 'for_each_oldnew_private_obj_in_state' + - 'for_each_old_plane_in_state' + - 'for_each_old_private_obj_in_state' + - 'for_each_online_cpu' + - 'for_each_online_node' + - 'for_each_online_pgdat' + - 'for_each_pci_bridge' + - 'for_each_pci_dev' + - 'for_each_pci_msi_entry' + - 'for_each_populated_zone' + - 'for_each_possible_cpu' + - 'for_each_present_cpu' + - 'for_each_prime_number' + - 'for_each_prime_number_from' + - 'for_each_process' + - 'for_each_process_thread' + - 'for_each_property_of_node' + - 'for_each_reserved_mem_region' + - 'for_each_resv_unavail_range' + - 'for_each_rtdcom' + - 'for_each_rtdcom_safe' + - 'for_each_set_bit' + - 'for_each_set_bit_from' + - 'for_each_sg' + - 'for_each_sg_page' + - '__for_each_thread' + - 'for_each_thread' + - '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' + - 'hash_for_each' + - 'hash_for_each_possible' + - 'hash_for_each_possible_rcu' + - 'hash_for_each_possible_rcu_notrace' + - 'hash_for_each_possible_safe' + - 'hash_for_each_rcu' + - 'hash_for_each_safe' + - 'hctx_for_each_ctx' + - 'hlist_bl_for_each_entry' + - 'hlist_bl_for_each_entry_rcu' + - 'hlist_bl_for_each_entry_safe' + - 'hlist_for_each' + - 'hlist_for_each_entry' + - 'hlist_for_each_entry_continue' + - 'hlist_for_each_entry_continue_rcu' + - 'hlist_for_each_entry_continue_rcu_bh' + - 'hlist_for_each_entry_from' + - 'hlist_for_each_entry_from_rcu' + - 'hlist_for_each_entry_rcu' + - 'hlist_for_each_entry_rcu_bh' + - 'hlist_for_each_entry_rcu_notrace' + - 'hlist_for_each_entry_safe' + - '__hlist_for_each_rcu' + - 'hlist_for_each_safe' + - 'hlist_nulls_for_each_entry' + - 'hlist_nulls_for_each_entry_from' + - 'hlist_nulls_for_each_entry_rcu' + - 'hlist_nulls_for_each_entry_safe' + - '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_ul' + - 'inet_bind_bucket_for_each' + - 'inet_lhash2_for_each_icsk_rcu' + - 'iov_for_each' + - 'key_for_each' + - 'key_for_each_safe' + - 'klp_for_each_func' + - 'klp_for_each_object' + - 'kvm_for_each_memslot' + - 'kvm_for_each_vcpu' + - 'list_for_each' + - '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_reverse' + - 'list_for_each_entry_lockless' + - 'list_for_each_entry_rcu' + - 'list_for_each_entry_reverse' + - 'list_for_each_entry_safe' + - 'list_for_each_entry_safe_continue' + - 'list_for_each_entry_safe_from' + - 'list_for_each_entry_safe_reverse' + - 'list_for_each_prev' + - 'list_for_each_prev_safe' + - 'list_for_each_safe' + - 'llist_for_each' + - 'llist_for_each_entry' + - 'llist_for_each_entry_safe' + - 'llist_for_each_safe' + - 'media_device_for_each_entity' + - 'media_device_for_each_intf' + - 'media_device_for_each_link' + - 'media_device_for_each_pad' + - 'netdev_for_each_lower_dev' + - 'netdev_for_each_lower_private' + - 'netdev_for_each_lower_private_rcu' + - 'netdev_for_each_mc_addr' + - 'netdev_for_each_uc_addr' + - 'netdev_for_each_upper_dev_rcu' + - 'netdev_hw_addr_list_for_each' + - 'nft_rule_for_each_expr' + - 'nla_for_each_attr' + - 'nla_for_each_nested' + - 'nlmsg_for_each_attr' + - 'nlmsg_for_each_msg' + - 'nr_neigh_for_each' + - 'nr_neigh_for_each_safe' + - 'nr_node_for_each' + - 'nr_node_for_each_safe' + - 'of_for_each_phandle' + - 'of_property_for_each_string' + - 'of_property_for_each_u32' + - 'pci_bus_for_each_resource' + - 'ping_portaddr_for_each_entry' + - 'plist_for_each' + - 'plist_for_each_continue' + - 'plist_for_each_entry' + - 'plist_for_each_entry_continue' + - 'plist_for_each_entry_safe' + - 'plist_for_each_safe' + - 'pnp_for_each_card' + - 'pnp_for_each_dev' + - 'protocol_for_each_card' + - 'protocol_for_each_dev' + - 'queue_for_each_hw_ctx' + - 'radix_tree_for_each_contig' + - 'radix_tree_for_each_slot' + - 'radix_tree_for_each_tagged' + - 'rbtree_postorder_for_each_entry_safe' + - '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_continue' + - 'rht_for_each_entry' + - 'rht_for_each_entry_continue' + - 'rht_for_each_entry_rcu' + - 'rht_for_each_entry_rcu_continue' + - 'rht_for_each_entry_safe' + - 'rht_for_each_rcu' + - 'rht_for_each_rcu_continue' + - '__rq_for_each_bio' + - '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_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' + - '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' + - 'zorro_for_each_dev' + +#IncludeBlocks: Preserve # Unknown to clang-format-5.0 +IncludeCategories: + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +#IndentPPDirectives: None # Unknown to clang-format-5.0 +IndentWidth: 8 +IndentWrappedFunctionNames: true +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: Inner +#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 +ObjCBlockIndentWidth: 8 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +# Taken from git's rules +#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SortIncludes: false +#SortUsingDeclarations: false # Unknown to clang-format-4.0 +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 +#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 +SpaceBeforeParens: ControlStatements +#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 8 +UseTab: Always +... diff --git a/.gitignore b/.gitignore index fcd6a60..7032bb1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ -build/ -CMakeCache.txt -Makefile -*.so +*.o* +*.ko* +*.symvers +*.mod* +*.mk* +build*/** \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..4c7180e --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "ms-vscode.cpptools", + "xaver.clang-format", + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4dc08b0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "editor.tabSize": 8, + "editor.insertSpaces": false, + "editor.rulers": [ + 80 + ], + "editor.formatOnSave": true, +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..c901648 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,73 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "type": "shell", + "presentation": { + "reveal": "always" + }, + "tasks": [ + { + "label": "Make Driver", + "command": "CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd)", + "group": "build", + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "Clean Driver", + "command": "CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) clean", + "group": "build", + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "Deploy Drivers", + "command": "sudo sh -c './unload.sh && ./loadDrivers.sh'", + "problemMatcher": [] + }, + { + "label": "Make and Deploy Drivers", + "command": "make && sudo sh -c './unload.sh && ./loadDrivers.sh'", + "problemMatcher": [] + }, + { + "label": "Unload new drivers", + "command": "sudo sh unload.sh", + "problemMatcher": [] + }, + { + "label": "Load new drivers", + "command": "sudo sh ./loadDrivers.sh", + "problemMatcher": [] + }, + { + "label": "Make Driver AGL", + "type": "shell", + "command": "./make-agl.sh bf869951", + "group": "build", + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "Clean Driver AGL", + "type": "shell", + "command": "./make-agl.sh bf869951 clean", + "group": "build", + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "Deploy Driver AGL", + "type": "shell", + "command": "rsync -av avirt_core.ko dummy/avirt_dummyap.ko root@192.168.1.198:~/", + "problemMatcher": [ + "$gcc" + ] + }, + ] +} \ No newline at end of file diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000..0e2a52a --- /dev/null +++ b/Kconfig @@ -0,0 +1,20 @@ +menuconfig AVIRT + tristate "AVIRT support" + default n + ---help--- + Say Y here if you want to enable AVIRT support. + This driver needs at least one additional audio path in + order to establish audio routing. + + To compile this driver as a module, choose M here: the + module will be called avirt_core. + + If in doubt, say N here. + + + +if AVIRT + +source "drivers/staging/avirt/dummy/Kconfig" + +endif diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e147c80 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 JOSHANNE + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..88eebbd --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_AVIRT) += avirt_core.o +avirt_core-y := core.o +avirt_core-y += alsa.o +avirt_core-y += alsa-pcm.o + +ifeq ($(CONFIG_AVIRT_BUILDLOCAL),) + CCFLAGS_AVIRT := "drivers/staging/" +else + CCFLAGS_AVIRT := "$(PWD)/../" +endif + +ccflags-y += -I${CCFLAGS_AVIRT} + +$(info $(KERNELRELEASE)) +obj-$(CONFIG_AVIRT_DUMMYAP) += dummy/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..59bcd08 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +Alsa Virtual Sound Driver +-------------------------- +[![pipeline status](https://git.fiberdyne.com.au/JOSHANNE/kmodule_development/badges/master/pipeline.svg)](https://git.fiberdyne.com.au/JOSHANNE/kmodule_development/commits/master) + +The ALSA Virtual Sound Driver (AVIRT) aims to provide a Linux kernel solution to the issue of audio routing in kernel-space, as well as security per-stream, and dynamic configuration of streams at the kernel level. + +A top-level abstract dynamic audio driver is presented to the user-space via an ALSA middle-layer card. From there, respective low-level "real" audio drivers can subscribe to it as an "Audio Path". + +The top-level driver is configured (currently) using module parameters, as is the norm for sound drivers in the Linux tree, however this will utilise a configfs configuration implementation in future. + +A sample dummy Audio Path is provided as an example to show how a low-level audio driver would subscribe to AVIRT, and accept audio routing for playback. + +## Building +### Out Of Tree +The kernel modules can be built either in-tree, or out-of-tree. +To build both AVIRT and the dummy Audio Path out-of-tree, use the following command: + +```sh +$ CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) +``` + +To build both AVIRT and the dummy Audio Path out-of-tree for [AGL](http://docs.automotivelinux.org/) (`aarch64` currently supported), use the [XDS](http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-1/0_Abstract.html) build system together with the `make_agl.sh` script: + +```sh +$ ./make_agl.sh ${XDS_SDK_ID} +``` +### In tree +``` +$ TODO +``` + +## Running +To run, we must load the kernel modules using the `loadDrivers.sh` script, which contains sample module parameters to AVIRT: +```sh +$ ./loadDrivers.sh +``` +To unload the drivers use: +```sh +$ ./unload.sh +``` + +## TODO + - Currently, playback only - implementing capture is WIP. + - Rework module parameters into configfs configuration. + - Create a loopback Audio Path for use with AVIRT, to demonstrate standard AGL soft-mixing capabilities. + - Modify Fiberdyne DSP driver for use with AVRIT, to demonstrate DSP off-loading and hard-mixing capabilities via Xtensa HiFi2. + diff --git a/alsa-pcm.c b/alsa-pcm.c new file mode 100644 index 0000000..bd705f4 --- /dev/null +++ b/alsa-pcm.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ALSA Virtual Soundcard + * + * alsa-pcm.c - ALSA PCM implementation + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#include "core.h" +#include "alsa.h" + +#define DO_AUDIOPATH_CB(callback, substream, ...) \ + do { \ + struct avirt_audiopath *ap; \ + ap = avirt_get_current_audiopath(); \ + CHK_NULL_V(ap, "Cannot find Audio Path!"); \ + if (ap->pcm_ops->callback) { \ + return ap->pcm_ops->callback(substream, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +/** + * configure_pcm - set up substream properties from user configuration + * @substream: pointer to ALSA PCM substream + * @return 0 on success or error code otherwise + */ +static int configure_pcm(struct snd_pcm_substream *substream) +{ + struct avirt_alsa_dev_config *config; + struct avirt_audiopath *audiopath; + struct avirt_alsa_dev_group *group; + struct snd_pcm_hardware *hw; + unsigned bytes_per_sample = 0, blocksize = 0; + + audiopath = avirt_get_current_audiopath(); + CHK_NULL_V(audiopath, "Cannot find Audio Path!"); + + blocksize = audiopath->blocksize; + + // Copy the hw params from the audiopath to the pcm + hw = &substream->runtime->hw; + memcpy(hw, audiopath->hw, sizeof(struct snd_pcm_hardware)); + pr_info("%s %d %d", __func__, blocksize, hw->periods_max); + + if (hw->formats == SNDRV_PCM_FMTBIT_S16_LE) + bytes_per_sample = 2; + else { + pr_err("[%s] PCM only supports SNDRV_PCM_FMTBIT_S16_LE", + __func__); + return -EINVAL; + } + + // Get device group (playback/capture) + group = avirt_alsa_get_dev_group(substream->stream); + CHK_NULL(group); + + // Check if substream id is valid + if (substream->pcm->device >= group->devices) + return -1; + + // Setup remaining hw properties + config = &group->config[substream->pcm->device]; + hw->channels_min = config->channels; + hw->channels_max = config->channels; + hw->buffer_bytes_max = blocksize * hw->periods_max * bytes_per_sample * + config->channels; + hw->period_bytes_min = blocksize * bytes_per_sample * config->channels; + hw->period_bytes_max = blocksize * bytes_per_sample * config->channels; + + return 0; +} + +/******************************************************************************* + * ALSA PCM Callbacks + ******************************************************************************/ +/** + * pcm_open - Implements 'open' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * This is called when an ALSA PCM substream is opened. The substream device is + * configured here. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_open(struct snd_pcm_substream *substream) +{ + // Setup the pcm device based on the configuration assigned + CHK_ERR_V(configure_pcm(substream), "Failed to setup pcm device"); + + // Do additional Audio Path 'open' callback + DO_AUDIOPATH_CB(open, substream); + + return 0; +} + +/** + * pcm_close - Implements 'close' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * This is called when a PCM substream is closed. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_close(struct snd_pcm_substream *substream) +{ + // Do additional Audio Path 'close' callback + DO_AUDIOPATH_CB(close, substream); + + return 0; +} + +/** + * pcm_hw_params - Implements 'hw_params' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * @hw_params: contains the hardware parameters for the PCM + * + * This is called when the hardware parameters are set, including buffer size, + * the period size, the format, etc. The buffer allocation should be done here. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int channels, err; + size_t bufsz; + struct avirt_audiopath *audiopath; + struct avirt_alsa_dev_group *group; + + group = avirt_alsa_get_dev_group(substream->stream); + CHK_NULL(group); + + channels = group->config[substream->pcm->device].channels; + + if ((params_channels(hw_params) > channels) || + (params_channels(hw_params) < channels)) { + pr_err("Requested number of channels not supported.\n"); + return -EINVAL; + } + + audiopath = avirt_get_current_audiopath(); + CHK_NULL_V(audiopath, "Cannot find Audio Path!"); + + bufsz = params_buffer_bytes(hw_params) * audiopath->hw->periods_max; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, bufsz); + if (err <= 0) { + pr_err("pcm: buffer allocation failed (%d)\n", err); + return err; + } + + // Do additional Audio Path 'hw_params' callback + // DO_AUDIOPATH_CB(hw_params, substream, hw_params); + + return 0; +} + +/** + * pcm_hw_free - Implements 'hw_free' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * Release the resources allocated via 'hw_params'. + * This function is always called before the 'close' callback . + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_hw_free(struct snd_pcm_substream *substream) +{ + CHK_ERR(snd_pcm_lib_free_vmalloc_buffer(substream)); + + // Do additional Audio Path 'hw_free' callback + // DO_AUDIOPATH_CB(hw_free, substream); + + return 0; +} + +/** + * pcm_prepare - Implements 'prepare' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * The format rate, sample rate, etc., can be set here. This callback can be + * called many times at each setup. This function is also used to handle overrun + * and underrun conditions when we try and resync the DMA (if we're using DMA). + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_prepare(struct snd_pcm_substream *substream) +{ + struct avirt_alsa_dev_group *group; + struct snd_pcm_runtime *runtime = substream->runtime; + + group = avirt_alsa_get_dev_group(substream->stream); + CHK_NULL(group); + + // Reset HW buffer index for the device + group->streams[substream->pcm->device].hw_frame_idx = 0; + + group->buffersize = frames_to_bytes(runtime, runtime->buffer_size); + + // Do additional Audio Path 'prepare' callback + DO_AUDIOPATH_CB(prepare, substream); + + return 0; +} + +/** + * pcm_trigger - Implements 'trigger' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * @cmd: action to be performed (start or stop) + * + * This is called when the PCM is started, stopped or paused. The action + * indicated action is specified in the second argument, SNDRV_PCM_TRIGGER_XXX + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct avirt_alsa_dev_group *group; + + group = avirt_alsa_get_dev_group(substream->stream); + CHK_NULL(group); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + group->streams[substream->pcm->device].substream = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + break; + default: + pr_err("trigger must be START or STOP"); + return -EINVAL; + } + + // Do additional Audio Path 'trigger' callback + DO_AUDIOPATH_CB(trigger, substream, cmd); + + return 0; +} + +/** + * pcm_pointer - Implements 'pointer' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * This gets called when the user space needs a DMA buffer index. IO errors will + * be generated if the index does not increment, or drives beyond the frame + * threshold of the buffer itself. + * + * Returns the current hardware buffer frame index. + */ +static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) +{ + struct avirt_alsa_dev_group *group; + + group = avirt_alsa_get_dev_group(substream->stream); + CHK_NULL(group); + + return group->streams[substream->pcm->device].hw_frame_idx; +} + +/** + * pcm_pointer - Implements 'get_time_info' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * @system_ts + * @audio_ts + * @audio_tstamp_config + * @audio_tstamp_report + * + * Generic way to get system timestamp and audio timestamp info + * + * Returns 0 on success or error code otherwise + */ +static int pcm_get_time_info( + struct snd_pcm_substream *substream, struct timespec *system_ts, + struct timespec *audio_ts, + struct snd_pcm_audio_tstamp_config *audio_tstamp_config, + struct snd_pcm_audio_tstamp_report *audio_tstamp_report) +{ + struct avirt_alsa_dev_group *group; + + group = avirt_alsa_get_dev_group(substream->stream); + CHK_NULL(group); + + DO_AUDIOPATH_CB(get_time_info, substream, system_ts, audio_ts, + audio_tstamp_config, audio_tstamp_report); + + return 0; +} + +/** + * pcm_copy_user - Implements 'copy_user' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * @channel: + * @pos: The offset in the DMA + * @src: Audio PCM data from the user space + * @count: + * + * This is where we need to copy user audio PCM data into the sound driver + * + * Returns 0 on success or error code otherwise. + * + */ +static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t pos, void __user *src, + snd_pcm_uframes_t count) +{ + //struct snd_pcm_runtime *runtime; + //int offset; + + //runtime = substream->runtime; + //offset = frames_to_bytes(runtime, pos); + + // Do additional Audio Path 'copy_user' callback + DO_AUDIOPATH_CB(copy_user, substream, channel, pos, src, count); + + return 0; +} + +/** + * pcm_copy_kernel - Implements 'copy_kernel' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * @channel: + * @pos: The offset in the DMA + * @src: Audio PCM data from the user space + * @count: + * + * This is where we need to copy kernel audio PCM data into the sound driver + * + * Returns 0 on success or error code otherwise. + * + */ +static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void *buf, unsigned long count) +{ + DO_AUDIOPATH_CB(copy_kernel, substream, channel, pos, buf, count); + return 0; +} + +/** + * pcm_ack - Implements 'ack' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * This is where we need to ack + * + * Returns 0 on success or error code otherwise. + * + */ +int pcm_ack(struct snd_pcm_substream *substream) +{ + DO_AUDIOPATH_CB(ack, substream); + return 0; +} + +static int pcm_silence(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +{ + DO_AUDIOPATH_CB(fill_silence, substream, channel, pos, count); + return 0; +} + +struct snd_pcm_ops pcm_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_prepare, + .trigger = pcm_trigger, + .pointer = pcm_pointer, + .get_time_info = pcm_get_time_info, + .fill_silence = pcm_silence, + .copy_user = pcm_copy_user, + .copy_kernel = pcm_copy_kernel, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + .ack = pcm_ack, + +}; diff --git a/alsa.c b/alsa.c new file mode 100755 index 0000000..0b08744 --- /dev/null +++ b/alsa.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ALSA Virtual Soundcard + * + * alsa.c - ALSA PCM driver for virtual ALSA card + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#include +#include +#include + +#include "alsa.h" +#include "core.h" + +static struct avirt_alsa_driver *_driver = NULL; + +extern struct snd_pcm_ops pcm_ops; + +/** + * pcm_constructor - Constructs the ALSA PCM middle devices for this driver + * @card: The snd_card struct to construct the devices for + * @return 0 on success or error code otherwise + */ +static int pcm_constructor(struct snd_card *card) +{ + struct snd_pcm *pcm; + int i; + + // Allocate Playback PCM instances + for (i = 0; i < _driver->playback.devices; i++) { + CHK_ERR(snd_pcm_new(card, + _driver->playback.config[i].devicename, i, + 1, 0, &pcm)); + + /** Register driver callbacks */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, _driver->playback.config[i].devicename); + } + + // Allocate Capture PCM instances + for (i = 0; i < _driver->capture.devices; i++) { + CHK_ERR(snd_pcm_new(card, _driver->capture.config[i].devicename, + i, 0, 1, &pcm)); + + /** Register driver callbacks */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, _driver->capture.config[i].devicename); + } + + return 0; +} + +/** + * alloc_dev_config - Allocates memory for ALSA device configuration + * @return: 0 on success or error code otherwise + */ +static int alloc_dev_config(struct avirt_alsa_dev_config **devconfig, + struct avirt_alsa_dev_config *userconfig, + unsigned numdevices) +{ + if (numdevices == 0) + return 0; + + *devconfig = kzalloc(sizeof(struct avirt_alsa_dev_config) * numdevices, + GFP_KERNEL); + if (!(*devconfig)) + return -ENOMEM; + + memcpy(*devconfig, userconfig, + sizeof(struct avirt_alsa_dev_config) * numdevices); + + return 0; +} + +/** + * alloc_dev_streams - Initializes ALSA device substream buffers + * @return: 0 on success or error code otherwise + */ +static int alloc_dev_streams(struct avirt_alsa_dev_config *config, + struct avirt_alsa_stream **streams, + unsigned numdevices) +{ + unsigned i; + + if (numdevices == 0) + return 0; + + *streams = kzalloc(sizeof(struct avirt_alsa_stream) * numdevices, + GFP_KERNEL); + + if (!(*streams)) + return -EFAULT; + + for (i = 0; i < numdevices; i++) + (*streams)[i].hw_frame_idx = 0; + + return 0; +} + +struct avirt_alsa_dev_group *avirt_alsa_get_dev_group(int direction) +{ + if (!_driver) { + pr_err("[%s] _driver is NULL", __func__); + return NULL; + } + + switch (direction) { + case SNDRV_PCM_STREAM_PLAYBACK: + return &_driver->playback; + case SNDRV_PCM_STREAM_CAPTURE: + return &_driver->capture; + default: + pr_err("[%s] Direction must be SNDRV_PCM_STREAM_XXX!", + __func__); + return NULL; + } +} + +/** + * avirt_alsa_init - Initializes the ALSA driver + * @return: 0 on success or error code otherwise + */ +int avirt_alsa_init() +{ + // Allocate memory for the driver + _driver = kzalloc(sizeof(struct avirt_alsa_driver), GFP_KERNEL); + if (!_driver) + return -ENOMEM; + + return 0; +} + +/** + * avirt_alsa_configure_pcm- Configure the PCM device + * @config: Device configuration structure array + * @direction: Direction of PCM (SNDRV_PCM_STREAM_XXX) + * @numdevices: Number of devices (array length) + * @return: 0 on success or error code otherwise + */ +int avirt_alsa_configure_pcm(struct avirt_alsa_dev_config *config, + int direction, unsigned numdevices) +{ + struct avirt_alsa_dev_group *group; + + group = avirt_alsa_get_dev_group(direction); + CHK_NULL(group); + + CHK_ERR(alloc_dev_config(&group->config, config, numdevices)); + + group->devices = numdevices; + + CHK_ERR(alloc_dev_streams(group->config, &group->streams, + group->devices)); + return 0; +} + +/** + * avirt_alsa_register - Registers the ALSA driver + * @devptr: Platform driver device + * @return: 0 on success or error code otherwise + */ +int avirt_alsa_register(struct platform_device *devptr) +{ + struct snd_card *card; + static struct snd_device_ops device_ops; + + if (!_driver) + return -EFAULT; + + // Create the card instance + CHK_ERR_V(snd_card_new(&devptr->dev, SNDRV_DEFAULT_IDX1, "avirt", + THIS_MODULE, 0, &card), + "Failed to create sound card"); + + strcpy(card->driver, "avirt-alsa-device"); + strcpy(card->shortname, "avirt"); + strcpy(card->longname, "A virtual sound card driver for ALSA"); + _driver->card = card; + + // Create new sound device + CHK_ERR_V((snd_device_new(card, SNDRV_DEV_LOWLEVEL, _driver, + &device_ops)), + "Failed to create sound device"); + + CHK_ERR((pcm_constructor(card))); + + /** Register with the ALSA framework */ + CHK_ERR_V(snd_card_register(card), "Device registration failed"); + + return 0; +} + +/** + * avirt_alsa_deregister - Deregisters the ALSA driver + * @devptr: Platform driver device + * @return: 0 on success or error code otherwise + */ +int avirt_alsa_deregister(void) +{ + snd_card_free(_driver->card); + + if (_driver->playback.config) + kfree(_driver->playback.config); + if (_driver->playback.streams) + kfree(_driver->playback.streams); + if (_driver->capture.config) + kfree(_driver->capture.config); + if (_driver->capture.streams) + kfree(_driver->capture.streams); + + kfree(_driver); + + return 0; +} + +/** + * pcm_buff_complete_cb - PCM buffer complete callback + * @substreamid: pointer to ALSA PCM substream + * @return 0 on success or error code otherwise + * + * This should be called from a child Audio Path once it has finished processing + * the pcm buffer + */ +int pcm_buff_complete_cb(struct snd_pcm_substream *substream) +{ + int maxframe, deviceid; + struct avirt_audiopath *audiopath; + struct avirt_alsa_dev_group *group; + + deviceid = substream->pcm->device; + + group = avirt_alsa_get_dev_group(substream->stream); + CHK_NULL(group); + + audiopath = avirt_get_current_audiopath(); + CHK_NULL_V(audiopath, "Cannot find Audio Path!"); + + group->streams[deviceid].hw_frame_idx += audiopath->blocksize; + maxframe = audiopath->blocksize * audiopath->hw->periods_max; + + // Once the index reaches the DMA buffer boundary, reset it to 0 + if (group->streams[deviceid].hw_frame_idx >= maxframe) + group->streams[deviceid].hw_frame_idx = 0; + + // Notify ALSA middle layer of the elapsed period boundary + snd_pcm_period_elapsed(group->streams[deviceid].substream); + + return 0; +} diff --git a/alsa.h b/alsa.h new file mode 100755 index 0000000..29fc64d --- /dev/null +++ b/alsa.h @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ALSA Virtual Soundcard + * + * alsa.h - Top level ALSA interface + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#ifndef __AVIRT_ALSA_H__ +#define __AVIRT_ALSA_H__ + +#include + +#define MAX_NAME_LEN 32 + +#define PRINT_ERR(errno, errmsg, ...) \ + pr_err("[%s()] %s [ERRNO:%d]", __func__, errmsg, ##__VA_ARGS__, errno); + +#define CHK_ERR(errno) \ + do { \ + if (errno < 0) \ + return errno; \ + } while (0) + +#define CHK_ERR_V(errno, errmsg, ...) \ + do { \ + if (errno < 0) { \ + PRINT_ERR(errno, errmsg, ##__VA_ARGS__) \ + return errno; \ + } \ + } while (0) + +#define CHK_NULL(x) \ + do { \ + if (!x) \ + return -EFAULT; \ + } while (0) + +#define CHK_NULL_V(x, errmsg, ...) \ + do { \ + if (!x) { \ + PRINT_ERR(EFAULT, errmsg, ##__VA_ARGS__) \ + return -EFAULT; \ + } \ + } while (0) + +/* + * Substream device configuration + */ +struct avirt_alsa_dev_config { + const char devicename[MAX_NAME_LEN]; + int channels; +}; + +/** + * Stream maintainance + */ +struct avirt_alsa_stream { + int hw_frame_idx; + struct snd_pcm_substream *substream; +}; + +/** + * Collection of devices + */ +struct avirt_alsa_dev_group { + struct avirt_alsa_dev_config *config; + struct avirt_alsa_stream *streams; + int devices; + int buffersize; +}; + +/** + * ALSA driver data + */ +struct avirt_alsa_driver { + struct snd_card *card; + struct avirt_alsa_dev_group playback; + struct avirt_alsa_dev_group capture; +}; + +/** + * avirt_alsa_init - Initializes the ALSA driver + * @return: 0 on success or error code otherwise + */ +int avirt_alsa_init(void); + +/** + * avirt_alsa_configure_pcm- Configure the PCM device + * @config: Device configuration structure array + * @direction: Direction of PCM (SNDRV_PCM_STREAM_XXX) + * @numdevices: Number of devices (array length) + * @return: 0 on success or error code otherwise + */ +int avirt_alsa_configure_pcm(struct avirt_alsa_dev_config *config, + int direction, unsigned numdevices); + +/** + * avirt_alsa_register - Registers the ALSA driver + * @devptr: Platform driver device + * @return: 0 on success or error code otherwise + */ +int avirt_alsa_register(struct platform_device *devptr); + +/** + * avirt_alsa_deregister - Deregisters the ALSA driver + * @devptr: Platform driver device + * @return: 0 on success or error code otherwise + */ +int avirt_alsa_deregister(void); + +/** + * avirt_alsa_get_dev_group - Gets the device group for the specified direction + * @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE + * @return: Either the playback or capture device group on success, + * or NULL otherwise + */ +struct avirt_alsa_dev_group *avirt_alsa_get_dev_group(int direction); + +/** + * pcm_buff_complete_cb - PCM buffer complete callback + * @substream: pointer to ALSA PCM substream + * @return 0 on success or error code otherwise + * + * This should be called from a child Audio Path once it has finished processing + * the PCM buffer + */ +int pcm_buff_complete_cb(struct snd_pcm_substream *substream); + +#endif // __AVIRT_ALSA_H__ diff --git a/core.c b/core.c new file mode 100644 index 0000000..d0a24ec --- /dev/null +++ b/core.c @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ALSA Virtual Soundcard + * + * core.c - Implementation of core module for virtual ALSA card + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#include +#include +#include + +#include "avirt/core.h" +#include "alsa.h" + +MODULE_AUTHOR("JOSHANNE "); +MODULE_AUTHOR("MFARRUGI "); +MODULE_DESCRIPTION("A configurable virtual soundcard"); +MODULE_LICENSE("GPL v2"); + +#define SND_AVIRTUAL_DRIVER "snd_avirt" +#define MAX_PCM_DEVS 8 +#define MAX_AUDIOPATHS 4 +#define DEFAULT_FILE_PERMS 0644 + +/* Number of playback devices to create (max = MAX_PCM_DEVS) */ +static unsigned playback_num = 0; +/* Number of capture devices to create (max = MAX_PCM_DEVS) */ +static unsigned capture_num = 0; +/* Names per playback device */ +static char *playback_names[MAX_PCM_DEVS]; +/* Names per capture device */ +static char *capture_names[MAX_PCM_DEVS]; +/* Channels per playback device */ +static unsigned playback_chans[MAX_PCM_DEVS]; +/* Channels per capture device */ +static unsigned capture_chans[MAX_PCM_DEVS]; + +module_param(playback_num, int, DEFAULT_FILE_PERMS); +MODULE_PARM_DESC(playback_num, + "Number of playback devices to create (max = MAX_PCM_DEVS)"); +module_param(capture_num, int, DEFAULT_FILE_PERMS); +MODULE_PARM_DESC(capture_num, + "Number of capture devices to create (max = MAX_PCM_DEVS)"); +module_param_array(playback_names, charp, NULL, DEFAULT_FILE_PERMS); +MODULE_PARM_DESC(playback_names, "Names per playback device"); +module_param_array(capture_names, charp, NULL, DEFAULT_FILE_PERMS); +MODULE_PARM_DESC(capture_names, "Names per capture device"); +module_param_array(playback_chans, int, NULL, DEFAULT_FILE_PERMS); +MODULE_PARM_DESC(playback_chans, "Channels per playback device"); +module_param_array(capture_chans, int, NULL, DEFAULT_FILE_PERMS); +MODULE_PARM_DESC(capture_chans, "Channels per capture device"); + +static struct avirt_core { + struct device *dev; + + struct class *avirt_class; + struct platform_device *platform_dev; +} core; + +static struct avirt_coreinfo coreinfo = { + .version = { 0, 0, 1 }, + .pcm_buff_complete = pcm_buff_complete_cb, +}; + +static struct list_head audiopath_list; + +/** + * avirt_probe - Register ALSA soundcard + * @devptr: Platform driver device + * @return: 0 on success or error code otherwise + */ +static int avirt_probe(struct platform_device *devptr) +{ + // struct avirt_alsa_dev_config capture_config[MAX_PCM_DEVS]; + struct avirt_alsa_dev_config playback_config[MAX_PCM_DEVS]; + int err = 0, i = 0; + + if (playback_num == 0 && capture_num == 0) { + pr_err("playback_num or capture_num must be greater than 0!\n"); + return -EINVAL; + } + + coreinfo.playback_num = playback_num; + coreinfo.capture_num = capture_num; + + err = avirt_alsa_init(); + if (err < 0) + return err; + + // Set up playback + for (i = 0; i < playback_num; i++) { + if (!playback_names[i]) { + pr_err("Playback config devicename is NULL for idx=%d\n", + i); + return -EINVAL; + } + memcpy((char *)playback_config[i].devicename, playback_names[i], + MAX_NAME_LEN); + if (playback_chans[i] == 0) { + pr_err("Playback config channels is 0 for idx=%d\n", i); + return -EINVAL; + } + playback_config[i].channels = playback_chans[i]; + } + err = avirt_alsa_configure_pcm(playback_config, + SNDRV_PCM_STREAM_PLAYBACK, playback_num); + if (err < 0) + return err; + +// Does not work yet! +#if 0 + // Set up capture + for (i = 0; i < capture_num; i++) { + if (!capture_names[i]) { + pr_err("Capture config devicename is NULL for idx=%d", + i); + return -EINVAL; + } + memcpy((char *)capture_config[i].devicename, capture_names[i], + 255); + + if (capture_chans[i] == 0) { + pr_err("Capture config channels is 0 for idx=%d"); + return -EINVAL; + } + capture_config[i].channels = capture_chans[i]; + } + err = avirt_alsa_configure_pcm(capture_config, SNDRV_PCM_STREAM_CAPTURE, capture_num); + if (err < 0) + return err; +#endif + + // Register for ALSA + CHK_ERR(avirt_alsa_register(devptr)); + + return err; +} + +/** + * avirt_remove - Deregister ALSA soundcard + * @devptr: Platform driver device + * @return: 0 on success or error code otherwise + */ +static int avirt_remove(struct platform_device *devptr) +{ + avirt_alsa_deregister(); + + return 0; +} + +static struct platform_driver avirt_driver = { + .probe = avirt_probe, + .remove = avirt_remove, + .driver = + { + .name = SND_AVIRTUAL_DRIVER, + }, +}; + +struct avirt_audiopath_obj { + struct kobject kobj; + struct list_head list; + struct avirt_audiopath *path; +}; + +static struct kset *avirt_audiopath_kset; +static struct kobject *kobj; + +#define to_audiopath_obj(d) container_of(d, struct avirt_audiopath_obj, kobj) +#define to_audiopath_attr(a) \ + container_of(a, struct avirt_audiopath_attribute, attr) + +/** + * struct avirt_audiopath_attribute - access the attributes of Audio Path + * @attr: attributes of an Audio Path + * @show: pointer to the show function + * @store: pointer to the store function + */ +struct avirt_audiopath_attribute { + struct attribute attr; + ssize_t (*show)(struct avirt_audiopath_obj *d, + struct avirt_audiopath_attribute *attr, char *buf); + ssize_t (*store)(struct avirt_audiopath_obj *d, + struct avirt_audiopath_attribute *attr, + const char *buf, size_t count); +}; + +/** + * audiopath_attr_show - show function of an Audio Path + * @kobj: pointer to kobject + * @attr: pointer to attribute struct + * @buf: buffer + */ +static ssize_t audiopath_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct avirt_audiopath_attribute *audiopath_attr; + struct avirt_audiopath_obj *audiopath_obj; + + audiopath_attr = to_audiopath_attr(attr); + audiopath_obj = to_audiopath_obj(kobj); + + if (!audiopath_attr->show) + return -EIO; + + return audiopath_attr->show(audiopath_obj, audiopath_attr, buf); +} + +/** + * audiopath_attr_store - attribute store function of Audio Path object + * @kobj: pointer to kobject + * @attr: pointer to attribute struct + * @buf: buffer + * @len: length of buffer + */ +static ssize_t audiopath_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t len) +{ + struct avirt_audiopath_attribute *audiopath_attr; + struct avirt_audiopath_obj *audiopath_obj; + + audiopath_attr = to_audiopath_attr(attr); + audiopath_obj = to_audiopath_obj(kobj); + + if (!audiopath_attr->store) + return -EIO; + return audiopath_attr->store(audiopath_obj, audiopath_attr, buf, len); +} + +static const struct sysfs_ops avirt_audiopath_sysfs_ops = { + .show = audiopath_attr_show, + .store = audiopath_attr_store, +}; + +/** + * avirt_audiopath_release - Audio Path release function + * @kobj: pointer to Audio Paths's kobject + */ +static void avirt_audiopath_release(struct kobject *kobj) +{ + struct avirt_audiopath_obj *audiopath_obj = to_audiopath_obj(kobj); + + kfree(audiopath_obj); +} + +static ssize_t audiopath_name_show(struct avirt_audiopath_obj *audiopath_obj, + struct avirt_audiopath_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", audiopath_obj->path->name); +} + +static ssize_t audiopath_version_show(struct avirt_audiopath_obj *audiopath_obj, + struct avirt_audiopath_attribute *attr, + char *buf) +{ + struct avirt_audiopath *audiopath = audiopath_obj->path; + return sprintf(buf, "%d.%d.%d\n", audiopath->version[0], + audiopath->version[1], audiopath->version[2]); +} + +static struct avirt_audiopath_attribute avirt_audiopath_attrs[] = { + __ATTR_RO(audiopath_name), + __ATTR_RO(audiopath_version), +}; + +static struct attribute *avirt_audiopath_def_attrs[] = { + &avirt_audiopath_attrs[0].attr, + &avirt_audiopath_attrs[1].attr, + NULL, +}; + +static struct kobj_type avirt_audiopath_ktype = { + .sysfs_ops = &avirt_audiopath_sysfs_ops, + .release = avirt_audiopath_release, + .default_attrs = avirt_audiopath_def_attrs, +}; + +/** + * create_avirt_audiopath_obj - creates an Audio Path object + * @name: name of the Audio Path + * + * This creates an Audio Path object and assigns the kset and registers + * it with sysfs. + * @return: Pointer to the Audio Path object or NULL if it failed. + */ +static struct avirt_audiopath_obj *create_avirt_audiopath_obj(const char *name) +{ + struct avirt_audiopath_obj *avirt_audiopath; + int retval; + + avirt_audiopath = kzalloc(sizeof(*avirt_audiopath), GFP_KERNEL); + if (!avirt_audiopath) + return NULL; + avirt_audiopath->kobj.kset = avirt_audiopath_kset; + retval = kobject_init_and_add(&avirt_audiopath->kobj, + &avirt_audiopath_ktype, kobj, "%s", name); + if (retval) { + kobject_put(&avirt_audiopath->kobj); + return NULL; + } + kobject_uevent(&avirt_audiopath->kobj, KOBJ_ADD); + return avirt_audiopath; +} + +/** + * destroy_avirt_audiopath_obj - destroys an Audio Path object + * @name: the Audio Path object + */ +static void destroy_avirt_audiopath_obj(struct avirt_audiopath_obj *p) +{ + kobject_put(&p->kobj); +} + +/** + * avirt_get_current_audiopath - retrieves the current Audio Path + * @return: Current Audio Path + */ +struct avirt_audiopath *avirt_get_current_audiopath() +{ + struct avirt_audiopath_obj *ap_obj = list_entry( + audiopath_list.next, struct avirt_audiopath_obj, list); + return ap_obj->path; +} + +/** + * avirt_register_audiopath - register Audio Path with ALSA virtual driver + * @audiopath: Audio Path to be registered + * @core: ALSA virtual driver core info + * @return: 0 on success or error code otherwise + */ +int avirt_register_audiopath(struct avirt_audiopath *audiopath, + struct avirt_coreinfo **info) +{ + struct avirt_audiopath_obj *audiopath_obj; + + if (!audiopath) { + pr_err("Bad audio path\n"); + return -EINVAL; + } + + audiopath_obj = create_avirt_audiopath_obj(audiopath->name); + if (!audiopath_obj) { + pr_info("failed to alloc driver object\n"); + return -ENOMEM; + } + audiopath_obj->path = audiopath; + + audiopath->context = audiopath_obj; + pr_info("Registered new Audio Path: %s\n", audiopath->name); + pr_info("\tBlocksize: %d, Periods: %d\n", audiopath->blocksize, + audiopath->hw->periods_max); + list_add_tail(&audiopath_obj->list, &audiopath_list); + + *info = &coreinfo; + + return 0; +} +EXPORT_SYMBOL_GPL(avirt_register_audiopath); + +/** + * avirt_deregister_audiopath - deregister Audio Path with ALSA virtual driver + * @audiopath: Audio Path to be deregistered + * @return: 0 on success or error code otherwise + */ +int avirt_deregister_audiopath(struct avirt_audiopath *audiopath) +{ + struct avirt_audiopath_obj *audiopath_obj; + + // Check if audio path is registered + if (!audiopath) { + pr_err("Bad Audio Path Driver\n"); + return -EINVAL; + } + + audiopath_obj = audiopath->context; + if (!audiopath_obj) { + pr_info("driver not registered.\n"); + return -EINVAL; + } + + list_del(&audiopath_obj->list); + destroy_avirt_audiopath_obj(audiopath_obj); + pr_info("Deregistered Audio Path %s\n", audiopath->name); + + return 0; +} +EXPORT_SYMBOL_GPL(avirt_deregister_audiopath); + +/** + * avirt_unregister_all - Unregister the platform device driver + */ +static void avirt_unregister_all(void) +{ + platform_device_unregister(core.platform_dev); + platform_driver_unregister(&avirt_driver); +} + +/** + * core_init - Initialize the kernel module + */ +static int __init core_init(void) +{ + int err; + + pr_info("Alsa Virtual Sound Driver avirt-%d.%d.%d\n", + coreinfo.version[0], coreinfo.version[1], coreinfo.version[2]); + + // Initialize audiopaths linked list + INIT_LIST_HEAD(&audiopath_list); + + err = platform_driver_register(&avirt_driver); + if (err < 0) + return err; + + core.platform_dev = platform_device_register_simple(SND_AVIRTUAL_DRIVER, + 0, NULL, 0); + if (IS_ERR(core.platform_dev)) { + err = PTR_ERR(core.platform_dev); + pr_err("platform_dev error [%d]!\n", err); + goto exit_platform_device; + } + + core.avirt_class = class_create(THIS_MODULE, SND_AVIRTUAL_DRIVER); + if (IS_ERR(core.avirt_class)) { + pr_err("No udev support\n"); + err = PTR_ERR(core.avirt_class); + goto exit_bus; + } + + core.dev = device_create(core.avirt_class, NULL, 0, NULL, "avirtcore"); + if (IS_ERR(core.dev)) { + err = PTR_ERR(core.dev); + goto exit_class; + } + + avirt_audiopath_kset = + kset_create_and_add("audiopaths", NULL, &core.dev->kobj); + if (!avirt_audiopath_kset) { + err = -ENOMEM; + goto exit_class_container; + } + + return 0; + +exit_class_container: + device_destroy(core.avirt_class, 0); +exit_class: + class_destroy(core.avirt_class); +exit_bus: +exit_platform_device: + avirt_unregister_all(); + return err; +} + +/** + * core_exit - Destroy the kernel module + */ +static void __exit core_exit(void) +{ + kset_unregister(avirt_audiopath_kset); + device_destroy(core.avirt_class, 0); + class_destroy(core.avirt_class); + + avirt_unregister_all(); + + pr_info("playback_num: %d, capture_num: %d\n", playback_num, + capture_num); + + pr_info("playback_chans: %d %d %d %d %d %d %d %d\n", playback_chans[0], + playback_chans[1], playback_chans[2], playback_chans[3], + playback_chans[4], playback_chans[5], playback_chans[6], + playback_chans[7]); + + pr_info("capture_chans: %d %d %d %d %d %d %d %d\n", capture_chans[0], + capture_chans[1], capture_chans[2], capture_chans[3], + capture_chans[4], capture_chans[5], capture_chans[6], + capture_chans[7]); + + pr_info("playback_names: %s %s %s %s %s %s %s %s\n", playback_names[0], + playback_names[1], playback_names[2], playback_names[3], + playback_names[4], playback_names[5], playback_names[6], + playback_names[7]); + + pr_info("capture_names: %s %s %s %s %s %s %s %s\n", capture_names[0], + capture_names[1], capture_names[2], capture_names[3], + capture_names[4], capture_names[5], capture_names[6], + capture_names[7]); +} + +module_init(core_init); +module_exit(core_exit); diff --git a/core.h b/core.h new file mode 100644 index 0000000..5eb6026 --- /dev/null +++ b/core.h @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ALSA Virtual Soundcard + * + * core.h - System-level header for virtual ALSA card + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#ifndef __AVIRT_CORE_H__ +#define __AVIRT_CORE_H__ + +#include + +/** + * PCM buffer complete callback + * + * These are called from the audiopath when a PCM buffer has completed, and + * new data can be submitted/retrieved + */ +typedef int (*avirt_buff_complete)(struct snd_pcm_substream *substream); + +struct avirt_audiopath { + const char *name; + unsigned version[3]; + int value; + struct snd_pcm_hardware *hw; + struct snd_pcm_ops *pcm_ops; + unsigned blocksize; + + void *context; +}; + +struct avirt_coreinfo { + unsigned version[3]; + unsigned playback_num; + unsigned capture_num; + + avirt_buff_complete pcm_buff_complete; +}; + +/** + * avirt_register_audiopath - register Audio Path with ALSA virtual driver + * @audiopath: Audio Path to be registered + * @core: ALSA virtual driver core info + * @return: 0 on success or error code otherwise + */ +int avirt_register_audiopath(struct avirt_audiopath *audiopath, + struct avirt_coreinfo **coreinfo); + +/** + * avirt_deregister_audiopath - deregister Audio Path with ALSA virtual driver + * @audiopath: Audio Path to be deregistered + * @return: 0 on success or error code otherwise + */ +int avirt_deregister_audiopath(struct avirt_audiopath *audiopath); + +/** + * avirt_get_current_audiopath - retrieves the current Audio Path + * @return: Current Audio Path + */ +struct avirt_audiopath *avirt_get_current_audiopath(void); + +#endif // __AVIRT_CORE_H__ diff --git a/dummy/Kconfig b/dummy/Kconfig new file mode 100644 index 0000000..6a706c0 --- /dev/null +++ b/dummy/Kconfig @@ -0,0 +1,12 @@ +# +# AVIRT Dummy Audio Path +# + +config AVIRT_DUMMYAP + tristate "DummyAP" + select SND_PCM + ---help--- + Say Y here if you want to add dummy audio path. + + To compile this driver as a module, choose M here: the + module will be called avirt_dummyap. diff --git a/dummy/Makefile b/dummy/Makefile new file mode 100644 index 0000000..965bd1a --- /dev/null +++ b/dummy/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_AVIRT_DUMMYAP) += avirt_dummyap.o + +$(info $(src)) +avirt_dummyap-objs := dummy.o +ccflags-y += -Idrivers/staging/ +ccflags-y += -I$(src)/../../ diff --git a/dummy/dummy.c b/dummy/dummy.c new file mode 100644 index 0000000..7522345 --- /dev/null +++ b/dummy/dummy.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ALSA Virtual Soundcard + * + * dummy_audiopath.c - AVIRT sample Audio Path definition + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#include +#include +#include + +MODULE_AUTHOR("JOSHANNE "); +MODULE_AUTHOR("MFARRUGI "); +MODULE_DESCRIPTION("Sample Audio Path Module Interface"); +MODULE_LICENSE("GPL"); + +#define DUMMY_SAMPLE_RATE 48000 +#define DUMMY_BLOCKSIZE 512 +#define DUMMY_PERIODS_MIN 1 +#define DUMMY_PERIODS_MAX 8 + +#define get_dummy_ops(substream) \ + (*(const struct dummy_timer_ops **)(substream)->runtime->private_data) + +static struct avirt_coreinfo *coreinfo; + +/******************************************************************************* + * System Timer Interface + * + * This is used to call the ALSA PCM callbacks required to notify AVIRT of the + * 'periods elapsed', so that buffers can keep getting transmitted here + * + * (Borrowed from the default ALSA 'dummy' driver (sound/driver/dummy.c)) + ******************************************************************************/ +struct dummy_timer_ops { + int (*create)(struct snd_pcm_substream *); + void (*free)(struct snd_pcm_substream *); + int (*prepare)(struct snd_pcm_substream *); + int (*start)(struct snd_pcm_substream *); + int (*stop)(struct snd_pcm_substream *); + snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *); +}; + +struct dummy_systimer_pcm { + /* ops must be the first item */ + const struct dummy_timer_ops *timer_ops; + spinlock_t lock; + struct timer_list timer; + unsigned long base_time; + unsigned int frac_pos; /* fractional sample position (based HZ) */ + unsigned int frac_period_rest; + unsigned int frac_buffer_size; /* buffer_size * HZ */ + unsigned int frac_period_size; /* period_size * HZ */ + unsigned int rate; + int elapsed; + struct snd_pcm_substream *substream; +}; + +static void dummy_systimer_rearm(struct dummy_systimer_pcm *dpcm) +{ + mod_timer(&dpcm->timer, jiffies + (dpcm->frac_period_rest + dpcm->rate - + 1) / dpcm->rate); +} + +static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm) +{ + unsigned long delta; + + delta = jiffies - dpcm->base_time; + if (!delta) + return; + dpcm->base_time += delta; + delta *= dpcm->rate; + dpcm->frac_pos += delta; + while (dpcm->frac_pos >= dpcm->frac_buffer_size) + dpcm->frac_pos -= dpcm->frac_buffer_size; + while (dpcm->frac_period_rest <= delta) { + dpcm->elapsed++; + dpcm->frac_period_rest += dpcm->frac_period_size; + } + dpcm->frac_period_rest -= delta; +} + +static int dummy_systimer_start(struct snd_pcm_substream *substream) +{ + struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; + spin_lock(&dpcm->lock); + dpcm->base_time = jiffies; + dummy_systimer_rearm(dpcm); + spin_unlock(&dpcm->lock); + return 0; +} + +static int dummy_systimer_stop(struct snd_pcm_substream *substream) +{ + struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; + spin_lock(&dpcm->lock); + del_timer(&dpcm->timer); + spin_unlock(&dpcm->lock); + return 0; +} + +static int dummy_systimer_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct dummy_systimer_pcm *dpcm = runtime->private_data; + + dpcm->frac_pos = 0; + dpcm->rate = runtime->rate; + dpcm->frac_buffer_size = runtime->buffer_size * HZ; + dpcm->frac_period_size = runtime->period_size * HZ; + dpcm->frac_period_rest = dpcm->frac_period_size; + dpcm->elapsed = 0; + + return 0; +} + +static void dummy_systimer_callback(struct timer_list *t) +{ + struct dummy_systimer_pcm *dpcm = from_timer(dpcm, t, timer); + unsigned long flags; + int elapsed = 0; + + spin_lock_irqsave(&dpcm->lock, flags); + dummy_systimer_update(dpcm); + dummy_systimer_rearm(dpcm); + elapsed = dpcm->elapsed; + dpcm->elapsed = 0; + spin_unlock_irqrestore(&dpcm->lock, flags); + if (elapsed) + coreinfo->pcm_buff_complete(dpcm->substream); +} + +static snd_pcm_uframes_t + dummy_systimer_pointer(struct snd_pcm_substream *substream) +{ + struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; + snd_pcm_uframes_t pos; + + spin_lock(&dpcm->lock); + dummy_systimer_update(dpcm); + pos = dpcm->frac_pos / HZ; + spin_unlock(&dpcm->lock); + return pos; +} + +static int dummy_systimer_create(struct snd_pcm_substream *substream) +{ + struct dummy_systimer_pcm *dpcm; + + dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); + if (!dpcm) + return -ENOMEM; + substream->runtime->private_data = dpcm; + timer_setup(&dpcm->timer, dummy_systimer_callback, 0); + spin_lock_init(&dpcm->lock); + dpcm->substream = substream; + return 0; +} + +static void dummy_systimer_free(struct snd_pcm_substream *substream) +{ + kfree(substream->runtime->private_data); +} + +static const struct dummy_timer_ops dummy_systimer_ops = { + .create = dummy_systimer_create, + .free = dummy_systimer_free, + .prepare = dummy_systimer_prepare, + .start = dummy_systimer_start, + .stop = dummy_systimer_stop, + .pointer = dummy_systimer_pointer, +}; + +/******************************************************************************* + * Audio Path ALSA PCM Callbacks + ******************************************************************************/ +static int dummy_pcm_open(struct snd_pcm_substream *substream) +{ + const struct dummy_timer_ops *ops; + int err; + + ops = &dummy_systimer_ops; + err = ops->create(substream); + if (err < 0) + return err; + get_dummy_ops(substream) = ops; + + return 0; +} + +static int dummy_pcm_close(struct snd_pcm_substream *substream) +{ + get_dummy_ops(substream)->free(substream); + return 0; +} + +static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream) +{ + return get_dummy_ops(substream)->pointer(substream); +} + +static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + return get_dummy_ops(substream)->start(substream); + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + return get_dummy_ops(substream)->stop(substream); + } + return -EINVAL; +} + +static int dummy_pcm_prepare(struct snd_pcm_substream *substream) +{ + return get_dummy_ops(substream)->prepare(substream); +} + +static struct snd_pcm_ops dummyap_pcm_ops = { + .open = dummy_pcm_open, + .close = dummy_pcm_close, + .prepare = dummy_pcm_prepare, + .pointer = dummy_pcm_pointer, + .trigger = dummy_pcm_trigger, +}; + +/******************************************************************************* + * Dummy Audio Path AVIRT registration + ******************************************************************************/ +static struct snd_pcm_hardware dummyap_hw = { + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .info = (SNDRV_PCM_INFO_INTERLEAVED // Channel interleaved audio + | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID), + .rates = SNDRV_PCM_RATE_48000, + .rate_min = DUMMY_SAMPLE_RATE, + .rate_max = DUMMY_SAMPLE_RATE, + .periods_min = DUMMY_PERIODS_MIN, + .periods_max = DUMMY_PERIODS_MAX, +}; + +static struct avirt_audiopath dummyap_module = { + .name = "Dummy Audio Path", + .version = { 0, 0, 1 }, + .value = 10, + .hw = &dummyap_hw, + .pcm_ops = &dummyap_pcm_ops, + .blocksize = DUMMY_BLOCKSIZE, +}; + +static int __init dummy_init(void) +{ + int err = 0; + + pr_info("init()\n"); + + err = avirt_register_audiopath(&dummyap_module, &coreinfo); + if ((err < 0) || (!coreinfo)) { + pr_err("%s: coreinfo is NULL!\n", __func__); + return err; + } + + return err; +} + +static void __exit dummy_exit(void) +{ + pr_info("exit()\n"); + + avirt_deregister_audiopath(&dummyap_module); +} + +module_init(dummy_init); +module_exit(dummy_exit); diff --git a/loadDrivers.sh b/loadDrivers.sh new file mode 100755 index 0000000..fca3203 --- /dev/null +++ b/loadDrivers.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Sample module parameters +params="\ +playback_num=4 \ +playback_chans=2,4,1,1 \ +playback_names=radio,media,nav,phone \ +capture_num=1 \ +capture_chans=2 \ +capture_names=voice" + +# Load the virtual driver +insmod avirt_core.ko "$params" + +# Load the additional audio path +insmod dummy/avirt_dummyap.ko + +echo "Drivers Loaded!" \ No newline at end of file diff --git a/make-agl.sh b/make-agl.sh new file mode 100755 index 0000000..4421af8 --- /dev/null +++ b/make-agl.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Get SDK path +sdk_id=$1 # first arg must be XDS_SDK_ID +shift 1 +long_sdkpath=$(xds-cli sdks get $sdk_id | grep Path) +sdkpath=${long_sdkpath:4} + +# Build +/opt/AGL/bin/xds-cli exec --config xds-project.conf -- \ + LDFLAGS= CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m \ + make -C $sdkpath/sysroots/aarch64-agl-linux/usr/src/kernel M=$(pwd) $@ diff --git a/unload.sh b/unload.sh new file mode 100755 index 0000000..02ed767 --- /dev/null +++ b/unload.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +rm_module() { + lsmod |grep "^$1\>" && rmmod $1 || true +} + +rm_module avirt_dummyap +rm_module avirt_core +echo "Drivers Removed!" \ No newline at end of file diff --git a/xds-project.conf b/xds-project.conf new file mode 100644 index 0000000..59a82f8 --- /dev/null +++ b/xds-project.conf @@ -0,0 +1,4 @@ +# XDS project settings +export XDS_AGENT_URL=localhost:8800 +export XDS_PROJECT_ID=4ad79245-a1c3-11e8-82d3-0242ac110002 +export XDS_SDK_ID=bf869951 -- cgit 1.2.3-korg From 04fbe08f7bfd544488cde9be949b88c7708f4b94 Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Mon, 27 Aug 2018 15:11:03 +1000 Subject: Update README.md Signed-off-by: James O'Shannessy --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 59bcd08..f60aa35 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ Alsa Virtual Sound Driver -------------------------- -[![pipeline status](https://git.fiberdyne.com.au/JOSHANNE/kmodule_development/badges/master/pipeline.svg)](https://git.fiberdyne.com.au/JOSHANNE/kmodule_development/commits/master) The ALSA Virtual Sound Driver (AVIRT) aims to provide a Linux kernel solution to the issue of audio routing in kernel-space, as well as security per-stream, and dynamic configuration of streams at the kernel level. -- cgit 1.2.3-korg From abfc8ab1d4c230f6ad32a22f3e22bcdd05136c4f Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Mon, 27 Aug 2018 16:07:06 +1000 Subject: Update README.md Signed-off-by: James O'Shannessy --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f60aa35..4c71476 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,15 @@ To build both AVIRT and the dummy Audio Path out-of-tree for [AGL](http://docs.a $ ./make_agl.sh ${XDS_SDK_ID} ``` ### In tree +To build in tree, the AVIRT Driver and required AudioPath modules can be turned on by setting to ``````. The drivers can be found here: ``` -$ TODO +$ make menuconfig + +# Navigate to: Device Drivers--->Staging Drivers--->AVIRT Support +``` +Finally build the kernel with the configuration selected by making. +``` +$ make ``` ## Running -- cgit 1.2.3-korg From 82a633ffac43f3849c211f13e369236e7e1ab4c6 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 27 Aug 2018 17:35:18 +1000 Subject: Update licensing Signed-off-by: Mark Farrugia --- LICENSE | 481 ++++++++++++++++++++++++++++++++++------------------------ alsa-pcm.c | 2 +- alsa.c | 2 +- alsa.h | 2 +- core.c | 2 +- core.h | 2 +- dummy/dummy.c | 4 +- 7 files changed, 287 insertions(+), 208 deletions(-) diff --git a/LICENSE b/LICENSE index e147c80..d8cf7d4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,280 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018 JOSHANNE - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/alsa-pcm.c b/alsa-pcm.c index bd705f4..efe64f2 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0 /* * ALSA Virtual Soundcard * diff --git a/alsa.c b/alsa.c index 0b08744..e283794 100755 --- a/alsa.c +++ b/alsa.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0 /* * ALSA Virtual Soundcard * diff --git a/alsa.h b/alsa.h index 29fc64d..113b575 100755 --- a/alsa.h +++ b/alsa.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0 /* * ALSA Virtual Soundcard * diff --git a/core.c b/core.c index d0a24ec..875e844 100644 --- a/core.c +++ b/core.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0 /* * ALSA Virtual Soundcard * diff --git a/core.h b/core.h index 5eb6026..1cf7c95 100644 --- a/core.h +++ b/core.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0 /* * ALSA Virtual Soundcard * diff --git a/dummy/dummy.c b/dummy/dummy.c index 7522345..cd5295f 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0 /* * ALSA Virtual Soundcard * @@ -14,7 +14,7 @@ MODULE_AUTHOR("JOSHANNE "); MODULE_AUTHOR("MFARRUGI "); MODULE_DESCRIPTION("Sample Audio Path Module Interface"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); #define DUMMY_SAMPLE_RATE 48000 #define DUMMY_BLOCKSIZE 512 -- cgit 1.2.3-korg From e0fe74f581d6eb6717d7a44514fe4bffe7b5b4bb Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Tue, 28 Aug 2018 16:30:13 +1000 Subject: Update README.md Mention the Linux fork on Fiberdyne GitHub for building in-tree Signed-off-by: Mark Farrugia --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c71476..77eb821 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ To build both AVIRT and the dummy Audio Path out-of-tree for [AGL](http://docs.a $ ./make_agl.sh ${XDS_SDK_ID} ``` ### In tree -To build in tree, the AVIRT Driver and required AudioPath modules can be turned on by setting to ``````. The drivers can be found here: +To build in tree, use the [Fiberdyne Linux fork](https://github.com/fiberdyne/linux), which will automatically clone the AVIRT Driver and required AudioPath modules to the `drivers/staging` directory. You can then turn AVIRT Support on by setting to ``. The drivers can be found here: ``` $ make menuconfig -- cgit 1.2.3-korg From 9461e4c40e95e42bd7e63c25a8f466c3633d9226 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 30 Aug 2018 18:18:34 +1000 Subject: Adds 'all' and 'clean' to Makefile for out-of-tree builds This will work for both AGL builds and non-AGL builds Signed-off-by: Mark Farrugia --- Makefile | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Makefile b/Makefile index 88eebbd..0c02c23 100644 --- a/Makefile +++ b/Makefile @@ -14,3 +14,22 @@ ccflags-y += -I${CCFLAGS_AVIRT} $(info $(KERNELRELEASE)) obj-$(CONFIG_AVIRT_DUMMYAP) += dummy/ + +### +# For out-of-tree building +### +ifdef PKG_CONFIG_SYSROOT_DIR + # AGL SDK kernel src path + KERNEL_SRC=$(PKG_CONFIG_SYSROOT_DIR)/usr/src/kernel +else + # General Linux distro kernel src path + KERNEL_SRC=/lib/modules/$(shell uname -r)/build +endif + +all: + CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m \ + make -C $(KERNEL_SRC) M=$(PWD) + +clean: + make -C $(KERNEL_SRC) M=$(PWD) clean + -- cgit 1.2.3-korg From 5594ba3d7b122052ef3a1de6812cca5d92cdbbc8 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 30 Aug 2018 18:14:54 +1000 Subject: Remove snd_pcm_lib_mmap_vmalloc from default pcm_ops struct. This has been removed as of Linux kernel 4.18 Signed-off-by: Mark Farrugia --- alsa-pcm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index efe64f2..342188b 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -375,7 +375,6 @@ struct snd_pcm_ops pcm_ops = { .copy_user = pcm_copy_user, .copy_kernel = pcm_copy_kernel, .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, .ack = pcm_ack, }; -- cgit 1.2.3-korg From 38d172808a61a6d0b404abea4db95cd4ee03b06e Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Sat, 15 Sep 2018 00:43:34 -0700 Subject: Adding groundwork for loopback Added a skeleton for the loopback audiopath. Signed-off-by: James O'Shannessy --- loopback/Kconfig | 12 ++++++ loopback/Makefile | 6 +++ loopback/loopback.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 loopback/Kconfig create mode 100644 loopback/Makefile create mode 100644 loopback/loopback.c diff --git a/loopback/Kconfig b/loopback/Kconfig new file mode 100644 index 0000000..6293c5c --- /dev/null +++ b/loopback/Kconfig @@ -0,0 +1,12 @@ +# +# AVIRT Loopback Audio Path +# + +config AVIRT_LOOPBACKAP + tristate "LoopbackAP" + select SND_PCM + ---help--- + Say Y here if you want to add loopback audio path. + + To compile this driver as a module, choose M here: the + module will be called avirt_loopbackap. diff --git a/loopback/Makefile b/loopback/Makefile new file mode 100644 index 0000000..55da206 --- /dev/null +++ b/loopback/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_AVIRT_LOOPBACKAP) += avirt_loopbackap.o + +$(info $(src)) +avirt_loopbackap-objs := loopback.o +ccflags-y += -Idrivers/staging/ +ccflags-y += -I$(src)/../../ diff --git a/loopback/loopback.c b/loopback/loopback.c new file mode 100644 index 0000000..57f3c34 --- /dev/null +++ b/loopback/loopback.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA Virtual Soundcard + * + * loopback_audiopath.c - AVIRT sample Audio Path definition + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#include +#include +#include + +MODULE_AUTHOR("JOSHANNE "); +MODULE_AUTHOR("MFARRUGI "); +MODULE_DESCRIPTION("Sample Audio Path Module Interface"); +MODULE_LICENSE("GPL v2"); + +static struct avirt_coreinfo *coreinfo; + +/******************************************************************************* + * Audio Path ALSA PCM Callbacks + ******************************************************************************/ +static int loopback_pcm_open(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int loopback_pcm_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static snd_pcm_uframes_t + loopback_pcm_pointer(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int loopback_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + return 0; + } + return -EINVAL; +} + +static int loopback_pcm_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +static struct snd_pcm_ops loopbackap_pcm_ops = { + .open = loopback_pcm_open, + .close = loopback_pcm_close, + .prepare = loopback_pcm_prepare, + .pointer = loopback_pcm_pointer, + .trigger = loopback_pcm_trigger, +}; + +/******************************************************************************* + * Loopback Audio Path AVIRT registration + ******************************************************************************/ +static struct snd_pcm_hardware loopbackap_hw = { + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .info = (SNDRV_PCM_INFO_INTERLEAVED // Channel interleaved audio + | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID), + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .periods_min = 1, + .periods_max = 8, +}; + +static struct avirt_audiopath loopbackap_module = { + .name = "Loopback Audio Path", + .version = { 0, 0, 1 }, + .value = 10, + .hw = &loopbackap_hw, + .pcm_ops = &loopbackap_pcm_ops, + .blocksize = 512, +}; + +static int __init loopback_init(void) +{ + int err = 0; + + pr_info("init()\n"); + + err = avirt_register_audiopath(&loopbackap_module, &coreinfo); + if ((err < 0) || (!coreinfo)) { + pr_err("%s: coreinfo is NULL!\n", __func__); + return err; + } + + return err; +} + +static void __exit loopback_exit(void) +{ + pr_info("exit()\n"); + + avirt_deregister_audiopath(&loopbackap_module); +} + +module_init(loopback_init); +module_exit(loopback_exit); -- cgit 1.2.3-korg From c656cad43025265094bf5139c1265d4bcc0ba84c Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Sat, 15 Sep 2018 00:44:37 -0700 Subject: Add updates to Makefiles and tasks for building Add required changes to Makefile and KConfig to implement building of loopback audio path as part of standard make execution. Signed-off-by: James O'Shannessy --- .vscode/tasks.json | 4 ++-- Kconfig | 1 + Makefile | 5 ++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c901648..d8063e7 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -9,7 +9,7 @@ "tasks": [ { "label": "Make Driver", - "command": "CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd)", + "command": "CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m CONFIG_AVIRT_LOOPBACKAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd)", "group": "build", "problemMatcher": [ "$gcc" @@ -17,7 +17,7 @@ }, { "label": "Clean Driver", - "command": "CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) clean", + "command": "CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m CONFIG_AVIRT_LOOPBACKAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) clean", "group": "build", "problemMatcher": [ "$gcc" diff --git a/Kconfig b/Kconfig index 0e2a52a..b15bcbe 100644 --- a/Kconfig +++ b/Kconfig @@ -16,5 +16,6 @@ menuconfig AVIRT if AVIRT source "drivers/staging/avirt/dummy/Kconfig" +source "drivers/staging/avirt/loopback/Kconfig" endif diff --git a/Makefile b/Makefile index 0c02c23..dfdc715 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ ccflags-y += -I${CCFLAGS_AVIRT} $(info $(KERNELRELEASE)) obj-$(CONFIG_AVIRT_DUMMYAP) += dummy/ +obj-$(CONFIG_AVIRT_LOOPBACKAP) += loopback/ ### # For out-of-tree building @@ -27,7 +28,9 @@ else endif all: - CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m \ + CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y \ + CONFIG_AVIRT_DUMMYAP=m \ + CONFIG_AVIRT_LOOPBACKAP=m \ make -C $(KERNEL_SRC) M=$(PWD) clean: -- cgit 1.2.3-korg From 7bbfc5e47a58851dbb56da1c4b77e534ca00c782 Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Tue, 18 Sep 2018 17:16:32 +1000 Subject: Updating loopback audio path. Added initial implementation with callback operating on timer. NOTE: Copying from PB to C is not yet implemented Signed-off-by: James O'Shannessy --- loopback/loopback.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 163 insertions(+), 5 deletions(-) diff --git a/loopback/loopback.c b/loopback/loopback.c index 57f3c34..6f2a5d9 100644 --- a/loopback/loopback.c +++ b/loopback/loopback.c @@ -18,23 +18,177 @@ MODULE_LICENSE("GPL v2"); static struct avirt_coreinfo *coreinfo; +struct loopback_pcm { + struct snd_pcm_substream *substream; + spinlock_t lock; + struct timer_list timer; + unsigned long base_time; + unsigned int frac_pos; /* fractional sample position (based HZ) */ + unsigned int frac_period_rest; + unsigned int frac_buffer_size; /* buffer_size * HZ */ + unsigned int frac_period_size; /* period_size * HZ */ + unsigned int rate; + int elapsed; +}; + +int systimer_create(struct snd_pcm_substream *substream); +void systimer_free(struct snd_pcm_substream *substream); +int systimer_start(struct snd_pcm_substream *substream); +int systimer_stop(struct snd_pcm_substream *substream); +snd_pcm_uframes_t systimer_pointer(struct snd_pcm_substream *substream); +int systimer_prepare(struct snd_pcm_substream *substream); +void systimer_rearm(struct loopback_pcm *dpcm); +void systimer_update(struct loopback_pcm *dpcm); + +void loopback_callback(struct timer_list *tlist); + +/******************************** + * Loopback Timer Functions + ********************************/ + +int systimer_create(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + struct loopback_pcm *dpcm; + dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); + if (!dpcm) + return -ENOMEM; + + timer_setup(&dpcm->timer, loopback_callback, 0); + spin_lock_init(&dpcm->lock); + dpcm->substream = substream; + + runtime->private_data = dpcm; + return 0; +} + +void systimer_free(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + kfree(runtime->private_data); +} + +int systimer_start(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + spin_lock(&dpcm->lock); + dpcm->base_time = jiffies; + systimer_rearm(dpcm); + spin_unlock(&dpcm->lock); + return 0; +} + +int systimer_stop(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + spin_lock(&dpcm->lock); + del_timer(&dpcm->timer); + spin_unlock(&dpcm->lock); + return 0; +} + +snd_pcm_uframes_t systimer_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + + snd_pcm_uframes_t pos; + + spin_lock(&dpcm->lock); + systimer_update(dpcm); + pos = dpcm->frac_pos / HZ; + spin_unlock(&dpcm->lock); + return pos; +} + +int systimer_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + + dpcm->frac_pos = 0; + dpcm->rate = runtime->rate; + dpcm->frac_buffer_size = runtime->buffer_size * HZ; + dpcm->frac_period_size = runtime->period_size * HZ; + dpcm->frac_period_rest = dpcm->frac_period_size; + dpcm->elapsed = 0; + + return 0; +} + +void systimer_rearm(struct loopback_pcm *dpcm) +{ + mod_timer(&dpcm->timer, jiffies + (dpcm->frac_period_rest + dpcm->rate - + 1) / dpcm->rate); +} + +void systimer_update(struct loopback_pcm *dpcm) +{ + unsigned long delta; + + delta = jiffies - dpcm->base_time; + if (!delta) + return; + dpcm->base_time += delta; + delta *= dpcm->rate; + dpcm->frac_pos += delta; + while (dpcm->frac_pos >= dpcm->frac_buffer_size) + dpcm->frac_pos -= dpcm->frac_buffer_size; + while (dpcm->frac_period_rest <= delta) { + dpcm->elapsed++; + dpcm->frac_period_rest += dpcm->frac_period_size; + } + dpcm->frac_period_rest -= delta; +} + +/******* + * Loopback Timer Callback + *******/ + +void loopback_callback(struct timer_list *tlist) +{ + struct loopback_pcm *dpcm = from_timer(dpcm, tlist, timer); + + unsigned long flags; + int elapsed = 0; + + spin_lock_irqsave(&dpcm->lock, flags); + + // Perform copy from playback to capture + systimer_update(dpcm); + systimer_rearm(dpcm); + elapsed = dpcm->elapsed; + dpcm->elapsed = 0; + spin_unlock_irqrestore(&dpcm->lock, flags); + if (elapsed) + coreinfo->pcm_buff_complete(dpcm->substream); +} + /******************************************************************************* * Audio Path ALSA PCM Callbacks ******************************************************************************/ static int loopback_pcm_open(struct snd_pcm_substream *substream) { + int err = systimer_create(substream); + if (err < 0) + return err; + return 0; } static int loopback_pcm_close(struct snd_pcm_substream *substream) { + systimer_free(substream); return 0; } static snd_pcm_uframes_t loopback_pcm_pointer(struct snd_pcm_substream *substream) { - return 0; + return systimer_pointer(substream); } static int loopback_pcm_trigger(struct snd_pcm_substream *substream, int cmd) @@ -42,16 +196,17 @@ static int loopback_pcm_trigger(struct snd_pcm_substream *substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: + return systimer_start(substream); case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: - return 0; + return systimer_stop(substream); } return -EINVAL; } static int loopback_pcm_prepare(struct snd_pcm_substream *substream) { - return 0; + return systimer_prepare(substream); } static struct snd_pcm_ops loopbackap_pcm_ops = { @@ -71,10 +226,13 @@ static struct snd_pcm_hardware loopbackap_hw = { | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, + .rate_min = 2000, .rate_max = 48000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 32768, .periods_min = 1, - .periods_max = 8, + .periods_max = 1024, }; static struct avirt_audiopath loopbackap_module = { -- cgit 1.2.3-korg From b46cfdf55851280f0c90c7b54ba041932ddfa397 Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Wed, 19 Sep 2018 16:26:40 +1000 Subject: Changes to loopback Added more detailed printing to loopback, alsa-pcm. Updated load and unload functions Signed-off-by: James O'Shannessy --- alsa-pcm.c | 9 +++++++++ loadDrivers.sh | 5 +++-- loopback/loopback.c | 56 +++++++++++++++++++++++++++++++++++++++++++++-------- unload.sh | 3 ++- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 342188b..0f623f6 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -10,6 +10,9 @@ #include "core.h" #include "alsa.h" +#define DINFO(fmt, args...) \ + printk(KERN_INFO "[CORE] %d:%s " fmt "\n", __LINE__, __func__, ##args) + #define DO_AUDIOPATH_CB(callback, substream, ...) \ do { \ struct avirt_audiopath *ap; \ @@ -86,6 +89,7 @@ static int configure_pcm(struct snd_pcm_substream *substream) */ static int pcm_open(struct snd_pcm_substream *substream) { + DINFO(""); // Setup the pcm device based on the configuration assigned CHK_ERR_V(configure_pcm(substream), "Failed to setup pcm device"); @@ -105,6 +109,7 @@ static int pcm_open(struct snd_pcm_substream *substream) */ static int pcm_close(struct snd_pcm_substream *substream) { + DINFO(""); // Do additional Audio Path 'close' callback DO_AUDIOPATH_CB(close, substream); @@ -191,6 +196,8 @@ static int pcm_prepare(struct snd_pcm_substream *substream) struct avirt_alsa_dev_group *group; struct snd_pcm_runtime *runtime = substream->runtime; + DINFO(""); + group = avirt_alsa_get_dev_group(substream->stream); CHK_NULL(group); @@ -219,6 +226,8 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct avirt_alsa_dev_group *group; + DINFO(""); + group = avirt_alsa_get_dev_group(substream->stream); CHK_NULL(group); diff --git a/loadDrivers.sh b/loadDrivers.sh index fca3203..9ff5960 100755 --- a/loadDrivers.sh +++ b/loadDrivers.sh @@ -13,6 +13,7 @@ capture_names=voice" insmod avirt_core.ko "$params" # Load the additional audio path -insmod dummy/avirt_dummyap.ko +#insmod dummy/avirt_dummyap.ko +insmod loopback/avirt_loopbackap.ko -echo "Drivers Loaded!" \ No newline at end of file +echo "Drivers Loaded!" diff --git a/loopback/loopback.c b/loopback/loopback.c index 6f2a5d9..215ed5d 100644 --- a/loopback/loopback.c +++ b/loopback/loopback.c @@ -16,6 +16,20 @@ MODULE_AUTHOR("MFARRUGI "); MODULE_DESCRIPTION("Sample Audio Path Module Interface"); MODULE_LICENSE("GPL v2"); +#define AP_LOGNAME "LOOPAP" + +#define DERROR(fmt, args...) \ + printk(KERN_ERR "[%s] %d:%s " fmt "\n", AP_LOGNAME, __LINE__, \ + __func__, ##args) + +#define DINFO(fmt, args...) \ + printk(KERN_INFO "[%s] %d:%s " fmt "\n", AP_LOGNAME, __LINE__, \ + __func__, ##args) + +#define DPRINT(fmt, args...) \ + printk(KERN_DEBUG "[%s] %d:%s " fmt "\n", AP_LOGNAME, __LINE__, \ + __func__, ##args) + static struct avirt_coreinfo *coreinfo; struct loopback_pcm { @@ -148,6 +162,7 @@ void systimer_update(struct loopback_pcm *dpcm) * Loopback Timer Callback *******/ +// void loopback_callback(struct timer_list *tlist) { struct loopback_pcm *dpcm = from_timer(dpcm, tlist, timer); @@ -172,7 +187,15 @@ void loopback_callback(struct timer_list *tlist) ******************************************************************************/ static int loopback_pcm_open(struct snd_pcm_substream *substream) { - int err = systimer_create(substream); + int err; + + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + + DINFO("Open\n%s\nrate: %d\nch: %d", substream->pcm->name, runtime->rate, + runtime->channels); + + err = systimer_create(substream); if (err < 0) return err; @@ -181,6 +204,11 @@ static int loopback_pcm_open(struct snd_pcm_substream *substream) static int loopback_pcm_close(struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + + DINFO("Close"); + systimer_free(substream); return 0; } @@ -188,11 +216,15 @@ static int loopback_pcm_close(struct snd_pcm_substream *substream) static snd_pcm_uframes_t loopback_pcm_pointer(struct snd_pcm_substream *substream) { + DINFO("Pointer"); + return systimer_pointer(substream); } static int loopback_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { + DINFO("Trigger"); + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -206,6 +238,12 @@ static int loopback_pcm_trigger(struct snd_pcm_substream *substream, int cmd) static int loopback_pcm_prepare(struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; + struct loopback_pcm *dpcm = runtime->private_data; + + DINFO("Prepare"); + DINFO("Runtime\nrate: %d\nch: %d", runtime->rate, runtime->channels); + return systimer_prepare(substream); } @@ -225,14 +263,16 @@ static struct snd_pcm_hardware loopbackap_hw = { .info = (SNDRV_PCM_INFO_INTERLEAVED // Channel interleaved audio | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 2000, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 8000, .rate_max = 48000, .channels_min = 1, .channels_max = 8, - .buffer_bytes_max = 32768, + //.buffer_bytes_max = 32768, .periods_min = 1, - .periods_max = 1024, + .periods_max = 8, }; static struct avirt_audiopath loopbackap_module = { @@ -248,11 +288,11 @@ static int __init loopback_init(void) { int err = 0; - pr_info("init()\n"); + DINFO("init()\n"); err = avirt_register_audiopath(&loopbackap_module, &coreinfo); if ((err < 0) || (!coreinfo)) { - pr_err("%s: coreinfo is NULL!\n", __func__); + DERROR("%s: coreinfo is NULL!\n", __func__); return err; } @@ -261,7 +301,7 @@ static int __init loopback_init(void) static void __exit loopback_exit(void) { - pr_info("exit()\n"); + DINFO("exit()\n"); avirt_deregister_audiopath(&loopbackap_module); } diff --git a/unload.sh b/unload.sh index 02ed767..a011a2b 100755 --- a/unload.sh +++ b/unload.sh @@ -4,6 +4,7 @@ rm_module() { lsmod |grep "^$1\>" && rmmod $1 || true } +rm_module avirt_loopbackap rm_module avirt_dummyap rm_module avirt_core -echo "Drivers Removed!" \ No newline at end of file +echo "Drivers Removed!" -- cgit 1.2.3-korg From bc249f293cab17c0dcde886fccc26deb0dfca33c Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Fri, 21 Sep 2018 16:14:14 +1000 Subject: Current state of loopback. Committing debug changes and loopback changes Signed-off-by: James O'Shannessy --- alsa-pcm.c | 22 ++++++++++++++++------ core.h | 11 +++++++++++ loopback/loopback.c | 34 +++++++++++++--------------------- 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 0f623f6..4fd4323 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -10,8 +10,7 @@ #include "core.h" #include "alsa.h" -#define DINFO(fmt, args...) \ - printk(KERN_INFO "[CORE] %d:%s " fmt "\n", __LINE__, __func__, ##args) +#define AP_LOGNAME "CORE" #define DO_AUDIOPATH_CB(callback, substream, ...) \ do { \ @@ -89,7 +88,7 @@ static int configure_pcm(struct snd_pcm_substream *substream) */ static int pcm_open(struct snd_pcm_substream *substream) { - DINFO(""); + DINFO(AP_LOGNAME, ""); // Setup the pcm device based on the configuration assigned CHK_ERR_V(configure_pcm(substream), "Failed to setup pcm device"); @@ -109,7 +108,7 @@ static int pcm_open(struct snd_pcm_substream *substream) */ static int pcm_close(struct snd_pcm_substream *substream) { - DINFO(""); + DINFO(AP_LOGNAME, ""); // Do additional Audio Path 'close' callback DO_AUDIOPATH_CB(close, substream); @@ -134,6 +133,8 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, struct avirt_audiopath *audiopath; struct avirt_alsa_dev_group *group; + DINFO(AP_LOGNAME, ""); + group = avirt_alsa_get_dev_group(substream->stream); CHK_NULL(group); @@ -173,6 +174,7 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, */ static int pcm_hw_free(struct snd_pcm_substream *substream) { + DINFO(AP_LOGNAME, ""); CHK_ERR(snd_pcm_lib_free_vmalloc_buffer(substream)); // Do additional Audio Path 'hw_free' callback @@ -196,7 +198,7 @@ static int pcm_prepare(struct snd_pcm_substream *substream) struct avirt_alsa_dev_group *group; struct snd_pcm_runtime *runtime = substream->runtime; - DINFO(""); + DINFO(AP_LOGNAME, ""); group = avirt_alsa_get_dev_group(substream->stream); CHK_NULL(group); @@ -226,7 +228,7 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct avirt_alsa_dev_group *group; - DINFO(""); + DINFO(AP_LOGNAME, ""); group = avirt_alsa_get_dev_group(substream->stream); CHK_NULL(group); @@ -264,6 +266,8 @@ static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) { struct avirt_alsa_dev_group *group; + DINFO(AP_LOGNAME, ""); + group = avirt_alsa_get_dev_group(substream->stream); CHK_NULL(group); @@ -290,6 +294,8 @@ static int pcm_get_time_info( { struct avirt_alsa_dev_group *group; + DINFO(AP_LOGNAME, ""); + group = avirt_alsa_get_dev_group(substream->stream); CHK_NULL(group); @@ -323,6 +329,7 @@ static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, //offset = frames_to_bytes(runtime, pos); // Do additional Audio Path 'copy_user' callback + DINFO(AP_LOGNAME, ""); DO_AUDIOPATH_CB(copy_user, substream, channel, pos, src, count); return 0; @@ -344,6 +351,7 @@ static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, unsigned long pos, void *buf, unsigned long count) { + DINFO(AP_LOGNAME, ""); DO_AUDIOPATH_CB(copy_kernel, substream, channel, pos, buf, count); return 0; } @@ -359,6 +367,7 @@ static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, */ int pcm_ack(struct snd_pcm_substream *substream) { + DINFO(AP_LOGNAME, ""); DO_AUDIOPATH_CB(ack, substream); return 0; } @@ -366,6 +375,7 @@ int pcm_ack(struct snd_pcm_substream *substream) static int pcm_silence(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) { + DINFO(AP_LOGNAME, ""); DO_AUDIOPATH_CB(fill_silence, substream, channel, pos, count); return 0; } diff --git a/core.h b/core.h index 1cf7c95..6e3b651 100644 --- a/core.h +++ b/core.h @@ -12,6 +12,17 @@ #include +#define DINFO(ap, fmt, args...) \ + printk(KERN_INFO "[%s] %d:%s " fmt "\n", ap, __LINE__, __func__, ##args) + +#define DERROR(ap, fmt, args...) \ + printk(KERN_ERR "[%s] %d:%s " fmt "\n", AP_LOGNAME, __LINE__, \ + __func__, ##args) + +#define DPRINT(ap, fmt, args...) \ + printk(KERN_DEBUG "[%s] %d:%s " fmt "\n", AP_LOGNAME, __LINE__, \ + __func__, ##args) + /** * PCM buffer complete callback * diff --git a/loopback/loopback.c b/loopback/loopback.c index 215ed5d..a925eaa 100644 --- a/loopback/loopback.c +++ b/loopback/loopback.c @@ -18,17 +18,9 @@ MODULE_LICENSE("GPL v2"); #define AP_LOGNAME "LOOPAP" -#define DERROR(fmt, args...) \ - printk(KERN_ERR "[%s] %d:%s " fmt "\n", AP_LOGNAME, __LINE__, \ - __func__, ##args) - -#define DINFO(fmt, args...) \ - printk(KERN_INFO "[%s] %d:%s " fmt "\n", AP_LOGNAME, __LINE__, \ - __func__, ##args) - -#define DPRINT(fmt, args...) \ - printk(KERN_DEBUG "[%s] %d:%s " fmt "\n", AP_LOGNAME, __LINE__, \ - __func__, ##args) +#define AP_INFOK(fmt, args...) DINFO(AP_LOGNAME, fmt, ##args) +#define AP_PRINTK(fmt, args...) DPRINT(AP_LOGNAME, fmt, ##args) +#define AP_ERRORK(fmt, args...) DERROR(AP_LOGNAME, fmt, ##args) static struct avirt_coreinfo *coreinfo; @@ -192,8 +184,8 @@ static int loopback_pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct loopback_pcm *dpcm = runtime->private_data; - DINFO("Open\n%s\nrate: %d\nch: %d", substream->pcm->name, runtime->rate, - runtime->channels); + AP_INFOK("Open\n%s\nrate: %d\nch: %d", substream->pcm->name, + runtime->rate, runtime->channels); err = systimer_create(substream); if (err < 0) @@ -207,7 +199,7 @@ static int loopback_pcm_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct loopback_pcm *dpcm = runtime->private_data; - DINFO("Close"); + AP_INFOK("Close"); systimer_free(substream); return 0; @@ -216,14 +208,14 @@ static int loopback_pcm_close(struct snd_pcm_substream *substream) static snd_pcm_uframes_t loopback_pcm_pointer(struct snd_pcm_substream *substream) { - DINFO("Pointer"); + AP_INFOK("Pointer"); return systimer_pointer(substream); } static int loopback_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - DINFO("Trigger"); + AP_INFOK("Trigger"); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -241,8 +233,8 @@ static int loopback_pcm_prepare(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct loopback_pcm *dpcm = runtime->private_data; - DINFO("Prepare"); - DINFO("Runtime\nrate: %d\nch: %d", runtime->rate, runtime->channels); + AP_INFOK("Prepare"); + AP_INFOK("Runtime\nrate: %d\nch: %d", runtime->rate, runtime->channels); return systimer_prepare(substream); } @@ -288,11 +280,11 @@ static int __init loopback_init(void) { int err = 0; - DINFO("init()\n"); + AP_INFOK("init()\n"); err = avirt_register_audiopath(&loopbackap_module, &coreinfo); if ((err < 0) || (!coreinfo)) { - DERROR("%s: coreinfo is NULL!\n", __func__); + AP_ERRORK("%s: coreinfo is NULL!\n", __func__); return err; } @@ -301,7 +293,7 @@ static int __init loopback_init(void) static void __exit loopback_exit(void) { - DINFO("exit()\n"); + AP_INFOK("exit()\n"); avirt_deregister_audiopath(&loopbackap_module); } -- cgit 1.2.3-korg From 55d9a9f22aa909449925da9cb2e8de4d5a41bdfb Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Tue, 2 Oct 2018 15:55:55 +1000 Subject: Loopback progress Loopback currently reads from dma_area but the initial copy from playback to capture has not been implemented. Signed-off-by: James O'Shannessy --- alsa-pcm.c | 10 +++++++--- alsa.c | 6 +++--- core.c | 18 ++++++++++++------ core.h | 16 ++++++++-------- loopback/loopback.c | 20 +++++++++++++------- 5 files changed, 43 insertions(+), 27 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 4fd4323..8c01c36 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -59,8 +59,12 @@ static int configure_pcm(struct snd_pcm_substream *substream) CHK_NULL(group); // Check if substream id is valid - if (substream->pcm->device >= group->devices) + pr_info("%d substream is < %d", substream->pcm->device, group->devices); + if (substream->pcm->device >= group->devices) { + pr_err("%s %d substream id is invalid expecting %d", __func__, + substream->pcm->device, group->devices); return -1; + } // Setup remaining hw properties config = &group->config[substream->pcm->device]; @@ -158,7 +162,7 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, } // Do additional Audio Path 'hw_params' callback - // DO_AUDIOPATH_CB(hw_params, substream, hw_params); + DO_AUDIOPATH_CB(hw_params, substream, hw_params); return 0; } @@ -178,7 +182,7 @@ static int pcm_hw_free(struct snd_pcm_substream *substream) CHK_ERR(snd_pcm_lib_free_vmalloc_buffer(substream)); // Do additional Audio Path 'hw_free' callback - // DO_AUDIOPATH_CB(hw_free, substream); + DO_AUDIOPATH_CB(hw_free, substream); return 0; } diff --git a/alsa.c b/alsa.c index e283794..44e0b15 100755 --- a/alsa.c +++ b/alsa.c @@ -32,10 +32,11 @@ static int pcm_constructor(struct snd_card *card) for (i = 0; i < _driver->playback.devices; i++) { CHK_ERR(snd_pcm_new(card, _driver->playback.config[i].devicename, i, - 1, 0, &pcm)); + 1, 1, &pcm)); /** Register driver callbacks */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); pcm->info_flags = 0; strcpy(pcm->name, _driver->playback.config[i].devicename); @@ -112,9 +113,8 @@ struct avirt_alsa_dev_group *avirt_alsa_get_dev_group(int direction) switch (direction) { case SNDRV_PCM_STREAM_PLAYBACK: - return &_driver->playback; case SNDRV_PCM_STREAM_CAPTURE: - return &_driver->capture; + return &_driver->playback; default: pr_err("[%s] Direction must be SNDRV_PCM_STREAM_XXX!", __func__); diff --git a/core.c b/core.c index 875e844..479b93e 100644 --- a/core.c +++ b/core.c @@ -19,6 +19,12 @@ MODULE_AUTHOR("MFARRUGI "); MODULE_DESCRIPTION("A configurable virtual soundcard"); MODULE_LICENSE("GPL v2"); +#define AP_LOGNAME "CORE" + +#define D_INFOK(fmt, args...) DINFO(AP_LOGNAME, fmt, ##args) +#define D_PRINTK(fmt, args...) DPRINT(AP_LOGNAME, fmt, ##args) +#define D_ERRORK(fmt, args...) DERROR(AP_LOGNAME, fmt, ##args) + #define SND_AVIRTUAL_DRIVER "snd_avirt" #define MAX_PCM_DEVS 8 #define MAX_AUDIOPATHS 4 @@ -350,8 +356,8 @@ int avirt_register_audiopath(struct avirt_audiopath *audiopath, audiopath_obj->path = audiopath; audiopath->context = audiopath_obj; - pr_info("Registered new Audio Path: %s\n", audiopath->name); - pr_info("\tBlocksize: %d, Periods: %d\n", audiopath->blocksize, + D_INFOK("Registered new Audio Path: %s", audiopath->name); + D_INFOK("\tBlocksize: %d, Periods: %d", audiopath->blocksize, audiopath->hw->periods_max); list_add_tail(&audiopath_obj->list, &audiopath_list); @@ -405,9 +411,9 @@ static void avirt_unregister_all(void) static int __init core_init(void) { int err; - - pr_info("Alsa Virtual Sound Driver avirt-%d.%d.%d\n", - coreinfo.version[0], coreinfo.version[1], coreinfo.version[2]); + D_INFOK("Starting new core\n\n\n\n"); + D_INFOK("Alsa Virtual Sound Driver avirt-%d.%d.%d", coreinfo.version[0], + coreinfo.version[1], coreinfo.version[2]); // Initialize audiopaths linked list INIT_LIST_HEAD(&audiopath_list); @@ -466,7 +472,7 @@ static void __exit core_exit(void) class_destroy(core.avirt_class); avirt_unregister_all(); - + D_INFOK("Exit begin!"); pr_info("playback_num: %d, capture_num: %d\n", playback_num, capture_num); diff --git a/core.h b/core.h index 6e3b651..7a562f4 100644 --- a/core.h +++ b/core.h @@ -12,16 +12,16 @@ #include -#define DINFO(ap, fmt, args...) \ - printk(KERN_INFO "[%s] %d:%s " fmt "\n", ap, __LINE__, __func__, ##args) +#define DINFO(ap, fmt, args...) \ + printk(KERN_INFO "[%s]: %d:%s " fmt "\n", ap, __LINE__, __func__, \ + ##args) -#define DERROR(ap, fmt, args...) \ - printk(KERN_ERR "[%s] %d:%s " fmt "\n", AP_LOGNAME, __LINE__, \ - __func__, ##args) +#define DERROR(ap, fmt, args...) \ + printk(KERN_ERR "[%s]: %d:%s " fmt "\n", ap, __LINE__, __func__, ##args) -#define DPRINT(ap, fmt, args...) \ - printk(KERN_DEBUG "[%s] %d:%s " fmt "\n", AP_LOGNAME, __LINE__, \ - __func__, ##args) +#define DPRINT(ap, fmt, args...) \ + printk(KERN_DEBUG "[%s]: %d:%s " fmt "\n", ap, __LINE__, __func__, \ + ##args) /** * PCM buffer complete callback diff --git a/loopback/loopback.c b/loopback/loopback.c index a925eaa..5694dce 100644 --- a/loopback/loopback.c +++ b/loopback/loopback.c @@ -162,6 +162,7 @@ void loopback_callback(struct timer_list *tlist) unsigned long flags; int elapsed = 0; + AP_INFOK(""); spin_lock_irqsave(&dpcm->lock, flags); // Perform copy from playback to capture @@ -169,6 +170,7 @@ void loopback_callback(struct timer_list *tlist) systimer_rearm(dpcm); elapsed = dpcm->elapsed; dpcm->elapsed = 0; + spin_unlock_irqrestore(&dpcm->lock, flags); if (elapsed) coreinfo->pcm_buff_complete(dpcm->substream); @@ -184,9 +186,6 @@ static int loopback_pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct loopback_pcm *dpcm = runtime->private_data; - AP_INFOK("Open\n%s\nrate: %d\nch: %d", substream->pcm->name, - runtime->rate, runtime->channels); - err = systimer_create(substream); if (err < 0) return err; @@ -233,8 +232,10 @@ static int loopback_pcm_prepare(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct loopback_pcm *dpcm = runtime->private_data; - AP_INFOK("Prepare"); - AP_INFOK("Runtime\nrate: %d\nch: %d", runtime->rate, runtime->channels); + AP_INFOK("%s\n%s\n%d\n%d", substream->pcm->name, + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "PLAYBACK" : + "CAPTURE", + runtime->rate, runtime->channels); return systimer_prepare(substream); } @@ -255,11 +256,16 @@ static struct snd_pcm_hardware loopbackap_hw = { .info = (SNDRV_PCM_INFO_INTERLEAVED // Channel interleaved audio | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), + /* .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .rate_min = 8000, .rate_max = 48000, + */ + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, .channels_min = 1, .channels_max = 8, //.buffer_bytes_max = 32768, @@ -280,7 +286,7 @@ static int __init loopback_init(void) { int err = 0; - AP_INFOK("init()\n"); + AP_INFOK("init()"); err = avirt_register_audiopath(&loopbackap_module, &coreinfo); if ((err < 0) || (!coreinfo)) { @@ -293,7 +299,7 @@ static int __init loopback_init(void) static void __exit loopback_exit(void) { - AP_INFOK("exit()\n"); + AP_INFOK("loopback: exit()"); avirt_deregister_audiopath(&loopbackap_module); } -- cgit 1.2.3-korg From 993a6fb52ee4a39775b7e14394650213f18913f2 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Fri, 31 Aug 2018 17:33:41 +1000 Subject: Fix initialization of the audiopath_list This wasn't being initialised properly, and NULL audiopaths were not being detected. Signed-off-by: Mark Farrugia --- core.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core.c b/core.c index 479b93e..bf31f0b 100644 --- a/core.c +++ b/core.c @@ -70,7 +70,7 @@ static struct avirt_coreinfo coreinfo = { .pcm_buff_complete = pcm_buff_complete_cb, }; -static struct list_head audiopath_list; +static LIST_HEAD(audiopath_list); /** * avirt_probe - Register ALSA soundcard @@ -415,9 +415,6 @@ static int __init core_init(void) D_INFOK("Alsa Virtual Sound Driver avirt-%d.%d.%d", coreinfo.version[0], coreinfo.version[1], coreinfo.version[2]); - // Initialize audiopaths linked list - INIT_LIST_HEAD(&audiopath_list); - err = platform_driver_register(&avirt_driver); if (err < 0) return err; -- cgit 1.2.3-korg From bde87475ff7d229619eb090af58c7e496bd3fa60 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Fri, 31 Aug 2018 17:40:44 +1000 Subject: Add additional NULL checks Check for NULL pointers before free'ing memory on cleanup of AVIRT Signed-off-by: Mark Farrugia --- alsa.c | 20 ++++++++++---------- core.c | 5 ++--- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/alsa.c b/alsa.c index 44e0b15..738048f 100755 --- a/alsa.c +++ b/alsa.c @@ -203,17 +203,17 @@ int avirt_alsa_register(struct platform_device *devptr) */ int avirt_alsa_deregister(void) { + CHK_NULL(_driver->card); snd_card_free(_driver->card); - - if (_driver->playback.config) - kfree(_driver->playback.config); - if (_driver->playback.streams) - kfree(_driver->playback.streams); - if (_driver->capture.config) - kfree(_driver->capture.config); - if (_driver->capture.streams) - kfree(_driver->capture.streams); - + CHK_NULL(_driver->playback.config); + kfree(_driver->playback.config); + CHK_NULL(_driver->playback.streams); + kfree(_driver->playback.streams); + CHK_NULL(_driver->capture.config); + kfree(_driver->capture.config); + CHK_NULL(_driver->capture.streams); + kfree(_driver->capture.streams); + CHK_NULL(_driver); kfree(_driver); return 0; diff --git a/core.c b/core.c index bf31f0b..996a452 100644 --- a/core.c +++ b/core.c @@ -151,9 +151,8 @@ static int avirt_probe(struct platform_device *devptr) */ static int avirt_remove(struct platform_device *devptr) { - avirt_alsa_deregister(); - - return 0; + DPRINTK(); + return avirt_alsa_deregister(); } static struct platform_driver avirt_driver = { -- cgit 1.2.3-korg From f7f995a28c82c126619858ed40b92b5f33ddf2fe Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 3 Sep 2018 15:18:18 +1000 Subject: Remove test 'value' parameter from avirt_audiopath This is not required any longer Signed-off-by: Mark Farrugia --- core.h | 1 - dummy/dummy.c | 1 - 2 files changed, 2 deletions(-) diff --git a/core.h b/core.h index 7a562f4..a31c916 100644 --- a/core.h +++ b/core.h @@ -34,7 +34,6 @@ typedef int (*avirt_buff_complete)(struct snd_pcm_substream *substream); struct avirt_audiopath { const char *name; unsigned version[3]; - int value; struct snd_pcm_hardware *hw; struct snd_pcm_ops *pcm_ops; unsigned blocksize; diff --git a/dummy/dummy.c b/dummy/dummy.c index cd5295f..54d4dcc 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -246,7 +246,6 @@ static struct snd_pcm_hardware dummyap_hw = { static struct avirt_audiopath dummyap_module = { .name = "Dummy Audio Path", .version = { 0, 0, 1 }, - .value = 10, .hw = &dummyap_hw, .pcm_ops = &dummyap_pcm_ops, .blocksize = DUMMY_BLOCKSIZE, -- cgit 1.2.3-korg From a0213245df08e8eb2f3316e924539af216a95599 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 3 Sep 2018 15:53:54 +1000 Subject: Remove the unnecessary allocation of substreams in avirt_alsa_dev_group Substreams are actually allocated by the PCM middle layer, so we do not need to do this here Signed-off-by: Mark Farrugia --- alsa-pcm.c | 9 --------- alsa.c | 52 +--------------------------------------------------- alsa.h | 10 +--------- 3 files changed, 2 insertions(+), 69 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 8c01c36..c5ff0b0 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -230,18 +230,9 @@ static int pcm_prepare(struct snd_pcm_substream *substream) */ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - struct avirt_alsa_dev_group *group; - - DINFO(AP_LOGNAME, ""); - - group = avirt_alsa_get_dev_group(substream->stream); - CHK_NULL(group); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: - group->streams[substream->pcm->device].substream = substream; - break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: break; diff --git a/alsa.c b/alsa.c index 738048f..045624b 100755 --- a/alsa.c +++ b/alsa.c @@ -79,31 +79,6 @@ static int alloc_dev_config(struct avirt_alsa_dev_config **devconfig, return 0; } -/** - * alloc_dev_streams - Initializes ALSA device substream buffers - * @return: 0 on success or error code otherwise - */ -static int alloc_dev_streams(struct avirt_alsa_dev_config *config, - struct avirt_alsa_stream **streams, - unsigned numdevices) -{ - unsigned i; - - if (numdevices == 0) - return 0; - - *streams = kzalloc(sizeof(struct avirt_alsa_stream) * numdevices, - GFP_KERNEL); - - if (!(*streams)) - return -EFAULT; - - for (i = 0; i < numdevices; i++) - (*streams)[i].hw_frame_idx = 0; - - return 0; -} - struct avirt_alsa_dev_group *avirt_alsa_get_dev_group(int direction) { if (!_driver) { @@ -155,8 +130,6 @@ int avirt_alsa_configure_pcm(struct avirt_alsa_dev_config *config, group->devices = numdevices; - CHK_ERR(alloc_dev_streams(group->config, &group->streams, - group->devices)); return 0; } @@ -207,12 +180,8 @@ int avirt_alsa_deregister(void) snd_card_free(_driver->card); CHK_NULL(_driver->playback.config); kfree(_driver->playback.config); - CHK_NULL(_driver->playback.streams); - kfree(_driver->playback.streams); CHK_NULL(_driver->capture.config); kfree(_driver->capture.config); - CHK_NULL(_driver->capture.streams); - kfree(_driver->capture.streams); CHK_NULL(_driver); kfree(_driver); @@ -229,27 +198,8 @@ int avirt_alsa_deregister(void) */ int pcm_buff_complete_cb(struct snd_pcm_substream *substream) { - int maxframe, deviceid; - struct avirt_audiopath *audiopath; - struct avirt_alsa_dev_group *group; - - deviceid = substream->pcm->device; - - group = avirt_alsa_get_dev_group(substream->stream); - CHK_NULL(group); - - audiopath = avirt_get_current_audiopath(); - CHK_NULL_V(audiopath, "Cannot find Audio Path!"); - - group->streams[deviceid].hw_frame_idx += audiopath->blocksize; - maxframe = audiopath->blocksize * audiopath->hw->periods_max; - - // Once the index reaches the DMA buffer boundary, reset it to 0 - if (group->streams[deviceid].hw_frame_idx >= maxframe) - group->streams[deviceid].hw_frame_idx = 0; - // Notify ALSA middle layer of the elapsed period boundary - snd_pcm_period_elapsed(group->streams[deviceid].substream); + snd_pcm_period_elapsed(substream); return 0; } diff --git a/alsa.h b/alsa.h index 113b575..ef9b9ca 100755 --- a/alsa.h +++ b/alsa.h @@ -11,6 +11,7 @@ #define __AVIRT_ALSA_H__ #include +#include #define MAX_NAME_LEN 32 @@ -53,20 +54,11 @@ struct avirt_alsa_dev_config { int channels; }; -/** - * Stream maintainance - */ -struct avirt_alsa_stream { - int hw_frame_idx; - struct snd_pcm_substream *substream; -}; - /** * Collection of devices */ struct avirt_alsa_dev_group { struct avirt_alsa_dev_config *config; - struct avirt_alsa_stream *streams; int devices; int buffersize; }; -- cgit 1.2.3-korg From 449c968072a9e6b95f10a28f4b426b70e2883f20 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 3 Sep 2018 15:57:13 +1000 Subject: Remove unused 'buffersize' avirt_alsa_dev_group buffersize is only needed by the ADSP Audio Path driver, so this has been migrated there. Signed-off-by: Mark Farrugia --- alsa-pcm.c | 13 ------------- alsa.h | 1 - 2 files changed, 14 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index c5ff0b0..d0fa228 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -199,19 +199,6 @@ static int pcm_hw_free(struct snd_pcm_substream *substream) */ static int pcm_prepare(struct snd_pcm_substream *substream) { - struct avirt_alsa_dev_group *group; - struct snd_pcm_runtime *runtime = substream->runtime; - - DINFO(AP_LOGNAME, ""); - - group = avirt_alsa_get_dev_group(substream->stream); - CHK_NULL(group); - - // Reset HW buffer index for the device - group->streams[substream->pcm->device].hw_frame_idx = 0; - - group->buffersize = frames_to_bytes(runtime, runtime->buffer_size); - // Do additional Audio Path 'prepare' callback DO_AUDIOPATH_CB(prepare, substream); diff --git a/alsa.h b/alsa.h index ef9b9ca..2e79d33 100755 --- a/alsa.h +++ b/alsa.h @@ -60,7 +60,6 @@ struct avirt_alsa_dev_config { struct avirt_alsa_dev_group { struct avirt_alsa_dev_config *config; int devices; - int buffersize; }; /** -- cgit 1.2.3-korg From 8be23a7c5fafd44296962620299569bf70529f3d Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 3 Sep 2018 16:00:08 +1000 Subject: Remove leftover variable 'hw_frame_idx' Remove remnant of earlier implementation Signed-off-by: Mark Farrugia --- alsa-pcm.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index d0fa228..faa7a83 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -246,14 +246,10 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) */ static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) { - struct avirt_alsa_dev_group *group; - - DINFO(AP_LOGNAME, ""); + // Do additional Audio Path 'pointer' callback + DO_AUDIOPATH_CB(pointer, substream); - group = avirt_alsa_get_dev_group(substream->stream); - CHK_NULL(group); - - return group->streams[substream->pcm->device].hw_frame_idx; + return 0; } /** -- cgit 1.2.3-korg From 4e675e7d45af2fab1ab8d8a6e8d0c86bc4a2a960 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Wed, 5 Sep 2018 14:53:53 +1000 Subject: Move ALSA info into coreinfo, for export to Audio Paths Remove the avirt_alsa_driver Signed-off-by: Mark Farrugia --- alsa-pcm.c | 19 ++++++--------- alsa.c | 79 +++++++++++++++++++++----------------------------------------- alsa.h | 43 +++++----------------------------- core.c | 13 +++-------- core.h | 38 ++++++++++++++++++++---------- 5 files changed, 69 insertions(+), 123 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index faa7a83..17255df 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -7,7 +7,6 @@ * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd */ -#include "core.h" #include "alsa.h" #define AP_LOGNAME "CORE" @@ -30,9 +29,9 @@ */ static int configure_pcm(struct snd_pcm_substream *substream) { - struct avirt_alsa_dev_config *config; + struct avirt_alsa_devconfig *config; struct avirt_audiopath *audiopath; - struct avirt_alsa_dev_group *group; + struct avirt_alsa_group *group; struct snd_pcm_hardware *hw; unsigned bytes_per_sample = 0, blocksize = 0; @@ -55,7 +54,7 @@ static int configure_pcm(struct snd_pcm_substream *substream) } // Get device group (playback/capture) - group = avirt_alsa_get_dev_group(substream->stream); + group = avirt_alsa_get_group(substream->stream); CHK_NULL(group); // Check if substream id is valid @@ -135,11 +134,9 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, int channels, err; size_t bufsz; struct avirt_audiopath *audiopath; - struct avirt_alsa_dev_group *group; + struct avirt_alsa_group *group; - DINFO(AP_LOGNAME, ""); - - group = avirt_alsa_get_dev_group(substream->stream); + group = avirt_alsa_get_group(substream->stream); CHK_NULL(group); channels = group->config[substream->pcm->device].channels; @@ -270,11 +267,9 @@ static int pcm_get_time_info( struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report) { - struct avirt_alsa_dev_group *group; - - DINFO(AP_LOGNAME, ""); + struct avirt_alsa_group *group; - group = avirt_alsa_get_dev_group(substream->stream); + group = avirt_alsa_get_group(substream->stream); CHK_NULL(group); DO_AUDIOPATH_CB(get_time_info, substream, system_ts, audio_ts, diff --git a/alsa.c b/alsa.c index 045624b..dd00ddb 100755 --- a/alsa.c +++ b/alsa.c @@ -12,10 +12,10 @@ #include #include "alsa.h" -#include "core.h" -static struct avirt_alsa_driver *_driver = NULL; +extern struct avirt_coreinfo coreinfo; +static struct snd_card *card = NULL; extern struct snd_pcm_ops pcm_ops; /** @@ -29,29 +29,29 @@ static int pcm_constructor(struct snd_card *card) int i; // Allocate Playback PCM instances - for (i = 0; i < _driver->playback.devices; i++) { + for (i = 0; i < coreinfo.playback.devices; i++) { CHK_ERR(snd_pcm_new(card, - _driver->playback.config[i].devicename, i, - 1, 1, &pcm)); + coreinfo.playback.config[i].devicename, i, + 1, 0, &pcm)); /** Register driver callbacks */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); pcm->info_flags = 0; - strcpy(pcm->name, _driver->playback.config[i].devicename); + strcpy(pcm->name, coreinfo.playback.config[i].devicename); } // Allocate Capture PCM instances - for (i = 0; i < _driver->capture.devices; i++) { - CHK_ERR(snd_pcm_new(card, _driver->capture.config[i].devicename, + for (i = 0; i < coreinfo.capture.devices; i++) { + CHK_ERR(snd_pcm_new(card, coreinfo.capture.config[i].devicename, i, 0, 1, &pcm)); /** Register driver callbacks */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); pcm->info_flags = 0; - strcpy(pcm->name, _driver->capture.config[i].devicename); + strcpy(pcm->name, coreinfo.capture.config[i].devicename); } return 0; @@ -61,35 +61,31 @@ static int pcm_constructor(struct snd_card *card) * alloc_dev_config - Allocates memory for ALSA device configuration * @return: 0 on success or error code otherwise */ -static int alloc_dev_config(struct avirt_alsa_dev_config **devconfig, - struct avirt_alsa_dev_config *userconfig, +static int alloc_dev_config(struct avirt_alsa_devconfig **devconfig, + struct avirt_alsa_devconfig *userconfig, unsigned numdevices) { if (numdevices == 0) return 0; - *devconfig = kzalloc(sizeof(struct avirt_alsa_dev_config) * numdevices, + *devconfig = kzalloc(sizeof(struct avirt_alsa_devconfig) * numdevices, GFP_KERNEL); if (!(*devconfig)) return -ENOMEM; memcpy(*devconfig, userconfig, - sizeof(struct avirt_alsa_dev_config) * numdevices); + sizeof(struct avirt_alsa_devconfig) * numdevices); return 0; } -struct avirt_alsa_dev_group *avirt_alsa_get_dev_group(int direction) +struct avirt_alsa_group *avirt_alsa_get_group(int direction) { - if (!_driver) { - pr_err("[%s] _driver is NULL", __func__); - return NULL; - } - switch (direction) { case SNDRV_PCM_STREAM_PLAYBACK: + return &coreinfo.playback; case SNDRV_PCM_STREAM_CAPTURE: - return &_driver->playback; + return &coreinfo.capture; default: pr_err("[%s] Direction must be SNDRV_PCM_STREAM_XXX!", __func__); @@ -97,20 +93,6 @@ struct avirt_alsa_dev_group *avirt_alsa_get_dev_group(int direction) } } -/** - * avirt_alsa_init - Initializes the ALSA driver - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_init() -{ - // Allocate memory for the driver - _driver = kzalloc(sizeof(struct avirt_alsa_driver), GFP_KERNEL); - if (!_driver) - return -ENOMEM; - - return 0; -} - /** * avirt_alsa_configure_pcm- Configure the PCM device * @config: Device configuration structure array @@ -118,12 +100,12 @@ int avirt_alsa_init() * @numdevices: Number of devices (array length) * @return: 0 on success or error code otherwise */ -int avirt_alsa_configure_pcm(struct avirt_alsa_dev_config *config, - int direction, unsigned numdevices) +int avirt_alsa_configure_pcm(struct avirt_alsa_devconfig *config, int direction, + unsigned numdevices) { - struct avirt_alsa_dev_group *group; + struct avirt_alsa_group *group; - group = avirt_alsa_get_dev_group(direction); + group = avirt_alsa_get_group(direction); CHK_NULL(group); CHK_ERR(alloc_dev_config(&group->config, config, numdevices)); @@ -140,12 +122,8 @@ int avirt_alsa_configure_pcm(struct avirt_alsa_dev_config *config, */ int avirt_alsa_register(struct platform_device *devptr) { - struct snd_card *card; static struct snd_device_ops device_ops; - if (!_driver) - return -EFAULT; - // Create the card instance CHK_ERR_V(snd_card_new(&devptr->dev, SNDRV_DEFAULT_IDX1, "avirt", THIS_MODULE, 0, &card), @@ -154,10 +132,9 @@ int avirt_alsa_register(struct platform_device *devptr) strcpy(card->driver, "avirt-alsa-device"); strcpy(card->shortname, "avirt"); strcpy(card->longname, "A virtual sound card driver for ALSA"); - _driver->card = card; // Create new sound device - CHK_ERR_V((snd_device_new(card, SNDRV_DEV_LOWLEVEL, _driver, + CHK_ERR_V((snd_device_new(card, SNDRV_DEV_LOWLEVEL, &coreinfo, &device_ops)), "Failed to create sound device"); @@ -176,14 +153,12 @@ int avirt_alsa_register(struct platform_device *devptr) */ int avirt_alsa_deregister(void) { - CHK_NULL(_driver->card); - snd_card_free(_driver->card); - CHK_NULL(_driver->playback.config); - kfree(_driver->playback.config); - CHK_NULL(_driver->capture.config); - kfree(_driver->capture.config); - CHK_NULL(_driver); - kfree(_driver); + CHK_NULL(card); + snd_card_free(card); + CHK_NULL(coreinfo.playback.config); + kfree(coreinfo.playback.config); + CHK_NULL(coreinfo.capture.config); + kfree(coreinfo.capture.config); return 0; } diff --git a/alsa.h b/alsa.h index 2e79d33..815dd65 100755 --- a/alsa.h +++ b/alsa.h @@ -10,11 +10,11 @@ #ifndef __AVIRT_ALSA_H__ #define __AVIRT_ALSA_H__ +#include "core.h" + #include #include -#define MAX_NAME_LEN 32 - #define PRINT_ERR(errno, errmsg, ...) \ pr_err("[%s()] %s [ERRNO:%d]", __func__, errmsg, ##__VA_ARGS__, errno); @@ -46,37 +46,6 @@ } \ } while (0) -/* - * Substream device configuration - */ -struct avirt_alsa_dev_config { - const char devicename[MAX_NAME_LEN]; - int channels; -}; - -/** - * Collection of devices - */ -struct avirt_alsa_dev_group { - struct avirt_alsa_dev_config *config; - int devices; -}; - -/** - * ALSA driver data - */ -struct avirt_alsa_driver { - struct snd_card *card; - struct avirt_alsa_dev_group playback; - struct avirt_alsa_dev_group capture; -}; - -/** - * avirt_alsa_init - Initializes the ALSA driver - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_init(void); - /** * avirt_alsa_configure_pcm- Configure the PCM device * @config: Device configuration structure array @@ -84,8 +53,8 @@ int avirt_alsa_init(void); * @numdevices: Number of devices (array length) * @return: 0 on success or error code otherwise */ -int avirt_alsa_configure_pcm(struct avirt_alsa_dev_config *config, - int direction, unsigned numdevices); +int avirt_alsa_configure_pcm(struct avirt_alsa_devconfig *config, int direction, + unsigned numdevices); /** * avirt_alsa_register - Registers the ALSA driver @@ -102,12 +71,12 @@ int avirt_alsa_register(struct platform_device *devptr); int avirt_alsa_deregister(void); /** - * avirt_alsa_get_dev_group - Gets the device group for the specified direction + * avirt_alsa_get_group - Gets the device group for the specified direction * @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE * @return: Either the playback or capture device group on success, * or NULL otherwise */ -struct avirt_alsa_dev_group *avirt_alsa_get_dev_group(int direction); +struct avirt_alsa_group *avirt_alsa_get_group(int direction); /** * pcm_buff_complete_cb - PCM buffer complete callback diff --git a/core.c b/core.c index 996a452..4b6b5af 100644 --- a/core.c +++ b/core.c @@ -65,7 +65,7 @@ static struct avirt_core { struct platform_device *platform_dev; } core; -static struct avirt_coreinfo coreinfo = { +struct avirt_coreinfo coreinfo = { .version = { 0, 0, 1 }, .pcm_buff_complete = pcm_buff_complete_cb, }; @@ -79,8 +79,8 @@ static LIST_HEAD(audiopath_list); */ static int avirt_probe(struct platform_device *devptr) { - // struct avirt_alsa_dev_config capture_config[MAX_PCM_DEVS]; - struct avirt_alsa_dev_config playback_config[MAX_PCM_DEVS]; + // struct avirt_alsa_devconfig capture_config[MAX_PCM_DEVS]; + struct avirt_alsa_devconfig playback_config[MAX_PCM_DEVS]; int err = 0, i = 0; if (playback_num == 0 && capture_num == 0) { @@ -88,13 +88,6 @@ static int avirt_probe(struct platform_device *devptr) return -EINVAL; } - coreinfo.playback_num = playback_num; - coreinfo.capture_num = capture_num; - - err = avirt_alsa_init(); - if (err < 0) - return err; - // Set up playback for (i = 0; i < playback_num; i++) { if (!playback_names[i]) { diff --git a/core.h b/core.h index a31c916..de428c2 100644 --- a/core.h +++ b/core.h @@ -12,16 +12,7 @@ #include -#define DINFO(ap, fmt, args...) \ - printk(KERN_INFO "[%s]: %d:%s " fmt "\n", ap, __LINE__, __func__, \ - ##args) - -#define DERROR(ap, fmt, args...) \ - printk(KERN_ERR "[%s]: %d:%s " fmt "\n", ap, __LINE__, __func__, ##args) - -#define DPRINT(ap, fmt, args...) \ - printk(KERN_DEBUG "[%s]: %d:%s " fmt "\n", ap, __LINE__, __func__, \ - ##args) +#define MAX_NAME_LEN 32 /** * PCM buffer complete callback @@ -31,6 +22,9 @@ */ typedef int (*avirt_buff_complete)(struct snd_pcm_substream *substream); +/** + * AVIRT Audio Path info + */ struct avirt_audiopath { const char *name; unsigned version[3]; @@ -41,10 +35,30 @@ struct avirt_audiopath { void *context; }; +/* + * ALSA Substream device configuration + */ +struct avirt_alsa_devconfig { + const char devicename[MAX_NAME_LEN]; + int channels; +}; + +/** + * Collection of ALSA devices + */ +struct avirt_alsa_group { + struct avirt_alsa_devconfig *config; + int devices; +}; + +/** + * AVIRT core info + */ struct avirt_coreinfo { unsigned version[3]; - unsigned playback_num; - unsigned capture_num; + + struct avirt_alsa_group playback; + struct avirt_alsa_group capture; avirt_buff_complete pcm_buff_complete; }; -- cgit 1.2.3-korg From 91c57bc05904d2f87af864ad9d5f686428ce827f Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Wed, 5 Sep 2018 16:08:08 +1000 Subject: Remove remnant debug print Remove leftover debug prints that are no longer needed. Signed-off-by: Mark Farrugia --- core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/core.c b/core.c index 4b6b5af..9fe7dba 100644 --- a/core.c +++ b/core.c @@ -144,7 +144,6 @@ static int avirt_probe(struct platform_device *devptr) */ static int avirt_remove(struct platform_device *devptr) { - DPRINTK(); return avirt_alsa_deregister(); } -- cgit 1.2.3-korg From 93db730a4d435e835d144e3393088968e1645c87 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Wed, 5 Sep 2018 16:38:25 +1000 Subject: Fix warnings generated by checkpatch.pl Signed-off-by: Mark Farrugia --- alsa-pcm.c | 37 ++++++++++++++++++------------------- alsa.c | 13 +++++-------- alsa.h | 41 ++++++++++++++++++++++------------------- core.c | 13 +++++++------ core.h | 10 +++++----- 5 files changed, 57 insertions(+), 57 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 17255df..9e3471b 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -17,7 +17,7 @@ ap = avirt_get_current_audiopath(); \ CHK_NULL_V(ap, "Cannot find Audio Path!"); \ if (ap->pcm_ops->callback) { \ - return ap->pcm_ops->callback(substream, \ + return ap->pcm_ops->callback((substream), \ ##__VA_ARGS__); \ } \ } while (0) @@ -33,7 +33,7 @@ static int configure_pcm(struct snd_pcm_substream *substream) struct avirt_audiopath *audiopath; struct avirt_alsa_group *group; struct snd_pcm_hardware *hw; - unsigned bytes_per_sample = 0, blocksize = 0; + unsigned int bytes_per_sample = 0, blocksize = 0; audiopath = avirt_get_current_audiopath(); CHK_NULL_V(audiopath, "Cannot find Audio Path!"); @@ -45,9 +45,9 @@ static int configure_pcm(struct snd_pcm_substream *substream) memcpy(hw, audiopath->hw, sizeof(struct snd_pcm_hardware)); pr_info("%s %d %d", __func__, blocksize, hw->periods_max); - if (hw->formats == SNDRV_PCM_FMTBIT_S16_LE) + if (hw->formats == SNDRV_PCM_FMTBIT_S16_LE) { bytes_per_sample = 2; - else { + } else { pr_err("[%s] PCM only supports SNDRV_PCM_FMTBIT_S16_LE", __func__); return -EINVAL; @@ -187,11 +187,11 @@ static int pcm_hw_free(struct snd_pcm_substream *substream) /** * pcm_prepare - Implements 'prepare' callback for PCM middle layer * @substream: pointer to ALSA PCM substream - * + * * The format rate, sample rate, etc., can be set here. This callback can be * called many times at each setup. This function is also used to handle overrun * and underrun conditions when we try and resync the DMA (if we're using DMA). - * + * * Returns 0 on success or error code otherwise. */ static int pcm_prepare(struct snd_pcm_substream *substream) @@ -206,10 +206,10 @@ static int pcm_prepare(struct snd_pcm_substream *substream) * pcm_trigger - Implements 'trigger' callback for PCM middle layer * @substream: pointer to ALSA PCM substream * @cmd: action to be performed (start or stop) - * + * * This is called when the PCM is started, stopped or paused. The action * indicated action is specified in the second argument, SNDRV_PCM_TRIGGER_XXX - * + * * Returns 0 on success or error code otherwise. */ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) @@ -234,11 +234,11 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) /** * pcm_pointer - Implements 'pointer' callback for PCM middle layer * @substream: pointer to ALSA PCM substream - * + * * This gets called when the user space needs a DMA buffer index. IO errors will * be generated if the index does not increment, or drives beyond the frame * threshold of the buffer itself. - * + * * Returns the current hardware buffer frame index. */ static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) @@ -256,9 +256,9 @@ static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) * @audio_ts * @audio_tstamp_config * @audio_tstamp_report - * + * * Generic way to get system timestamp and audio timestamp info - * + * * Returns 0 on success or error code otherwise */ static int pcm_get_time_info( @@ -285,9 +285,9 @@ static int pcm_get_time_info( * @pos: The offset in the DMA * @src: Audio PCM data from the user space * @count: - * + * * This is where we need to copy user audio PCM data into the sound driver - * + * * Returns 0 on success or error code otherwise. * */ @@ -315,9 +315,9 @@ static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, * @pos: The offset in the DMA * @src: Audio PCM data from the user space * @count: - * + * * This is where we need to copy kernel audio PCM data into the sound driver - * + * * Returns 0 on success or error code otherwise. * */ @@ -332,9 +332,9 @@ static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, /** * pcm_ack - Implements 'ack' callback for PCM middle layer * @substream: pointer to ALSA PCM substream - * + * * This is where we need to ack - * + * * Returns 0 on success or error code otherwise. * */ @@ -368,5 +368,4 @@ struct snd_pcm_ops pcm_ops = { .copy_kernel = pcm_copy_kernel, .page = snd_pcm_lib_get_vmalloc_page, .ack = pcm_ack, - }; diff --git a/alsa.c b/alsa.c index dd00ddb..a8609b4 100755 --- a/alsa.c +++ b/alsa.c @@ -13,10 +13,7 @@ #include "alsa.h" -extern struct avirt_coreinfo coreinfo; - -static struct snd_card *card = NULL; -extern struct snd_pcm_ops pcm_ops; +static struct snd_card *card; /** * pcm_constructor - Constructs the ALSA PCM middle devices for this driver @@ -63,12 +60,12 @@ static int pcm_constructor(struct snd_card *card) */ static int alloc_dev_config(struct avirt_alsa_devconfig **devconfig, struct avirt_alsa_devconfig *userconfig, - unsigned numdevices) + unsigned int numdevices) { if (numdevices == 0) return 0; - *devconfig = kzalloc(sizeof(struct avirt_alsa_devconfig) * numdevices, + *devconfig = kzalloc(sizeof(*devconfig) * numdevices, GFP_KERNEL); if (!(*devconfig)) return -ENOMEM; @@ -101,7 +98,7 @@ struct avirt_alsa_group *avirt_alsa_get_group(int direction) * @return: 0 on success or error code otherwise */ int avirt_alsa_configure_pcm(struct avirt_alsa_devconfig *config, int direction, - unsigned numdevices) + unsigned int numdevices) { struct avirt_alsa_group *group; @@ -167,7 +164,7 @@ int avirt_alsa_deregister(void) * pcm_buff_complete_cb - PCM buffer complete callback * @substreamid: pointer to ALSA PCM substream * @return 0 on success or error code otherwise - * + * * This should be called from a child Audio Path once it has finished processing * the pcm buffer */ diff --git a/alsa.h b/alsa.h index 815dd65..12148b9 100755 --- a/alsa.h +++ b/alsa.h @@ -18,34 +18,37 @@ #define PRINT_ERR(errno, errmsg, ...) \ pr_err("[%s()] %s [ERRNO:%d]", __func__, errmsg, ##__VA_ARGS__, errno); -#define CHK_ERR(errno) \ - do { \ - if (errno < 0) \ - return errno; \ +#define CHK_ERR(errno) \ + do { \ + if ((errno) < 0) \ + return (errno); \ } while (0) -#define CHK_ERR_V(errno, errmsg, ...) \ - do { \ - if (errno < 0) { \ - PRINT_ERR(errno, errmsg, ##__VA_ARGS__) \ - return errno; \ - } \ +#define CHK_ERR_V(errno, errmsg, ...) \ + do { \ + if ((errno) < 0) { \ + PRINT_ERR((errno), (errmsg), ##__VA_ARGS__) \ + return (errno); \ + } \ } while (0) #define CHK_NULL(x) \ do { \ - if (!x) \ + if (!(x)) \ return -EFAULT; \ } while (0) -#define CHK_NULL_V(x, errmsg, ...) \ - do { \ - if (!x) { \ - PRINT_ERR(EFAULT, errmsg, ##__VA_ARGS__) \ - return -EFAULT; \ - } \ +#define CHK_NULL_V(x, errmsg, ...) \ + do { \ + if (!(x)) { \ + PRINT_ERR(EFAULT, (errmsg), ##__VA_ARGS__) \ + return -EFAULT; \ + } \ } while (0) +extern struct avirt_coreinfo coreinfo; +extern struct snd_pcm_ops pcm_ops; + /** * avirt_alsa_configure_pcm- Configure the PCM device * @config: Device configuration structure array @@ -54,7 +57,7 @@ * @return: 0 on success or error code otherwise */ int avirt_alsa_configure_pcm(struct avirt_alsa_devconfig *config, int direction, - unsigned numdevices); + unsigned int numdevices); /** * avirt_alsa_register - Registers the ALSA driver @@ -82,7 +85,7 @@ struct avirt_alsa_group *avirt_alsa_get_group(int direction); * pcm_buff_complete_cb - PCM buffer complete callback * @substream: pointer to ALSA PCM substream * @return 0 on success or error code otherwise - * + * * This should be called from a child Audio Path once it has finished processing * the PCM buffer */ diff --git a/core.c b/core.c index 9fe7dba..e77dfb9 100644 --- a/core.c +++ b/core.c @@ -31,17 +31,17 @@ MODULE_LICENSE("GPL v2"); #define DEFAULT_FILE_PERMS 0644 /* Number of playback devices to create (max = MAX_PCM_DEVS) */ -static unsigned playback_num = 0; +static unsigned int playback_num; /* Number of capture devices to create (max = MAX_PCM_DEVS) */ -static unsigned capture_num = 0; +static unsigned int capture_num; /* Names per playback device */ static char *playback_names[MAX_PCM_DEVS]; /* Names per capture device */ static char *capture_names[MAX_PCM_DEVS]; /* Channels per playback device */ -static unsigned playback_chans[MAX_PCM_DEVS]; +static unsigned int playback_chans[MAX_PCM_DEVS]; /* Channels per capture device */ -static unsigned capture_chans[MAX_PCM_DEVS]; +static unsigned int capture_chans[MAX_PCM_DEVS]; module_param(playback_num, int, DEFAULT_FILE_PERMS); MODULE_PARM_DESC(playback_num, @@ -91,7 +91,7 @@ static int avirt_probe(struct platform_device *devptr) // Set up playback for (i = 0; i < playback_num; i++) { if (!playback_names[i]) { - pr_err("Playback config devicename is NULL for idx=%d\n", + pr_err("Playback config dev name is NULL for idx=%d\n", i); return -EINVAL; } @@ -255,6 +255,7 @@ static ssize_t audiopath_version_show(struct avirt_audiopath_obj *audiopath_obj, char *buf) { struct avirt_audiopath *audiopath = audiopath_obj->path; + return sprintf(buf, "%d.%d.%d\n", audiopath->version[0], audiopath->version[1], audiopath->version[2]); } @@ -316,7 +317,7 @@ static void destroy_avirt_audiopath_obj(struct avirt_audiopath_obj *p) * avirt_get_current_audiopath - retrieves the current Audio Path * @return: Current Audio Path */ -struct avirt_audiopath *avirt_get_current_audiopath() +struct avirt_audiopath *avirt_get_current_audiopath(void) { struct avirt_audiopath_obj *ap_obj = list_entry( audiopath_list.next, struct avirt_audiopath_obj, list); diff --git a/core.h b/core.h index de428c2..fb64030 100644 --- a/core.h +++ b/core.h @@ -16,8 +16,8 @@ /** * PCM buffer complete callback - * - * These are called from the audiopath when a PCM buffer has completed, and + * + * These are called from the audiopath when a PCM buffer has completed, and * new data can be submitted/retrieved */ typedef int (*avirt_buff_complete)(struct snd_pcm_substream *substream); @@ -27,10 +27,10 @@ typedef int (*avirt_buff_complete)(struct snd_pcm_substream *substream); */ struct avirt_audiopath { const char *name; - unsigned version[3]; + unsigned int version[3]; struct snd_pcm_hardware *hw; struct snd_pcm_ops *pcm_ops; - unsigned blocksize; + unsigned int blocksize; void *context; }; @@ -55,7 +55,7 @@ struct avirt_alsa_group { * AVIRT core info */ struct avirt_coreinfo { - unsigned version[3]; + unsigned int version[3]; struct avirt_alsa_group playback; struct avirt_alsa_group capture; -- cgit 1.2.3-korg From ae40030429b19a8d22517e99bd604cac8b37fd6e Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Tue, 11 Sep 2018 11:50:43 +1000 Subject: Remove configure_pcm function, absorb into pcm_open No longer any need for the configure_pcm function in it's current form. Signed-off-by: Mark Farrugia --- alsa-pcm.c | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 9e3471b..9b55a14 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -22,12 +22,19 @@ } \ } while (0) +/******************************************************************************* + * ALSA PCM Callbacks + ******************************************************************************/ /** - * configure_pcm - set up substream properties from user configuration + * pcm_open - Implements 'open' callback for PCM middle layer * @substream: pointer to ALSA PCM substream - * @return 0 on success or error code otherwise + * + * This is called when an ALSA PCM substream is opened. The substream device is + * configured here. + * + * Returns 0 on success or error code otherwise. */ -static int configure_pcm(struct snd_pcm_substream *substream) +static int pcm_open(struct snd_pcm_substream *substream) { struct avirt_alsa_devconfig *config; struct avirt_audiopath *audiopath; @@ -74,27 +81,6 @@ static int configure_pcm(struct snd_pcm_substream *substream) hw->period_bytes_min = blocksize * bytes_per_sample * config->channels; hw->period_bytes_max = blocksize * bytes_per_sample * config->channels; - return 0; -} - -/******************************************************************************* - * ALSA PCM Callbacks - ******************************************************************************/ -/** - * pcm_open - Implements 'open' callback for PCM middle layer - * @substream: pointer to ALSA PCM substream - * - * This is called when an ALSA PCM substream is opened. The substream device is - * configured here. - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_open(struct snd_pcm_substream *substream) -{ - DINFO(AP_LOGNAME, ""); - // Setup the pcm device based on the configuration assigned - CHK_ERR_V(configure_pcm(substream), "Failed to setup pcm device"); - // Do additional Audio Path 'open' callback DO_AUDIOPATH_CB(open, substream); -- cgit 1.2.3-korg From 99a09bc4fe16d275b2bf839cb8f37192b2235e24 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 13 Sep 2018 15:16:09 +1000 Subject: Fix kernel crash, where kzalloc is not alloc'ing enough memory! The memory originally being allocated was not for a *, but for a **, hence the kernel crashes due to memory not being adequately allocated. Signed-off-by: Mark Farrugia --- alsa.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/alsa.c b/alsa.c index a8609b4..a899bb1 100755 --- a/alsa.c +++ b/alsa.c @@ -65,8 +65,7 @@ static int alloc_dev_config(struct avirt_alsa_devconfig **devconfig, if (numdevices == 0) return 0; - *devconfig = kzalloc(sizeof(*devconfig) * numdevices, - GFP_KERNEL); + *devconfig = kzalloc(sizeof(**devconfig) * numdevices, GFP_KERNEL); if (!(*devconfig)) return -ENOMEM; -- cgit 1.2.3-korg From 3786b607d4dd5e738cbe491dbfb03c2283e74358 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 20 Sep 2018 12:09:20 +1000 Subject: Add 'uid' field to AP, store AP in PCM private data, fix helper macros We want to use a UID when registering APs, that must start with "ap_", and acts as a unique identifier for each AP. To move forward with the adoption of routing PCMs to differing APs, we now store the AP in the PCM private data - set at pcm_open. A fix to the helper macros now allows additional args to be passed in for inclusion to the debug string Signed-off-by: Mark Farrugia --- alsa-pcm.c | 53 ++++++++++++++++++++++++++++------------------------- alsa.c | 1 - alsa.h | 20 ++++++++++++-------- core.c | 33 +++++++++++++++++++-------------- core.h | 27 +++++++++++++++++++-------- dummy/dummy.c | 1 + 6 files changed, 79 insertions(+), 56 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 9b55a14..ac9175b 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -9,13 +9,8 @@ #include "alsa.h" -#define AP_LOGNAME "CORE" - -#define DO_AUDIOPATH_CB(callback, substream, ...) \ +#define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ do { \ - struct avirt_audiopath *ap; \ - ap = avirt_get_current_audiopath(); \ - CHK_NULL_V(ap, "Cannot find Audio Path!"); \ if (ap->pcm_ops->callback) { \ return ap->pcm_ops->callback((substream), \ ##__VA_ARGS__); \ @@ -42,8 +37,10 @@ static int pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_hardware *hw; unsigned int bytes_per_sample = 0, blocksize = 0; - audiopath = avirt_get_current_audiopath(); - CHK_NULL_V(audiopath, "Cannot find Audio Path!"); + char *uid = "ap_dummy"; // TD MF: Make this dynamic! + audiopath = avirt_get_audiopath(uid); + CHK_NULL_V(audiopath, "Cannot find Audio Path uid: '%s'!", uid); + substream->private_data = audiopath; blocksize = audiopath->blocksize; @@ -82,7 +79,7 @@ static int pcm_open(struct snd_pcm_substream *substream) hw->period_bytes_max = blocksize * bytes_per_sample * config->channels; // Do additional Audio Path 'open' callback - DO_AUDIOPATH_CB(open, substream); + DO_AUDIOPATH_CB(audiopath, open, substream); return 0; } @@ -99,7 +96,8 @@ static int pcm_close(struct snd_pcm_substream *substream) { DINFO(AP_LOGNAME, ""); // Do additional Audio Path 'close' callback - DO_AUDIOPATH_CB(close, substream); + DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), + close, substream); return 0; } @@ -133,9 +131,7 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - audiopath = avirt_get_current_audiopath(); - CHK_NULL_V(audiopath, "Cannot find Audio Path!"); - + audiopath = ((struct avirt_audiopath *)substream->private_data); bufsz = params_buffer_bytes(hw_params) * audiopath->hw->periods_max; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, bufsz); @@ -183,7 +179,8 @@ static int pcm_hw_free(struct snd_pcm_substream *substream) static int pcm_prepare(struct snd_pcm_substream *substream) { // Do additional Audio Path 'prepare' callback - DO_AUDIOPATH_CB(prepare, substream); + DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), + prepare, substream); return 0; } @@ -212,7 +209,8 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) } // Do additional Audio Path 'trigger' callback - DO_AUDIOPATH_CB(trigger, substream, cmd); + DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), + trigger, substream, cmd); return 0; } @@ -230,7 +228,8 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) { // Do additional Audio Path 'pointer' callback - DO_AUDIOPATH_CB(pointer, substream); + DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), + pointer, substream); return 0; } @@ -258,7 +257,8 @@ static int pcm_get_time_info( group = avirt_alsa_get_group(substream->stream); CHK_NULL(group); - DO_AUDIOPATH_CB(get_time_info, substream, system_ts, audio_ts, + DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), + get_time_info, substream, system_ts, audio_ts, audio_tstamp_config, audio_tstamp_report); return 0; @@ -288,8 +288,8 @@ static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, //offset = frames_to_bytes(runtime, pos); // Do additional Audio Path 'copy_user' callback - DINFO(AP_LOGNAME, ""); - DO_AUDIOPATH_CB(copy_user, substream, channel, pos, src, count); + DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), + copy_user, substream, channel, pos, src, count); return 0; } @@ -310,8 +310,9 @@ static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, unsigned long pos, void *buf, unsigned long count) { - DINFO(AP_LOGNAME, ""); - DO_AUDIOPATH_CB(copy_kernel, substream, channel, pos, buf, count); + DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), + copy_kernel, substream, channel, pos, buf, count); + return 0; } @@ -326,16 +327,18 @@ static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, */ int pcm_ack(struct snd_pcm_substream *substream) { - DINFO(AP_LOGNAME, ""); - DO_AUDIOPATH_CB(ack, substream); + DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), + ack, substream); + return 0; } static int pcm_silence(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) { - DINFO(AP_LOGNAME, ""); - DO_AUDIOPATH_CB(fill_silence, substream, channel, pos, count); + DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), + fill_silence, substream, channel, pos, count); + return 0; } diff --git a/alsa.c b/alsa.c index a899bb1..fdbe6b0 100755 --- a/alsa.c +++ b/alsa.c @@ -7,7 +7,6 @@ * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd */ -#include #include #include diff --git a/alsa.h b/alsa.h index 12148b9..c22a93c 100755 --- a/alsa.h +++ b/alsa.h @@ -13,10 +13,11 @@ #include "core.h" #include +#include #include -#define PRINT_ERR(errno, errmsg, ...) \ - pr_err("[%s()] %s [ERRNO:%d]", __func__, errmsg, ##__VA_ARGS__, errno); +#define PRINT_ERR(errno, errmsg) \ + pr_err("[%s]:[ERRNO:%d]: %s ", __func__, errno, (errmsg)); #define CHK_ERR(errno) \ do { \ @@ -38,12 +39,15 @@ return -EFAULT; \ } while (0) -#define CHK_NULL_V(x, errmsg, ...) \ - do { \ - if (!(x)) { \ - PRINT_ERR(EFAULT, (errmsg), ##__VA_ARGS__) \ - return -EFAULT; \ - } \ +#define CHK_NULL_V(x, errmsg, ...) \ + do { \ + if (!(x)) { \ + char *errmsg_done = \ + kasprintf(GFP_KERNEL, errmsg, ##__VA_ARGS__); \ + PRINT_ERR(EFAULT, errmsg_done); \ + kfree(errmsg_done); \ + return -EFAULT; \ + } \ } while (0) extern struct avirt_coreinfo coreinfo; diff --git a/core.c b/core.c index e77dfb9..360f174 100644 --- a/core.c +++ b/core.c @@ -8,7 +8,6 @@ */ #include -#include #include #include "avirt/core.h" @@ -279,13 +278,13 @@ static struct kobj_type avirt_audiopath_ktype = { /** * create_avirt_audiopath_obj - creates an Audio Path object - * @name: name of the Audio Path + * @uid: Unique ID of the Audio Path * * This creates an Audio Path object and assigns the kset and registers * it with sysfs. * @return: Pointer to the Audio Path object or NULL if it failed. */ -static struct avirt_audiopath_obj *create_avirt_audiopath_obj(const char *name) +static struct avirt_audiopath_obj *create_avirt_audiopath_obj(const char *uid) { struct avirt_audiopath_obj *avirt_audiopath; int retval; @@ -295,7 +294,7 @@ static struct avirt_audiopath_obj *create_avirt_audiopath_obj(const char *name) return NULL; avirt_audiopath->kobj.kset = avirt_audiopath_kset; retval = kobject_init_and_add(&avirt_audiopath->kobj, - &avirt_audiopath_ktype, kobj, "%s", name); + &avirt_audiopath_ktype, kobj, "%s", uid); if (retval) { kobject_put(&avirt_audiopath->kobj); return NULL; @@ -314,14 +313,20 @@ static void destroy_avirt_audiopath_obj(struct avirt_audiopath_obj *p) } /** - * avirt_get_current_audiopath - retrieves the current Audio Path - * @return: Current Audio Path + * avirt_get_audiopath - retrieves the Audio Path by its UID + * @uid: Unique ID for the Audio Path + * @return: Corresponding Audio Path */ -struct avirt_audiopath *avirt_get_current_audiopath(void) +struct avirt_audiopath *avirt_get_audiopath(const char *uid) { - struct avirt_audiopath_obj *ap_obj = list_entry( - audiopath_list.next, struct avirt_audiopath_obj, list); - return ap_obj->path; + struct avirt_audiopath_obj *ap_obj; + list_for_each_entry (ap_obj, &audiopath_list, list) { + pr_info("get_ap %s\n", ap_obj->path->uid); + if (!strcmp(ap_obj->path->uid, uid)) + return ap_obj->path; + } + + return NULL; } /** @@ -340,7 +345,7 @@ int avirt_register_audiopath(struct avirt_audiopath *audiopath, return -EINVAL; } - audiopath_obj = create_avirt_audiopath_obj(audiopath->name); + audiopath_obj = create_avirt_audiopath_obj(audiopath->uid); if (!audiopath_obj) { pr_info("failed to alloc driver object\n"); return -ENOMEM; @@ -348,8 +353,8 @@ int avirt_register_audiopath(struct avirt_audiopath *audiopath, audiopath_obj->path = audiopath; audiopath->context = audiopath_obj; - D_INFOK("Registered new Audio Path: %s", audiopath->name); - D_INFOK("\tBlocksize: %d, Periods: %d", audiopath->blocksize, + pr_info("Registered new Audio Path: %s\n", audiopath->uid); + pr_info("\tBlocksize: %d, Periods: %d\n", audiopath->blocksize, audiopath->hw->periods_max); list_add_tail(&audiopath_obj->list, &audiopath_list); @@ -382,7 +387,7 @@ int avirt_deregister_audiopath(struct avirt_audiopath *audiopath) list_del(&audiopath_obj->list); destroy_avirt_audiopath_obj(audiopath_obj); - pr_info("Deregistered Audio Path %s\n", audiopath->name); + pr_info("Deregistered Audio Path %s\n", audiopath->uid); return 0; } diff --git a/core.h b/core.h index fb64030..98d1bbd 100644 --- a/core.h +++ b/core.h @@ -26,11 +26,12 @@ typedef int (*avirt_buff_complete)(struct snd_pcm_substream *substream); * AVIRT Audio Path info */ struct avirt_audiopath { - const char *name; - unsigned int version[3]; - struct snd_pcm_hardware *hw; - struct snd_pcm_ops *pcm_ops; - unsigned int blocksize; + const char *uid; /* Unique identifier */ + const char *name; /* Pretty name */ + unsigned int version[3]; /* Version - Major.Minor.Ext */ + struct snd_pcm_hardware *hw; /* ALSA PCM HW conf */ + struct snd_pcm_ops *pcm_ops; /* ALSA PCM op table */ + unsigned int blocksize; /* Audio frame size accepted */ void *context; }; @@ -80,9 +81,19 @@ int avirt_register_audiopath(struct avirt_audiopath *audiopath, int avirt_deregister_audiopath(struct avirt_audiopath *audiopath); /** - * avirt_get_current_audiopath - retrieves the current Audio Path - * @return: Current Audio Path + * avirt_get_audiopath - retrieves the Audio Path by it's UID + * @uid: Unique ID for the Audio Path + * @return: Corresponding Audio Path + */ +struct avirt_audiopath *avirt_get_audiopath(const char *uid); + +/** + * avirt_subscribe_stream - subscribe the Audio Path to the given streams + * @audiopath: Audio Path to subscribe for + * @streams: The streams to subscribe the Audio Path to + * return: 0 on success or error code otherwise */ -struct avirt_audiopath *avirt_get_current_audiopath(void); +int avirt_subscribe_stream(struct avirt_audiopath *audiopath, + const char **streams); #endif // __AVIRT_CORE_H__ diff --git a/dummy/dummy.c b/dummy/dummy.c index 54d4dcc..fa9dd41 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -244,6 +244,7 @@ static struct snd_pcm_hardware dummyap_hw = { }; static struct avirt_audiopath dummyap_module = { + .uid = "ap_dummy", .name = "Dummy Audio Path", .version = { 0, 0, 1 }, .hw = &dummyap_hw, -- cgit 1.2.3-korg From c032639678c494710f685612acffeae9b0f603f3 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 20 Sep 2018 17:23:42 +1000 Subject: Create avirt_stream structure in place of old config structures Remove unnecessary extra memory allocation with the old config structures. We can store the snd_pcm pointer, which contains lots of info already. Signed-off-by: Mark Farrugia --- Makefile | 1 - alsa-pcm.c | 69 ++++++++++++++++-------- alsa-pcm.h | 28 ++++++++++ alsa.c | 175 ------------------------------------------------------------- alsa.h | 98 ---------------------------------- core.c | 118 +++++++++++++++++++++++++---------------- core.h | 22 ++++---- utils.h | 49 +++++++++++++++++ 8 files changed, 210 insertions(+), 350 deletions(-) create mode 100755 alsa-pcm.h delete mode 100755 alsa.c delete mode 100755 alsa.h create mode 100644 utils.h diff --git a/Makefile b/Makefile index dfdc715..af92bc5 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_AVIRT) += avirt_core.o avirt_core-y := core.o -avirt_core-y += alsa.o avirt_core-y += alsa-pcm.o ifeq ($(CONFIG_AVIRT_BUILDLOCAL),) diff --git a/alsa-pcm.c b/alsa-pcm.c index ac9175b..d65809c 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -2,12 +2,13 @@ /* * ALSA Virtual Soundcard * - * alsa-pcm.c - ALSA PCM implementation + * alsa-pcm.c - AVIRT ALSA PCM interface * * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd */ -#include "alsa.h" +#include "alsa-pcm.h" +#include "utils.h" #define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ do { \ @@ -17,6 +18,36 @@ } \ } while (0) +/** + * pcm_buff_complete_cb - PCM buffer complete callback + * @substreamid: pointer to ALSA PCM substream + * @return 0 on success or error code otherwise + * + * This should be called from a child Audio Path once it has finished processing + * the pcm buffer + */ +int pcm_buff_complete_cb(struct snd_pcm_substream *substream) +{ + // Notify ALSA middle layer of the elapsed period boundary + snd_pcm_period_elapsed(substream); + + return 0; +} + +static struct avirt_stream_group *avirt_stream_get_group(int direction) +{ + switch (direction) { + case SNDRV_PCM_STREAM_PLAYBACK: + return &coreinfo.playback; + case SNDRV_PCM_STREAM_CAPTURE: + return &coreinfo.capture; + default: + pr_err("[%s] Direction must be SNDRV_PCM_STREAM_XXX!", + __func__); + return NULL; + } +} + /******************************************************************************* * ALSA PCM Callbacks ******************************************************************************/ @@ -31,13 +62,12 @@ */ static int pcm_open(struct snd_pcm_substream *substream) { - struct avirt_alsa_devconfig *config; struct avirt_audiopath *audiopath; - struct avirt_alsa_group *group; + struct avirt_stream_group *group; struct snd_pcm_hardware *hw; - unsigned int bytes_per_sample = 0, blocksize = 0; + unsigned int bytes_per_sample = 0, blocksize = 0, chans = 0; - char *uid = "ap_dummy"; // TD MF: Make this dynamic! + char *uid = "ap_fddsp"; // TD MF: Make this dynamic! audiopath = avirt_get_audiopath(uid); CHK_NULL_V(audiopath, "Cannot find Audio Path uid: '%s'!", uid); substream->private_data = audiopath; @@ -58,7 +88,7 @@ static int pcm_open(struct snd_pcm_substream *substream) } // Get device group (playback/capture) - group = avirt_alsa_get_group(substream->stream); + group = avirt_stream_get_group(substream->stream); CHK_NULL(group); // Check if substream id is valid @@ -70,13 +100,13 @@ static int pcm_open(struct snd_pcm_substream *substream) } // Setup remaining hw properties - config = &group->config[substream->pcm->device]; - hw->channels_min = config->channels; - hw->channels_max = config->channels; - hw->buffer_bytes_max = blocksize * hw->periods_max * bytes_per_sample * - config->channels; - hw->period_bytes_min = blocksize * bytes_per_sample * config->channels; - hw->period_bytes_max = blocksize * bytes_per_sample * config->channels; + chans = group->streams[substream->pcm->device].channels; + hw->channels_min = chans; + hw->channels_max = chans; + hw->buffer_bytes_max = + blocksize * hw->periods_max * bytes_per_sample * chans; + hw->period_bytes_min = blocksize * bytes_per_sample * chans; + hw->period_bytes_max = blocksize * bytes_per_sample * chans; // Do additional Audio Path 'open' callback DO_AUDIOPATH_CB(audiopath, open, substream); @@ -118,12 +148,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, int channels, err; size_t bufsz; struct avirt_audiopath *audiopath; - struct avirt_alsa_group *group; + struct avirt_stream_group *group; - group = avirt_alsa_get_group(substream->stream); + group = avirt_stream_get_group(substream->stream); CHK_NULL(group); - channels = group->config[substream->pcm->device].channels; + channels = group->streams[substream->pcm->device].channels; if ((params_channels(hw_params) > channels) || (params_channels(hw_params) < channels)) { @@ -252,11 +282,6 @@ static int pcm_get_time_info( struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report) { - struct avirt_alsa_group *group; - - group = avirt_alsa_get_group(substream->stream); - CHK_NULL(group); - DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), get_time_info, substream, system_ts, audio_ts, audio_tstamp_config, audio_tstamp_report); diff --git a/alsa-pcm.h b/alsa-pcm.h new file mode 100755 index 0000000..c45e2a7 --- /dev/null +++ b/alsa-pcm.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA Virtual Soundcard + * + * alsa-pcm.h - AVIRT ALSA PCM interface + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#ifndef __AVIRT_ALSA_PCM_H__ +#define __AVIRT_ALSA_PCM_H__ + +#include "core.h" + +extern struct avirt_coreinfo coreinfo; +extern struct snd_pcm_ops pcm_ops; + +/** + * pcm_buff_complete_cb - PCM buffer complete callback + * @substream: pointer to ALSA PCM substream + * @return 0 on success or error code otherwise + * + * This should be called from a child Audio Path once it has finished processing + * the PCM buffer + */ +int pcm_buff_complete_cb(struct snd_pcm_substream *substream); + +#endif // __AVIRT_ALSA_PCM_H__ diff --git a/alsa.c b/alsa.c deleted file mode 100755 index fdbe6b0..0000000 --- a/alsa.c +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ALSA Virtual Soundcard - * - * alsa.c - ALSA PCM driver for virtual ALSA card - * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd - */ - -#include -#include - -#include "alsa.h" - -static struct snd_card *card; - -/** - * pcm_constructor - Constructs the ALSA PCM middle devices for this driver - * @card: The snd_card struct to construct the devices for - * @return 0 on success or error code otherwise - */ -static int pcm_constructor(struct snd_card *card) -{ - struct snd_pcm *pcm; - int i; - - // Allocate Playback PCM instances - for (i = 0; i < coreinfo.playback.devices; i++) { - CHK_ERR(snd_pcm_new(card, - coreinfo.playback.config[i].devicename, i, - 1, 0, &pcm)); - - /** Register driver callbacks */ - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); - - pcm->info_flags = 0; - strcpy(pcm->name, coreinfo.playback.config[i].devicename); - } - - // Allocate Capture PCM instances - for (i = 0; i < coreinfo.capture.devices; i++) { - CHK_ERR(snd_pcm_new(card, coreinfo.capture.config[i].devicename, - i, 0, 1, &pcm)); - - /** Register driver callbacks */ - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); - - pcm->info_flags = 0; - strcpy(pcm->name, coreinfo.capture.config[i].devicename); - } - - return 0; -} - -/** - * alloc_dev_config - Allocates memory for ALSA device configuration - * @return: 0 on success or error code otherwise - */ -static int alloc_dev_config(struct avirt_alsa_devconfig **devconfig, - struct avirt_alsa_devconfig *userconfig, - unsigned int numdevices) -{ - if (numdevices == 0) - return 0; - - *devconfig = kzalloc(sizeof(**devconfig) * numdevices, GFP_KERNEL); - if (!(*devconfig)) - return -ENOMEM; - - memcpy(*devconfig, userconfig, - sizeof(struct avirt_alsa_devconfig) * numdevices); - - return 0; -} - -struct avirt_alsa_group *avirt_alsa_get_group(int direction) -{ - switch (direction) { - case SNDRV_PCM_STREAM_PLAYBACK: - return &coreinfo.playback; - case SNDRV_PCM_STREAM_CAPTURE: - return &coreinfo.capture; - default: - pr_err("[%s] Direction must be SNDRV_PCM_STREAM_XXX!", - __func__); - return NULL; - } -} - -/** - * avirt_alsa_configure_pcm- Configure the PCM device - * @config: Device configuration structure array - * @direction: Direction of PCM (SNDRV_PCM_STREAM_XXX) - * @numdevices: Number of devices (array length) - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_configure_pcm(struct avirt_alsa_devconfig *config, int direction, - unsigned int numdevices) -{ - struct avirt_alsa_group *group; - - group = avirt_alsa_get_group(direction); - CHK_NULL(group); - - CHK_ERR(alloc_dev_config(&group->config, config, numdevices)); - - group->devices = numdevices; - - return 0; -} - -/** - * avirt_alsa_register - Registers the ALSA driver - * @devptr: Platform driver device - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_register(struct platform_device *devptr) -{ - static struct snd_device_ops device_ops; - - // Create the card instance - CHK_ERR_V(snd_card_new(&devptr->dev, SNDRV_DEFAULT_IDX1, "avirt", - THIS_MODULE, 0, &card), - "Failed to create sound card"); - - strcpy(card->driver, "avirt-alsa-device"); - strcpy(card->shortname, "avirt"); - strcpy(card->longname, "A virtual sound card driver for ALSA"); - - // Create new sound device - CHK_ERR_V((snd_device_new(card, SNDRV_DEV_LOWLEVEL, &coreinfo, - &device_ops)), - "Failed to create sound device"); - - CHK_ERR((pcm_constructor(card))); - - /** Register with the ALSA framework */ - CHK_ERR_V(snd_card_register(card), "Device registration failed"); - - return 0; -} - -/** - * avirt_alsa_deregister - Deregisters the ALSA driver - * @devptr: Platform driver device - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_deregister(void) -{ - CHK_NULL(card); - snd_card_free(card); - CHK_NULL(coreinfo.playback.config); - kfree(coreinfo.playback.config); - CHK_NULL(coreinfo.capture.config); - kfree(coreinfo.capture.config); - - return 0; -} - -/** - * pcm_buff_complete_cb - PCM buffer complete callback - * @substreamid: pointer to ALSA PCM substream - * @return 0 on success or error code otherwise - * - * This should be called from a child Audio Path once it has finished processing - * the pcm buffer - */ -int pcm_buff_complete_cb(struct snd_pcm_substream *substream) -{ - // Notify ALSA middle layer of the elapsed period boundary - snd_pcm_period_elapsed(substream); - - return 0; -} diff --git a/alsa.h b/alsa.h deleted file mode 100755 index c22a93c..0000000 --- a/alsa.h +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ALSA Virtual Soundcard - * - * alsa.h - Top level ALSA interface - * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd - */ - -#ifndef __AVIRT_ALSA_H__ -#define __AVIRT_ALSA_H__ - -#include "core.h" - -#include -#include -#include - -#define PRINT_ERR(errno, errmsg) \ - pr_err("[%s]:[ERRNO:%d]: %s ", __func__, errno, (errmsg)); - -#define CHK_ERR(errno) \ - do { \ - if ((errno) < 0) \ - return (errno); \ - } while (0) - -#define CHK_ERR_V(errno, errmsg, ...) \ - do { \ - if ((errno) < 0) { \ - PRINT_ERR((errno), (errmsg), ##__VA_ARGS__) \ - return (errno); \ - } \ - } while (0) - -#define CHK_NULL(x) \ - do { \ - if (!(x)) \ - return -EFAULT; \ - } while (0) - -#define CHK_NULL_V(x, errmsg, ...) \ - do { \ - if (!(x)) { \ - char *errmsg_done = \ - kasprintf(GFP_KERNEL, errmsg, ##__VA_ARGS__); \ - PRINT_ERR(EFAULT, errmsg_done); \ - kfree(errmsg_done); \ - return -EFAULT; \ - } \ - } while (0) - -extern struct avirt_coreinfo coreinfo; -extern struct snd_pcm_ops pcm_ops; - -/** - * avirt_alsa_configure_pcm- Configure the PCM device - * @config: Device configuration structure array - * @direction: Direction of PCM (SNDRV_PCM_STREAM_XXX) - * @numdevices: Number of devices (array length) - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_configure_pcm(struct avirt_alsa_devconfig *config, int direction, - unsigned int numdevices); - -/** - * avirt_alsa_register - Registers the ALSA driver - * @devptr: Platform driver device - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_register(struct platform_device *devptr); - -/** - * avirt_alsa_deregister - Deregisters the ALSA driver - * @devptr: Platform driver device - * @return: 0 on success or error code otherwise - */ -int avirt_alsa_deregister(void); - -/** - * avirt_alsa_get_group - Gets the device group for the specified direction - * @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE - * @return: Either the playback or capture device group on success, - * or NULL otherwise - */ -struct avirt_alsa_group *avirt_alsa_get_group(int direction); - -/** - * pcm_buff_complete_cb - PCM buffer complete callback - * @substream: pointer to ALSA PCM substream - * @return 0 on success or error code otherwise - * - * This should be called from a child Audio Path once it has finished processing - * the PCM buffer - */ -int pcm_buff_complete_cb(struct snd_pcm_substream *substream); - -#endif // __AVIRT_ALSA_H__ diff --git a/core.c b/core.c index 360f174..c9e8d25 100644 --- a/core.c +++ b/core.c @@ -9,9 +9,13 @@ #include #include +#include +#include +#include #include "avirt/core.h" -#include "alsa.h" +#include "alsa-pcm.h" +#include "utils.h" MODULE_AUTHOR("JOSHANNE "); MODULE_AUTHOR("MFARRUGI "); @@ -58,8 +62,8 @@ module_param_array(capture_chans, int, NULL, DEFAULT_FILE_PERMS); MODULE_PARM_DESC(capture_chans, "Channels per capture device"); static struct avirt_core { + struct snd_card *card; struct device *dev; - struct class *avirt_class; struct platform_device *platform_dev; } core; @@ -78,8 +82,8 @@ static LIST_HEAD(audiopath_list); */ static int avirt_probe(struct platform_device *devptr) { - // struct avirt_alsa_devconfig capture_config[MAX_PCM_DEVS]; - struct avirt_alsa_devconfig playback_config[MAX_PCM_DEVS]; + static struct snd_device_ops device_ops; + struct snd_pcm **pcm; int err = 0, i = 0; if (playback_num == 0 && capture_num == 0) { @@ -87,51 +91,71 @@ static int avirt_probe(struct platform_device *devptr) return -EINVAL; } - // Set up playback - for (i = 0; i < playback_num; i++) { - if (!playback_names[i]) { - pr_err("Playback config dev name is NULL for idx=%d\n", - i); - return -EINVAL; - } - memcpy((char *)playback_config[i].devicename, playback_names[i], - MAX_NAME_LEN); - if (playback_chans[i] == 0) { - pr_err("Playback config channels is 0 for idx=%d\n", i); - return -EINVAL; + // Create the card instance + CHK_ERR_V(snd_card_new(&devptr->dev, SNDRV_DEFAULT_IDX1, "avirt", + THIS_MODULE, 0, &core.card), + "Failed to create sound card"); + + strcpy(core.card->driver, "avirt-alsa-device"); + strcpy(core.card->shortname, "avirt"); + strcpy(core.card->longname, "A virtual sound card driver for ALSA"); + + // Create new sound device + CHK_ERR_V((snd_device_new(core.card, SNDRV_DEV_LOWLEVEL, &coreinfo, + &device_ops)), + "Failed to create sound device"); + + // TEMP + if (playback_num > 0) { + coreinfo.playback.devices = playback_num; + coreinfo.playback.streams = kzalloc( + sizeof(*coreinfo.playback.streams) * playback_num, + GFP_KERNEL); + for (i = 0; i < playback_num; i++) { + pcm = &coreinfo.playback.streams[i].pcm; + CHK_ERR(snd_pcm_new(core.card, playback_names[i], i, 1, + 0, pcm)); + + /** Register driver callbacks */ + snd_pcm_set_ops(*pcm, SNDRV_PCM_STREAM_PLAYBACK, + &pcm_ops); + + (*pcm)->info_flags = 0; + strcpy((*pcm)->name, playback_names[i]); + coreinfo.playback.streams[i].channels = + playback_chans[i]; + pr_info("snd_pcm_new: name: %s, chans: %d\n", + (*pcm)->name, + coreinfo.playback.streams[i].channels); } - playback_config[i].channels = playback_chans[i]; } - err = avirt_alsa_configure_pcm(playback_config, - SNDRV_PCM_STREAM_PLAYBACK, playback_num); - if (err < 0) - return err; - -// Does not work yet! -#if 0 - // Set up capture - for (i = 0; i < capture_num; i++) { - if (!capture_names[i]) { - pr_err("Capture config devicename is NULL for idx=%d", - i); - return -EINVAL; - } - memcpy((char *)capture_config[i].devicename, capture_names[i], - 255); - if (capture_chans[i] == 0) { - pr_err("Capture config channels is 0 for idx=%d"); - return -EINVAL; + if (capture_num > 0) { + coreinfo.capture.devices = capture_num; + coreinfo.capture.streams = + kzalloc(sizeof(*coreinfo.capture.streams) * capture_num, + GFP_KERNEL); + for (i = 0; i < capture_num; i++) { + pcm = &coreinfo.capture.streams[i].pcm; + CHK_ERR(snd_pcm_new(core.card, capture_names[i], + i + playback_num, 0, 1, pcm)); + + /** Register driver callbacks */ + snd_pcm_set_ops(*pcm, SNDRV_PCM_STREAM_CAPTURE, + &pcm_ops); + + (*pcm)->info_flags = 0; + strcpy((*pcm)->name, capture_names[i]); + coreinfo.capture.streams[i].channels = capture_chans[i]; + pr_info("snd_pcm_new: name: %s, chans: %d\n", + (*pcm)->name, + coreinfo.capture.streams[i].channels); } - capture_config[i].channels = capture_chans[i]; } - err = avirt_alsa_configure_pcm(capture_config, SNDRV_PCM_STREAM_CAPTURE, capture_num); - if (err < 0) - return err; -#endif + // TEMP - // Register for ALSA - CHK_ERR(avirt_alsa_register(devptr)); + /** Register with the ALSA framework */ + CHK_ERR_V(snd_card_register(core.card), "Device registration failed"); return err; } @@ -143,7 +167,13 @@ static int avirt_probe(struct platform_device *devptr) */ static int avirt_remove(struct platform_device *devptr) { - return avirt_alsa_deregister(); + snd_card_free(core.card); + CHK_NULL(coreinfo.playback.streams); + kfree(coreinfo.playback.streams); + CHK_NULL(coreinfo.capture.streams); + kfree(coreinfo.capture.streams); + + return 0; } static struct platform_driver avirt_driver = { diff --git a/core.h b/core.h index 98d1bbd..4a0157a 100644 --- a/core.h +++ b/core.h @@ -11,6 +11,7 @@ #define __AVIRT_CORE_H__ #include +#include #define MAX_NAME_LEN 32 @@ -37,19 +38,20 @@ struct avirt_audiopath { }; /* - * ALSA Substream device configuration + * Audio stream configuration */ -struct avirt_alsa_devconfig { - const char devicename[MAX_NAME_LEN]; - int channels; +struct avirt_stream { + struct snd_pcm *pcm; /* Stream PCM device */ + unsigned int channels; /* Stream channel count */ + struct config_item item; /* configfs item reference */ }; /** - * Collection of ALSA devices + * Collection of audio streams */ -struct avirt_alsa_group { - struct avirt_alsa_devconfig *config; - int devices; +struct avirt_stream_group { + struct avirt_stream *streams; /* AVIRT stream array */ + unsigned int devices; /* Number of stream devices */ }; /** @@ -58,8 +60,8 @@ struct avirt_alsa_group { struct avirt_coreinfo { unsigned int version[3]; - struct avirt_alsa_group playback; - struct avirt_alsa_group capture; + struct avirt_stream_group playback; + struct avirt_stream_group capture; avirt_buff_complete pcm_buff_complete; }; diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..b449938 --- /dev/null +++ b/utils.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA Virtual Soundcard + * + * utils.h - Some useful utilities for AVIRT + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#ifndef __AVIRT_UTILS_H__ +#define __AVIRT_UTILS_H__ + +#include + +#define PRINT_ERR(errno, errmsg) \ + pr_err("[%s]:[ERRNO:%d]: %s ", __func__, errno, (errmsg)); + +#define CHK_ERR(errno) \ + do { \ + if ((errno) < 0) \ + return (errno); \ + } while (0) + +#define CHK_ERR_V(errno, errmsg, ...) \ + do { \ + if ((errno) < 0) { \ + PRINT_ERR((errno), (errmsg), ##__VA_ARGS__) \ + return (errno); \ + } \ + } while (0) + +#define CHK_NULL(x) \ + do { \ + if (!(x)) \ + return -EFAULT; \ + } while (0) + +#define CHK_NULL_V(x, errmsg, ...) \ + do { \ + if (!(x)) { \ + char *errmsg_done = \ + kasprintf(GFP_KERNEL, errmsg, ##__VA_ARGS__); \ + PRINT_ERR(EFAULT, errmsg_done); \ + kfree(errmsg_done); \ + return -EFAULT; \ + } \ + } while (0) + +#endif -- cgit 1.2.3-korg From d20d3b185bd84889a1e54b755a8198200c31bc7d Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Fri, 21 Sep 2018 17:25:31 +1000 Subject: Refactor top-level AVIRT API to be avirt__ This makes for a more organized function/variable naming convention Signed-off-by: Mark Farrugia --- alsa-pcm.c | 2 +- core.c | 16 ++++++++-------- core.h | 12 ++++++------ dummy/dummy.c | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index d65809c..798f76c 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -68,7 +68,7 @@ static int pcm_open(struct snd_pcm_substream *substream) unsigned int bytes_per_sample = 0, blocksize = 0, chans = 0; char *uid = "ap_fddsp"; // TD MF: Make this dynamic! - audiopath = avirt_get_audiopath(uid); + audiopath = avirt_audiopath_get(uid); CHK_NULL_V(audiopath, "Cannot find Audio Path uid: '%s'!", uid); substream->private_data = audiopath; diff --git a/core.c b/core.c index c9e8d25..e1afce8 100644 --- a/core.c +++ b/core.c @@ -343,11 +343,11 @@ static void destroy_avirt_audiopath_obj(struct avirt_audiopath_obj *p) } /** - * avirt_get_audiopath - retrieves the Audio Path by its UID + * avirt_audiopath_get - retrieves the Audio Path by its UID * @uid: Unique ID for the Audio Path * @return: Corresponding Audio Path */ -struct avirt_audiopath *avirt_get_audiopath(const char *uid) +struct avirt_audiopath *avirt_audiopath_get(const char *uid) { struct avirt_audiopath_obj *ap_obj; list_for_each_entry (ap_obj, &audiopath_list, list) { @@ -360,12 +360,12 @@ struct avirt_audiopath *avirt_get_audiopath(const char *uid) } /** - * avirt_register_audiopath - register Audio Path with ALSA virtual driver + * avirt_audiopath_register - register Audio Path with ALSA virtual driver * @audiopath: Audio Path to be registered * @core: ALSA virtual driver core info * @return: 0 on success or error code otherwise */ -int avirt_register_audiopath(struct avirt_audiopath *audiopath, +int avirt_audiopath_register(struct avirt_audiopath *audiopath, struct avirt_coreinfo **info) { struct avirt_audiopath_obj *audiopath_obj; @@ -392,14 +392,14 @@ int avirt_register_audiopath(struct avirt_audiopath *audiopath, return 0; } -EXPORT_SYMBOL_GPL(avirt_register_audiopath); +EXPORT_SYMBOL_GPL(avirt_audiopath_register); /** - * avirt_deregister_audiopath - deregister Audio Path with ALSA virtual driver + * avirt_audiopath_deregister - deregister Audio Path with ALSA virtual driver * @audiopath: Audio Path to be deregistered * @return: 0 on success or error code otherwise */ -int avirt_deregister_audiopath(struct avirt_audiopath *audiopath) +int avirt_audiopath_deregister(struct avirt_audiopath *audiopath) { struct avirt_audiopath_obj *audiopath_obj; @@ -421,7 +421,7 @@ int avirt_deregister_audiopath(struct avirt_audiopath *audiopath) return 0; } -EXPORT_SYMBOL_GPL(avirt_deregister_audiopath); +EXPORT_SYMBOL_GPL(avirt_audiopath_deregister); /** * avirt_unregister_all - Unregister the platform device driver diff --git a/core.h b/core.h index 4a0157a..c11ee1d 100644 --- a/core.h +++ b/core.h @@ -67,27 +67,27 @@ struct avirt_coreinfo { }; /** - * avirt_register_audiopath - register Audio Path with ALSA virtual driver + * avirt_audiopath_register - register Audio Path with ALSA virtual driver * @audiopath: Audio Path to be registered * @core: ALSA virtual driver core info * @return: 0 on success or error code otherwise */ -int avirt_register_audiopath(struct avirt_audiopath *audiopath, +int avirt_audiopath_register(struct avirt_audiopath *audiopath, struct avirt_coreinfo **coreinfo); /** - * avirt_deregister_audiopath - deregister Audio Path with ALSA virtual driver + * avirt_audiopath_deregister - deregister Audio Path with ALSA virtual driver * @audiopath: Audio Path to be deregistered * @return: 0 on success or error code otherwise */ -int avirt_deregister_audiopath(struct avirt_audiopath *audiopath); +int avirt_audiopath_deregister(struct avirt_audiopath *audiopath); /** - * avirt_get_audiopath - retrieves the Audio Path by it's UID + * avirt_audiopath_get - retrieves the Audio Path by it's UID * @uid: Unique ID for the Audio Path * @return: Corresponding Audio Path */ -struct avirt_audiopath *avirt_get_audiopath(const char *uid); +struct avirt_audiopath *avirt_audiopath_get(const char *uid); /** * avirt_subscribe_stream - subscribe the Audio Path to the given streams diff --git a/dummy/dummy.c b/dummy/dummy.c index fa9dd41..d619786 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -258,7 +258,7 @@ static int __init dummy_init(void) pr_info("init()\n"); - err = avirt_register_audiopath(&dummyap_module, &coreinfo); + err = avirt_audiopath_register(&dummyap_module, &coreinfo); if ((err < 0) || (!coreinfo)) { pr_err("%s: coreinfo is NULL!\n", __func__); return err; @@ -271,7 +271,7 @@ static void __exit dummy_exit(void) { pr_info("exit()\n"); - avirt_deregister_audiopath(&dummyap_module); + avirt_audiopath_deregister(&dummyap_module); } module_init(dummy_init); -- cgit 1.2.3-korg From 329a6c0e00cc58e4faceaa0ef4abd3aeb50fb2d5 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Fri, 21 Sep 2018 17:28:09 +1000 Subject: Fix memory freeing of capture/playback streams Since playback and capture streams are not always assigned, we do not want to error when one or the other is not present for free'ing. Signed-off-by: Mark Farrugia --- core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core.c b/core.c index e1afce8..c9a9aa3 100644 --- a/core.c +++ b/core.c @@ -168,10 +168,10 @@ static int avirt_probe(struct platform_device *devptr) static int avirt_remove(struct platform_device *devptr) { snd_card_free(core.card); - CHK_NULL(coreinfo.playback.streams); - kfree(coreinfo.playback.streams); - CHK_NULL(coreinfo.capture.streams); - kfree(coreinfo.capture.streams); + if (coreinfo.playback.streams) + kfree(coreinfo.playback.streams); + if (coreinfo.capture.streams) + kfree(coreinfo.capture.streams); return 0; } -- cgit 1.2.3-korg From 6c5c0d66a792ecbbf92538a7822b62a36710a341 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Sun, 23 Sep 2018 22:03:30 +1000 Subject: Make pcm_ack static Add missing static declaration to pcm_ack callback function Signed-off-by: Mark Farrugia --- alsa-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 798f76c..21287ee 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -350,7 +350,7 @@ static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, * Returns 0 on success or error code otherwise. * */ -int pcm_ack(struct snd_pcm_substream *substream) +static int pcm_ack(struct snd_pcm_substream *substream) { DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), ack, substream); -- cgit 1.2.3-korg From 3247d61d378afd8fc76f1e9182e5691bd538ab3f Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 1 Oct 2018 17:43:39 +1000 Subject: Add configfs interface, revamp stream grouping Add additional callbacks for audio path configuring, after card has been sealed. Signed-off-by: Mark Farrugia --- Makefile | 1 + alsa-pcm.c | 82 +++++-------- alsa-pcm.h | 28 ----- configfs.c | 229 ++++++++++++++++++++++++++++++++++++ core.c | 354 ++++++++++++++++++++++++-------------------------------- core.h | 76 ++++++------ core_internal.h | 50 ++++++++ dummy/dummy.c | 22 +++- utils.h | 50 ++++---- 9 files changed, 548 insertions(+), 344 deletions(-) delete mode 100755 alsa-pcm.h create mode 100644 configfs.c create mode 100644 core_internal.h diff --git a/Makefile b/Makefile index af92bc5..44874ca 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_AVIRT) += avirt_core.o avirt_core-y := core.o avirt_core-y += alsa-pcm.o +avirt_core-y += configfs.o ifeq ($(CONFIG_AVIRT_BUILDLOCAL),) CCFLAGS_AVIRT := "drivers/staging/" diff --git a/alsa-pcm.c b/alsa-pcm.c index 21287ee..5a30aff 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -7,46 +7,30 @@ * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd */ -#include "alsa-pcm.h" -#include "utils.h" - -#define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ - do { \ - if (ap->pcm_ops->callback) { \ - return ap->pcm_ops->callback((substream), \ - ##__VA_ARGS__); \ - } \ +#include "core_internal.h" + +#define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ + do { \ + if ((ap)->pcm_ops->callback) { \ + return (ap)->pcm_ops->callback((substream), \ + ##__VA_ARGS__); \ + } \ } while (0) /** - * pcm_buff_complete_cb - PCM buffer complete callback + * avirt_pcm_period_elapsed - PCM buffer complete callback * @substreamid: pointer to ALSA PCM substream - * @return 0 on success or error code otherwise * * This should be called from a child Audio Path once it has finished processing * the pcm buffer */ -int pcm_buff_complete_cb(struct snd_pcm_substream *substream) +void avirt_pcm_period_elapsed(struct snd_pcm_substream *substream) { // Notify ALSA middle layer of the elapsed period boundary snd_pcm_period_elapsed(substream); - - return 0; } +EXPORT_SYMBOL_GPL(avirt_pcm_period_elapsed); -static struct avirt_stream_group *avirt_stream_get_group(int direction) -{ - switch (direction) { - case SNDRV_PCM_STREAM_PLAYBACK: - return &coreinfo.playback; - case SNDRV_PCM_STREAM_CAPTURE: - return &coreinfo.capture; - default: - pr_err("[%s] Direction must be SNDRV_PCM_STREAM_XXX!", - __func__); - return NULL; - } -} /******************************************************************************* * ALSA PCM Callbacks @@ -63,13 +47,14 @@ static struct avirt_stream_group *avirt_stream_get_group(int direction) static int pcm_open(struct snd_pcm_substream *substream) { struct avirt_audiopath *audiopath; - struct avirt_stream_group *group; + struct avirt_stream *stream; struct snd_pcm_hardware *hw; unsigned int bytes_per_sample = 0, blocksize = 0, chans = 0; char *uid = "ap_fddsp"; // TD MF: Make this dynamic! audiopath = avirt_audiopath_get(uid); - CHK_NULL_V(audiopath, "Cannot find Audio Path uid: '%s'!", uid); + CHK_NULL_V(audiopath, -EFAULT, "Cannot find Audio Path uid: '%s'!", + uid); substream->private_data = audiopath; blocksize = audiopath->blocksize; @@ -87,20 +72,12 @@ static int pcm_open(struct snd_pcm_substream *substream) return -EINVAL; } - // Get device group (playback/capture) - group = avirt_stream_get_group(substream->stream); - CHK_NULL(group); - - // Check if substream id is valid - pr_info("%d substream is < %d", substream->pcm->device, group->devices); - if (substream->pcm->device >= group->devices) { - pr_err("%s %d substream id is invalid expecting %d", __func__, - substream->pcm->device, group->devices); - return -1; - } + stream = __avirt_stream_find_by_device(substream->pcm->device); + if (IS_ERR_VALUE(stream) || !stream) + return PTR_ERR(stream); // Setup remaining hw properties - chans = group->streams[substream->pcm->device].channels; + chans = stream->channels; hw->channels_min = chans; hw->channels_max = chans; hw->buffer_bytes_max = @@ -145,18 +122,17 @@ static int pcm_close(struct snd_pcm_substream *substream) static int pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - int channels, err; + int err; size_t bufsz; struct avirt_audiopath *audiopath; - struct avirt_stream_group *group; - - group = avirt_stream_get_group(substream->stream); - CHK_NULL(group); + struct avirt_stream *stream; - channels = group->streams[substream->pcm->device].channels; + stream = __avirt_stream_find_by_device(substream->pcm->device); + if (IS_ERR_VALUE(stream) || !stream) + return PTR_ERR(stream); - if ((params_channels(hw_params) > channels) || - (params_channels(hw_params) < channels)) { + if ((params_channels(hw_params) > stream->channels) + || (params_channels(hw_params) < stream->channels)) { pr_err("Requested number of channels not supported.\n"); return -EINVAL; } @@ -306,11 +282,11 @@ static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) { - //struct snd_pcm_runtime *runtime; - //int offset; + // struct snd_pcm_runtime *runtime; + // int offset; - //runtime = substream->runtime; - //offset = frames_to_bytes(runtime, pos); + // runtime = substream->runtime; + // offset = frames_to_bytes(runtime, pos); // Do additional Audio Path 'copy_user' callback DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), diff --git a/alsa-pcm.h b/alsa-pcm.h deleted file mode 100755 index c45e2a7..0000000 --- a/alsa-pcm.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ALSA Virtual Soundcard - * - * alsa-pcm.h - AVIRT ALSA PCM interface - * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd - */ - -#ifndef __AVIRT_ALSA_PCM_H__ -#define __AVIRT_ALSA_PCM_H__ - -#include "core.h" - -extern struct avirt_coreinfo coreinfo; -extern struct snd_pcm_ops pcm_ops; - -/** - * pcm_buff_complete_cb - PCM buffer complete callback - * @substream: pointer to ALSA PCM substream - * @return 0 on success or error code otherwise - * - * This should be called from a child Audio Path once it has finished processing - * the PCM buffer - */ -int pcm_buff_complete_cb(struct snd_pcm_substream *substream); - -#endif // __AVIRT_ALSA_PCM_H__ diff --git a/configfs.c b/configfs.c new file mode 100644 index 0000000..dd7b7df --- /dev/null +++ b/configfs.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA Virtual Soundcard + * + * configfs.c - configfs for virtual ALSA card + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#include +#include "core_internal.h" + +static bool streams_sealed = false; + +static ssize_t cfg_avirt_stream_direction_show(struct config_item *item, + char *page) +{ + ssize_t count; + struct avirt_stream *stream = avirt_stream_from_config_item(item); + + count = sprintf(page, "%d\n", stream->direction); + + return count; +} +CONFIGFS_ATTR_RO(cfg_avirt_stream_, direction); + +static ssize_t cfg_avirt_stream_map_show(struct config_item *item, char *page) +{ + struct avirt_stream *stream = avirt_stream_from_config_item(item); + + return sprintf(page, "%s\n", stream->map); +} + +static ssize_t cfg_avirt_stream_map_store(struct config_item *item, + const char *page, size_t count) +{ + struct avirt_stream *stream = avirt_stream_from_config_item(item); + + memcpy(stream->map, (char *)page, count); + + return count; +} +CONFIGFS_ATTR(cfg_avirt_stream_, map); + +static ssize_t cfg_avirt_stream_channels_show(struct config_item *item, + char *page) +{ + ssize_t count; + struct avirt_stream *stream = avirt_stream_from_config_item(item); + + count = sprintf(page, "%d\n", stream->channels); + + return count; +} + +static ssize_t cfg_avirt_stream_channels_store(struct config_item *item, + const char *page, size_t count) +{ + int err; + struct avirt_stream *stream = avirt_stream_from_config_item(item); + unsigned long tmp; + char *p = (char *)page; + + err = kstrtoul(p, 10, &tmp); + if (err < 0) + return err; + + if (tmp > INT_MAX) + return -ERANGE; + + stream->channels = tmp; + + pr_info("%s [ARGS] channels: %d\n", __func__, stream->channels); + + return count; +} +CONFIGFS_ATTR(cfg_avirt_stream_, channels); + +static struct configfs_attribute *cfg_avirt_stream_attrs[] = { + &cfg_avirt_stream_attr_channels, + &cfg_avirt_stream_attr_map, + &cfg_avirt_stream_attr_direction, + NULL, +}; + +static void cfg_avirt_stream_release(struct config_item *item) +{ + pr_info("%s [ARGS] item->name:%s\n", __func__, item->ci_namebuf); + kfree(avirt_stream_from_config_item(item)); +} + +static struct configfs_item_operations cfg_avirt_stream_ops = { + .release = cfg_avirt_stream_release, +}; + +static struct config_item_type cfg_avirt_stream_type = { + .ct_item_ops = &cfg_avirt_stream_ops, + .ct_attrs = cfg_avirt_stream_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item * +cfg_avirt_stream_make_item(struct config_group *group, const char *name) +{ + char *split; + int direction; + struct avirt_stream *stream; + + // Get prefix (playback_ or capture_) + split = strsep((char **)&name, "_"); + if (!split) { + pr_err("Stream name: '%s' invalid!\n", split); + pr_err("Must begin with playback_ * or capture_ *\n"); + return ERR_PTR(-EINVAL); + } + if (!strcmp(split, "playback")) { + direction = SNDRV_PCM_STREAM_PLAYBACK; + } else if (!strcmp(split, "capture")) { + direction = SNDRV_PCM_STREAM_CAPTURE; + } else { + pr_err("Stream name: '%s' invalid!\n", split); + pr_err("Must begin with playback_ * or capture_ *\n"); + return ERR_PTR(-EINVAL); + } + + // Get stream name, and create PCM for stream + split = strsep((char **)&name, "\n"); + stream = __avirt_stream_create(split, direction); + if (IS_ERR(stream)) + return ERR_PTR(PTR_ERR(stream)); + + config_item_init_type_name(&stream->item, name, &cfg_avirt_stream_type); + + return &stream->item; +} + +static ssize_t cfg_avirt_stream_group_sealed_show(struct config_item *item, + char *page) +{ + return snprintf(page, PAGE_SIZE, "%d\n", streams_sealed); +} + +static ssize_t cfg_avirt_stream_group_sealed_store(struct config_item *item, + const char *page, + size_t count) +{ + unsigned long tmp; + char *p = (char *)page; + + if (streams_sealed) { + pr_err("AVIRT streams are already sealed!\n"); + return -EPERM; + } + + CHK_ERR(kstrtoul(p, 10, &tmp)); + + if (tmp != 1) { + pr_err("AVIRT streams can only be sealed, not unsealed!\n"); + return -ERANGE; + } + + streams_sealed = (bool)tmp; + + CHK_ERR(__avirt_card_register()); + + return count; +} +CONFIGFS_ATTR(cfg_avirt_stream_group_, sealed); + +static struct configfs_attribute *cfg_avirt_stream_group_attrs[] = { + &cfg_avirt_stream_group_attr_sealed, + NULL, +}; + +static struct configfs_group_operations cfg_avirt_stream_group_ops = { + .make_item = cfg_avirt_stream_make_item}; + +static struct config_item_type cfg_stream_group_type = { + .ct_group_ops = &cfg_avirt_stream_group_ops, + .ct_attrs = cfg_avirt_stream_group_attrs, + .ct_owner = THIS_MODULE}; + +static struct config_item_type cfg_avirt_group_type = { + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem cfg_subsys = { + .su_group = + { + .cg_item = + { + .ci_namebuf = "avirt", + .ci_type = &cfg_avirt_group_type, + }, + }, +}; + +int __init __avirt_configfs_init(struct avirt_core *core) +{ + int err; + + config_group_init(&cfg_subsys.su_group); + mutex_init(&cfg_subsys.su_mutex); + err = configfs_register_subsystem(&cfg_subsys); + if (err) { + pr_err("Cannot register configfs subsys!\n"); + return err; + } + core->stream_group = configfs_register_default_group( + &cfg_subsys.su_group, "streams", &cfg_stream_group_type); + if (IS_ERR(core->stream_group)) { + err = PTR_ERR(core->stream_group); + pr_err("Cannot register configfs default group 'streams'!\n"); + goto exit_configfs; + } + + return 0; + +exit_configfs: + configfs_unregister_subsystem(&cfg_subsys); + + return err; +} + +void __exit __avirt_configfs_exit(struct avirt_core *core) +{ + configfs_unregister_default_group(core->stream_group); + configfs_unregister_subsystem(&cfg_subsys); +} diff --git a/core.c b/core.c index c9a9aa3..bf122ed 100644 --- a/core.c +++ b/core.c @@ -8,14 +8,12 @@ */ #include -#include #include #include #include +#include -#include "avirt/core.h" -#include "alsa-pcm.h" -#include "utils.h" +#include "core_internal.h" MODULE_AUTHOR("JOSHANNE "); MODULE_AUTHOR("MFARRUGI "); @@ -29,162 +27,19 @@ MODULE_LICENSE("GPL v2"); #define D_ERRORK(fmt, args...) DERROR(AP_LOGNAME, fmt, ##args) #define SND_AVIRTUAL_DRIVER "snd_avirt" -#define MAX_PCM_DEVS 8 -#define MAX_AUDIOPATHS 4 -#define DEFAULT_FILE_PERMS 0644 - -/* Number of playback devices to create (max = MAX_PCM_DEVS) */ -static unsigned int playback_num; -/* Number of capture devices to create (max = MAX_PCM_DEVS) */ -static unsigned int capture_num; -/* Names per playback device */ -static char *playback_names[MAX_PCM_DEVS]; -/* Names per capture device */ -static char *capture_names[MAX_PCM_DEVS]; -/* Channels per playback device */ -static unsigned int playback_chans[MAX_PCM_DEVS]; -/* Channels per capture device */ -static unsigned int capture_chans[MAX_PCM_DEVS]; - -module_param(playback_num, int, DEFAULT_FILE_PERMS); -MODULE_PARM_DESC(playback_num, - "Number of playback devices to create (max = MAX_PCM_DEVS)"); -module_param(capture_num, int, DEFAULT_FILE_PERMS); -MODULE_PARM_DESC(capture_num, - "Number of capture devices to create (max = MAX_PCM_DEVS)"); -module_param_array(playback_names, charp, NULL, DEFAULT_FILE_PERMS); -MODULE_PARM_DESC(playback_names, "Names per playback device"); -module_param_array(capture_names, charp, NULL, DEFAULT_FILE_PERMS); -MODULE_PARM_DESC(capture_names, "Names per capture device"); -module_param_array(playback_chans, int, NULL, DEFAULT_FILE_PERMS); -MODULE_PARM_DESC(playback_chans, "Channels per playback device"); -module_param_array(capture_chans, int, NULL, DEFAULT_FILE_PERMS); -MODULE_PARM_DESC(capture_chans, "Channels per capture device"); - -static struct avirt_core { - struct snd_card *card; - struct device *dev; - struct class *avirt_class; - struct platform_device *platform_dev; -} core; + +extern struct snd_pcm_ops pcm_ops; + +static struct avirt_core core = { + .stream_count = 0, +}; struct avirt_coreinfo coreinfo = { - .version = { 0, 0, 1 }, - .pcm_buff_complete = pcm_buff_complete_cb, + .version = {0, 0, 1}, }; static LIST_HEAD(audiopath_list); -/** - * avirt_probe - Register ALSA soundcard - * @devptr: Platform driver device - * @return: 0 on success or error code otherwise - */ -static int avirt_probe(struct platform_device *devptr) -{ - static struct snd_device_ops device_ops; - struct snd_pcm **pcm; - int err = 0, i = 0; - - if (playback_num == 0 && capture_num == 0) { - pr_err("playback_num or capture_num must be greater than 0!\n"); - return -EINVAL; - } - - // Create the card instance - CHK_ERR_V(snd_card_new(&devptr->dev, SNDRV_DEFAULT_IDX1, "avirt", - THIS_MODULE, 0, &core.card), - "Failed to create sound card"); - - strcpy(core.card->driver, "avirt-alsa-device"); - strcpy(core.card->shortname, "avirt"); - strcpy(core.card->longname, "A virtual sound card driver for ALSA"); - - // Create new sound device - CHK_ERR_V((snd_device_new(core.card, SNDRV_DEV_LOWLEVEL, &coreinfo, - &device_ops)), - "Failed to create sound device"); - - // TEMP - if (playback_num > 0) { - coreinfo.playback.devices = playback_num; - coreinfo.playback.streams = kzalloc( - sizeof(*coreinfo.playback.streams) * playback_num, - GFP_KERNEL); - for (i = 0; i < playback_num; i++) { - pcm = &coreinfo.playback.streams[i].pcm; - CHK_ERR(snd_pcm_new(core.card, playback_names[i], i, 1, - 0, pcm)); - - /** Register driver callbacks */ - snd_pcm_set_ops(*pcm, SNDRV_PCM_STREAM_PLAYBACK, - &pcm_ops); - - (*pcm)->info_flags = 0; - strcpy((*pcm)->name, playback_names[i]); - coreinfo.playback.streams[i].channels = - playback_chans[i]; - pr_info("snd_pcm_new: name: %s, chans: %d\n", - (*pcm)->name, - coreinfo.playback.streams[i].channels); - } - } - - if (capture_num > 0) { - coreinfo.capture.devices = capture_num; - coreinfo.capture.streams = - kzalloc(sizeof(*coreinfo.capture.streams) * capture_num, - GFP_KERNEL); - for (i = 0; i < capture_num; i++) { - pcm = &coreinfo.capture.streams[i].pcm; - CHK_ERR(snd_pcm_new(core.card, capture_names[i], - i + playback_num, 0, 1, pcm)); - - /** Register driver callbacks */ - snd_pcm_set_ops(*pcm, SNDRV_PCM_STREAM_CAPTURE, - &pcm_ops); - - (*pcm)->info_flags = 0; - strcpy((*pcm)->name, capture_names[i]); - coreinfo.capture.streams[i].channels = capture_chans[i]; - pr_info("snd_pcm_new: name: %s, chans: %d\n", - (*pcm)->name, - coreinfo.capture.streams[i].channels); - } - } - // TEMP - - /** Register with the ALSA framework */ - CHK_ERR_V(snd_card_register(core.card), "Device registration failed"); - - return err; -} - -/** - * avirt_remove - Deregister ALSA soundcard - * @devptr: Platform driver device - * @return: 0 on success or error code otherwise - */ -static int avirt_remove(struct platform_device *devptr) -{ - snd_card_free(core.card); - if (coreinfo.playback.streams) - kfree(coreinfo.playback.streams); - if (coreinfo.capture.streams) - kfree(coreinfo.capture.streams); - - return 0; -} - -static struct platform_driver avirt_driver = { - .probe = avirt_probe, - .remove = avirt_remove, - .driver = - { - .name = SND_AVIRTUAL_DRIVER, - }, -}; - struct avirt_audiopath_obj { struct kobject kobj; struct list_head list; @@ -195,7 +50,7 @@ static struct kset *avirt_audiopath_kset; static struct kobject *kobj; #define to_audiopath_obj(d) container_of(d, struct avirt_audiopath_obj, kobj) -#define to_audiopath_attr(a) \ +#define to_audiopath_attr(a) \ container_of(a, struct avirt_audiopath_attribute, attr) /** @@ -350,8 +205,10 @@ static void destroy_avirt_audiopath_obj(struct avirt_audiopath_obj *p) struct avirt_audiopath *avirt_audiopath_get(const char *uid) { struct avirt_audiopath_obj *ap_obj; - list_for_each_entry (ap_obj, &audiopath_list, list) { - pr_info("get_ap %s\n", ap_obj->path->uid); + + list_for_each_entry(ap_obj, &audiopath_list, list) + { + // pr_info("get_ap %s\n", ap_obj->path->uid); if (!strcmp(ap_obj->path->uid, uid)) return ap_obj->path; } @@ -424,12 +281,121 @@ int avirt_audiopath_deregister(struct avirt_audiopath *audiopath) EXPORT_SYMBOL_GPL(avirt_audiopath_deregister); /** - * avirt_unregister_all - Unregister the platform device driver + * avirt_stream_count - get the stream count for the given direction + * @direction: The direction to get the stream count for + * @return: The stream count */ -static void avirt_unregister_all(void) +int avirt_stream_count(unsigned int direction) { - platform_device_unregister(core.platform_dev); - platform_driver_unregister(&avirt_driver); + struct list_head *entry; + struct config_item *item; + struct avirt_stream *stream; + unsigned int count = 0; + + if (direction > 1) + return -ERANGE; + + list_for_each(entry, &core.stream_group->cg_children) + { + item = container_of(entry, struct config_item, ci_entry); + stream = avirt_stream_from_config_item(item); + if (!stream) + return -EFAULT; + if (stream->direction == direction) + count++; + } + + return count; +} +EXPORT_SYMBOL_GPL(avirt_stream_count); + +/** + * __avirt_stream_create - Create audio stream, including it's ALSA PCM device + * @name: The name designated to the audio stream + * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or + * SNDRV_PCM_STREAM_CAPTURE) + * @return: The newly created audio stream if successful, or an error pointer + */ +struct avirt_stream *__avirt_stream_create(const char *name, int direction) +{ + struct snd_pcm *pcm; + struct avirt_stream *stream; + int err; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return ERR_PTR(-ENOMEM); + + strcpy(stream->name, name); + strcpy(stream->map, "ap_fddsp"); + stream->channels = 0; + stream->direction = direction; + stream->device = core.stream_count++; + + err = snd_pcm_new(core.card, name, stream->device, !direction, + direction, &pcm); + if (err < 0) + return ERR_PTR(err); + + // TD MF: IMPORTANT: NEED TO TEST >8 PCM DEVICES ON A + // CARD! + /** Register driver callbacks */ + snd_pcm_set_ops(pcm, direction, &pcm_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, name); + + pr_info("%s [ARGS] name: %s device:%d\n", __func__, name, + stream->device); + + // coreinfo.streams[stream_idx] = stream; + + return stream; +} + +int __avirt_card_register(void) +{ + int err = 0; + + struct avirt_audiopath_obj *ap_obj; + list_for_each_entry(ap_obj, &audiopath_list, list) + { + pr_info("Calling configure for AP uid: %s\n", + ap_obj->path->uid); + ap_obj->path->configure(core.stream_group, core.stream_count); + } + + err = snd_card_register(core.card); + if (err < 0) { + pr_err("Sound card registration failed!"); + snd_card_free(core.card); + } + + return err; +} + +struct avirt_stream *__avirt_stream_find_by_device(unsigned int device) +{ + struct avirt_stream *stream; + struct config_item *item; + struct list_head *entry; + + if (device >= core.stream_count) { + pr_err("Stream device number is greater than number streams available\n"); + return ERR_PTR(-EINVAL); + } + + list_for_each(entry, &core.stream_group->cg_children) + { + item = container_of(entry, struct config_item, ci_entry); + stream = avirt_stream_from_config_item(item); + if (!stream) + return ERR_PTR(-EFAULT); + if (stream->device == device) + return stream; + } + + return NULL; } /** @@ -442,23 +408,10 @@ static int __init core_init(void) D_INFOK("Alsa Virtual Sound Driver avirt-%d.%d.%d", coreinfo.version[0], coreinfo.version[1], coreinfo.version[2]); - err = platform_driver_register(&avirt_driver); - if (err < 0) - return err; - - core.platform_dev = platform_device_register_simple(SND_AVIRTUAL_DRIVER, - 0, NULL, 0); - if (IS_ERR(core.platform_dev)) { - err = PTR_ERR(core.platform_dev); - pr_err("platform_dev error [%d]!\n", err); - goto exit_platform_device; - } - core.avirt_class = class_create(THIS_MODULE, SND_AVIRTUAL_DRIVER); if (IS_ERR(core.avirt_class)) { pr_err("No udev support\n"); - err = PTR_ERR(core.avirt_class); - goto exit_bus; + return PTR_ERR(core.avirt_class); } core.dev = device_create(core.avirt_class, NULL, 0, NULL, "avirtcore"); @@ -467,22 +420,38 @@ static int __init core_init(void) goto exit_class; } + // Create the card instance + err = snd_card_new(core.dev, SNDRV_DEFAULT_IDX1, "avirt", THIS_MODULE, + 0, &core.card); + if (err < 0) { + pr_err("Failed to create sound card"); + goto exit_class_container; + } + + strcpy(core.card->driver, "avirt-alsa-device"); + strcpy(core.card->shortname, "avirt"); + strcpy(core.card->longname, "A virtual sound card driver for ALSA"); + avirt_audiopath_kset = kset_create_and_add("audiopaths", NULL, &core.dev->kobj); if (!avirt_audiopath_kset) { err = -ENOMEM; - goto exit_class_container; + goto exit_snd_card; } + err = __avirt_configfs_init(&core); + if (err < 0) + goto exit_snd_card; + return 0; +exit_snd_card: + snd_card_free(core.card); exit_class_container: device_destroy(core.avirt_class, 0); exit_class: class_destroy(core.avirt_class); -exit_bus: -exit_platform_device: - avirt_unregister_all(); + return err; } @@ -491,34 +460,11 @@ exit_platform_device: */ static void __exit core_exit(void) { + __avirt_configfs_exit(&core); + kset_unregister(avirt_audiopath_kset); device_destroy(core.avirt_class, 0); class_destroy(core.avirt_class); - - avirt_unregister_all(); - D_INFOK("Exit begin!"); - pr_info("playback_num: %d, capture_num: %d\n", playback_num, - capture_num); - - pr_info("playback_chans: %d %d %d %d %d %d %d %d\n", playback_chans[0], - playback_chans[1], playback_chans[2], playback_chans[3], - playback_chans[4], playback_chans[5], playback_chans[6], - playback_chans[7]); - - pr_info("capture_chans: %d %d %d %d %d %d %d %d\n", capture_chans[0], - capture_chans[1], capture_chans[2], capture_chans[3], - capture_chans[4], capture_chans[5], capture_chans[6], - capture_chans[7]); - - pr_info("playback_names: %s %s %s %s %s %s %s %s\n", playback_names[0], - playback_names[1], playback_names[2], playback_names[3], - playback_names[4], playback_names[5], playback_names[6], - playback_names[7]); - - pr_info("capture_names: %s %s %s %s %s %s %s %s\n", capture_names[0], - capture_names[1], capture_names[2], capture_names[3], - capture_names[4], capture_names[5], capture_names[6], - capture_names[7]); } module_init(core_init); diff --git a/core.h b/core.h index c11ee1d..493fc2b 100644 --- a/core.h +++ b/core.h @@ -13,26 +13,28 @@ #include #include -#define MAX_NAME_LEN 32 +#define MAX_STREAMS 16 +#define MAX_NAME_LEN 80 /** - * PCM buffer complete callback - * - * These are called from the audiopath when a PCM buffer has completed, and - * new data can be submitted/retrieved + * AVIRT Audio Path configure function type + * Each Audio Path registers this at avirt_audiopath_register time. + * It is then called by the core once AVIRT has been configured */ -typedef int (*avirt_buff_complete)(struct snd_pcm_substream *substream); +typedef int (*avirt_audiopath_configure)(struct config_group *stream_group, + unsigned int stream_count); /** * AVIRT Audio Path info */ struct avirt_audiopath { - const char *uid; /* Unique identifier */ - const char *name; /* Pretty name */ - unsigned int version[3]; /* Version - Major.Minor.Ext */ - struct snd_pcm_hardware *hw; /* ALSA PCM HW conf */ - struct snd_pcm_ops *pcm_ops; /* ALSA PCM op table */ - unsigned int blocksize; /* Audio frame size accepted */ + const char *uid; /* Unique identifier */ + const char *name; /* Pretty name */ + unsigned int version[3]; /* Version - Major.Minor.Ext */ + struct snd_pcm_hardware *hw; /* ALSA PCM HW conf */ + struct snd_pcm_ops *pcm_ops; /* ALSA PCM op table */ + unsigned int blocksize; /* Audio frame size accepted */ + avirt_audiopath_configure configure; /* Configure callback function */ void *context; }; @@ -41,29 +43,19 @@ struct avirt_audiopath { * Audio stream configuration */ struct avirt_stream { - struct snd_pcm *pcm; /* Stream PCM device */ - unsigned int channels; /* Stream channel count */ + char name[MAX_NAME_LEN]; /* Stream name */ + char map[MAX_NAME_LEN]; /* Stream Audio Path mapping */ + unsigned int channels; /* Stream channel count */ + unsigned int device; /* Stream PCM device no. */ + unsigned int direction; /* Stream direction */ struct config_item item; /* configfs item reference */ }; -/** - * Collection of audio streams - */ -struct avirt_stream_group { - struct avirt_stream *streams; /* AVIRT stream array */ - unsigned int devices; /* Number of stream devices */ -}; - /** * AVIRT core info */ struct avirt_coreinfo { unsigned int version[3]; - - struct avirt_stream_group playback; - struct avirt_stream_group capture; - - avirt_buff_complete pcm_buff_complete; }; /** @@ -90,12 +82,30 @@ int avirt_audiopath_deregister(struct avirt_audiopath *audiopath); struct avirt_audiopath *avirt_audiopath_get(const char *uid); /** - * avirt_subscribe_stream - subscribe the Audio Path to the given streams - * @audiopath: Audio Path to subscribe for - * @streams: The streams to subscribe the Audio Path to - * return: 0 on success or error code otherwise + * avirt_stream_count - get the stream count for the given direction + * @direction: The direction to get the stream count for + * @return: The stream count + */ +int avirt_stream_count(unsigned int direction); + +/** + * avirt_stream_from_config_item - Convert a config_item to an avirt_stream + * @item: The config_item to convert from + * @return: The item's avirt_stream if successful, NULL otherwise + */ +static inline struct avirt_stream * +avirt_stream_from_config_item(struct config_item *item) +{ + return item ? container_of(item, struct avirt_stream, item) : NULL; +} + +/** + * avirt_pcm_period_elapsed - PCM buffer complete callback + * @substream: pointer to ALSA PCM substream + * + * This should be called from a child Audio Path once it has finished processing + * the PCM buffer */ -int avirt_subscribe_stream(struct avirt_audiopath *audiopath, - const char **streams); +void avirt_pcm_period_elapsed(struct snd_pcm_substream *substream); #endif // __AVIRT_CORE_H__ diff --git a/core_internal.h b/core_internal.h new file mode 100644 index 0000000..3639d6e --- /dev/null +++ b/core_internal.h @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA Virtual Soundcard + * + * core.h - Internal header for virtual ALSA card + * + * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + */ + +#ifndef __AVIRT_CORE_INTERNAL_H__ +#define __AVIRT_CORE_INTERNAL_H__ + +#include + +#include "utils.h" + +struct avirt_core { + struct snd_card *card; + struct device *dev; + struct class *avirt_class; + struct config_group *stream_group; + unsigned int stream_count; +}; + +int __init __avirt_configfs_init(struct avirt_core *core); +void __exit __avirt_configfs_exit(struct avirt_core *core); + +/** + * __avirt_card_register - Register the sound card to user space + * @return: 0 on success, negative ERRNO on failure + */ +int __avirt_card_register(void); + +/** + * __avirt_stream_find_by_device - Get audio stream from device number + * @device: The PCM device number corresponding to the desired stream + * @return: The audio stream if found, or an error pointer otherwise + */ +struct avirt_stream *__avirt_stream_find_by_device(unsigned int device); + +/** + * __avirt_stream_create - Create audio stream, including it's ALSA PCM device + * @name: The name designated to the audio stream + * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or + * SNDRV_PCM_STREAM_CAPTURE) + * @return: The newly created audio stream if successful, or an error pointer + */ +struct avirt_stream *__avirt_stream_create(const char *name, int direction); + +#endif // __AVIRT_CORE_INTERNAL_H__ diff --git a/dummy/dummy.c b/dummy/dummy.c index d619786..606b22d 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -130,7 +130,7 @@ static void dummy_systimer_callback(struct timer_list *t) dpcm->elapsed = 0; spin_unlock_irqrestore(&dpcm->lock, flags); if (elapsed) - coreinfo->pcm_buff_complete(dpcm->substream); + avirt_pcm_period_elapsed(dpcm->substream); } static snd_pcm_uframes_t @@ -231,6 +231,25 @@ static struct snd_pcm_ops dummyap_pcm_ops = { /******************************************************************************* * Dummy Audio Path AVIRT registration ******************************************************************************/ +int dummy_configure(struct config_group *avirt_stream_group, + unsigned int stream_count) +{ + // Do something with streams + + struct list_head *entry; + list_for_each(entry, &avirt_stream_group->cg_children) + { + struct config_item *item = + container_of(entry, struct config_item, ci_entry); + struct avirt_stream *stream = + avirt_stream_from_config_item(item); + pr_info("%s: stream name:%s device:%d channels:%d\n", __func__, + stream->name, stream->device, stream->channels); + } + + return 0; +} + static struct snd_pcm_hardware dummyap_hw = { .formats = SNDRV_PCM_FMTBIT_S16_LE, .info = (SNDRV_PCM_INFO_INTERLEAVED // Channel interleaved audio @@ -250,6 +269,7 @@ static struct avirt_audiopath dummyap_module = { .hw = &dummyap_hw, .pcm_ops = &dummyap_pcm_ops, .blocksize = DUMMY_BLOCKSIZE, + .configure = dummy_configure, }; static int __init dummy_init(void) diff --git a/utils.h b/utils.h index b449938..34e273f 100644 --- a/utils.h +++ b/utils.h @@ -12,38 +12,38 @@ #include -#define PRINT_ERR(errno, errmsg) \ - pr_err("[%s]:[ERRNO:%d]: %s ", __func__, errno, (errmsg)); +#define PRINT_ERR(errno, errmsg) \ + pr_err("[%s]:[ERRNO:%d]: %s \n", __func__, errno, (errmsg)); -#define CHK_ERR(errno) \ - do { \ - if ((errno) < 0) \ - return (errno); \ +#define CHK_ERR(errno) \ + do { \ + if ((errno) < 0) \ + return (errno); \ } while (0) -#define CHK_ERR_V(errno, errmsg, ...) \ - do { \ - if ((errno) < 0) { \ - PRINT_ERR((errno), (errmsg), ##__VA_ARGS__) \ - return (errno); \ - } \ +#define CHK_ERR_V(errno, errmsg, ...) \ + do { \ + if ((errno) < 0) { \ + PRINT_ERR((errno), (errmsg), ##__VA_ARGS__) \ + return (errno); \ + } \ } while (0) -#define CHK_NULL(x) \ - do { \ - if (!(x)) \ - return -EFAULT; \ +#define CHK_NULL(x, errno) \ + do { \ + if (!(x)) \ + return errno; \ } while (0) -#define CHK_NULL_V(x, errmsg, ...) \ - do { \ - if (!(x)) { \ - char *errmsg_done = \ - kasprintf(GFP_KERNEL, errmsg, ##__VA_ARGS__); \ - PRINT_ERR(EFAULT, errmsg_done); \ - kfree(errmsg_done); \ - return -EFAULT; \ - } \ +#define CHK_NULL_V(x, errno, errmsg, ...) \ + do { \ + if (!(x)) { \ + char *errmsg_done = \ + kasprintf(GFP_KERNEL, errmsg, ##__VA_ARGS__); \ + PRINT_ERR(EFAULT, errmsg_done); \ + kfree(errmsg_done); \ + return errno; \ + } \ } while (0) #endif -- cgit 1.2.3-korg From 81693e7f5e73c8fde658a2a050f85dd8481a1057 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 1 Oct 2018 17:47:44 +1000 Subject: Add configfs test script Add initial bash script for configuring AVIRT via the configfs Linux filesystem Signed-off-by: Mark Farrugia --- scripts/test_configfs.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100755 scripts/test_configfs.sh diff --git a/scripts/test_configfs.sh b/scripts/test_configfs.sh new file mode 100755 index 0000000..1ae0fe5 --- /dev/null +++ b/scripts/test_configfs.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +mkdir -p /config && mount -t configfs none /config + +mkdir /config/avirt/streams/playback_media +echo "2">/config/avirt/streams/playback_media/channels + +mkdir /config/avirt/streams/playback_navigation +echo "1">/config/avirt/streams/playback_navigation/channels + +mkdir /config/avirt/streams/playback_emergency +echo "1">/config/avirt/streams/playback_emergency/channels + +mkdir /config/avirt/streams/capture_voice +echo "1">/config/avirt/streams/capture_voice/channels + +echo "1">/config/avirt/streams/sealed -- cgit 1.2.3-korg From e82e7c61dde6ff9bd799cc4b40e8d718cff6951f Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 1 Oct 2018 18:19:29 +1000 Subject: Update README.md Signed-off-by: Mark Farrugia --- README.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++++--------- loadDrivers.sh | 19 --------------- 2 files changed, 65 insertions(+), 31 deletions(-) delete mode 100755 loadDrivers.sh diff --git a/README.md b/README.md index 77eb821..d6564fa 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -Alsa Virtual Sound Driver --------------------------- +## Alsa Virtual Sound Driver The ALSA Virtual Sound Driver (AVIRT) aims to provide a Linux kernel solution to the issue of audio routing in kernel-space, as well as security per-stream, and dynamic configuration of streams at the kernel level. @@ -9,45 +8,99 @@ The top-level driver is configured (currently) using module parameters, as is th A sample dummy Audio Path is provided as an example to show how a low-level audio driver would subscribe to AVIRT, and accept audio routing for playback. +Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL +reference platform, and a default loopback softmixer is in development. + ## Building + ### Out Of Tree + The kernel modules can be built either in-tree, or out-of-tree. To build both AVIRT and the dummy Audio Path out-of-tree, use the following command: ```sh -$ CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) +$ make all ``` -To build both AVIRT and the dummy Audio Path out-of-tree for [AGL](http://docs.automotivelinux.org/) (`aarch64` currently supported), use the [XDS](http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-1/0_Abstract.html) build system together with the `make_agl.sh` script: +If building for [AGL](http://docs.automotivelinux.org/), use the [XDS](http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-1/0_Abstract.html) build system together with the `make_agl.sh` script: ```sh $ ./make_agl.sh ${XDS_SDK_ID} ``` + ### In tree + To build in tree, use the [Fiberdyne Linux fork](https://github.com/fiberdyne/linux), which will automatically clone the AVIRT Driver and required AudioPath modules to the `drivers/staging` directory. You can then turn AVIRT Support on by setting to ``. The drivers can be found here: + ``` $ make menuconfig # Navigate to: Device Drivers--->Staging Drivers--->AVIRT Support ``` + Finally build the kernel with the configuration selected by making. + ``` $ make ``` ## Running -To run, we must load the kernel modules using the `loadDrivers.sh` script, which contains sample module parameters to AVIRT: + +Firstly, install the resulting kernel modules to the appropriate modules +directory, and run`depmod`. For example, in AGL: + +```sh +cp avirt_core.ko avirt_dummyap.ko /lib/modules/4.14.0-yocto-standard/ +depmod +``` + +Now we can load the modules: + ```sh -$ ./loadDrivers.sh +modprobe avirt_core +modprobe avirt_dummyap ``` -To unload the drivers use: + +We must now configure AVIRT. We can do this with the test script: + +```sh +source scripts/test_configfs.sh +``` + +We can see the newly created streams by using the `aplay` utility. For example: + ```sh -$ ./unload.sh +aplay -l +... +card 2: avirt [avirt], device 0: multimedia [multimedia] + Subdevices: 1/1 + Subdevice #0: subdevice #0 +card 2: avirt [avirt], device 1: navigation [navigation] + Subdevices: 1/1 + Subdevice #0: subdevice #0 +card 2: avirt [avirt], device 2: emergency [emergency] + Subdevices: 1/1 + Subdevice #0: subdevice #0 ``` +## 4A Integration + +AVIRT has been integrated with [4a-softmixer](https://github.com/iotbzh/4a-softmixer) +and [4a-hal-generic](https://github.com/iotbzh/4a-hal-generic) to provide a smooth +transition from the existing aloop implementation to the future AVIRT loopback implementation. + +### User-space Library + +The user-space library [libavirt](https://github.com/fiberdyne/libavirt) can be used to configure AVIRT from within a given AGL 4A binding. + +### Hardmixer + +A new 4A mixer binding has been developed to demonstrate the capabilities of the +Fiberdyne DSP mixer operating on the HiFi2 core on-board the Renesas R-Car M3 M3ULCB +AGL reference platform. This is called the 'hardmixer', and is a faster, lower +latency alternative to the softmixer. + ## TODO - - Currently, playback only - implementing capture is WIP. - - Rework module parameters into configfs configuration. - - Create a loopback Audio Path for use with AVIRT, to demonstrate standard AGL soft-mixing capabilities. - - Modify Fiberdyne DSP driver for use with AVRIT, to demonstrate DSP off-loading and hard-mixing capabilities via Xtensa HiFi2. +- Currently, playback only - implementing capture is WIP. +- Create a loopback Audio Path for use with AVIRT, to demonstrate standard AGL soft-mixing capabilities. diff --git a/loadDrivers.sh b/loadDrivers.sh deleted file mode 100755 index 9ff5960..0000000 --- a/loadDrivers.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# Sample module parameters -params="\ -playback_num=4 \ -playback_chans=2,4,1,1 \ -playback_names=radio,media,nav,phone \ -capture_num=1 \ -capture_chans=2 \ -capture_names=voice" - -# Load the virtual driver -insmod avirt_core.ko "$params" - -# Load the additional audio path -#insmod dummy/avirt_dummyap.ko -insmod loopback/avirt_loopbackap.ko - -echo "Drivers Loaded!" -- cgit 1.2.3-korg From 7e4d6e2f61c97da680591b77fb89a69f69a3af7f Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Wed, 3 Oct 2018 08:44:49 +1000 Subject: Fix scripts, move all to scripts folder Signed-off-by: Mark Farrugia --- make-agl.sh | 12 ------------ scripts/load.sh | 10 ++++++++++ scripts/make-agl.sh | 12 ++++++++++++ scripts/unload.sh | 10 ++++++++++ unload.sh | 10 ---------- 5 files changed, 32 insertions(+), 22 deletions(-) delete mode 100755 make-agl.sh create mode 100755 scripts/load.sh create mode 100755 scripts/make-agl.sh create mode 100755 scripts/unload.sh delete mode 100755 unload.sh diff --git a/make-agl.sh b/make-agl.sh deleted file mode 100755 index 4421af8..0000000 --- a/make-agl.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# Get SDK path -sdk_id=$1 # first arg must be XDS_SDK_ID -shift 1 -long_sdkpath=$(xds-cli sdks get $sdk_id | grep Path) -sdkpath=${long_sdkpath:4} - -# Build -/opt/AGL/bin/xds-cli exec --config xds-project.conf -- \ - LDFLAGS= CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m \ - make -C $sdkpath/sysroots/aarch64-agl-linux/usr/src/kernel M=$(pwd) $@ diff --git a/scripts/load.sh b/scripts/load.sh new file mode 100755 index 0000000..2cd472d --- /dev/null +++ b/scripts/load.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# Load the core +insmod avirt_core.ko + +# Load the additional Audio Paths +#insmod dummy/avirt_dummyap.ko +insmod loopback/avirt_loopbackap.ko + +echo "Drivers Loaded!" diff --git a/scripts/make-agl.sh b/scripts/make-agl.sh new file mode 100755 index 0000000..4421af8 --- /dev/null +++ b/scripts/make-agl.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Get SDK path +sdk_id=$1 # first arg must be XDS_SDK_ID +shift 1 +long_sdkpath=$(xds-cli sdks get $sdk_id | grep Path) +sdkpath=${long_sdkpath:4} + +# Build +/opt/AGL/bin/xds-cli exec --config xds-project.conf -- \ + LDFLAGS= CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m \ + make -C $sdkpath/sysroots/aarch64-agl-linux/usr/src/kernel M=$(pwd) $@ diff --git a/scripts/unload.sh b/scripts/unload.sh new file mode 100755 index 0000000..a011a2b --- /dev/null +++ b/scripts/unload.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +rm_module() { + lsmod |grep "^$1\>" && rmmod $1 || true +} + +rm_module avirt_loopbackap +rm_module avirt_dummyap +rm_module avirt_core +echo "Drivers Removed!" diff --git a/unload.sh b/unload.sh deleted file mode 100755 index a011a2b..0000000 --- a/unload.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -rm_module() { - lsmod |grep "^$1\>" && rmmod $1 || true -} - -rm_module avirt_loopbackap -rm_module avirt_dummyap -rm_module avirt_core -echo "Drivers Removed!" -- cgit 1.2.3-korg From 86c77302e07b229d6723dc1f8e3da02765cff1f1 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Sun, 7 Oct 2018 12:42:31 +1000 Subject: Clean up sound card on module exit Sound card was not being free'd properly on module exit. Signed-off-by: Mark Farrugia --- core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/core.c b/core.c index bf122ed..15df64f 100644 --- a/core.c +++ b/core.c @@ -463,6 +463,7 @@ static void __exit core_exit(void) __avirt_configfs_exit(&core); kset_unregister(avirt_audiopath_kset); + snd_card_free(core.card); device_destroy(core.avirt_class, 0); class_destroy(core.avirt_class); } -- cgit 1.2.3-korg From aeb43d0374f4b1fb383da088b198de55f139a280 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Sun, 7 Oct 2018 18:12:48 +1100 Subject: Ensure no buffer overflow when setting card description Sanity guard the sound card string buffers with strncpy Signed-off-by: Mark Farrugia --- configfs.c | 6 ++++-- core.c | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/configfs.c b/configfs.c index dd7b7df..bca0f56 100644 --- a/configfs.c +++ b/configfs.c @@ -173,12 +173,14 @@ static struct configfs_attribute *cfg_avirt_stream_group_attrs[] = { }; static struct configfs_group_operations cfg_avirt_stream_group_ops = { - .make_item = cfg_avirt_stream_make_item}; + .make_item = cfg_avirt_stream_make_item +}; static struct config_item_type cfg_stream_group_type = { .ct_group_ops = &cfg_avirt_stream_group_ops, .ct_attrs = cfg_avirt_stream_group_attrs, - .ct_owner = THIS_MODULE}; + .ct_owner = THIS_MODULE +}; static struct config_item_type cfg_avirt_group_type = { .ct_owner = THIS_MODULE, diff --git a/core.c b/core.c index 15df64f..96a4686 100644 --- a/core.c +++ b/core.c @@ -428,9 +428,10 @@ static int __init core_init(void) goto exit_class_container; } - strcpy(core.card->driver, "avirt-alsa-device"); - strcpy(core.card->shortname, "avirt"); - strcpy(core.card->longname, "A virtual sound card driver for ALSA"); + strncpy(core.card->driver, "avirt-alsa-dev", 16); + strncpy(core.card->shortname, "avirt", 32); + strncpy(core.card->longname, "A virtual sound card driver for ALSA", + 80); avirt_audiopath_kset = kset_create_and_add("audiopaths", NULL, &core.dev->kobj); -- cgit 1.2.3-korg From f834eb7b0799a4678639d2a9f71c1dc4352469ce Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Sun, 7 Oct 2018 21:52:53 +1100 Subject: Move 'streams_sealed' to avirt_core Signed-off-by: Mark Farrugia --- configfs.c | 8 ++------ core.c | 23 ++++++++++++++++++++--- core_internal.h | 8 ++++++++ 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/configfs.c b/configfs.c index bca0f56..cdfd171 100644 --- a/configfs.c +++ b/configfs.c @@ -10,8 +10,6 @@ #include #include "core_internal.h" -static bool streams_sealed = false; - static ssize_t cfg_avirt_stream_direction_show(struct config_item *item, char *page) { @@ -137,7 +135,7 @@ cfg_avirt_stream_make_item(struct config_group *group, const char *name) static ssize_t cfg_avirt_stream_group_sealed_show(struct config_item *item, char *page) { - return snprintf(page, PAGE_SIZE, "%d\n", streams_sealed); + return snprintf(page, PAGE_SIZE, "%d\n", __avirt_streams_sealed()); } static ssize_t cfg_avirt_stream_group_sealed_store(struct config_item *item, @@ -147,7 +145,7 @@ static ssize_t cfg_avirt_stream_group_sealed_store(struct config_item *item, unsigned long tmp; char *p = (char *)page; - if (streams_sealed) { + if (__avirt_streams_sealed()) { pr_err("AVIRT streams are already sealed!\n"); return -EPERM; } @@ -159,8 +157,6 @@ static ssize_t cfg_avirt_stream_group_sealed_store(struct config_item *item, return -ERANGE; } - streams_sealed = (bool)tmp; - CHK_ERR(__avirt_card_register()); return count; diff --git a/core.c b/core.c index 96a4686..d925eae 100644 --- a/core.c +++ b/core.c @@ -32,6 +32,7 @@ extern struct snd_pcm_ops pcm_ops; static struct avirt_core core = { .stream_count = 0, + .streams_sealed = false, }; struct avirt_coreinfo coreinfo = { @@ -245,6 +246,11 @@ int avirt_audiopath_register(struct avirt_audiopath *audiopath, audiopath->hw->periods_max); list_add_tail(&audiopath_obj->list, &audiopath_list); + // If we have already sealed the streams, configure this AP + if (core.streams_sealed) + audiopath->configure(core.card, core.stream_group, + core.stream_count); + *info = &coreinfo; return 0; @@ -356,10 +362,14 @@ struct avirt_stream *__avirt_stream_create(const char *name, int direction) int __avirt_card_register(void) { int err = 0; - struct avirt_audiopath_obj *ap_obj; - list_for_each_entry(ap_obj, &audiopath_list, list) - { + + if (core.streams_sealed) { + pr_err("Streams already sealed!\n"); + return -1; + } + + list_for_each_entry (ap_obj, &audiopath_list, list) { pr_info("Calling configure for AP uid: %s\n", ap_obj->path->uid); ap_obj->path->configure(core.stream_group, core.stream_count); @@ -371,9 +381,16 @@ int __avirt_card_register(void) snd_card_free(core.card); } + core.streams_sealed = true; + return err; } +bool __avirt_streams_sealed(void) +{ + return core.streams_sealed; +} + struct avirt_stream *__avirt_stream_find_by_device(unsigned int device) { struct avirt_stream *stream; diff --git a/core_internal.h b/core_internal.h index 3639d6e..e7bea07 100644 --- a/core_internal.h +++ b/core_internal.h @@ -20,6 +20,7 @@ struct avirt_core { struct class *avirt_class; struct config_group *stream_group; unsigned int stream_count; + bool streams_sealed; }; int __init __avirt_configfs_init(struct avirt_core *core); @@ -31,6 +32,13 @@ void __exit __avirt_configfs_exit(struct avirt_core *core); */ int __avirt_card_register(void); +/** + * __avirt_streams_sealed - Check whether the streams have been sealed or not + * @return: true if sealed, false otherwise + */ +bool __avirt_streams_sealed(void); + +/** /** * __avirt_stream_find_by_device - Get audio stream from device number * @device: The PCM device number corresponding to the desired stream -- cgit 1.2.3-korg From 0acef1799beace4dd38d41bda9df77e84a5fe398 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Sun, 7 Oct 2018 22:00:10 +1100 Subject: Remove blocksize from audiopath This is no longer needed in AVIRT, since it is only actually used for the ADSP Audio Path. Clean up white space, add function documentation, remove out of place debug prints Signed-off-by: Mark Farrugia --- alsa-pcm.c | 13 +------------ core.c | 5 ++--- core.h | 21 ++++++++++----------- core_internal.h | 10 ++++++++++ dummy/dummy.c | 1 - loopback/loopback.c | 2 +- 6 files changed, 24 insertions(+), 28 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 5a30aff..958ccbb 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -49,7 +49,7 @@ static int pcm_open(struct snd_pcm_substream *substream) struct avirt_audiopath *audiopath; struct avirt_stream *stream; struct snd_pcm_hardware *hw; - unsigned int bytes_per_sample = 0, blocksize = 0, chans = 0; + unsigned int chans = 0; char *uid = "ap_fddsp"; // TD MF: Make this dynamic! audiopath = avirt_audiopath_get(uid); @@ -57,20 +57,9 @@ static int pcm_open(struct snd_pcm_substream *substream) uid); substream->private_data = audiopath; - blocksize = audiopath->blocksize; - // Copy the hw params from the audiopath to the pcm hw = &substream->runtime->hw; memcpy(hw, audiopath->hw, sizeof(struct snd_pcm_hardware)); - pr_info("%s %d %d", __func__, blocksize, hw->periods_max); - - if (hw->formats == SNDRV_PCM_FMTBIT_S16_LE) { - bytes_per_sample = 2; - } else { - pr_err("[%s] PCM only supports SNDRV_PCM_FMTBIT_S16_LE", - __func__); - return -EINVAL; - } stream = __avirt_stream_find_by_device(substream->pcm->device); if (IS_ERR_VALUE(stream) || !stream) diff --git a/core.c b/core.c index d925eae..567021e 100644 --- a/core.c +++ b/core.c @@ -241,9 +241,8 @@ int avirt_audiopath_register(struct avirt_audiopath *audiopath, audiopath_obj->path = audiopath; audiopath->context = audiopath_obj; - pr_info("Registered new Audio Path: %s\n", audiopath->uid); - pr_info("\tBlocksize: %d, Periods: %d\n", audiopath->blocksize, - audiopath->hw->periods_max); + D_INFOK("Registered new Audio Path: %s", audiopath->name); + list_add_tail(&audiopath_obj->list, &audiopath_list); // If we have already sealed the streams, configure this AP diff --git a/core.h b/core.h index 493fc2b..d9b1382 100644 --- a/core.h +++ b/core.h @@ -28,12 +28,11 @@ typedef int (*avirt_audiopath_configure)(struct config_group *stream_group, * AVIRT Audio Path info */ struct avirt_audiopath { - const char *uid; /* Unique identifier */ - const char *name; /* Pretty name */ - unsigned int version[3]; /* Version - Major.Minor.Ext */ - struct snd_pcm_hardware *hw; /* ALSA PCM HW conf */ - struct snd_pcm_ops *pcm_ops; /* ALSA PCM op table */ - unsigned int blocksize; /* Audio frame size accepted */ + const char *uid; /* Unique identifier */ + const char *name; /* Pretty name */ + unsigned int version[3]; /* Version - Major.Minor.Ext */ + const struct snd_pcm_hardware *hw; /* ALSA PCM HW conf */ + const struct snd_pcm_ops *pcm_ops; /* ALSA PCM op table */ avirt_audiopath_configure configure; /* Configure callback function */ void *context; @@ -44,10 +43,10 @@ struct avirt_audiopath { */ struct avirt_stream { char name[MAX_NAME_LEN]; /* Stream name */ - char map[MAX_NAME_LEN]; /* Stream Audio Path mapping */ - unsigned int channels; /* Stream channel count */ - unsigned int device; /* Stream PCM device no. */ - unsigned int direction; /* Stream direction */ + char map[MAX_NAME_LEN]; /* Stream Audio Path mapping */ + unsigned int channels; /* Stream channel count */ + unsigned int device; /* Stream PCM device no. */ + unsigned int direction; /* Stream direction */ struct config_item item; /* configfs item reference */ }; @@ -94,7 +93,7 @@ int avirt_stream_count(unsigned int direction); * @return: The item's avirt_stream if successful, NULL otherwise */ static inline struct avirt_stream * -avirt_stream_from_config_item(struct config_item *item) + avirt_stream_from_config_item(struct config_item *item) { return item ? container_of(item, struct avirt_stream, item) : NULL; } diff --git a/core_internal.h b/core_internal.h index e7bea07..5979a50 100644 --- a/core_internal.h +++ b/core_internal.h @@ -23,7 +23,17 @@ struct avirt_core { bool streams_sealed; }; +/** + * __avirt_configfs_init - Initialise the configfs system + * @core: The avirt_core pointer + * @return: 0 on success, negative ERRNO on failure + */ int __init __avirt_configfs_init(struct avirt_core *core); + +/** + * __avirt_configfs_exit - Clean up and exit the configfs system + * @core: The avirt_core pointer + */ void __exit __avirt_configfs_exit(struct avirt_core *core); /** diff --git a/dummy/dummy.c b/dummy/dummy.c index 606b22d..8808e8e 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -268,7 +268,6 @@ static struct avirt_audiopath dummyap_module = { .version = { 0, 0, 1 }, .hw = &dummyap_hw, .pcm_ops = &dummyap_pcm_ops, - .blocksize = DUMMY_BLOCKSIZE, .configure = dummy_configure, }; diff --git a/loopback/loopback.c b/loopback/loopback.c index 5694dce..e9c4bcb 100644 --- a/loopback/loopback.c +++ b/loopback/loopback.c @@ -279,7 +279,7 @@ static struct avirt_audiopath loopbackap_module = { .value = 10, .hw = &loopbackap_hw, .pcm_ops = &loopbackap_pcm_ops, - .blocksize = 512, + .configure = loopback_configure, }; static int __init loopback_init(void) -- cgit 1.2.3-korg From 931ac3b329c106c9058250b994e6a150c977b606 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Sun, 7 Oct 2018 22:01:09 +1100 Subject: Fix issue with 'map' configfs store The trailing \n was improperly being stored to the 'map' variable Signed-off-by: Mark Farrugia --- configfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/configfs.c b/configfs.c index cdfd171..7e820c6 100644 --- a/configfs.c +++ b/configfs.c @@ -32,9 +32,11 @@ static ssize_t cfg_avirt_stream_map_show(struct config_item *item, char *page) static ssize_t cfg_avirt_stream_map_store(struct config_item *item, const char *page, size_t count) { + char *split; struct avirt_stream *stream = avirt_stream_from_config_item(item); - memcpy(stream->map, (char *)page, count); + split = strsep((char **)&page, "\n"); + memcpy(stream->map, (char *)split, count); return count; } -- cgit 1.2.3-korg From 470fdcb5f773ab45f715a665af6cb35a79ef38ce Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 8 Oct 2018 11:42:14 +1100 Subject: Refactor to use new DINFO macros Signed-off-by: Mark Farrugia --- alsa-pcm.c | 10 +++++++--- configfs.c | 24 +++++++++++++++--------- core.c | 36 ++++++++++++++++++------------------ core.h | 11 +++++++++++ 4 files changed, 51 insertions(+), 30 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 958ccbb..efa0a15 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -9,6 +9,12 @@ #include "core_internal.h" +#define D_LOGNAME "pcm" + +#define D_INFOK(fmt, args...) DINFO(D_LOGNAME, fmt, ##args) +#define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args) +#define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args) + #define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ do { \ if ((ap)->pcm_ops->callback) { \ @@ -31,7 +37,6 @@ void avirt_pcm_period_elapsed(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(avirt_pcm_period_elapsed); - /******************************************************************************* * ALSA PCM Callbacks ******************************************************************************/ @@ -90,7 +95,6 @@ static int pcm_open(struct snd_pcm_substream *substream) */ static int pcm_close(struct snd_pcm_substream *substream) { - DINFO(AP_LOGNAME, ""); // Do additional Audio Path 'close' callback DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), close, substream); @@ -199,7 +203,7 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_SUSPEND: break; default: - pr_err("trigger must be START or STOP"); + D_ERRORK("Invalid trigger cmd: %d", cmd); return -EINVAL; } diff --git a/configfs.c b/configfs.c index 7e820c6..6a82b0b 100644 --- a/configfs.c +++ b/configfs.c @@ -10,6 +10,12 @@ #include #include "core_internal.h" +#define D_LOGNAME "configfs" + +#define D_INFOK(fmt, args...) DINFO(D_LOGNAME, fmt, ##args) +#define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args) +#define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args) + static ssize_t cfg_avirt_stream_direction_show(struct config_item *item, char *page) { @@ -70,7 +76,7 @@ static ssize_t cfg_avirt_stream_channels_store(struct config_item *item, stream->channels = tmp; - pr_info("%s [ARGS] channels: %d\n", __func__, stream->channels); + D_INFOK("channels: %d", stream->channels); return count; } @@ -85,7 +91,7 @@ static struct configfs_attribute *cfg_avirt_stream_attrs[] = { static void cfg_avirt_stream_release(struct config_item *item) { - pr_info("%s [ARGS] item->name:%s\n", __func__, item->ci_namebuf); + D_INFOK("item->name:%s", item->ci_namebuf); kfree(avirt_stream_from_config_item(item)); } @@ -109,8 +115,8 @@ cfg_avirt_stream_make_item(struct config_group *group, const char *name) // Get prefix (playback_ or capture_) split = strsep((char **)&name, "_"); if (!split) { - pr_err("Stream name: '%s' invalid!\n", split); - pr_err("Must begin with playback_ * or capture_ *\n"); + D_ERRORK("Stream name: '%s' invalid!", split); + D_ERRORK("Must begin with playback_ * or capture_ *"); return ERR_PTR(-EINVAL); } if (!strcmp(split, "playback")) { @@ -118,8 +124,8 @@ cfg_avirt_stream_make_item(struct config_group *group, const char *name) } else if (!strcmp(split, "capture")) { direction = SNDRV_PCM_STREAM_CAPTURE; } else { - pr_err("Stream name: '%s' invalid!\n", split); - pr_err("Must begin with playback_ * or capture_ *\n"); + D_ERRORK("Stream name: '%s' invalid!", split); + D_ERRORK("Must begin with playback_ * or capture_ "); return ERR_PTR(-EINVAL); } @@ -155,7 +161,7 @@ static ssize_t cfg_avirt_stream_group_sealed_store(struct config_item *item, CHK_ERR(kstrtoul(p, 10, &tmp)); if (tmp != 1) { - pr_err("AVIRT streams can only be sealed, not unsealed!\n"); + D_ERRORK("streams can only be sealed, not unsealed!"); return -ERANGE; } @@ -203,14 +209,14 @@ int __init __avirt_configfs_init(struct avirt_core *core) mutex_init(&cfg_subsys.su_mutex); err = configfs_register_subsystem(&cfg_subsys); if (err) { - pr_err("Cannot register configfs subsys!\n"); + D_ERRORK("Cannot register configfs subsys!"); return err; } core->stream_group = configfs_register_default_group( &cfg_subsys.su_group, "streams", &cfg_stream_group_type); if (IS_ERR(core->stream_group)) { err = PTR_ERR(core->stream_group); - pr_err("Cannot register configfs default group 'streams'!\n"); + D_ERRORK("Cannot register configfs default group 'streams'!"); goto exit_configfs; } diff --git a/core.c b/core.c index 567021e..cdc7556 100644 --- a/core.c +++ b/core.c @@ -20,11 +20,11 @@ MODULE_AUTHOR("MFARRUGI "); MODULE_DESCRIPTION("A configurable virtual soundcard"); MODULE_LICENSE("GPL v2"); -#define AP_LOGNAME "CORE" +#define D_LOGNAME "core" -#define D_INFOK(fmt, args...) DINFO(AP_LOGNAME, fmt, ##args) -#define D_PRINTK(fmt, args...) DPRINT(AP_LOGNAME, fmt, ##args) -#define D_ERRORK(fmt, args...) DERROR(AP_LOGNAME, fmt, ##args) +#define D_INFOK(fmt, args...) DINFO(D_LOGNAME, fmt, ##args) +#define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args) +#define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args) #define SND_AVIRTUAL_DRIVER "snd_avirt" @@ -229,13 +229,13 @@ int avirt_audiopath_register(struct avirt_audiopath *audiopath, struct avirt_audiopath_obj *audiopath_obj; if (!audiopath) { - pr_err("Bad audio path\n"); + D_ERRORK("Audio Path is NULL!"); return -EINVAL; } audiopath_obj = create_avirt_audiopath_obj(audiopath->uid); if (!audiopath_obj) { - pr_info("failed to alloc driver object\n"); + D_INFOK("Failed to alloc driver object"); return -ENOMEM; } audiopath_obj->path = audiopath; @@ -267,19 +267,19 @@ int avirt_audiopath_deregister(struct avirt_audiopath *audiopath) // Check if audio path is registered if (!audiopath) { - pr_err("Bad Audio Path Driver\n"); + D_ERRORK("Bad Audio Path Driver"); return -EINVAL; } audiopath_obj = audiopath->context; if (!audiopath_obj) { - pr_info("driver not registered.\n"); + D_INFOK("driver not registered"); return -EINVAL; } list_del(&audiopath_obj->list); destroy_avirt_audiopath_obj(audiopath_obj); - pr_info("Deregistered Audio Path %s\n", audiopath->uid); + D_INFOK("Deregistered Audio Path %s", audiopath->uid); return 0; } @@ -364,19 +364,19 @@ int __avirt_card_register(void) struct avirt_audiopath_obj *ap_obj; if (core.streams_sealed) { - pr_err("Streams already sealed!\n"); + D_ERRORK("streams are already sealed!"); return -1; } list_for_each_entry (ap_obj, &audiopath_list, list) { - pr_info("Calling configure for AP uid: %s\n", - ap_obj->path->uid); - ap_obj->path->configure(core.stream_group, core.stream_count); + D_INFOK("configure() AP uid: %s", ap_obj->path->uid); + ap_obj->path->configure(core.card, core.stream_group, + core.stream_count); } err = snd_card_register(core.card); if (err < 0) { - pr_err("Sound card registration failed!"); + D_ERRORK("Sound card registration failed!"); snd_card_free(core.card); } @@ -397,7 +397,7 @@ struct avirt_stream *__avirt_stream_find_by_device(unsigned int device) struct list_head *entry; if (device >= core.stream_count) { - pr_err("Stream device number is greater than number streams available\n"); + D_ERRORK("Stream device number is larger than stream count"); return ERR_PTR(-EINVAL); } @@ -420,13 +420,13 @@ struct avirt_stream *__avirt_stream_find_by_device(unsigned int device) static int __init core_init(void) { int err; - D_INFOK("Starting new core\n\n\n\n"); + D_INFOK("Alsa Virtual Sound Driver avirt-%d.%d.%d", coreinfo.version[0], coreinfo.version[1], coreinfo.version[2]); core.avirt_class = class_create(THIS_MODULE, SND_AVIRTUAL_DRIVER); if (IS_ERR(core.avirt_class)) { - pr_err("No udev support\n"); + D_ERRORK("No udev support"); return PTR_ERR(core.avirt_class); } @@ -440,7 +440,7 @@ static int __init core_init(void) err = snd_card_new(core.dev, SNDRV_DEFAULT_IDX1, "avirt", THIS_MODULE, 0, &core.card); if (err < 0) { - pr_err("Failed to create sound card"); + D_ERRORK("Failed to create sound card"); goto exit_class_container; } diff --git a/core.h b/core.h index d9b1382..ec44081 100644 --- a/core.h +++ b/core.h @@ -16,6 +16,17 @@ #define MAX_STREAMS 16 #define MAX_NAME_LEN 80 +#define DINFO(logname, fmt, args...) \ + printk(KERN_INFO "[AVIRT][%s]: " fmt "\n", logname, ##args) + +#define DERROR(logname, fmt, args...) \ + printk(KERN_ERR "[AVIRT][%s]: %d:%s " fmt "\n", logname, __LINE__, \ + __func__, ##args) + +#define DDEBUG(logname, fmt, args...) \ + printk(KERN_DEBUG "[AVIRT][%s]: %d:%s " fmt "\n", logname, __LINE__, \ + __func__, ##args) + /** * AVIRT Audio Path configure function type * Each Audio Path registers this at avirt_audiopath_register time. -- cgit 1.2.3-korg From 85510ff4540c13609b3ec749a80638ae502fd098 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 8 Oct 2018 12:03:56 +1100 Subject: Add card to the configure callback The loopback audio path might need to add it's own controls. Signed-off-by: Mark Farrugia --- core.h | 3 ++- dummy/dummy.c | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core.h b/core.h index ec44081..b7c250a 100644 --- a/core.h +++ b/core.h @@ -32,7 +32,8 @@ * Each Audio Path registers this at avirt_audiopath_register time. * It is then called by the core once AVIRT has been configured */ -typedef int (*avirt_audiopath_configure)(struct config_group *stream_group, +typedef int (*avirt_audiopath_configure)(struct snd_card *card, + struct config_group *stream_group, unsigned int stream_count); /** diff --git a/dummy/dummy.c b/dummy/dummy.c index 8808e8e..b77301f 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -231,14 +231,14 @@ static struct snd_pcm_ops dummyap_pcm_ops = { /******************************************************************************* * Dummy Audio Path AVIRT registration ******************************************************************************/ -int dummy_configure(struct config_group *avirt_stream_group, +int dummy_configure(struct snd_card *card, + struct config_group *avirt_stream_group, unsigned int stream_count) { // Do something with streams struct list_head *entry; - list_for_each(entry, &avirt_stream_group->cg_children) - { + list_for_each (entry, &avirt_stream_group->cg_children) { struct config_item *item = container_of(entry, struct config_item, ci_entry); struct avirt_stream *stream = -- cgit 1.2.3-korg From 1b8e8c7a0b998e9e458094e5f1b7b60bc1c4b297 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 8 Oct 2018 12:06:46 +1100 Subject: Refactor avirt_card_register, move PCM creation to it. When creating the PCMs we need information regarding the stream's mapping. For the loopback, we need to add both playback and capture substreams. Signed-off-by: Mark Farrugia --- configfs.c | 7 +----- core.c | 72 ++++++++++++++++++++++++++++++++++++++++----------------- core.h | 3 ++- core_internal.h | 5 ++-- 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/configfs.c b/configfs.c index 6a82b0b..924866c 100644 --- a/configfs.c +++ b/configfs.c @@ -153,11 +153,6 @@ static ssize_t cfg_avirt_stream_group_sealed_store(struct config_item *item, unsigned long tmp; char *p = (char *)page; - if (__avirt_streams_sealed()) { - pr_err("AVIRT streams are already sealed!\n"); - return -EPERM; - } - CHK_ERR(kstrtoul(p, 10, &tmp)); if (tmp != 1) { @@ -165,7 +160,7 @@ static ssize_t cfg_avirt_stream_group_sealed_store(struct config_item *item, return -ERANGE; } - CHK_ERR(__avirt_card_register()); + __avirt_streams_seal(); return count; } diff --git a/core.c b/core.c index cdc7556..f4f8310 100644 --- a/core.c +++ b/core.c @@ -189,6 +189,41 @@ static struct avirt_audiopath_obj *create_avirt_audiopath_obj(const char *uid) return avirt_audiopath; } +static struct snd_pcm *pcm_create(struct avirt_stream *stream) +{ + bool playback = false, capture = false; + struct snd_pcm *pcm; + int err; + + if (!strcmp(stream->map, "ap_loopback")) { + playback = true; + capture = true; + } else if (!stream->direction) { + playback = true; + } else { + capture = true; + } + + err = snd_pcm_new(core.card, stream->name, stream->device, playback, + capture, &pcm); + + if (err < 0) + return ERR_PTR(err); + + // TD MF: IMPORTANT: NEED TO TEST >8 PCM DEVICES ON A + // CARD! + /** Register driver callbacks */ + if (playback) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); + if (capture) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); + + pcm->info_flags = 0; + strcpy(pcm->name, stream->name); + + return pcm; +} + /** * destroy_avirt_audiopath_obj - destroys an Audio Path object * @name: the Audio Path object @@ -323,51 +358,46 @@ EXPORT_SYMBOL_GPL(avirt_stream_count); */ struct avirt_stream *__avirt_stream_create(const char *name, int direction) { - struct snd_pcm *pcm; struct avirt_stream *stream; - int err; stream = kzalloc(sizeof(*stream), GFP_KERNEL); if (!stream) return ERR_PTR(-ENOMEM); strcpy(stream->name, name); - strcpy(stream->map, "ap_fddsp"); + strcpy(stream->map, "none"); stream->channels = 0; stream->direction = direction; stream->device = core.stream_count++; - err = snd_pcm_new(core.card, name, stream->device, !direction, - direction, &pcm); - if (err < 0) - return ERR_PTR(err); - - // TD MF: IMPORTANT: NEED TO TEST >8 PCM DEVICES ON A - // CARD! - /** Register driver callbacks */ - snd_pcm_set_ops(pcm, direction, &pcm_ops); - - pcm->info_flags = 0; - strcpy(pcm->name, name); - - pr_info("%s [ARGS] name: %s device:%d\n", __func__, name, - stream->device); - - // coreinfo.streams[stream_idx] = stream; + D_INFOK("name: %s device:%d", name, stream->device); return stream; } -int __avirt_card_register(void) +int __avirt_streams_seal(void) { int err = 0; struct avirt_audiopath_obj *ap_obj; + struct avirt_stream *stream; + struct config_item *item; + struct list_head *entry; if (core.streams_sealed) { D_ERRORK("streams are already sealed!"); return -1; } + list_for_each (entry, &core.stream_group->cg_children) { + item = container_of(entry, struct config_item, ci_entry); + stream = avirt_stream_from_config_item(item); + if (!stream) + return -EFAULT; + stream->pcm = pcm_create(stream); + if (IS_ERR_OR_NULL(stream->pcm)) + return (PTR_ERR(stream->pcm)); + } + list_for_each_entry (ap_obj, &audiopath_list, list) { D_INFOK("configure() AP uid: %s", ap_obj->path->uid); ap_obj->path->configure(core.card, core.stream_group, diff --git a/core.h b/core.h index b7c250a..bac4fc0 100644 --- a/core.h +++ b/core.h @@ -59,6 +59,7 @@ struct avirt_stream { unsigned int channels; /* Stream channel count */ unsigned int device; /* Stream PCM device no. */ unsigned int direction; /* Stream direction */ + struct snd_pcm *pcm; /* ALSA PCM */ struct config_item item; /* configfs item reference */ }; @@ -105,7 +106,7 @@ int avirt_stream_count(unsigned int direction); * @return: The item's avirt_stream if successful, NULL otherwise */ static inline struct avirt_stream * - avirt_stream_from_config_item(struct config_item *item) +avirt_stream_from_config_item(struct config_item *item) { return item ? container_of(item, struct avirt_stream, item) : NULL; } diff --git a/core_internal.h b/core_internal.h index 5979a50..1ab8d3c 100644 --- a/core_internal.h +++ b/core_internal.h @@ -37,10 +37,10 @@ int __init __avirt_configfs_init(struct avirt_core *core); void __exit __avirt_configfs_exit(struct avirt_core *core); /** - * __avirt_card_register - Register the sound card to user space + * __avirt_streams_seal - Register the sound card to user space * @return: 0 on success, negative ERRNO on failure */ -int __avirt_card_register(void); +int __avirt_streams_seal(void); /** * __avirt_streams_sealed - Check whether the streams have been sealed or not @@ -48,7 +48,6 @@ int __avirt_card_register(void); */ bool __avirt_streams_sealed(void); -/** /** * __avirt_stream_find_by_device - Get audio stream from device number * @device: The PCM device number corresponding to the desired stream -- cgit 1.2.3-korg From d996ba9ae3f5c589fe551c2349f03e10c59b5512 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 8 Oct 2018 12:08:24 +1100 Subject: Make Audio Path searching dynamic again Remove hardcoded test Audio Path UID, and use dynamic Audio Path UID Signed-off-by: Mark Farrugia --- alsa-pcm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index efa0a15..dbb1275 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -56,10 +56,10 @@ static int pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_hardware *hw; unsigned int chans = 0; - char *uid = "ap_fddsp"; // TD MF: Make this dynamic! - audiopath = avirt_audiopath_get(uid); + stream = __avirt_stream_find_by_device(substream->pcm->device); + audiopath = avirt_audiopath_get(stream->map); CHK_NULL_V(audiopath, -EFAULT, "Cannot find Audio Path uid: '%s'!", - uid); + stream->map); substream->private_data = audiopath; // Copy the hw params from the audiopath to the pcm -- cgit 1.2.3-korg From de58013ed69a18b28816894e4740ef4b71814bd9 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 8 Oct 2018 16:57:23 +1100 Subject: Remove remaining blocksize associated calcs These should be done in the Audio Path Signed-off-by: Mark Farrugia --- alsa-pcm.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index dbb1275..9ec21fc 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -74,10 +74,6 @@ static int pcm_open(struct snd_pcm_substream *substream) chans = stream->channels; hw->channels_min = chans; hw->channels_max = chans; - hw->buffer_bytes_max = - blocksize * hw->periods_max * bytes_per_sample * chans; - hw->period_bytes_min = blocksize * bytes_per_sample * chans; - hw->period_bytes_max = blocksize * bytes_per_sample * chans; // Do additional Audio Path 'open' callback DO_AUDIOPATH_CB(audiopath, open, substream); -- cgit 1.2.3-korg From 3b2d2634474398bb40c64941c828a4cf6b363d4a Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 8 Oct 2018 16:59:57 +1100 Subject: Fix DO_AUDIOPATH_CB, remove unused PCM cbs for now We don't want to return inside the macro call necessarily. Some PCM cbs require differing return values. Signed-off-by: Mark Farrugia --- alsa-pcm.c | 117 ++++++++++++++++++++++++++----------------------------------- core.c | 1 + 2 files changed, 51 insertions(+), 67 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 9ec21fc..17c08fd 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -16,12 +16,9 @@ #define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args) #define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ - do { \ - if ((ap)->pcm_ops->callback) { \ - return (ap)->pcm_ops->callback((substream), \ - ##__VA_ARGS__); \ - } \ - } while (0) + (((ap)->pcm_ops->callback) ? \ + (ap)->pcm_ops->callback((substream), ##__VA_ARGS__) : \ + 0) /** * avirt_pcm_period_elapsed - PCM buffer complete callback @@ -76,9 +73,7 @@ static int pcm_open(struct snd_pcm_substream *substream) hw->channels_max = chans; // Do additional Audio Path 'open' callback - DO_AUDIOPATH_CB(audiopath, open, substream); - - return 0; + return DO_AUDIOPATH_CB(audiopath, open, substream); } /** @@ -92,10 +87,9 @@ static int pcm_open(struct snd_pcm_substream *substream) static int pcm_close(struct snd_pcm_substream *substream) { // Do additional Audio Path 'close' callback - DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), - close, substream); - - return 0; + return DO_AUDIOPATH_CB( + ((struct avirt_audiopath *)substream->private_data), close, + substream); } /** @@ -111,7 +105,7 @@ static int pcm_close(struct snd_pcm_substream *substream) static int pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - int err; + int retval; size_t bufsz; struct avirt_audiopath *audiopath; struct avirt_stream *stream; @@ -120,25 +114,21 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (IS_ERR_VALUE(stream) || !stream) return PTR_ERR(stream); - if ((params_channels(hw_params) > stream->channels) - || (params_channels(hw_params) < stream->channels)) { - pr_err("Requested number of channels not supported.\n"); + if ((params_channels(hw_params) > stream->channels) || + (params_channels(hw_params) < stream->channels)) { + D_ERRORK("Requested number of channels: %d not supported", + params_channels(hw_params)); return -EINVAL; } audiopath = ((struct avirt_audiopath *)substream->private_data); bufsz = params_buffer_bytes(hw_params) * audiopath->hw->periods_max; - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, bufsz); - if (err <= 0) { - pr_err("pcm: buffer allocation failed (%d)\n", err); - return err; - } + retval = snd_pcm_lib_alloc_vmalloc_buffer(substream, bufsz); + if (retval < 0) + D_ERRORK("pcm: buffer allocation failed: %d", retval); - // Do additional Audio Path 'hw_params' callback - DO_AUDIOPATH_CB(hw_params, substream, hw_params); - - return 0; + return retval; } /** @@ -152,13 +142,14 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, */ static int pcm_hw_free(struct snd_pcm_substream *substream) { - DINFO(AP_LOGNAME, ""); - CHK_ERR(snd_pcm_lib_free_vmalloc_buffer(substream)); + int err; // Do additional Audio Path 'hw_free' callback - DO_AUDIOPATH_CB(hw_free, substream); + err = DO_AUDIOPATH_CB( + ((struct avirt_audiopath *)substream->private_data), hw_free, + substream); - return 0; + return snd_pcm_lib_free_vmalloc_buffer(substream); } /** @@ -174,10 +165,9 @@ static int pcm_hw_free(struct snd_pcm_substream *substream) static int pcm_prepare(struct snd_pcm_substream *substream) { // Do additional Audio Path 'prepare' callback - DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), - prepare, substream); - - return 0; + return DO_AUDIOPATH_CB( + ((struct avirt_audiopath *)substream->private_data), prepare, + substream); } /** @@ -204,10 +194,9 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) } // Do additional Audio Path 'trigger' callback - DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), - trigger, substream, cmd); - - return 0; + return DO_AUDIOPATH_CB( + ((struct avirt_audiopath *)substream->private_data), trigger, + substream, cmd); } /** @@ -223,10 +212,9 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) { // Do additional Audio Path 'pointer' callback - DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), - pointer, substream); - - return 0; + return DO_AUDIOPATH_CB( + ((struct avirt_audiopath *)substream->private_data), pointer, + substream); } /** @@ -247,11 +235,10 @@ static int pcm_get_time_info( struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report) { - DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), - get_time_info, substream, system_ts, audio_ts, - audio_tstamp_config, audio_tstamp_report); - - return 0; + return DO_AUDIOPATH_CB( + ((struct avirt_audiopath *)substream->private_data), + get_time_info, substream, system_ts, audio_ts, + audio_tstamp_config, audio_tstamp_report); } /** @@ -278,10 +265,9 @@ static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, // offset = frames_to_bytes(runtime, pos); // Do additional Audio Path 'copy_user' callback - DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), - copy_user, substream, channel, pos, src, count); - - return 0; + return DO_AUDIOPATH_CB( + ((struct avirt_audiopath *)substream->private_data), copy_user, + substream, channel, pos, src, count); } /** @@ -300,10 +286,9 @@ static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, unsigned long pos, void *buf, unsigned long count) { - DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), - copy_kernel, substream, channel, pos, buf, count); - - return 0; + return DO_AUDIOPATH_CB( + ((struct avirt_audiopath *)substream->private_data), + copy_kernel, substream, channel, pos, buf, count); } /** @@ -317,19 +302,17 @@ static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, */ static int pcm_ack(struct snd_pcm_substream *substream) { - DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), - ack, substream); - - return 0; + return DO_AUDIOPATH_CB( + ((struct avirt_audiopath *)substream->private_data), ack, + substream); } static int pcm_silence(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) { - DO_AUDIOPATH_CB(((struct avirt_audiopath *)substream->private_data), - fill_silence, substream, channel, pos, count); - - return 0; + return DO_AUDIOPATH_CB( + ((struct avirt_audiopath *)substream->private_data), + fill_silence, substream, channel, pos, count); } struct snd_pcm_ops pcm_ops = { @@ -341,10 +324,10 @@ struct snd_pcm_ops pcm_ops = { .prepare = pcm_prepare, .trigger = pcm_trigger, .pointer = pcm_pointer, - .get_time_info = pcm_get_time_info, - .fill_silence = pcm_silence, + //.get_time_info = pcm_get_time_info, + //.fill_silence = pcm_silence, .copy_user = pcm_copy_user, - .copy_kernel = pcm_copy_kernel, + //.copy_kernel = pcm_copy_kernel, .page = snd_pcm_lib_get_vmalloc_page, - .ack = pcm_ack, + //.ack = pcm_ack, }; diff --git a/core.c b/core.c index f4f8310..1ea8107 100644 --- a/core.c +++ b/core.c @@ -198,6 +198,7 @@ static struct snd_pcm *pcm_create(struct avirt_stream *stream) if (!strcmp(stream->map, "ap_loopback")) { playback = true; capture = true; + (&pcm_ops)->copy_user = NULL; } else if (!stream->direction) { playback = true; } else { -- cgit 1.2.3-korg From 39af5aad096d103c171096f05cc0487e66de0571 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 8 Oct 2018 17:02:51 +1100 Subject: Add working loopback implementation, helper scripts Reworked the original aloop implementation to have the PCM devices loop on themselves, and subscribe the the AVIRT system. Signed-off-by: Mark Farrugia --- loopback/Makefile | 1 + loopback/loopback.c | 1185 +++++++++++++++++++++++++++++++++++++++------- scripts/load.sh | 5 +- scripts/test_configfs.sh | 4 + scripts/unload.sh | 3 + 5 files changed, 1013 insertions(+), 185 deletions(-) diff --git a/loopback/Makefile b/loopback/Makefile index 55da206..0464aba 100644 --- a/loopback/Makefile +++ b/loopback/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_AVIRT_LOOPBACKAP) += avirt_loopbackap.o $(info $(src)) diff --git a/loopback/loopback.c b/loopback/loopback.c index e9c4bcb..2e02711 100644 --- a/loopback/loopback.c +++ b/loopback/loopback.c @@ -1,308 +1,1125 @@ -// SPDX-License-Identifier: GPL-2.0 /* - * ALSA Virtual Soundcard + * Loopback soundcard * - * loopback_audiopath.c - AVIRT sample Audio Path definition + * Original code: + * Copyright (c) by Jaroslav Kysela + * + * More accurate positioning and full-duplex support: + * Copyright (c) Ahmet Ä°nan + * + * Major (almost complete) rewrite: + * Copyright (c) by Takashi Iwai + * + * A next major update in 2010 (separate timers for playback and capture): + * Copyright (c) Jaroslav Kysela + * + * Adapt to use AVIRT, looping is now conducted on the same device + * Copyright (c) by Mark Farrugia + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd */ -#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -MODULE_AUTHOR("JOSHANNE "); -MODULE_AUTHOR("MFARRUGI "); -MODULE_DESCRIPTION("Sample Audio Path Module Interface"); -MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("A loopback soundcard"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}"); + +#define AP_UID "ap_loopback" -#define AP_LOGNAME "LOOPAP" +#define AP_INFOK(fmt, args...) DINFO(AP_UID, fmt, ##args) +#define AP_PRINTK(fmt, args...) DDEBUG(AP_UID, fmt, ##args) +#define AP_ERRORK(fmt, args...) DERROR(AP_UID, fmt, ##args) -#define AP_INFOK(fmt, args...) DINFO(AP_LOGNAME, fmt, ##args) -#define AP_PRINTK(fmt, args...) DPRINT(AP_LOGNAME, fmt, ##args) -#define AP_ERRORK(fmt, args...) DERROR(AP_LOGNAME, fmt, ##args) +#define NO_PITCH 100000 static struct avirt_coreinfo *coreinfo; +static struct loopback *loopback; + +struct loopback_pcm; + +struct loopback_cable { + spinlock_t lock; + struct loopback_pcm *streams[2]; + struct snd_pcm_hardware hw; + /* flags */ + unsigned int valid; + unsigned int running; + unsigned int pause; +}; + +struct loopback_setup { + unsigned int notify : 1; + unsigned int rate_shift; + unsigned int format; + unsigned int rate; + unsigned int channels; + struct snd_ctl_elem_id active_id; + struct snd_ctl_elem_id format_id; + struct snd_ctl_elem_id rate_id; + struct snd_ctl_elem_id channels_id; +}; + +struct loopback { + struct snd_card *card; + struct mutex cable_lock; + struct loopback_cable *cables[MAX_STREAMS]; + struct snd_pcm *pcm[MAX_STREAMS]; + struct loopback_setup setup[MAX_STREAMS]; +}; struct loopback_pcm { + struct loopback *loopback; struct snd_pcm_substream *substream; - spinlock_t lock; + struct loopback_cable *cable; + unsigned int pcm_buffer_size; + unsigned int buf_pos; /* position in buffer */ + unsigned int silent_size; + /* PCM parameters */ + unsigned int pcm_period_size; + unsigned int pcm_bps; /* bytes per second */ + unsigned int pcm_salign; /* bytes per sample * channels */ + unsigned int pcm_rate_shift; /* rate shift value */ + /* flags */ + unsigned int period_update_pending : 1; + /* timer stuff */ + unsigned int irq_pos; /* fractional IRQ position */ + unsigned int period_size_frac; + unsigned int last_drift; + unsigned long last_jiffies; struct timer_list timer; - unsigned long base_time; - unsigned int frac_pos; /* fractional sample position (based HZ) */ - unsigned int frac_period_rest; - unsigned int frac_buffer_size; /* buffer_size * HZ */ - unsigned int frac_period_size; /* period_size * HZ */ - unsigned int rate; - int elapsed; }; -int systimer_create(struct snd_pcm_substream *substream); -void systimer_free(struct snd_pcm_substream *substream); -int systimer_start(struct snd_pcm_substream *substream); -int systimer_stop(struct snd_pcm_substream *substream); -snd_pcm_uframes_t systimer_pointer(struct snd_pcm_substream *substream); -int systimer_prepare(struct snd_pcm_substream *substream); -void systimer_rearm(struct loopback_pcm *dpcm); -void systimer_update(struct loopback_pcm *dpcm); +static inline unsigned int byte_pos(struct loopback_pcm *dpcm, unsigned int x) +{ + if (dpcm->pcm_rate_shift == NO_PITCH) { + x /= HZ; + } else { + x = div_u64(NO_PITCH * (unsigned long long)x, + HZ * (unsigned long long)dpcm->pcm_rate_shift); + } + return x - (x % dpcm->pcm_salign); +} -void loopback_callback(struct timer_list *tlist); +static inline unsigned int frac_pos(struct loopback_pcm *dpcm, unsigned int x) +{ + if (dpcm->pcm_rate_shift == NO_PITCH) { /* no pitch */ + return x * HZ; + } else { + x = div_u64(dpcm->pcm_rate_shift * (unsigned long long)x * HZ, + NO_PITCH); + } + return x; +} -/******************************** - * Loopback Timer Functions - ********************************/ +static inline struct loopback_setup *get_setup(struct loopback_pcm *dpcm) +{ + return &dpcm->loopback->setup[dpcm->substream->pcm->device]; +} -int systimer_create(struct snd_pcm_substream *substream) +static inline unsigned int get_notify(struct loopback_pcm *dpcm) { - struct snd_pcm_runtime *runtime = substream->runtime; + return get_setup(dpcm)->notify; +} - struct loopback_pcm *dpcm; - dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); - if (!dpcm) - return -ENOMEM; +static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm) +{ + return get_setup(dpcm)->rate_shift; +} - timer_setup(&dpcm->timer, loopback_callback, 0); - spin_lock_init(&dpcm->lock); - dpcm->substream = substream; +/* call in cable->lock */ +static void loopback_timer_start(struct loopback_pcm *dpcm) +{ + unsigned long tick; + unsigned int rate_shift = get_rate_shift(dpcm); - runtime->private_data = dpcm; - return 0; + if (rate_shift != dpcm->pcm_rate_shift) { + dpcm->pcm_rate_shift = rate_shift; + dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size); + } + if (dpcm->period_size_frac <= dpcm->irq_pos) { + dpcm->irq_pos %= dpcm->period_size_frac; + dpcm->period_update_pending = 1; + } + tick = dpcm->period_size_frac - dpcm->irq_pos; + tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps; + mod_timer(&dpcm->timer, jiffies + tick); } -void systimer_free(struct snd_pcm_substream *substream) +/* call in cable->lock */ +static inline void loopback_timer_stop(struct loopback_pcm *dpcm) { - struct snd_pcm_runtime *runtime = substream->runtime; - kfree(runtime->private_data); + del_timer(&dpcm->timer); + dpcm->timer.expires = 0; } -int systimer_start(struct snd_pcm_substream *substream) +static inline void loopback_timer_stop_sync(struct loopback_pcm *dpcm) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct loopback_pcm *dpcm = runtime->private_data; - spin_lock(&dpcm->lock); - dpcm->base_time = jiffies; - systimer_rearm(dpcm); - spin_unlock(&dpcm->lock); + del_timer_sync(&dpcm->timer); +} + +#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK) +#define CABLE_VALID_CAPTURE (1 << SNDRV_PCM_STREAM_CAPTURE) +#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK | CABLE_VALID_CAPTURE) + +static int loopback_check_format(struct loopback_cable *cable, int stream) +{ + struct snd_pcm_runtime *runtime, *cruntime; + struct loopback_setup *setup; + struct snd_card *card; + int check; + + if (cable->valid != CABLE_VALID_BOTH) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + goto __notify; + return 0; + } + runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->substream->runtime; + cruntime = cable->streams[SNDRV_PCM_STREAM_CAPTURE]->substream->runtime; + check = runtime->format != cruntime->format || + runtime->rate != cruntime->rate || + runtime->channels != cruntime->channels; + if (!check) + return 0; + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + return -EIO; + } else { + snd_pcm_stop( + cable->streams[SNDRV_PCM_STREAM_CAPTURE]->substream, + SNDRV_PCM_STATE_DRAINING); + __notify: + runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK] + ->substream->runtime; + setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]); + card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card; + if (setup->format != runtime->format) { + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &setup->format_id); + setup->format = runtime->format; + } + if (setup->rate != runtime->rate) { + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &setup->rate_id); + setup->rate = runtime->rate; + } + if (setup->channels != runtime->channels) { + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &setup->channels_id); + setup->channels = runtime->channels; + } + } return 0; } -int systimer_stop(struct snd_pcm_substream *substream) +static void loopback_active_notify(struct loopback_pcm *dpcm) +{ + snd_ctl_notify(dpcm->loopback->card, SNDRV_CTL_EVENT_MASK_VALUE, + &get_setup(dpcm)->active_id); +} + +static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; struct loopback_pcm *dpcm = runtime->private_data; - spin_lock(&dpcm->lock); - del_timer(&dpcm->timer); - spin_unlock(&dpcm->lock); + struct loopback_cable *cable = dpcm->cable; + int err, stream = 1 << substream->stream; + + AP_INFOK(); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err = loopback_check_format(cable, substream->stream); + if (err < 0) + return err; + dpcm->last_jiffies = jiffies; + dpcm->pcm_rate_shift = 0; + dpcm->last_drift = 0; + spin_lock(&cable->lock); + cable->running |= stream; + cable->pause &= ~stream; + loopback_timer_start(dpcm); + spin_unlock(&cable->lock); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + loopback_active_notify(dpcm); + break; + case SNDRV_PCM_TRIGGER_STOP: + spin_lock(&cable->lock); + cable->running &= ~stream; + cable->pause &= ~stream; + loopback_timer_stop(dpcm); + spin_unlock(&cable->lock); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + loopback_active_notify(dpcm); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + spin_lock(&cable->lock); + cable->pause |= stream; + loopback_timer_stop(dpcm); + spin_unlock(&cable->lock); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + loopback_active_notify(dpcm); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + spin_lock(&cable->lock); + dpcm->last_jiffies = jiffies; + cable->pause &= ~stream; + loopback_timer_start(dpcm); + spin_unlock(&cable->lock); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + loopback_active_notify(dpcm); + break; + default: + return -EINVAL; + } return 0; } -snd_pcm_uframes_t systimer_pointer(struct snd_pcm_substream *substream) +static void params_change(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct loopback_pcm *dpcm = runtime->private_data; + struct loopback_cable *cable = dpcm->cable; - snd_pcm_uframes_t pos; - - spin_lock(&dpcm->lock); - systimer_update(dpcm); - pos = dpcm->frac_pos / HZ; - spin_unlock(&dpcm->lock); - return pos; + cable->hw.formats = pcm_format_to_bits(runtime->format); + cable->hw.rate_min = runtime->rate; + cable->hw.rate_max = runtime->rate; + cable->hw.channels_min = runtime->channels; + cable->hw.channels_max = runtime->channels; } -int systimer_prepare(struct snd_pcm_substream *substream) +static int loopback_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct loopback_pcm *dpcm = runtime->private_data; + struct loopback_cable *cable = dpcm->cable; + int bps, salign; - dpcm->frac_pos = 0; - dpcm->rate = runtime->rate; - dpcm->frac_buffer_size = runtime->buffer_size * HZ; - dpcm->frac_period_size = runtime->period_size * HZ; - dpcm->frac_period_rest = dpcm->frac_period_size; - dpcm->elapsed = 0; + loopback_timer_stop_sync(dpcm); + + salign = + (snd_pcm_format_width(runtime->format) * runtime->channels) / 8; + bps = salign * runtime->rate; + if (bps <= 0 || salign <= 0) + return -EINVAL; + + dpcm->buf_pos = 0; + dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + /* clear capture buffer */ + dpcm->silent_size = dpcm->pcm_buffer_size; + snd_pcm_format_set_silence(runtime->format, runtime->dma_area, + runtime->buffer_size * + runtime->channels); + } + + dpcm->irq_pos = 0; + dpcm->period_update_pending = 0; + dpcm->pcm_bps = bps; + dpcm->pcm_salign = salign; + dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size); + + mutex_lock(&dpcm->loopback->cable_lock); + if (!(cable->valid & ~(1 << substream->stream)) || + (get_setup(dpcm)->notify && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) + params_change(substream); + cable->valid |= 1 << substream->stream; + mutex_unlock(&dpcm->loopback->cable_lock); return 0; } -void systimer_rearm(struct loopback_pcm *dpcm) +static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes) { - mod_timer(&dpcm->timer, jiffies + (dpcm->frac_period_rest + dpcm->rate - - 1) / dpcm->rate); + struct snd_pcm_runtime *runtime = dpcm->substream->runtime; + char *dst = runtime->dma_area; + unsigned int dst_off = dpcm->buf_pos; + + if (dpcm->silent_size >= dpcm->pcm_buffer_size) + return; + if (dpcm->silent_size + bytes > dpcm->pcm_buffer_size) + bytes = dpcm->pcm_buffer_size - dpcm->silent_size; + + for (;;) { + unsigned int size = bytes; + if (dst_off + size > dpcm->pcm_buffer_size) + size = dpcm->pcm_buffer_size - dst_off; + snd_pcm_format_set_silence(runtime->format, dst + dst_off, + bytes_to_frames(runtime, size) * + runtime->channels); + dpcm->silent_size += size; + bytes -= size; + if (!bytes) + break; + dst_off = 0; + } } -void systimer_update(struct loopback_pcm *dpcm) +static void copy_play_buf(struct loopback_pcm *play, struct loopback_pcm *capt, + unsigned int bytes) { - unsigned long delta; + struct snd_pcm_runtime *runtime = play->substream->runtime; + char *src = runtime->dma_area; + char *dst = capt->substream->runtime->dma_area; + unsigned int src_off = play->buf_pos; + unsigned int dst_off = capt->buf_pos; + unsigned int clear_bytes = 0; - delta = jiffies - dpcm->base_time; - if (!delta) - return; - dpcm->base_time += delta; - delta *= dpcm->rate; - dpcm->frac_pos += delta; - while (dpcm->frac_pos >= dpcm->frac_buffer_size) - dpcm->frac_pos -= dpcm->frac_buffer_size; - while (dpcm->frac_period_rest <= delta) { - dpcm->elapsed++; - dpcm->frac_period_rest += dpcm->frac_period_size; + /* check if playback is draining, trim the capture copy size + * when our pointer is at the end of playback ring buffer */ + if (runtime->status->state == SNDRV_PCM_STATE_DRAINING && + snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) { + snd_pcm_uframes_t appl_ptr, appl_ptr1, diff; + appl_ptr = appl_ptr1 = runtime->control->appl_ptr; + appl_ptr1 -= appl_ptr1 % runtime->buffer_size; + appl_ptr1 += play->buf_pos / play->pcm_salign; + if (appl_ptr < appl_ptr1) + appl_ptr1 -= runtime->buffer_size; + diff = (appl_ptr - appl_ptr1) * play->pcm_salign; + if (diff < bytes) { + clear_bytes = bytes - diff; + bytes = diff; + } + } + + for (;;) { + unsigned int size = bytes; + if (src_off + size > play->pcm_buffer_size) + size = play->pcm_buffer_size - src_off; + if (dst_off + size > capt->pcm_buffer_size) + size = capt->pcm_buffer_size - dst_off; + memcpy(dst + dst_off, src + src_off, size); + capt->silent_size = 0; + bytes -= size; + if (!bytes) + break; + src_off = (src_off + size) % play->pcm_buffer_size; + dst_off = (dst_off + size) % capt->pcm_buffer_size; + } + + if (clear_bytes > 0) { + clear_capture_buf(capt, clear_bytes); + capt->silent_size = 0; } - dpcm->frac_period_rest -= delta; } -/******* - * Loopback Timer Callback - *******/ +static inline unsigned int bytepos_delta(struct loopback_pcm *dpcm, + unsigned int jiffies_delta) +{ + unsigned long last_pos; + unsigned int delta; -// -void loopback_callback(struct timer_list *tlist) + last_pos = byte_pos(dpcm, dpcm->irq_pos); + dpcm->irq_pos += jiffies_delta * dpcm->pcm_bps; + delta = byte_pos(dpcm, dpcm->irq_pos) - last_pos; + if (delta >= dpcm->last_drift) + delta -= dpcm->last_drift; + dpcm->last_drift = 0; + if (dpcm->irq_pos >= dpcm->period_size_frac) { + dpcm->irq_pos %= dpcm->period_size_frac; + dpcm->period_update_pending = 1; + } + return delta; +} + +static inline void bytepos_finish(struct loopback_pcm *dpcm, unsigned int delta) { - struct loopback_pcm *dpcm = from_timer(dpcm, tlist, timer); + dpcm->buf_pos += delta; + dpcm->buf_pos %= dpcm->pcm_buffer_size; +} - unsigned long flags; - int elapsed = 0; +/* call in cable->lock */ +static unsigned int loopback_pos_update(struct loopback_cable *cable) +{ + struct loopback_pcm *dpcm_play = + cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; + struct loopback_pcm *dpcm_capt = + cable->streams[SNDRV_PCM_STREAM_CAPTURE]; + unsigned long delta_play = 0, delta_capt = 0; + unsigned int running, count1, count2; + + running = cable->running ^ cable->pause; + if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { + delta_play = jiffies - dpcm_play->last_jiffies; + dpcm_play->last_jiffies += delta_play; + } + + if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { + delta_capt = jiffies - dpcm_capt->last_jiffies; + dpcm_capt->last_jiffies += delta_capt; + } - AP_INFOK(""); - spin_lock_irqsave(&dpcm->lock, flags); + if (delta_play == 0 && delta_capt == 0) + goto unlock; - // Perform copy from playback to capture - systimer_update(dpcm); - systimer_rearm(dpcm); - elapsed = dpcm->elapsed; - dpcm->elapsed = 0; + if (delta_play > delta_capt) { + count1 = bytepos_delta(dpcm_play, delta_play - delta_capt); + bytepos_finish(dpcm_play, count1); + delta_play = delta_capt; + } else if (delta_play < delta_capt) { + count1 = bytepos_delta(dpcm_capt, delta_capt - delta_play); + clear_capture_buf(dpcm_capt, count1); + bytepos_finish(dpcm_capt, count1); + delta_capt = delta_play; + } + + if (delta_play == 0 && delta_capt == 0) + goto unlock; - spin_unlock_irqrestore(&dpcm->lock, flags); - if (elapsed) - coreinfo->pcm_buff_complete(dpcm->substream); + /* note delta_capt == delta_play at this moment */ + count1 = bytepos_delta(dpcm_play, delta_play); + count2 = bytepos_delta(dpcm_capt, delta_capt); + if (count1 < count2) { + dpcm_capt->last_drift = count2 - count1; + count1 = count2; + } else if (count1 > count2) { + dpcm_play->last_drift = count1 - count2; + } + copy_play_buf(dpcm_play, dpcm_capt, count1); + bytepos_finish(dpcm_play, count1); + bytepos_finish(dpcm_capt, count1); +unlock: + return running; } -/******************************************************************************* - * Audio Path ALSA PCM Callbacks - ******************************************************************************/ -static int loopback_pcm_open(struct snd_pcm_substream *substream) +static void loopback_timer_function(struct timer_list *t) { - int err; + struct loopback_pcm *dpcm = from_timer(dpcm, t, timer); + unsigned long flags; + + spin_lock_irqsave(&dpcm->cable->lock, flags); + if (loopback_pos_update(dpcm->cable) & (1 << dpcm->substream->stream)) { + loopback_timer_start(dpcm); + if (dpcm->period_update_pending) { + dpcm->period_update_pending = 0; + spin_unlock_irqrestore(&dpcm->cable->lock, flags); + /* need to unlock before calling below */ + avirt_pcm_period_elapsed(dpcm->substream); + return; + } + } + spin_unlock_irqrestore(&dpcm->cable->lock, flags); +} +static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream) +{ struct snd_pcm_runtime *runtime = substream->runtime; struct loopback_pcm *dpcm = runtime->private_data; + snd_pcm_uframes_t pos; - err = systimer_create(substream); - if (err < 0) - return err; + spin_lock(&dpcm->cable->lock); + loopback_pos_update(dpcm->cable); + pos = dpcm->buf_pos; + spin_unlock(&dpcm->cable->lock); + return bytes_to_frames(runtime, pos); +} - return 0; +static const struct snd_pcm_hardware loopback_pcm_hardware = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | + SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 32, + .buffer_bytes_max = 2 * 1024 * 1024, + .period_bytes_min = 64, + /* note check overflow in frac_pos() using pcm_rate_shift before + changing period_bytes_max value */ + .period_bytes_max = 1024 * 1024, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static void loopback_runtime_free(struct snd_pcm_runtime *runtime) +{ + struct loopback_pcm *dpcm = runtime->private_data; + kfree(dpcm); } -static int loopback_pcm_close(struct snd_pcm_substream *substream) +static int loopback_hw_free(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct loopback_pcm *dpcm = runtime->private_data; + struct loopback_cable *cable = dpcm->cable; - AP_INFOK("Close"); + mutex_lock(&dpcm->loopback->cable_lock); + cable->valid &= ~(1 << substream->stream); + mutex_unlock(&dpcm->loopback->cable_lock); - systimer_free(substream); return 0; } -static snd_pcm_uframes_t - loopback_pcm_pointer(struct snd_pcm_substream *substream) +static int rule_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) { - AP_INFOK("Pointer"); + struct loopback_pcm *dpcm = rule->private; + struct loopback_cable *cable = dpcm->cable; + struct snd_mask m; - return systimer_pointer(substream); + snd_mask_none(&m); + mutex_lock(&dpcm->loopback->cable_lock); + m.bits[0] = (u_int32_t)cable->hw.formats; + m.bits[1] = (u_int32_t)(cable->hw.formats >> 32); + mutex_unlock(&dpcm->loopback->cable_lock); + return snd_mask_refine(hw_param_mask(params, rule->var), &m); } -static int loopback_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) { - AP_INFOK("Trigger"); + struct loopback_pcm *dpcm = rule->private; + struct loopback_cable *cable = dpcm->cable; + struct snd_interval t; - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - return systimer_start(substream); - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - return systimer_stop(substream); + mutex_lock(&dpcm->loopback->cable_lock); + t.min = cable->hw.rate_min; + t.max = cable->hw.rate_max; + mutex_unlock(&dpcm->loopback->cable_lock); + t.openmin = t.openmax = 0; + t.integer = 0; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct loopback_pcm *dpcm = rule->private; + struct loopback_cable *cable = dpcm->cable; + struct snd_interval t; + + mutex_lock(&dpcm->loopback->cable_lock); + t.min = cable->hw.channels_min; + t.max = cable->hw.channels_max; + mutex_unlock(&dpcm->loopback->cable_lock); + t.openmin = t.openmax = 0; + t.integer = 0; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static void free_cable(struct snd_pcm_substream *substream) +{ + struct loopback_cable *cable; + + cable = loopback->cables[substream->pcm->device]; + if (!cable) + return; + if (cable->streams[!substream->stream]) { + /* other stream is still alive */ + spin_lock_irq(&cable->lock); + cable->streams[substream->stream] = NULL; + spin_unlock_irq(&cable->lock); + } else { + /* free the cable */ + loopback->cables[substream->pcm->device] = NULL; + kfree(cable); } - return -EINVAL; } -static int loopback_pcm_prepare(struct snd_pcm_substream *substream) +static int loopback_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct loopback_pcm *dpcm = runtime->private_data; + struct loopback_pcm *dpcm; + struct loopback_cable *cable = NULL; + int err = 0; + + mutex_lock(&loopback->cable_lock); + dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); + if (!dpcm) { + err = -ENOMEM; + goto unlock; + } + dpcm->loopback = loopback; + dpcm->substream = substream; + timer_setup(&dpcm->timer, loopback_timer_function, 0); + + cable = loopback->cables[substream->pcm->device]; + if (!cable) { + cable = kzalloc(sizeof(*cable), GFP_KERNEL); + if (!cable) { + err = -ENOMEM; + goto unlock; + } + spin_lock_init(&cable->lock); + cable->hw = loopback_pcm_hardware; + loopback->cables[substream->pcm->device] = cable; + } + dpcm->cable = cable; + + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + + /* use dynamic rules based on actual runtime->hw values */ + /* note that the default rules created in the PCM midlevel code */ + /* are cached -> they do not reflect the actual state */ + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + rule_format, dpcm, SNDRV_PCM_HW_PARAM_FORMAT, + -1); + if (err < 0) + goto unlock; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + rule_rate, dpcm, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + goto unlock; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + rule_channels, dpcm, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + goto unlock; + + runtime->private_data = dpcm; + runtime->private_free = loopback_runtime_free; + if (get_notify(dpcm)) + runtime->hw = loopback_pcm_hardware; + else + runtime->hw = cable->hw; + + spin_lock_irq(&cable->lock); + cable->streams[substream->stream] = dpcm; + spin_unlock_irq(&cable->lock); + +unlock: + if (err < 0) { + free_cable(substream); + kfree(dpcm); + } + mutex_unlock(&loopback->cable_lock); + return err; +} + +static int loopback_close(struct snd_pcm_substream *substream) +{ + struct loopback_pcm *dpcm = substream->runtime->private_data; + + loopback_timer_stop_sync(dpcm); + mutex_lock(&loopback->cable_lock); + free_cable(substream); + mutex_unlock(&loopback->cable_lock); + return 0; +} + +static const struct snd_pcm_ops loopbackap_pcm_ops = { + .open = loopback_open, + .close = loopback_close, + .hw_free = loopback_hw_free, + .prepare = loopback_prepare, + .trigger = loopback_trigger, + .pointer = loopback_pointer, +}; + +static int loopback_rate_shift_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 80000; + uinfo->value.integer.max = 120000; + uinfo->value.integer.step = 1; + return 0; +} + +static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + + mutex_lock(&loopback->cable_lock); + ucontrol->value.integer.value[0] = + loopback->setup[kcontrol->id.device].rate_shift; + mutex_unlock(&loopback->cable_lock); + return 0; +} + +static int loopback_rate_shift_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + + val = ucontrol->value.integer.value[0]; + if (val < 80000) + val = 80000; + if (val > 120000) + val = 120000; + mutex_lock(&loopback->cable_lock); + if (val != loopback->setup[kcontrol->id.device].rate_shift) { + loopback->setup[kcontrol->id.device].rate_shift = val; + change = 1; + } + mutex_unlock(&loopback->cable_lock); + return change; +} + +static int loopback_notify_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + + mutex_lock(&loopback->cable_lock); + ucontrol->value.integer.value[0] = + loopback->setup[kcontrol->id.device].notify; + mutex_unlock(&loopback->cable_lock); + return 0; +} + +static int loopback_notify_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change = 0; + val = ucontrol->value.integer.value[0] ? 1 : 0; + mutex_lock(&loopback->cable_lock); + if (val != loopback->setup[kcontrol->id.device].notify) { + loopback->setup[kcontrol->id.device].notify = val; + change = 1; + } + mutex_unlock(&loopback->cable_lock); + return change; +} + +static int loopback_active_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + struct loopback_cable *cable; + + unsigned int val = 0; + + mutex_lock(&loopback->cable_lock); + cable = loopback->cables[kcontrol->id.device]; + if (cable != NULL) { + unsigned int running = cable->running ^ cable->pause; + + val = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 1 : 0; + } + mutex_unlock(&loopback->cable_lock); + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int loopback_format_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST; + uinfo->value.integer.step = 1; + return 0; +} + +static int loopback_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + loopback->setup[kcontrol->id.device].format; + return 0; +} + +static int loopback_rate_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 192000; + uinfo->value.integer.step = 1; + return 0; +} - AP_INFOK("%s\n%s\n%d\n%d", substream->pcm->name, - substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "PLAYBACK" : - "CAPTURE", - runtime->rate, runtime->channels); +static int loopback_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + mutex_lock(&loopback->cable_lock); + ucontrol->value.integer.value[0] = + loopback->setup[kcontrol->id.device].rate; + mutex_unlock(&loopback->cable_lock); + return 0; +} - return systimer_prepare(substream); +static int loopback_channels_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 1; + uinfo->value.integer.max = 1024; + uinfo->value.integer.step = 1; + return 0; } -static struct snd_pcm_ops loopbackap_pcm_ops = { - .open = loopback_pcm_open, - .close = loopback_pcm_close, - .prepare = loopback_pcm_prepare, - .pointer = loopback_pcm_pointer, - .trigger = loopback_pcm_trigger, +static int loopback_channels_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct loopback *loopback = snd_kcontrol_chip(kcontrol); + + mutex_lock(&loopback->cable_lock); + ucontrol->value.integer.value[0] = + loopback->setup[kcontrol->id.device].channels; + mutex_unlock(&loopback->cable_lock); + return 0; +} + +static struct snd_kcontrol_new loopback_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Rate Shift 100000", + .info = loopback_rate_shift_info, + .get = loopback_rate_shift_get, + .put = loopback_rate_shift_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Notify", + .info = snd_ctl_boolean_mono_info, + .get = loopback_notify_get, + .put = loopback_notify_put, + }, +#define ACTIVE_IDX 2 + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Slave Active", + .info = snd_ctl_boolean_mono_info, + .get = loopback_active_get, + }, +#define FORMAT_IDX 3 + { .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Slave Format", + .info = loopback_format_info, + .get = loopback_format_get }, +#define RATE_IDX 4 + { .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Slave Rate", + .info = loopback_rate_info, + .get = loopback_rate_get }, +#define CHANNELS_IDX 5 + { .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Slave Channels", + .info = loopback_channels_info, + .get = loopback_channels_get } }; +static int loopback_mixer_new(struct loopback *loopback, int notify) +{ + struct snd_card *card = loopback->card; + struct snd_pcm *pcm; + struct snd_kcontrol *kctl; + struct loopback_setup *setup; + int dev, dev_count, idx, err; + + strcpy(card->mixername, "Loopback Mixer"); + dev_count = avirt_stream_count(SNDRV_PCM_STREAM_PLAYBACK); + for (dev = 0; dev < dev_count; dev++) { + pcm = loopback->pcm[dev]; + setup = &loopback->setup[dev]; + setup->notify = notify; + setup->rate_shift = NO_PITCH; + setup->format = SNDRV_PCM_FORMAT_S16_LE; + setup->rate = 48000; + setup->channels = 2; + for (idx = 0; idx < ARRAY_SIZE(loopback_controls); idx++) { + kctl = snd_ctl_new1(&loopback_controls[idx], loopback); + if (!kctl) + return -ENOMEM; + kctl->id.device = dev; + kctl->id.subdevice = 0; + switch (idx) { + case ACTIVE_IDX: + setup->active_id = kctl->id; + break; + case FORMAT_IDX: + setup->format_id = kctl->id; + break; + case RATE_IDX: + setup->rate_id = kctl->id; + break; + case CHANNELS_IDX: + setup->channels_id = kctl->id; + break; + default: + break; + } + err = snd_ctl_add(card, kctl); + if (err < 0) + return err; + } + } + return 0; +} + +static void print_dpcm_info(struct snd_info_buffer *buffer, + struct loopback_pcm *dpcm, const char *id) +{ + snd_iprintf(buffer, " %s\n", id); + if (dpcm == NULL) { + snd_iprintf(buffer, " inactive\n"); + return; + } + snd_iprintf(buffer, " buffer_size:\t%u\n", dpcm->pcm_buffer_size); + snd_iprintf(buffer, " buffer_pos:\t\t%u\n", dpcm->buf_pos); + snd_iprintf(buffer, " silent_size:\t%u\n", dpcm->silent_size); + snd_iprintf(buffer, " period_size:\t%u\n", dpcm->pcm_period_size); + snd_iprintf(buffer, " bytes_per_sec:\t%u\n", dpcm->pcm_bps); + snd_iprintf(buffer, " sample_align:\t%u\n", dpcm->pcm_salign); + snd_iprintf(buffer, " rate_shift:\t\t%u\n", dpcm->pcm_rate_shift); + snd_iprintf(buffer, " update_pending:\t%u\n", + dpcm->period_update_pending); + snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos); + snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac); + snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n", + dpcm->last_jiffies, jiffies); + snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires); +} + +static void print_substream_info(struct snd_info_buffer *buffer, + struct loopback *loopback, int device) +{ + struct loopback_cable *cable = loopback->cables[device]; + + snd_iprintf(buffer, "Cable device %i:\n", device); + if (cable == NULL) { + snd_iprintf(buffer, " inactive\n"); + return; + } + snd_iprintf(buffer, " valid: %u\n", cable->valid); + snd_iprintf(buffer, " running: %u\n", cable->running); + snd_iprintf(buffer, " pause: %u\n", cable->pause); + print_dpcm_info(buffer, cable->streams[0], "Playback"); + print_dpcm_info(buffer, cable->streams[1], "Capture"); +} + +static void print_cable_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct loopback *loopback = entry->private_data; + int device; + + mutex_lock(&loopback->cable_lock); + for (device = 0; device < MAX_STREAMS; device++) + print_substream_info(buffer, loopback, device); + mutex_unlock(&loopback->cable_lock); +} + +static int loopback_proc_new(struct loopback *loopback, int cidx) +{ + char name[32]; + struct snd_info_entry *entry; + int err; + + snprintf(name, sizeof(name), "cable#%d", cidx); + err = snd_card_proc_new(loopback->card, name, &entry); + if (err < 0) + return err; + + snd_info_set_text_ops(entry, loopback, print_cable_info); + return 0; +} + +int loopbackap_configure(struct snd_card *card, + struct config_group *avirt_stream_group, + unsigned int stream_count) +{ + int err; + struct list_head *entry; + + loopback = kzalloc(sizeof(struct loopback), GFP_KERNEL); + if (!loopback) + return -ENOMEM; + loopback->card = card; + mutex_init(&loopback->cable_lock); + + list_for_each (entry, &avirt_stream_group->cg_children) { + struct config_item *item = + container_of(entry, struct config_item, ci_entry); + struct avirt_stream *stream = + avirt_stream_from_config_item(item); + loopback->pcm[stream->device] = stream->pcm; + + AP_INFOK("stream name:%s device:%d channels:%d", stream->name, + stream->device, stream->channels); + } + + err = loopback_mixer_new(loopback, 1); + if (err < 0) + return err; + + loopback_proc_new(loopback, 0); + loopback_proc_new(loopback, 1); + + return 0; +} + /******************************************************************************* * Loopback Audio Path AVIRT registration ******************************************************************************/ -static struct snd_pcm_hardware loopbackap_hw = { - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .info = (SNDRV_PCM_INFO_INTERLEAVED // Channel interleaved audio - | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID), - /* - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, - .rate_min = 8000, - .rate_max = 48000, - */ - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 1, - .channels_max = 8, - //.buffer_bytes_max = 32768, - .periods_min = 1, - .periods_max = 8, -}; - static struct avirt_audiopath loopbackap_module = { + .uid = AP_UID, .name = "Loopback Audio Path", .version = { 0, 0, 1 }, - .value = 10, - .hw = &loopbackap_hw, + .hw = &loopback_pcm_hardware, .pcm_ops = &loopbackap_pcm_ops, - .configure = loopback_configure, + .configure = loopbackap_configure, }; -static int __init loopback_init(void) +static int __init alsa_card_loopback_init(void) { int err = 0; - AP_INFOK("init()"); - - err = avirt_register_audiopath(&loopbackap_module, &coreinfo); + err = avirt_audiopath_register(&loopbackap_module, &coreinfo); if ((err < 0) || (!coreinfo)) { - AP_ERRORK("%s: coreinfo is NULL!\n", __func__); + AP_ERRORK("coreinfo is NULL!"); return err; } - return err; + return 0; } -static void __exit loopback_exit(void) +static void __exit alsa_card_loopback_exit(void) { - AP_INFOK("loopback: exit()"); - - avirt_deregister_audiopath(&loopbackap_module); + avirt_audiopath_deregister(&loopbackap_module); } -module_init(loopback_init); -module_exit(loopback_exit); +module_init(alsa_card_loopback_init); +module_exit(alsa_card_loopback_exit) diff --git a/scripts/load.sh b/scripts/load.sh index 2cd472d..82f613a 100755 --- a/scripts/load.sh +++ b/scripts/load.sh @@ -4,7 +4,10 @@ insmod avirt_core.ko # Load the additional Audio Paths -#insmod dummy/avirt_dummyap.ko +insmod dummy/avirt_dummyap.ko insmod loopback/avirt_loopbackap.ko +# Run the test script +./scripts/test_configfs.sh + echo "Drivers Loaded!" diff --git a/scripts/test_configfs.sh b/scripts/test_configfs.sh index 1ae0fe5..baa5e58 100755 --- a/scripts/test_configfs.sh +++ b/scripts/test_configfs.sh @@ -4,14 +4,18 @@ mkdir -p /config && mount -t configfs none /config mkdir /config/avirt/streams/playback_media echo "2">/config/avirt/streams/playback_media/channels +echo "ap_loopback">/config/avirt/streams/playback_media/map mkdir /config/avirt/streams/playback_navigation echo "1">/config/avirt/streams/playback_navigation/channels +echo "ap_loopback">/config/avirt/streams/playback_navigation/map mkdir /config/avirt/streams/playback_emergency echo "1">/config/avirt/streams/playback_emergency/channels +echo "ap_loopback">/config/avirt/streams/playback_emergency/map mkdir /config/avirt/streams/capture_voice echo "1">/config/avirt/streams/capture_voice/channels +echo "ap_loopback">/config/avirt/streams/capture_voice/map echo "1">/config/avirt/streams/sealed diff --git a/scripts/unload.sh b/scripts/unload.sh index a011a2b..2c158d5 100755 --- a/scripts/unload.sh +++ b/scripts/unload.sh @@ -1,5 +1,8 @@ #!/bin/sh +rmdir /config/avirt/streams/playback_* +rmdir /config/avirt/streams/capture_* + rm_module() { lsmod |grep "^$1\>" && rmmod $1 || true } -- cgit 1.2.3-korg From cce1002dbeb69354cc9fe5076f13739d815c56ef Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 8 Oct 2018 17:25:46 +1100 Subject: Update VSC tasks, XDS conf Signed-off-by: Mark Farrugia --- .vscode/tasks.json | 134 +++++++++++++++++++++++++---------------------------- xds-project.conf | 4 +- 2 files changed, 64 insertions(+), 74 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index d8063e7..8a0a070 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,73 +1,63 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "type": "shell", - "presentation": { - "reveal": "always" - }, - "tasks": [ - { - "label": "Make Driver", - "command": "CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m CONFIG_AVIRT_LOOPBACKAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd)", - "group": "build", - "problemMatcher": [ - "$gcc" - ] - }, - { - "label": "Clean Driver", - "command": "CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m CONFIG_AVIRT_LOOPBACKAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) clean", - "group": "build", - "problemMatcher": [ - "$gcc" - ] - }, - { - "label": "Deploy Drivers", - "command": "sudo sh -c './unload.sh && ./loadDrivers.sh'", - "problemMatcher": [] - }, - { - "label": "Make and Deploy Drivers", - "command": "make && sudo sh -c './unload.sh && ./loadDrivers.sh'", - "problemMatcher": [] - }, - { - "label": "Unload new drivers", - "command": "sudo sh unload.sh", - "problemMatcher": [] - }, - { - "label": "Load new drivers", - "command": "sudo sh ./loadDrivers.sh", - "problemMatcher": [] - }, - { - "label": "Make Driver AGL", - "type": "shell", - "command": "./make-agl.sh bf869951", - "group": "build", - "problemMatcher": [ - "$gcc" - ] - }, - { - "label": "Clean Driver AGL", - "type": "shell", - "command": "./make-agl.sh bf869951 clean", - "group": "build", - "problemMatcher": [ - "$gcc" - ] - }, - { - "label": "Deploy Driver AGL", - "type": "shell", - "command": "rsync -av avirt_core.ko dummy/avirt_dummyap.ko root@192.168.1.198:~/", - "problemMatcher": [ - "$gcc" - ] - }, - ] -} \ No newline at end of file + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "type": "shell", + "presentation": { + "reveal": "always" + }, + "tasks": [ + { + "label": "Make Driver", + "command": "make all", + "group": "build", + "problemMatcher": ["$gcc"] + }, + { + "label": "Clean Driver", + "command": "make clean", + "group": "build", + "problemMatcher": ["$gcc"] + }, + { + "label": "Deploy Drivers", + "command": "sudo sh -c './scripts/unload.sh && ./scripts/load.sh'", + "problemMatcher": [] + }, + { + "label": "Make and Deploy Drivers", + "command": "make all && sudo sh -c './scripts/unload.sh && ./scripts/load.sh'", + "problemMatcher": [] + }, + { + "label": "Unload new drivers", + "command": "sudo sh ./scripts/unload.sh", + "problemMatcher": [] + }, + { + "label": "Load new drivers", + "command": "sudo sh ./scripts/load.sh", + "problemMatcher": [] + }, + { + "label": "Make Driver AGL", + "type": "shell", + "command": "./scripts/make-agl.sh 6864c558", + "group": "build", + "problemMatcher": ["$gcc"] + }, + { + "label": "Clean Driver AGL", + "type": "shell", + "command": "./scripts/make-agl.sh 6864c558 clean", + "group": "build", + "problemMatcher": ["$gcc"] + }, + { + "label": "Deploy Driver AGL", + "type": "shell", + "command": "rsync -av avirt_core.ko dummy/avirt_dummyap.ko loopback/avirt_loopbackap.ko root@192.168.1.193:/lib/modules/4.14.0-yocto-standard/extra", + "problemMatcher": ["$gcc"] + } + ] +} diff --git a/xds-project.conf b/xds-project.conf index 59a82f8..6296119 100644 --- a/xds-project.conf +++ b/xds-project.conf @@ -1,4 +1,4 @@ # XDS project settings export XDS_AGENT_URL=localhost:8800 -export XDS_PROJECT_ID=4ad79245-a1c3-11e8-82d3-0242ac110002 -export XDS_SDK_ID=bf869951 +export XDS_PROJECT_ID=1c520c49 +export XDS_SDK_ID=6864c558 -- cgit 1.2.3-korg From 386c29da3416d86b66430e590aaa5c51f2942477 Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Thu, 13 Sep 2018 16:13:48 -0700 Subject: Add docs folder for in depth breakdown of module. After discussion at AGL F2F, it was suggested to have simpler readmes that summarise the system/architecture/build process/etc. Starting to dotpoint out that process with these readmes. Signed-off-by: James O'Shannessy --- docs/1. Introduction.md | 0 docs/2. Building.md | 0 docs/3. Usage.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/1. Introduction.md create mode 100644 docs/2. Building.md create mode 100644 docs/3. Usage.md diff --git a/docs/1. Introduction.md b/docs/1. Introduction.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/2. Building.md b/docs/2. Building.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/3. Usage.md b/docs/3. Usage.md new file mode 100644 index 0000000..e69de29 -- cgit 1.2.3-korg From 5a2a915287e7cd12a347a03d37861310701a72fb Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Thu, 13 Sep 2018 16:34:25 -0700 Subject: Changes to readme files Dot points to introduction, and shifting the build instructions to docs/2.\ Building.md Signed-off-by: James O'Shannessy --- docs/1. Introduction.md | 18 ++++++++++++++++++ docs/2. Building.md | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/docs/1. Introduction.md b/docs/1. Introduction.md index e69de29..f2d088d 100644 --- a/docs/1. Introduction.md +++ b/docs/1. Introduction.md @@ -0,0 +1,18 @@ +Introduction +=================== + +## Problem +- Insecure +- Limited +- Not fully optimised for use case (ie. Routing Audio to DSP vs. Loopback) +- Not generic interface for highly configurable hardware +- + +## Solution + +## Added Features +- Dynamic Runtime Alsa Front end +- Adjustable Audio Path +- Mappable Audio Path +- Secure Audio Routing w/ SMACK +- \ No newline at end of file diff --git a/docs/2. Building.md b/docs/2. Building.md index e69de29..983db8d 100644 --- a/docs/2. Building.md +++ b/docs/2. Building.md @@ -0,0 +1,39 @@ +Building AVIRT +=================================== + +## Table of Contents +1. [Out of Tree](#out-of-tree) +2. [In Tree](#in-tree) + + + +## Out Of Tree + +The kernel modules can be built either in-tree, or out-of-tree. +To build both AVIRT and the dummy Audio Path out-of-tree, use the following command: + +```sh +$ CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) +``` + +To build both AVIRT and the dummy Audio Path out-of-tree for [AGL](http://docs.automotivelinux.org/) (`aarch64` currently supported), use the [XDS](http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-1/0_Abstract.html) build system together with the `make_agl.sh` script: + +```sh +$ ./make_agl.sh ${XDS_SDK_ID} +``` + + + +## In tree + + +To build in tree, use the [Fiberdyne Linux fork](https://github.com/fiberdyne/linux), which will automatically clone the AVIRT Driver and required AudioPath modules to the `drivers/staging` directory. You can then turn AVIRT Support on by setting to ``. The drivers can be found here: +``` +$ make menuconfig + +# Navigate to: Device Drivers--->Staging Drivers--->AVIRT Support +``` +Finally build the kernel with the configuration selected by making. +``` +$ make +``` \ No newline at end of file -- cgit 1.2.3-korg From 72499de21381625577160fac2e1e31af0d1f104d Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Thu, 13 Sep 2018 17:20:37 -0700 Subject: Updating build instructions Cleaning up build instructions for building AVIRT Signed-off-by: James O'Shannessy --- docs/2. Building.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/2. Building.md b/docs/2. Building.md index 983db8d..0016822 100644 --- a/docs/2. Building.md +++ b/docs/2. Building.md @@ -2,20 +2,34 @@ Building AVIRT =================================== ## Table of Contents -1. [Out of Tree](#out-of-tree) -2. [In Tree](#in-tree) +1. [Introduction](#intro) +2. [Out of Tree](#out-of-tree) +3. [In Tree](#in-tree) + + + +## Introduction +The AVIRT module and submodules can be built using a variety of methods. There are Visual Code build tasks for in and out of tree builds, make file support for CLI building (both in and out of tree), and xds build support. + ## Out Of Tree -The kernel modules can be built either in-tree, or out-of-tree. -To build both AVIRT and the dummy Audio Path out-of-tree, use the following command: +### Building with CLI +To build both AVIRT and the additional Audio Paths out-of-tree, use the following command: + +```sh +$ make +``` +or ```sh $ CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) ``` +The latter is executed internally with the make file. +### Building with XDS SDK To build both AVIRT and the dummy Audio Path out-of-tree for [AGL](http://docs.automotivelinux.org/) (`aarch64` currently supported), use the [XDS](http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-1/0_Abstract.html) build system together with the `make_agl.sh` script: ```sh @@ -26,6 +40,7 @@ $ ./make_agl.sh ${XDS_SDK_ID} ## In tree +The kernal modules can be built in tree, dependent on the avirt repository being located in drivers/staging - If using your own fork of the linux kernel, you will need to update the staging Makefiles and Kconfigs accordingly. To build in tree, use the [Fiberdyne Linux fork](https://github.com/fiberdyne/linux), which will automatically clone the AVIRT Driver and required AudioPath modules to the `drivers/staging` directory. You can then turn AVIRT Support on by setting to ``. The drivers can be found here: ``` @@ -33,6 +48,7 @@ $ make menuconfig # Navigate to: Device Drivers--->Staging Drivers--->AVIRT Support ``` + Finally build the kernel with the configuration selected by making. ``` $ make -- cgit 1.2.3-korg From 819bbaa22430988a88999e283ceb02368ad0f7b3 Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Mon, 8 Oct 2018 12:22:32 +1100 Subject: Committing usage document Updated usage document with loading/unloading, configuration information Signed-off-by: James O'Shannessy --- docs/3. Usage.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/docs/3. Usage.md b/docs/3. Usage.md index e69de29..b5ec136 100644 --- a/docs/3. Usage.md +++ b/docs/3. Usage.md @@ -0,0 +1,70 @@ +Using AVIRT +=================================== + +## Introduction + +## Table of Contents +1. [Load/Unload AVIRT](#un-load-avirt) +2. [Configuring AVIRT](#configuring-avirt) + + + +## Loading and Unloading AVIRT + +To run, we must load the kernel modules using the `loadDrivers.sh` script, which contains sample module parameters to AVIRT: +```sh +$ ./loadDrivers.sh +``` +To unload the drivers use: +```sh +$ ./unload.sh +``` + + + +## Configuring AVIRT + +AVIRT is configured using configfs. +### Note: +A sample script for the following is supplied inside the ```scripts``` folder. + +First, verify the kernel supports the use of configfs. +```sh +fgrep configfs /proc/filesystems +``` + +the expected output is: +``` +nodev configfs +``` + +Once confirmed, we can mount the configfs at the conventional point: +```sh +#Check configfs is mounted on the system +mkdir -p /config +mount -t configfs none /config +``` + +Finally, we can configure AVIRT: + +```sh +# Set up each of the cards channels +mkdir /config/avirt/streams/playback_media +echo "2">/config/avirt/streams/playback_media/channels + +mkdir /config/avirt/streams/playback_navigation +echo "1">/config/avirt/streams/playback_navigation/channels + +mkdir /config/avirt/streams/playback_emergency +echo "1">/config/avirt/streams/playback_emergency/channels + +mkdir /config/avirt/streams/capture_voice +echo "1">/config/avirt/streams/capture_voice/channels + +# Finally, seal the card, and initiate configuration +echo "1">/config/avirt/streams/sealed +``` + +### Note: +A simplified helper library, libavirt, is available [here](https://github.com/fiberdyne/libavirt). +Please refer to the readme in libavirt for further details. -- cgit 1.2.3-korg From a3e9a9eb2b927ef05161f1c2bf5feb62db84c5b3 Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Mon, 8 Oct 2018 12:26:39 +1100 Subject: Added load/unload documentation. Updated documentation to use load/unload Signed-off-by: James O'Shannessy --- docs/3. Usage.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/3. Usage.md b/docs/3. Usage.md index b5ec136..7eb46d3 100644 --- a/docs/3. Usage.md +++ b/docs/3. Usage.md @@ -11,6 +11,16 @@ Using AVIRT ## Loading and Unloading AVIRT +As root, load the required ```avirt_core.ko``` and subsequent audio path. +As an example, the Dummy Audiopath is being loaded here. + +```sh +insmod avirt_core.ko +insmod dummy/dummy_audiopath.ko +``` + +### Note: +The loading and unloading of drivers can be performed using the helper scripts. To run, we must load the kernel modules using the `loadDrivers.sh` script, which contains sample module parameters to AVIRT: ```sh $ ./loadDrivers.sh -- cgit 1.2.3-korg From 83ab0cdf84def71eebdebf0c813cebfce8f99d06 Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Thu, 13 Sep 2018 16:34:25 -0700 Subject: Changes to readme files Dot points to introduction, and shifting the build instructions to docs/2.\ Building.md Signed-off-by: James O'Shannessy --- docs/2. Building.md | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/docs/2. Building.md b/docs/2. Building.md index 0016822..983db8d 100644 --- a/docs/2. Building.md +++ b/docs/2. Building.md @@ -2,34 +2,20 @@ Building AVIRT =================================== ## Table of Contents -1. [Introduction](#intro) -2. [Out of Tree](#out-of-tree) -3. [In Tree](#in-tree) - - - -## Introduction -The AVIRT module and submodules can be built using a variety of methods. There are Visual Code build tasks for in and out of tree builds, make file support for CLI building (both in and out of tree), and xds build support. - +1. [Out of Tree](#out-of-tree) +2. [In Tree](#in-tree) ## Out Of Tree -### Building with CLI -To build both AVIRT and the additional Audio Paths out-of-tree, use the following command: - -```sh -$ make -``` +The kernel modules can be built either in-tree, or out-of-tree. +To build both AVIRT and the dummy Audio Path out-of-tree, use the following command: -or ```sh $ CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) ``` -The latter is executed internally with the make file. -### Building with XDS SDK To build both AVIRT and the dummy Audio Path out-of-tree for [AGL](http://docs.automotivelinux.org/) (`aarch64` currently supported), use the [XDS](http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-1/0_Abstract.html) build system together with the `make_agl.sh` script: ```sh @@ -40,7 +26,6 @@ $ ./make_agl.sh ${XDS_SDK_ID} ## In tree -The kernal modules can be built in tree, dependent on the avirt repository being located in drivers/staging - If using your own fork of the linux kernel, you will need to update the staging Makefiles and Kconfigs accordingly. To build in tree, use the [Fiberdyne Linux fork](https://github.com/fiberdyne/linux), which will automatically clone the AVIRT Driver and required AudioPath modules to the `drivers/staging` directory. You can then turn AVIRT Support on by setting to ``. The drivers can be found here: ``` @@ -48,7 +33,6 @@ $ make menuconfig # Navigate to: Device Drivers--->Staging Drivers--->AVIRT Support ``` - Finally build the kernel with the configuration selected by making. ``` $ make -- cgit 1.2.3-korg From 7e4db2ff6865d723a2a6233a9542730876d06260 Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Thu, 13 Sep 2018 17:20:37 -0700 Subject: Updating build instructions Cleaning up build instructions for building AVIRT Signed-off-by: James O'Shannessy --- README.md | 89 ------------------------------------------------- docs/1. Introduction.md | 21 +++++------- docs/2. Building.md | 24 ++++++++++--- docs/4. 4A Integration | 17 ++++++++++ 4 files changed, 45 insertions(+), 106 deletions(-) create mode 100644 docs/4. 4A Integration diff --git a/README.md b/README.md index d6564fa..3ed571f 100644 --- a/README.md +++ b/README.md @@ -11,95 +11,6 @@ A sample dummy Audio Path is provided as an example to show how a low-level audi Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL reference platform, and a default loopback softmixer is in development. -## Building - -### Out Of Tree - -The kernel modules can be built either in-tree, or out-of-tree. -To build both AVIRT and the dummy Audio Path out-of-tree, use the following command: - -```sh -$ make all -``` - -If building for [AGL](http://docs.automotivelinux.org/), use the [XDS](http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-1/0_Abstract.html) build system together with the `make_agl.sh` script: - -```sh -$ ./make_agl.sh ${XDS_SDK_ID} -``` - -### In tree - -To build in tree, use the [Fiberdyne Linux fork](https://github.com/fiberdyne/linux), which will automatically clone the AVIRT Driver and required AudioPath modules to the `drivers/staging` directory. You can then turn AVIRT Support on by setting to ``. The drivers can be found here: - -``` -$ make menuconfig - -# Navigate to: Device Drivers--->Staging Drivers--->AVIRT Support -``` - -Finally build the kernel with the configuration selected by making. - -``` -$ make -``` - -## Running - -Firstly, install the resulting kernel modules to the appropriate modules -directory, and run`depmod`. For example, in AGL: - -```sh -cp avirt_core.ko avirt_dummyap.ko /lib/modules/4.14.0-yocto-standard/ -depmod -``` - -Now we can load the modules: - -```sh -modprobe avirt_core -modprobe avirt_dummyap -``` - -We must now configure AVIRT. We can do this with the test script: - -```sh -source scripts/test_configfs.sh -``` - -We can see the newly created streams by using the `aplay` utility. For example: - -```sh -aplay -l -... -card 2: avirt [avirt], device 0: multimedia [multimedia] - Subdevices: 1/1 - Subdevice #0: subdevice #0 -card 2: avirt [avirt], device 1: navigation [navigation] - Subdevices: 1/1 - Subdevice #0: subdevice #0 -card 2: avirt [avirt], device 2: emergency [emergency] - Subdevices: 1/1 - Subdevice #0: subdevice #0 -``` - -## 4A Integration - -AVIRT has been integrated with [4a-softmixer](https://github.com/iotbzh/4a-softmixer) -and [4a-hal-generic](https://github.com/iotbzh/4a-hal-generic) to provide a smooth -transition from the existing aloop implementation to the future AVIRT loopback implementation. - -### User-space Library - -The user-space library [libavirt](https://github.com/fiberdyne/libavirt) can be used to configure AVIRT from within a given AGL 4A binding. - -### Hardmixer - -A new 4A mixer binding has been developed to demonstrate the capabilities of the -Fiberdyne DSP mixer operating on the HiFi2 core on-board the Renesas R-Car M3 M3ULCB -AGL reference platform. This is called the 'hardmixer', and is a faster, lower -latency alternative to the softmixer. - ## TODO - Currently, playback only - implementing capture is WIP. diff --git a/docs/1. Introduction.md b/docs/1. Introduction.md index f2d088d..29cac71 100644 --- a/docs/1. Introduction.md +++ b/docs/1. Introduction.md @@ -1,18 +1,13 @@ Introduction =================== -## Problem -- Insecure -- Limited -- Not fully optimised for use case (ie. Routing Audio to DSP vs. Loopback) -- Not generic interface for highly configurable hardware -- +The ALSA Virtual Sound Driver (AVIRT) aims to provide a Linux kernel solution to the issue of audio routing in kernel-space, as well as security per-stream, and dynamic configuration of streams at the kernel level. -## Solution +A top-level abstract dynamic audio driver is presented to the user-space via an ALSA middle-layer card. From there, respective low-level "real" audio drivers can subscribe to it as an "Audio Path". -## Added Features -- Dynamic Runtime Alsa Front end -- Adjustable Audio Path -- Mappable Audio Path -- Secure Audio Routing w/ SMACK -- \ No newline at end of file +The top-level driver is configured (currently) using module parameters, as is the norm for sound drivers in the Linux tree, however this will utilise a configfs configuration implementation in future. + +A sample dummy Audio Path is provided as an example to show how a low-level audio driver would subscribe to AVIRT, and accept audio routing for playback. + +Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL +reference platform, and a default loopback softmixer is in development. \ No newline at end of file diff --git a/docs/2. Building.md b/docs/2. Building.md index 983db8d..0016822 100644 --- a/docs/2. Building.md +++ b/docs/2. Building.md @@ -2,20 +2,34 @@ Building AVIRT =================================== ## Table of Contents -1. [Out of Tree](#out-of-tree) -2. [In Tree](#in-tree) +1. [Introduction](#intro) +2. [Out of Tree](#out-of-tree) +3. [In Tree](#in-tree) + + + +## Introduction +The AVIRT module and submodules can be built using a variety of methods. There are Visual Code build tasks for in and out of tree builds, make file support for CLI building (both in and out of tree), and xds build support. + ## Out Of Tree -The kernel modules can be built either in-tree, or out-of-tree. -To build both AVIRT and the dummy Audio Path out-of-tree, use the following command: +### Building with CLI +To build both AVIRT and the additional Audio Paths out-of-tree, use the following command: + +```sh +$ make +``` +or ```sh $ CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) ``` +The latter is executed internally with the make file. +### Building with XDS SDK To build both AVIRT and the dummy Audio Path out-of-tree for [AGL](http://docs.automotivelinux.org/) (`aarch64` currently supported), use the [XDS](http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-1/0_Abstract.html) build system together with the `make_agl.sh` script: ```sh @@ -26,6 +40,7 @@ $ ./make_agl.sh ${XDS_SDK_ID} ## In tree +The kernal modules can be built in tree, dependent on the avirt repository being located in drivers/staging - If using your own fork of the linux kernel, you will need to update the staging Makefiles and Kconfigs accordingly. To build in tree, use the [Fiberdyne Linux fork](https://github.com/fiberdyne/linux), which will automatically clone the AVIRT Driver and required AudioPath modules to the `drivers/staging` directory. You can then turn AVIRT Support on by setting to ``. The drivers can be found here: ``` @@ -33,6 +48,7 @@ $ make menuconfig # Navigate to: Device Drivers--->Staging Drivers--->AVIRT Support ``` + Finally build the kernel with the configuration selected by making. ``` $ make diff --git a/docs/4. 4A Integration b/docs/4. 4A Integration new file mode 100644 index 0000000..7c9b980 --- /dev/null +++ b/docs/4. 4A Integration @@ -0,0 +1,17 @@ +4A Integration +=================== + +AVIRT has been integrated with [4a-softmixer](https://github.com/iotbzh/4a-softmixer) +and [4a-hal-generic](https://github.com/iotbzh/4a-hal-generic) to provide a smooth +transition from the existing aloop implementation to the future AVIRT loopback implementation. + +### User-space Library + +The user-space library [libavirt](https://github.com/fiberdyne/libavirt) can be used to configure AVIRT from within a given AGL 4A binding. + +### Hardmixer + +A new 4A mixer binding has been developed to demonstrate the capabilities of the +Fiberdyne DSP mixer operating on the HiFi2 core on-board the Renesas R-Car M3 M3ULCB +AGL reference platform. This is called the 'hardmixer', and is a faster, lower +latency alternative to the softmixer. \ No newline at end of file -- cgit 1.2.3-korg From 13610114b7f283718a678c42e0421c3c51de636e Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Mon, 8 Oct 2018 15:10:46 +1100 Subject: Adding table of for contents for README documentation Signed-off-by: James O'Shannessy --- README.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/README.md b/README.md index 3ed571f..fe4a883 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,6 @@ ## Alsa Virtual Sound Driver -The ALSA Virtual Sound Driver (AVIRT) aims to provide a Linux kernel solution to the issue of audio routing in kernel-space, as well as security per-stream, and dynamic configuration of streams at the kernel level. - -A top-level abstract dynamic audio driver is presented to the user-space via an ALSA middle-layer card. From there, respective low-level "real" audio drivers can subscribe to it as an "Audio Path". - -The top-level driver is configured (currently) using module parameters, as is the norm for sound drivers in the Linux tree, however this will utilise a configfs configuration implementation in future. - -A sample dummy Audio Path is provided as an example to show how a low-level audio driver would subscribe to AVIRT, and accept audio routing for playback. - -Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL -reference platform, and a default loopback softmixer is in development. +[1. Introduction](docs/1. Introduction.md) ## TODO -- cgit 1.2.3-korg From 056c3754bc7be349de1a41ac6d4819f3d032bd0c Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Mon, 8 Oct 2018 15:19:34 +1100 Subject: Fixing table of contents in README documentation Restructure docs file tree Signed-off-by: James O'Shannessy --- README.md | 5 ++- docs/1. Introduction.md | 13 -------- docs/1.Introduction.md | 13 ++++++++ docs/2. Building.md | 55 --------------------------------- docs/2.Building.md | 56 +++++++++++++++++++++++++++++++++ docs/3. Usage.md | 80 ------------------------------------------------ docs/3.Usage.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ docs/4. 4A Integration | 17 ---------- docs/4.4A-Integration.md | 17 ++++++++++ 9 files changed, 170 insertions(+), 166 deletions(-) delete mode 100644 docs/1. Introduction.md create mode 100644 docs/1.Introduction.md delete mode 100644 docs/2. Building.md create mode 100644 docs/2.Building.md delete mode 100644 docs/3. Usage.md create mode 100644 docs/3.Usage.md delete mode 100644 docs/4. 4A Integration create mode 100644 docs/4.4A-Integration.md diff --git a/README.md b/README.md index fe4a883..f19e7d5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ ## Alsa Virtual Sound Driver -[1. Introduction](docs/1. Introduction.md) +[1. Introduction](docs/1.Introduction.md) +[2. Building](docs/2.Building.md) +[3. Usage](docs/3.Usage.md) +[4. 4A Integration](docs/4.4A-Integration.md) ## TODO diff --git a/docs/1. Introduction.md b/docs/1. Introduction.md deleted file mode 100644 index 29cac71..0000000 --- a/docs/1. Introduction.md +++ /dev/null @@ -1,13 +0,0 @@ -Introduction -=================== - -The ALSA Virtual Sound Driver (AVIRT) aims to provide a Linux kernel solution to the issue of audio routing in kernel-space, as well as security per-stream, and dynamic configuration of streams at the kernel level. - -A top-level abstract dynamic audio driver is presented to the user-space via an ALSA middle-layer card. From there, respective low-level "real" audio drivers can subscribe to it as an "Audio Path". - -The top-level driver is configured (currently) using module parameters, as is the norm for sound drivers in the Linux tree, however this will utilise a configfs configuration implementation in future. - -A sample dummy Audio Path is provided as an example to show how a low-level audio driver would subscribe to AVIRT, and accept audio routing for playback. - -Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL -reference platform, and a default loopback softmixer is in development. \ No newline at end of file diff --git a/docs/1.Introduction.md b/docs/1.Introduction.md new file mode 100644 index 0000000..29cac71 --- /dev/null +++ b/docs/1.Introduction.md @@ -0,0 +1,13 @@ +Introduction +=================== + +The ALSA Virtual Sound Driver (AVIRT) aims to provide a Linux kernel solution to the issue of audio routing in kernel-space, as well as security per-stream, and dynamic configuration of streams at the kernel level. + +A top-level abstract dynamic audio driver is presented to the user-space via an ALSA middle-layer card. From there, respective low-level "real" audio drivers can subscribe to it as an "Audio Path". + +The top-level driver is configured (currently) using module parameters, as is the norm for sound drivers in the Linux tree, however this will utilise a configfs configuration implementation in future. + +A sample dummy Audio Path is provided as an example to show how a low-level audio driver would subscribe to AVIRT, and accept audio routing for playback. + +Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL +reference platform, and a default loopback softmixer is in development. \ No newline at end of file diff --git a/docs/2. Building.md b/docs/2. Building.md deleted file mode 100644 index 0016822..0000000 --- a/docs/2. Building.md +++ /dev/null @@ -1,55 +0,0 @@ -Building AVIRT -=================================== - -## Table of Contents -1. [Introduction](#intro) -2. [Out of Tree](#out-of-tree) -3. [In Tree](#in-tree) - - - -## Introduction -The AVIRT module and submodules can be built using a variety of methods. There are Visual Code build tasks for in and out of tree builds, make file support for CLI building (both in and out of tree), and xds build support. - - - - -## Out Of Tree - -### Building with CLI -To build both AVIRT and the additional Audio Paths out-of-tree, use the following command: - -```sh -$ make -``` - -or -```sh -$ CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) -``` -The latter is executed internally with the make file. - -### Building with XDS SDK -To build both AVIRT and the dummy Audio Path out-of-tree for [AGL](http://docs.automotivelinux.org/) (`aarch64` currently supported), use the [XDS](http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-1/0_Abstract.html) build system together with the `make_agl.sh` script: - -```sh -$ ./make_agl.sh ${XDS_SDK_ID} -``` - - - -## In tree - -The kernal modules can be built in tree, dependent on the avirt repository being located in drivers/staging - If using your own fork of the linux kernel, you will need to update the staging Makefiles and Kconfigs accordingly. - -To build in tree, use the [Fiberdyne Linux fork](https://github.com/fiberdyne/linux), which will automatically clone the AVIRT Driver and required AudioPath modules to the `drivers/staging` directory. You can then turn AVIRT Support on by setting to ``. The drivers can be found here: -``` -$ make menuconfig - -# Navigate to: Device Drivers--->Staging Drivers--->AVIRT Support -``` - -Finally build the kernel with the configuration selected by making. -``` -$ make -``` \ No newline at end of file diff --git a/docs/2.Building.md b/docs/2.Building.md new file mode 100644 index 0000000..237d834 --- /dev/null +++ b/docs/2.Building.md @@ -0,0 +1,56 @@ +Building AVIRT +=================================== + +## Table of Contents +1. [Introduction](#intro) +2. [Out of Tree](#out-of-tree) +3. [In Tree](#in-tree) + + + +## Introduction +The AVIRT module and submodules can be built using a variety of methods. There are Visual Code build tasks for in and out of tree builds, make file support for CLI building (both in and out of tree), and xds build support. + + + + +## Out Of Tree + +### Building with CLI +To build both AVIRT and the additional Audio Paths out-of-tree, use the following command: + +```sh +$ make +``` + +or +```sh +$ CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) +``` +The latter is executed internally with the make file. + +### Building with XDS SDK +To build both AVIRT and the dummy Audio Path out-of-tree for [AGL](http://docs.automotivelinux.org/) (`aarch64` currently supported), use the [XDS](http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-1/0_Abstract.html) build system together with the `make_agl.sh` script: + +```sh +$ ./make_agl.sh ${XDS_SDK_ID} +``` + + + +## In tree + +The kernal modules can be built in tree, dependent on the avirt repository being located in drivers/staging - If using your own fork of the linux kernel, you will need to update the staging Makefiles and Kconfigs accordingly. + +To build in tree, use the [Fiberdyne Linux fork](https://github.com/fiberdyne/linux), which will automatically clone the AVIRT Driver and required AudioPath modules to the `drivers/staging` directory. You can then turn AVIRT Support on by setting to ``. The drivers can be found here: + +``` +$ make menuconfig + +# Navigate to: Device Drivers--->Staging Drivers--->AVIRT Support +``` + +Finally build the kernel with the configuration selected by making. +``` +$ make +``` \ No newline at end of file diff --git a/docs/3. Usage.md b/docs/3. Usage.md deleted file mode 100644 index 7eb46d3..0000000 --- a/docs/3. Usage.md +++ /dev/null @@ -1,80 +0,0 @@ -Using AVIRT -=================================== - -## Introduction - -## Table of Contents -1. [Load/Unload AVIRT](#un-load-avirt) -2. [Configuring AVIRT](#configuring-avirt) - - - -## Loading and Unloading AVIRT - -As root, load the required ```avirt_core.ko``` and subsequent audio path. -As an example, the Dummy Audiopath is being loaded here. - -```sh -insmod avirt_core.ko -insmod dummy/dummy_audiopath.ko -``` - -### Note: -The loading and unloading of drivers can be performed using the helper scripts. -To run, we must load the kernel modules using the `loadDrivers.sh` script, which contains sample module parameters to AVIRT: -```sh -$ ./loadDrivers.sh -``` -To unload the drivers use: -```sh -$ ./unload.sh -``` - - - -## Configuring AVIRT - -AVIRT is configured using configfs. -### Note: -A sample script for the following is supplied inside the ```scripts``` folder. - -First, verify the kernel supports the use of configfs. -```sh -fgrep configfs /proc/filesystems -``` - -the expected output is: -``` -nodev configfs -``` - -Once confirmed, we can mount the configfs at the conventional point: -```sh -#Check configfs is mounted on the system -mkdir -p /config -mount -t configfs none /config -``` - -Finally, we can configure AVIRT: - -```sh -# Set up each of the cards channels -mkdir /config/avirt/streams/playback_media -echo "2">/config/avirt/streams/playback_media/channels - -mkdir /config/avirt/streams/playback_navigation -echo "1">/config/avirt/streams/playback_navigation/channels - -mkdir /config/avirt/streams/playback_emergency -echo "1">/config/avirt/streams/playback_emergency/channels - -mkdir /config/avirt/streams/capture_voice -echo "1">/config/avirt/streams/capture_voice/channels - -# Finally, seal the card, and initiate configuration -echo "1">/config/avirt/streams/sealed -``` - -### Note: -A simplified helper library, libavirt, is available [here](https://github.com/fiberdyne/libavirt). -Please refer to the readme in libavirt for further details. diff --git a/docs/3.Usage.md b/docs/3.Usage.md new file mode 100644 index 0000000..7eb46d3 --- /dev/null +++ b/docs/3.Usage.md @@ -0,0 +1,80 @@ +Using AVIRT +=================================== + +## Introduction + +## Table of Contents +1. [Load/Unload AVIRT](#un-load-avirt) +2. [Configuring AVIRT](#configuring-avirt) + + + +## Loading and Unloading AVIRT + +As root, load the required ```avirt_core.ko``` and subsequent audio path. +As an example, the Dummy Audiopath is being loaded here. + +```sh +insmod avirt_core.ko +insmod dummy/dummy_audiopath.ko +``` + +### Note: +The loading and unloading of drivers can be performed using the helper scripts. +To run, we must load the kernel modules using the `loadDrivers.sh` script, which contains sample module parameters to AVIRT: +```sh +$ ./loadDrivers.sh +``` +To unload the drivers use: +```sh +$ ./unload.sh +``` + + + +## Configuring AVIRT + +AVIRT is configured using configfs. +### Note: +A sample script for the following is supplied inside the ```scripts``` folder. + +First, verify the kernel supports the use of configfs. +```sh +fgrep configfs /proc/filesystems +``` + +the expected output is: +``` +nodev configfs +``` + +Once confirmed, we can mount the configfs at the conventional point: +```sh +#Check configfs is mounted on the system +mkdir -p /config +mount -t configfs none /config +``` + +Finally, we can configure AVIRT: + +```sh +# Set up each of the cards channels +mkdir /config/avirt/streams/playback_media +echo "2">/config/avirt/streams/playback_media/channels + +mkdir /config/avirt/streams/playback_navigation +echo "1">/config/avirt/streams/playback_navigation/channels + +mkdir /config/avirt/streams/playback_emergency +echo "1">/config/avirt/streams/playback_emergency/channels + +mkdir /config/avirt/streams/capture_voice +echo "1">/config/avirt/streams/capture_voice/channels + +# Finally, seal the card, and initiate configuration +echo "1">/config/avirt/streams/sealed +``` + +### Note: +A simplified helper library, libavirt, is available [here](https://github.com/fiberdyne/libavirt). +Please refer to the readme in libavirt for further details. diff --git a/docs/4. 4A Integration b/docs/4. 4A Integration deleted file mode 100644 index 7c9b980..0000000 --- a/docs/4. 4A Integration +++ /dev/null @@ -1,17 +0,0 @@ -4A Integration -=================== - -AVIRT has been integrated with [4a-softmixer](https://github.com/iotbzh/4a-softmixer) -and [4a-hal-generic](https://github.com/iotbzh/4a-hal-generic) to provide a smooth -transition from the existing aloop implementation to the future AVIRT loopback implementation. - -### User-space Library - -The user-space library [libavirt](https://github.com/fiberdyne/libavirt) can be used to configure AVIRT from within a given AGL 4A binding. - -### Hardmixer - -A new 4A mixer binding has been developed to demonstrate the capabilities of the -Fiberdyne DSP mixer operating on the HiFi2 core on-board the Renesas R-Car M3 M3ULCB -AGL reference platform. This is called the 'hardmixer', and is a faster, lower -latency alternative to the softmixer. \ No newline at end of file diff --git a/docs/4.4A-Integration.md b/docs/4.4A-Integration.md new file mode 100644 index 0000000..7c9b980 --- /dev/null +++ b/docs/4.4A-Integration.md @@ -0,0 +1,17 @@ +4A Integration +=================== + +AVIRT has been integrated with [4a-softmixer](https://github.com/iotbzh/4a-softmixer) +and [4a-hal-generic](https://github.com/iotbzh/4a-hal-generic) to provide a smooth +transition from the existing aloop implementation to the future AVIRT loopback implementation. + +### User-space Library + +The user-space library [libavirt](https://github.com/fiberdyne/libavirt) can be used to configure AVIRT from within a given AGL 4A binding. + +### Hardmixer + +A new 4A mixer binding has been developed to demonstrate the capabilities of the +Fiberdyne DSP mixer operating on the HiFi2 core on-board the Renesas R-Car M3 M3ULCB +AGL reference platform. This is called the 'hardmixer', and is a faster, lower +latency alternative to the softmixer. \ No newline at end of file -- cgit 1.2.3-korg From 94eb09750343f0adc8f4bf56eb4ee05ce9def8f1 Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Mon, 8 Oct 2018 16:03:12 +1100 Subject: Updating Introduction doc Signed-off-by: James O'Shannessy --- README.md | 2 +- docs/1.Introduction.md | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f19e7d5..dc72ff1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Alsa Virtual Sound Driver -[1. Introduction](docs/1.Introduction.md) +1. [Introduction](docs/1.Introduction.md) [2. Building](docs/2.Building.md) [3. Usage](docs/3.Usage.md) [4. 4A Integration](docs/4.4A-Integration.md) diff --git a/docs/1.Introduction.md b/docs/1.Introduction.md index 29cac71..25b3ea8 100644 --- a/docs/1.Introduction.md +++ b/docs/1.Introduction.md @@ -1,13 +1,27 @@ Introduction =================== +## Problem +The existing solution contains the following drawbacks: + 1. Non-generic interface for configurable hardware cases + - Unable to configure streams mapped to outputs + 2. Not fully optimized for certain use cases (ie. Routing Audio to DSP vs. aloop) + - Routing audio to hardware to reduce overhead + 3. Inability to secure on all audio streams + - Substreams cannot have SMACK labels applied, inherently leaving different audio streams insecure, or a single label applied to all. + +## AVIRTs Solution The ALSA Virtual Sound Driver (AVIRT) aims to provide a Linux kernel solution to the issue of audio routing in kernel-space, as well as security per-stream, and dynamic configuration of streams at the kernel level. A top-level abstract dynamic audio driver is presented to the user-space via an ALSA middle-layer card. From there, respective low-level "real" audio drivers can subscribe to it as an "Audio Path". -The top-level driver is configured (currently) using module parameters, as is the norm for sound drivers in the Linux tree, however this will utilise a configfs configuration implementation in future. +The top-level driver is configured (currently) using by utilising configfs. + +AVIRT attempts to address the aforementioned problems by: + 1. Providing a standard interface through configfs to configure a variable number of 'streams' which can be mapped to audio paths. + 2. Optimize the audio routing by connecting directly with the desired audio path. This can include routing audio through directly to hardware, or to an implemented loopback interface. + 3. Each 'stream' is separated to its own device, therefore security can be applied to each 'stream'. This means that each of the audio roles can be separately secured from each other. A sample dummy Audio Path is provided as an example to show how a low-level audio driver would subscribe to AVIRT, and accept audio routing for playback. -Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL -reference platform, and a default loopback softmixer is in development. \ No newline at end of file +Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL reference platform, and a default loopback softmixer is in development. \ No newline at end of file -- cgit 1.2.3-korg From c6c2c324c13fec5a3e1d52059454e4d8b17b1e28 Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Mon, 8 Oct 2018 15:20:38 +1100 Subject: Update README.md Fix table of contents Markdown links Fixing whitespace problems in Markdown docs Fix alignment of table of contents Signed-off-by: James O'Shannessy --- README.md | 9 +++++---- docs/1.Introduction.md | 18 +++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index dc72ff1..9c70cfb 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ ## Alsa Virtual Sound Driver 1. [Introduction](docs/1.Introduction.md) -[2. Building](docs/2.Building.md) -[3. Usage](docs/3.Usage.md) -[4. 4A Integration](docs/4.4A-Integration.md) +2. [Building](docs/2.Building.md) +3. [Usage](docs/3.Usage.md) +4. [4A Integration](docs/4.4A-Integration.md) + ## TODO - Currently, playback only - implementing capture is WIP. -- Create a loopback Audio Path for use with AVIRT, to demonstrate standard AGL soft-mixing capabilities. + diff --git a/docs/1.Introduction.md b/docs/1.Introduction.md index 25b3ea8..7bf3ebe 100644 --- a/docs/1.Introduction.md +++ b/docs/1.Introduction.md @@ -3,12 +3,12 @@ Introduction ## Problem The existing solution contains the following drawbacks: - 1. Non-generic interface for configurable hardware cases - - Unable to configure streams mapped to outputs - 2. Not fully optimized for certain use cases (ie. Routing Audio to DSP vs. aloop) - - Routing audio to hardware to reduce overhead - 3. Inability to secure on all audio streams - - Substreams cannot have SMACK labels applied, inherently leaving different audio streams insecure, or a single label applied to all. +1. Non-generic interface for configurable hardware cases + - Unable to configure streams mapped to outputs +2. Not fully optimized for certain use cases (ie. Routing Audio to DSP vs. aloop) + - Routing audio to hardware to reduce overhead +3. Inability to secure on all audio streams + - Substreams cannot have SMACK labels applied, inherently leaving different audio streams insecure, or a single label applied to all. ## AVIRTs Solution The ALSA Virtual Sound Driver (AVIRT) aims to provide a Linux kernel solution to the issue of audio routing in kernel-space, as well as security per-stream, and dynamic configuration of streams at the kernel level. @@ -18,9 +18,9 @@ A top-level abstract dynamic audio driver is presented to the user-space via an The top-level driver is configured (currently) using by utilising configfs. AVIRT attempts to address the aforementioned problems by: - 1. Providing a standard interface through configfs to configure a variable number of 'streams' which can be mapped to audio paths. - 2. Optimize the audio routing by connecting directly with the desired audio path. This can include routing audio through directly to hardware, or to an implemented loopback interface. - 3. Each 'stream' is separated to its own device, therefore security can be applied to each 'stream'. This means that each of the audio roles can be separately secured from each other. +1. Providing a standard interface through configfs to configure a variable number of 'streams' which can be mapped to audio paths. +2. Optimize the audio routing by connecting directly with the desired audio path. This can include routing audio through directly to hardware, or to an implemented loopback interface. +3. Each 'stream' is separated to its own device, therefore security can be applied to each 'stream'. This means that each of the audio roles can be separately secured from each other. A sample dummy Audio Path is provided as an example to show how a low-level audio driver would subscribe to AVIRT, and accept audio routing for playback. -- cgit 1.2.3-korg From 07505fddec4f96f187ec79a55f4363a68f083a03 Mon Sep 17 00:00:00 2001 From: James O'Shannessy Date: Mon, 8 Oct 2018 16:37:41 +1100 Subject: Adding github preview extensions for vs code Signed-off-by: James O'Shannessy --- .vscode/extensions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 4c7180e..1e19bad 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,5 +4,6 @@ "recommendations": [ "ms-vscode.cpptools", "xaver.clang-format", + "bierner.markdown-preview-github-styles", ] } \ No newline at end of file -- cgit 1.2.3-korg From 3b62055c816764ff02aeb3a548e9bba36df8aae4 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 8 Oct 2018 16:47:25 +1100 Subject: Update documentation Add 'map' to configfs test, remove old mentions of module parameters Signed-off-by: Mark Farrugia --- docs/1.Introduction.md | 17 ++++++++++++---- docs/2.Building.md | 12 ++++++++--- docs/3.Usage.md | 53 +++++++++++++++++++++++++++++++++++++++++------- docs/4.4A-Integration.md | 4 ++-- 4 files changed, 70 insertions(+), 16 deletions(-) diff --git a/docs/1.Introduction.md b/docs/1.Introduction.md index 7bf3ebe..2ddaf57 100644 --- a/docs/1.Introduction.md +++ b/docs/1.Introduction.md @@ -2,15 +2,23 @@ Introduction =================== ## Problem + The existing solution contains the following drawbacks: + 1. Non-generic interface for configurable hardware cases - - Unable to configure streams mapped to outputs + + - Unable to configure streams mapped to outputs + 2. Not fully optimized for certain use cases (ie. Routing Audio to DSP vs. aloop) - - Routing audio to hardware to reduce overhead + + - Routing audio to hardware to reduce overhead + 3. Inability to secure on all audio streams - - Substreams cannot have SMACK labels applied, inherently leaving different audio streams insecure, or a single label applied to all. + + - Substreams cannot have SMACK labels applied, inherently leaving different audio streams insecure, or a single label applied to all. ## AVIRTs Solution + The ALSA Virtual Sound Driver (AVIRT) aims to provide a Linux kernel solution to the issue of audio routing in kernel-space, as well as security per-stream, and dynamic configuration of streams at the kernel level. A top-level abstract dynamic audio driver is presented to the user-space via an ALSA middle-layer card. From there, respective low-level "real" audio drivers can subscribe to it as an "Audio Path". @@ -18,10 +26,11 @@ A top-level abstract dynamic audio driver is presented to the user-space via an The top-level driver is configured (currently) using by utilising configfs. AVIRT attempts to address the aforementioned problems by: + 1. Providing a standard interface through configfs to configure a variable number of 'streams' which can be mapped to audio paths. 2. Optimize the audio routing by connecting directly with the desired audio path. This can include routing audio through directly to hardware, or to an implemented loopback interface. 3. Each 'stream' is separated to its own device, therefore security can be applied to each 'stream'. This means that each of the audio roles can be separately secured from each other. A sample dummy Audio Path is provided as an example to show how a low-level audio driver would subscribe to AVIRT, and accept audio routing for playback. -Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL reference platform, and a default loopback softmixer is in development. \ No newline at end of file +Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL reference platform, and a default loopback softmixer is in development. diff --git a/docs/2.Building.md b/docs/2.Building.md index 237d834..3e42746 100644 --- a/docs/2.Building.md +++ b/docs/2.Building.md @@ -2,21 +2,23 @@ Building AVIRT =================================== ## Table of Contents + 1. [Introduction](#intro) -2. [Out of Tree](#out-of-tree) +2. [Out of Tree](#out-of-tree) 3. [In Tree](#in-tree) ## Introduction -The AVIRT module and submodules can be built using a variety of methods. There are Visual Code build tasks for in and out of tree builds, make file support for CLI building (both in and out of tree), and xds build support. +The AVIRT module and submodules can be built using a variety of methods. There are Visual Code build tasks for in and out of tree builds, make file support for CLI building (both in and out of tree), and xds build support. ## Out Of Tree ### Building with CLI + To build both AVIRT and the additional Audio Paths out-of-tree, use the following command: ```sh @@ -24,12 +26,15 @@ $ make ``` or + ```sh $ CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) ``` + The latter is executed internally with the make file. ### Building with XDS SDK + To build both AVIRT and the dummy Audio Path out-of-tree for [AGL](http://docs.automotivelinux.org/) (`aarch64` currently supported), use the [XDS](http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-1/0_Abstract.html) build system together with the `make_agl.sh` script: ```sh @@ -51,6 +56,7 @@ $ make menuconfig ``` Finally build the kernel with the configuration selected by making. + ``` $ make -``` \ No newline at end of file +``` diff --git a/docs/3.Usage.md b/docs/3.Usage.md index 7eb46d3..6d4d9f1 100644 --- a/docs/3.Usage.md +++ b/docs/3.Usage.md @@ -4,6 +4,7 @@ Using AVIRT ## Introduction ## Table of Contents + 1. [Load/Unload AVIRT](#un-load-avirt) 2. [Configuring AVIRT](#configuring-avirt) @@ -11,7 +12,9 @@ Using AVIRT ## Loading and Unloading AVIRT -As root, load the required ```avirt_core.ko``` and subsequent audio path. +### Load Out of Tree + +As root, load the required `avirt_core.ko` and subsequent audio path. As an example, the Dummy Audiopath is being loaded here. ```sh @@ -20,14 +23,38 @@ insmod dummy/dummy_audiopath.ko ``` ### Note: + The loading and unloading of drivers can be performed using the helper scripts. -To run, we must load the kernel modules using the `loadDrivers.sh` script, which contains sample module parameters to AVIRT: +To run, we must load the kernel modules using the `scripts/load.sh` script: + ```sh -$ ./loadDrivers.sh +$ ./scripts/load.sh ``` + To unload the drivers use: + +```sh +$ ./scripts/unload.sh +``` + +### Load In Tree + +#### Note: + +Either build the module in tree, or you can copy the modules manually and then run `depmod` to generate dependencies. +For example, in AGL: + ```sh -$ ./unload.sh +mkdir -p /lib/modules/$(uname -r)/extra +cp avirt_core.ko avirt_dummyap.ko /lib/modules/$(uname -r)/extra +depmod +``` + +Once the modules are in place, we can load the modules using: + +```sh +modprobe avirt_core +modprobe avirt_dummyap ``` @@ -35,22 +62,27 @@ $ ./unload.sh ## Configuring AVIRT AVIRT is configured using configfs. -### Note: -A sample script for the following is supplied inside the ```scripts``` folder. + +### Note: + +A sample script for the following is supplied inside the `scripts` folder. First, verify the kernel supports the use of configfs. + ```sh fgrep configfs /proc/filesystems ``` the expected output is: + ``` nodev configfs ``` Once confirmed, we can mount the configfs at the conventional point: + ```sh -#Check configfs is mounted on the system +# Check configfs is mounted on the system mkdir -p /config mount -t configfs none /config ``` @@ -61,20 +93,27 @@ Finally, we can configure AVIRT: # Set up each of the cards channels mkdir /config/avirt/streams/playback_media echo "2">/config/avirt/streams/playback_media/channels +echo "ap_dummy">/config/avirt/streams/playback_media/map mkdir /config/avirt/streams/playback_navigation echo "1">/config/avirt/streams/playback_navigation/channels +echo "ap_dummy">/config/avirt/streams/playback_navigation/map mkdir /config/avirt/streams/playback_emergency echo "1">/config/avirt/streams/playback_emergency/channels +echo "ap_dummy">/config/avirt/streams/playback_emergency/map mkdir /config/avirt/streams/capture_voice echo "1">/config/avirt/streams/capture_voice/channels +echo "ap_dummy">/config/avirt/streams/capture_voice/map # Finally, seal the card, and initiate configuration echo "1">/config/avirt/streams/sealed ``` +Alternatively, the test script at `scripts/test_configfs.sh` can be used. + ### Note: + A simplified helper library, libavirt, is available [here](https://github.com/fiberdyne/libavirt). Please refer to the readme in libavirt for further details. diff --git a/docs/4.4A-Integration.md b/docs/4.4A-Integration.md index 7c9b980..da4eeb7 100644 --- a/docs/4.4A-Integration.md +++ b/docs/4.4A-Integration.md @@ -13,5 +13,5 @@ The user-space library [libavirt](https://github.com/fiberdyne/libavirt) can be A new 4A mixer binding has been developed to demonstrate the capabilities of the Fiberdyne DSP mixer operating on the HiFi2 core on-board the Renesas R-Car M3 M3ULCB -AGL reference platform. This is called the 'hardmixer', and is a faster, lower -latency alternative to the softmixer. \ No newline at end of file +AGL reference platform. This is called the '**hardmixer**', and is a faster, lower +latency alternative to the softmixer. -- cgit 1.2.3-korg From cd128853f392960ceec44476f37f0d569a680db5 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Mon, 8 Oct 2018 17:59:15 +1100 Subject: Update docs to include loopback Signed-off-by: Mark Farrugia --- docs/1.Introduction.md | 2 +- docs/3.Usage.md | 6 ++++-- docs/4.4A-Integration.md | 7 ++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/1.Introduction.md b/docs/1.Introduction.md index 2ddaf57..e189c7c 100644 --- a/docs/1.Introduction.md +++ b/docs/1.Introduction.md @@ -33,4 +33,4 @@ AVIRT attempts to address the aforementioned problems by: A sample dummy Audio Path is provided as an example to show how a low-level audio driver would subscribe to AVIRT, and accept audio routing for playback. -Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL reference platform, and a default loopback softmixer is in development. +Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL reference platform, and the default loopback softmixer can be used to emulate this in cases where the former is not available. diff --git a/docs/3.Usage.md b/docs/3.Usage.md index 6d4d9f1..df56cf1 100644 --- a/docs/3.Usage.md +++ b/docs/3.Usage.md @@ -19,7 +19,8 @@ As an example, the Dummy Audiopath is being loaded here. ```sh insmod avirt_core.ko -insmod dummy/dummy_audiopath.ko +insmod dummy/avirt_dummyap.ko +insmod loopback/avirt_loopbackap.ko ``` ### Note: @@ -46,7 +47,7 @@ For example, in AGL: ```sh mkdir -p /lib/modules/$(uname -r)/extra -cp avirt_core.ko avirt_dummyap.ko /lib/modules/$(uname -r)/extra +cp avirt_core.ko avirt_dummyap.ko avirt_loopbackap.ko /lib/modules/$(uname -r)/extra depmod ``` @@ -55,6 +56,7 @@ Once the modules are in place, we can load the modules using: ```sh modprobe avirt_core modprobe avirt_dummyap +modprobe avirt_loopbackap ``` diff --git a/docs/4.4A-Integration.md b/docs/4.4A-Integration.md index da4eeb7..cb88dd3 100644 --- a/docs/4.4A-Integration.md +++ b/docs/4.4A-Integration.md @@ -9,9 +9,14 @@ transition from the existing aloop implementation to the future AVIRT loopback i The user-space library [libavirt](https://github.com/fiberdyne/libavirt) can be used to configure AVIRT from within a given AGL 4A binding. +### Softmixer + +The AVIRT loopback can be used in-place of the standard aloop module. Modifications are currently underway +to port the current 4a-softmixer to the new AVIRT loopback. + ### Hardmixer -A new 4A mixer binding has been developed to demonstrate the capabilities of the +A new 4A mixer controller plugin (`.ctlso`) has been developed to demonstrate the capabilities of the Fiberdyne DSP mixer operating on the HiFi2 core on-board the Renesas R-Car M3 M3ULCB AGL reference platform. This is called the '**hardmixer**', and is a faster, lower latency alternative to the softmixer. -- cgit 1.2.3-korg From 964987727646a7139a3d259830f25a739937fa73 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Tue, 9 Oct 2018 10:10:49 +1100 Subject: Add docs: Add architecture diagram for 4A Tidy other docs Signed-off-by: Mark Farrugia --- README.md | 2 +- docs/1.Introduction.md | 24 +++++++++++++----------- docs/3.Usage.md | 30 ++++++++++++++++++++++++------ docs/images/arch-4a.png | Bin 0 -> 282311 bytes 4 files changed, 38 insertions(+), 18 deletions(-) create mode 100644 docs/images/arch-4a.png diff --git a/README.md b/README.md index 9c70cfb..a653f0e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Alsa Virtual Sound Driver +# Alsa Virtual Sound Driver 1. [Introduction](docs/1.Introduction.md) 2. [Building](docs/2.Building.md) diff --git a/docs/1.Introduction.md b/docs/1.Introduction.md index e189c7c..c36717c 100644 --- a/docs/1.Introduction.md +++ b/docs/1.Introduction.md @@ -3,7 +3,7 @@ Introduction ## Problem -The existing solution contains the following drawbacks: +The existing audio solution in [AGL](https://www.automotivelinux.org/) (Automotive Grade Linux) utilizes the ALSA loopback module together with [4a-softmixer](https://github.com/iotbzh/4a-softmixer). There are drawbacks to this solution: 1. Non-generic interface for configurable hardware cases @@ -17,20 +17,22 @@ The existing solution contains the following drawbacks: - Substreams cannot have SMACK labels applied, inherently leaving different audio streams insecure, or a single label applied to all. -## AVIRTs Solution +## The AVIRT Solution -The ALSA Virtual Sound Driver (AVIRT) aims to provide a Linux kernel solution to the issue of audio routing in kernel-space, as well as security per-stream, and dynamic configuration of streams at the kernel level. +The ALSA Virtual Sound Driver (AVIRT) aims to provide a Linux kernel solution to the issue of audio routing in kernel-space, as well as security per audio stream, and dynamic configuration of audio streams at the kernel level. -A top-level abstract dynamic audio driver is presented to the user-space via an ALSA middle-layer card. From there, respective low-level "real" audio drivers can subscribe to it as an "Audio Path". +AVIRT addresses the aforementioned problems by: -The top-level driver is configured (currently) using by utilising configfs. +1. Providing a standard user-space interface via `configfs` to configure a variable number of 'streams' which can be mapped to audio paths. +2. Optimize the audio routing by connecting directly with the desired audio path. This can include routing audio through directly to hardware, to an implemented loopback interface, or to a DSP. +3. Each 'stream' is separated to its own ALSA PCM device, therefore differing security attributes can be applied to each 'stream'. This means that each of the audio roles can have separate security contexts from one another. -AVIRT attempts to address the aforementioned problems by: +A top-level abstract dynamic audio driver is presented to the user-space via an ALSA middle-layer card. The card's PCM devices are configured using `configfs` via the accompanying userspace library [libavirt](https://github.com/fiberdyne/libavirt). The low-level "real" audio drivers can subscribe to the top-level as an "Audio Path". The Audio Path accepts routed audio streams from the abstracted top-level, and is then able to direct the audio streams accordingly. -1. Providing a standard interface through configfs to configure a variable number of 'streams' which can be mapped to audio paths. -2. Optimize the audio routing by connecting directly with the desired audio path. This can include routing audio through directly to hardware, or to an implemented loopback interface. -3. Each 'stream' is separated to its own device, therefore security can be applied to each 'stream'. This means that each of the audio roles can be separately secured from each other. +![alt text](images/arch-4a.png "AVIRT Architecture") -A sample dummy Audio Path is provided as an example to show how a low-level audio driver would subscribe to AVIRT, and accept audio routing for playback. +The following Audio Paths are currently implemented for AVIRT: -Currently, the Fiberdyne DSP hardmixer is supported on the Renesas R-Car M3 AGL reference platform, and the default loopback softmixer can be used to emulate this in cases where the former is not available. +- **ap_dummy** - This is provided as an example to show how a low-level audio driver would subscribe to AVIRT, and accept audio routing for playback. +- **ap_fddsp** - This is the Fiberdyne DSP hardmixer. This is only supported on the Renesas R-Car M3 AGL reference platform, and utilizes the HiFi2 DSP core to provide advanced DSP and audio mixing. An accompanying UI can be used to control the DSP parameters. +- **ap_loopback** - This is the default loopback for use with the softmixer. This is the stock AGL solution to be used to emulate the hardmixer when not available. diff --git a/docs/3.Usage.md b/docs/3.Usage.md index df56cf1..d65d50d 100644 --- a/docs/3.Usage.md +++ b/docs/3.Usage.md @@ -75,13 +75,13 @@ First, verify the kernel supports the use of configfs. fgrep configfs /proc/filesystems ``` -the expected output is: +The expected output is: ``` nodev configfs ``` -Once confirmed, we can mount the configfs at the conventional point: +Once confirmed, we can mount the `configfs` filesystem at the conventional point: ```sh # Check configfs is mounted on the system @@ -89,7 +89,7 @@ mkdir -p /config mount -t configfs none /config ``` -Finally, we can configure AVIRT: +Finally, we can configure AVIRT, for example: ```sh # Set up each of the cards channels @@ -115,7 +115,25 @@ echo "1">/config/avirt/streams/sealed Alternatively, the test script at `scripts/test_configfs.sh` can be used. -### Note: +The user-space library, [libavirt](https://github.com/fiberdyne/libavirt) can be used to interact with the configfs interface. Please refer to the README in libavirt for further details. + + + +## 3. Checking AVIRT Loaded Correctly -A simplified helper library, libavirt, is available [here](https://github.com/fiberdyne/libavirt). -Please refer to the readme in libavirt for further details. +We can see the newly created streams by using the `aplay` utility. For example, the device list might look a little like this: + +```sh +aplay -l +... +card 2: avirt [avirt], device 0: multimedia [multimedia] + Subdevices: 1/1 + Subdevice #0: subdevice #0 +card 2: avirt [avirt], device 1: navigation [navigation] + Subdevices: 1/1 + Subdevice #0: subdevice #0 +card 2: avirt [avirt], device 2: emergency [emergency] + Subdevices: 1/1 + Subdevice #0: subdevice #0 +... +``` diff --git a/docs/images/arch-4a.png b/docs/images/arch-4a.png new file mode 100644 index 0000000..6f085db Binary files /dev/null and b/docs/images/arch-4a.png differ -- cgit 1.2.3-korg From f0029c35ecb07fcfcf05b0093b536054f674a303 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Wed, 24 Oct 2018 23:18:26 +1100 Subject: Fix module authors/descriptions These need to be in the format the Linux expects, and uniform across AVIRT Signed-off-by: Mark Farrugia --- core.c | 7 ++++--- dummy/dummy.c | 6 +++--- loopback/loopback.c | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/core.c b/core.c index 1ea8107..b526949 100644 --- a/core.c +++ b/core.c @@ -15,10 +15,11 @@ #include "core_internal.h" -MODULE_AUTHOR("JOSHANNE "); -MODULE_AUTHOR("MFARRUGI "); -MODULE_DESCRIPTION("A configurable virtual soundcard"); +MODULE_AUTHOR("James O'Shannessy "); +MODULE_AUTHOR("Mark Farrugia "); +MODULE_DESCRIPTION("ALSA virtual, dynamic soundcard"); MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("{{ALSA,AVIRT soundcard}}"); #define D_LOGNAME "core" diff --git a/dummy/dummy.c b/dummy/dummy.c index b77301f..660305e 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -11,9 +11,9 @@ #include #include -MODULE_AUTHOR("JOSHANNE "); -MODULE_AUTHOR("MFARRUGI "); -MODULE_DESCRIPTION("Sample Audio Path Module Interface"); +MODULE_AUTHOR("James O'Shannessy "); +MODULE_AUTHOR("Mark Farrugia "); +MODULE_DESCRIPTION("Dummy Audio Path for AVIRT"); MODULE_LICENSE("GPL v2"); #define DUMMY_SAMPLE_RATE 48000 diff --git a/loopback/loopback.c b/loopback/loopback.c index 2e02711..8ca23bd 100644 --- a/loopback/loopback.c +++ b/loopback/loopback.c @@ -1,5 +1,5 @@ /* - * Loopback soundcard + * Loopback Audio Path for AVIRT * * Original code: * Copyright (c) by Jaroslav Kysela @@ -48,7 +48,7 @@ #include MODULE_AUTHOR("Jaroslav Kysela "); -MODULE_DESCRIPTION("A loopback soundcard"); +MODULE_DESCRIPTION("Loopback Audio Path for AVIRT"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}"); -- cgit 1.2.3-korg From 6b1a48a65fca8c81a4299f95f2096dbef4815b75 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 25 Oct 2018 14:02:41 +1100 Subject: Update license headers All license headers need to be uniform across AVIRT Signed-off-by: Mark Farrugia --- alsa-pcm.c | 8 ++++---- configfs.c | 10 +++++----- core.c | 8 ++++---- core_internal.h | 8 ++++---- dummy/dummy.c | 14 +++++++++----- loopback/loopback.c | 46 +++++++++++++++++----------------------------- 6 files changed, 43 insertions(+), 51 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 17c08fd..34a658c 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* - * ALSA Virtual Soundcard + * AVIRT - ALSA Virtual Soundcard * - * alsa-pcm.c - AVIRT ALSA PCM interface - * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd + * + * alsa-pcm.c - AVIRT PCM interface */ #include "core_internal.h" diff --git a/configfs.c b/configfs.c index 924866c..ce55aa0 100644 --- a/configfs.c +++ b/configfs.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 -/* - * ALSA Virtual Soundcard +/* + * AVIRT - ALSA Virtual Soundcard * - * configfs.c - configfs for virtual ALSA card - * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd + * + * configfs.c - AVIRT configfs support */ #include diff --git a/core.c b/core.c index b526949..a8554b7 100644 --- a/core.c +++ b/core.c @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* - * ALSA Virtual Soundcard + * AVIRT - ALSA Virtual Soundcard * - * core.c - Implementation of core module for virtual ALSA card - * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd + * + * core.c - AVIRT core internals */ #include diff --git a/core_internal.h b/core_internal.h index 1ab8d3c..5bff1c8 100644 --- a/core_internal.h +++ b/core_internal.h @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* - * ALSA Virtual Soundcard + * AVIRT - ALSA Virtual Soundcard * - * core.h - Internal header for virtual ALSA card - * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd + * + * core_internal.h - AVIRT internal header */ #ifndef __AVIRT_CORE_INTERNAL_H__ diff --git a/dummy/dummy.c b/dummy/dummy.c index 660305e..2841585 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -1,10 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 -/* - * ALSA Virtual Soundcard - * - * dummy_audiopath.c - AVIRT sample Audio Path definition +/* + * Dummy Audio Path for AVIRT + * + * Original systimer code: + * Copyright (c) by Jaroslav Kysela * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + * Adapt to use AVIRT + * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd + * + * dummy.c - Dummy Audio Path driver implementation for AVIRT */ #include diff --git a/loopback/loopback.c b/loopback/loopback.c index 8ca23bd..d465cc6 100644 --- a/loopback/loopback.c +++ b/loopback/loopback.c @@ -1,35 +1,23 @@ -/* - * Loopback Audio Path for AVIRT - * - * Original code: - * Copyright (c) by Jaroslav Kysela - * - * More accurate positioning and full-duplex support: - * Copyright (c) Ahmet Ä°nan - * - * Major (almost complete) rewrite: - * Copyright (c) by Takashi Iwai - * - * A next major update in 2010 (separate timers for playback and capture): - * Copyright (c) Jaroslav Kysela +// SPDX-License-Identifier: GPL-2.0 +/* + * Loopback Audio Path for AVIRT * - * Adapt to use AVIRT, looping is now conducted on the same device - * Copyright (c) by Mark Farrugia - * - * 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. + * Original code: + * Copyright (c) by Jaroslav Kysela + * + * More accurate positioning and full-duplex support: + * Copyright (c) Ahmet Ä°nan * - * 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 + * Major (almost complete) rewrite: + * Copyright (c) by Takashi Iwai * + * A next major update in 2010 (separate timers for playback and capture): + * Copyright (c) Jaroslav Kysela + * + * Adapt to use AVIRT, looping is now conducted on the same PCM device + * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd + * + * loopback.c - Loopback Audio Path driver implementation for AVIRT */ #include -- cgit 1.2.3-korg From 92492a28f45725d128edc3cb4fba47b08446c7c4 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 25 Oct 2018 14:03:30 +1100 Subject: Reinstate temporarily removed PCM callbacks These can still be used, although proceed with caution when adding new Audio Paths. The copy_user PCM callback caused lots of strife when porting the loopback driver! Signed-off-by: Mark Farrugia --- alsa-pcm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 34a658c..0c39a4d 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -324,10 +324,10 @@ struct snd_pcm_ops pcm_ops = { .prepare = pcm_prepare, .trigger = pcm_trigger, .pointer = pcm_pointer, - //.get_time_info = pcm_get_time_info, - //.fill_silence = pcm_silence, + .get_time_info = pcm_get_time_info, + .fill_silence = pcm_silence, .copy_user = pcm_copy_user, - //.copy_kernel = pcm_copy_kernel, + .copy_kernel = pcm_copy_kernel, .page = snd_pcm_lib_get_vmalloc_page, - //.ack = pcm_ack, + .ack = pcm_ack, }; -- cgit 1.2.3-korg From 6e5b92cb45996adaa309b9534a53ed81989986de Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 25 Oct 2018 14:05:10 +1100 Subject: Remove unused MODULE_SUPPORTED_DEVICE macros, removed obsolete comments Added debug macros for Dummy AP Signed-off-by: Mark Farrugia --- configfs.c | 2 -- core.c | 4 +--- dummy/dummy.c | 10 ++++++++-- loopback/loopback.c | 1 - 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/configfs.c b/configfs.c index ce55aa0..13e38b2 100644 --- a/configfs.c +++ b/configfs.c @@ -76,8 +76,6 @@ static ssize_t cfg_avirt_stream_channels_store(struct config_item *item, stream->channels = tmp; - D_INFOK("channels: %d", stream->channels); - return count; } CONFIGFS_ATTR(cfg_avirt_stream_, channels); diff --git a/core.c b/core.c index a8554b7..b63944a 100644 --- a/core.c +++ b/core.c @@ -19,7 +19,6 @@ MODULE_AUTHOR("James O'Shannessy "); MODULE_AUTHOR("Mark Farrugia "); MODULE_DESCRIPTION("ALSA virtual, dynamic soundcard"); MODULE_LICENSE("GPL v2"); -MODULE_SUPPORTED_DEVICE("{{ALSA,AVIRT soundcard}}"); #define D_LOGNAME "core" @@ -196,6 +195,7 @@ static struct snd_pcm *pcm_create(struct avirt_stream *stream) struct snd_pcm *pcm; int err; + /** Special case: loopback */ if (!strcmp(stream->map, "ap_loopback")) { playback = true; capture = true; @@ -212,8 +212,6 @@ static struct snd_pcm *pcm_create(struct avirt_stream *stream) if (err < 0) return ERR_PTR(err); - // TD MF: IMPORTANT: NEED TO TEST >8 PCM DEVICES ON A - // CARD! /** Register driver callbacks */ if (playback) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); diff --git a/dummy/dummy.c b/dummy/dummy.c index 2841585..596838d 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -20,6 +20,12 @@ MODULE_AUTHOR("Mark Farrugia "); MODULE_DESCRIPTION("Dummy Audio Path for AVIRT"); MODULE_LICENSE("GPL v2"); +#define AP_UID "ap_dummy" + +#define AP_INFOK(fmt, args...) DINFO(AP_UID, fmt, ##args) +#define AP_PRINTK(fmt, args...) DDEBUG(AP_UID, fmt, ##args) +#define AP_ERRORK(fmt, args...) DERROR(AP_UID, fmt, ##args) + #define DUMMY_SAMPLE_RATE 48000 #define DUMMY_BLOCKSIZE 512 #define DUMMY_PERIODS_MIN 1 @@ -247,8 +253,8 @@ int dummy_configure(struct snd_card *card, container_of(entry, struct config_item, ci_entry); struct avirt_stream *stream = avirt_stream_from_config_item(item); - pr_info("%s: stream name:%s device:%d channels:%d\n", __func__, - stream->name, stream->device, stream->channels); + AP_INFOK("stream name:%s device:%d channels:%d", stream->name, + stream->device, stream->channels); } return 0; diff --git a/loopback/loopback.c b/loopback/loopback.c index d465cc6..acddf99 100644 --- a/loopback/loopback.c +++ b/loopback/loopback.c @@ -38,7 +38,6 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Loopback Audio Path for AVIRT"); MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}"); #define AP_UID "ap_loopback" -- cgit 1.2.3-korg From 18007ca500654b48011e8a8cf96c47b1a5aa3be7 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 25 Oct 2018 15:56:58 +1100 Subject: Move system-leavel header to sound directory Change include guards to reflect Linux sound driver format Signed-off-by: Mark Farrugia --- Makefile | 2 +- configfs.c | 3 +- core.c | 3 +- core.h | 123 ---------------------------------------------------- core_internal.h | 8 ++-- dummy/Makefile | 2 +- dummy/dummy.c | 2 +- loopback/Makefile | 2 +- loopback/loopback.c | 3 +- sound/avirt.h | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++ utils.h | 14 +++--- 11 files changed, 141 insertions(+), 143 deletions(-) delete mode 100644 core.h create mode 100644 sound/avirt.h diff --git a/Makefile b/Makefile index 44874ca..791a60a 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ avirt_core-y += configfs.o ifeq ($(CONFIG_AVIRT_BUILDLOCAL),) CCFLAGS_AVIRT := "drivers/staging/" else - CCFLAGS_AVIRT := "$(PWD)/../" + CCFLAGS_AVIRT := "$(PWD)" endif ccflags-y += -I${CCFLAGS_AVIRT} diff --git a/configfs.c b/configfs.c index 13e38b2..49eaa78 100644 --- a/configfs.c +++ b/configfs.c @@ -7,7 +7,8 @@ * configfs.c - AVIRT configfs support */ -#include +#include + #include "core_internal.h" #define D_LOGNAME "configfs" diff --git a/core.c b/core.c index b63944a..6509b55 100644 --- a/core.c +++ b/core.c @@ -9,9 +9,8 @@ #include #include -#include +#include #include -#include #include "core_internal.h" diff --git a/core.h b/core.h deleted file mode 100644 index bac4fc0..0000000 --- a/core.h +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ALSA Virtual Soundcard - * - * core.h - System-level header for virtual ALSA card - * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd - */ - -#ifndef __AVIRT_CORE_H__ -#define __AVIRT_CORE_H__ - -#include -#include - -#define MAX_STREAMS 16 -#define MAX_NAME_LEN 80 - -#define DINFO(logname, fmt, args...) \ - printk(KERN_INFO "[AVIRT][%s]: " fmt "\n", logname, ##args) - -#define DERROR(logname, fmt, args...) \ - printk(KERN_ERR "[AVIRT][%s]: %d:%s " fmt "\n", logname, __LINE__, \ - __func__, ##args) - -#define DDEBUG(logname, fmt, args...) \ - printk(KERN_DEBUG "[AVIRT][%s]: %d:%s " fmt "\n", logname, __LINE__, \ - __func__, ##args) - -/** - * AVIRT Audio Path configure function type - * Each Audio Path registers this at avirt_audiopath_register time. - * It is then called by the core once AVIRT has been configured - */ -typedef int (*avirt_audiopath_configure)(struct snd_card *card, - struct config_group *stream_group, - unsigned int stream_count); - -/** - * AVIRT Audio Path info - */ -struct avirt_audiopath { - const char *uid; /* Unique identifier */ - const char *name; /* Pretty name */ - unsigned int version[3]; /* Version - Major.Minor.Ext */ - const struct snd_pcm_hardware *hw; /* ALSA PCM HW conf */ - const struct snd_pcm_ops *pcm_ops; /* ALSA PCM op table */ - avirt_audiopath_configure configure; /* Configure callback function */ - - void *context; -}; - -/* - * Audio stream configuration - */ -struct avirt_stream { - char name[MAX_NAME_LEN]; /* Stream name */ - char map[MAX_NAME_LEN]; /* Stream Audio Path mapping */ - unsigned int channels; /* Stream channel count */ - unsigned int device; /* Stream PCM device no. */ - unsigned int direction; /* Stream direction */ - struct snd_pcm *pcm; /* ALSA PCM */ - struct config_item item; /* configfs item reference */ -}; - -/** - * AVIRT core info - */ -struct avirt_coreinfo { - unsigned int version[3]; -}; - -/** - * avirt_audiopath_register - register Audio Path with ALSA virtual driver - * @audiopath: Audio Path to be registered - * @core: ALSA virtual driver core info - * @return: 0 on success or error code otherwise - */ -int avirt_audiopath_register(struct avirt_audiopath *audiopath, - struct avirt_coreinfo **coreinfo); - -/** - * avirt_audiopath_deregister - deregister Audio Path with ALSA virtual driver - * @audiopath: Audio Path to be deregistered - * @return: 0 on success or error code otherwise - */ -int avirt_audiopath_deregister(struct avirt_audiopath *audiopath); - -/** - * avirt_audiopath_get - retrieves the Audio Path by it's UID - * @uid: Unique ID for the Audio Path - * @return: Corresponding Audio Path - */ -struct avirt_audiopath *avirt_audiopath_get(const char *uid); - -/** - * avirt_stream_count - get the stream count for the given direction - * @direction: The direction to get the stream count for - * @return: The stream count - */ -int avirt_stream_count(unsigned int direction); - -/** - * avirt_stream_from_config_item - Convert a config_item to an avirt_stream - * @item: The config_item to convert from - * @return: The item's avirt_stream if successful, NULL otherwise - */ -static inline struct avirt_stream * -avirt_stream_from_config_item(struct config_item *item) -{ - return item ? container_of(item, struct avirt_stream, item) : NULL; -} - -/** - * avirt_pcm_period_elapsed - PCM buffer complete callback - * @substream: pointer to ALSA PCM substream - * - * This should be called from a child Audio Path once it has finished processing - * the PCM buffer - */ -void avirt_pcm_period_elapsed(struct snd_pcm_substream *substream); - -#endif // __AVIRT_CORE_H__ diff --git a/core_internal.h b/core_internal.h index 5bff1c8..f3fcb48 100644 --- a/core_internal.h +++ b/core_internal.h @@ -7,10 +7,10 @@ * core_internal.h - AVIRT internal header */ -#ifndef __AVIRT_CORE_INTERNAL_H__ -#define __AVIRT_CORE_INTERNAL_H__ +#ifndef __SOUND_AVIRT_CORE_H +#define __SOUND_AVIRT_CORE_H -#include +#include #include "utils.h" @@ -64,4 +64,4 @@ struct avirt_stream *__avirt_stream_find_by_device(unsigned int device); */ struct avirt_stream *__avirt_stream_create(const char *name, int direction); -#endif // __AVIRT_CORE_INTERNAL_H__ +#endif /* __SOUND_AVIRT_CORE_H */ diff --git a/dummy/Makefile b/dummy/Makefile index 965bd1a..78579d7 100644 --- a/dummy/Makefile +++ b/dummy/Makefile @@ -3,4 +3,4 @@ obj-$(CONFIG_AVIRT_DUMMYAP) += avirt_dummyap.o $(info $(src)) avirt_dummyap-objs := dummy.o ccflags-y += -Idrivers/staging/ -ccflags-y += -I$(src)/../../ +ccflags-y += -I$(src)/../ diff --git a/dummy/dummy.c b/dummy/dummy.c index 596838d..100972a 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -13,7 +13,7 @@ #include #include -#include +#include MODULE_AUTHOR("James O'Shannessy "); MODULE_AUTHOR("Mark Farrugia "); diff --git a/loopback/Makefile b/loopback/Makefile index 0464aba..9d3c606 100644 --- a/loopback/Makefile +++ b/loopback/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_AVIRT_LOOPBACKAP) += avirt_loopbackap.o $(info $(src)) avirt_loopbackap-objs := loopback.o ccflags-y += -Idrivers/staging/ -ccflags-y += -I$(src)/../../ +ccflags-y += -I$(src)/../ diff --git a/loopback/loopback.c b/loopback/loopback.c index acddf99..ba9225e 100644 --- a/loopback/loopback.c +++ b/loopback/loopback.c @@ -27,13 +27,12 @@ #include #include #include -#include #include #include #include #include #include -#include +#include MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Loopback Audio Path for AVIRT"); diff --git a/sound/avirt.h b/sound/avirt.h new file mode 100644 index 0000000..5df84fe --- /dev/null +++ b/sound/avirt.h @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AVIRT - ALSA Virtual Soundcard + * + * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd + * + * avirt.h - AVIRT system-level header + */ + +#ifndef __SOUND_AVIRT_H +#define __SOUND_AVIRT_H + +#include +#include +#include + +#define MAX_STREAMS 16 +#define MAX_NAME_LEN 80 + +#define DINFO(logname, fmt, args...) \ + snd_printk(KERN_INFO "AVIRT: %s: " fmt "\n", logname, ##args) + +#define DERROR(logname, fmt, args...) \ + snd_printk(KERN_ERR "AVIRT: %s: " fmt "\n", logname, ##args) + +#define DDEBUG(logname, fmt, args...) \ + snd_printk(KERN_DEBUG "AVIRT: %s: " fmt "\n", logname, ##args) + +/** + * AVIRT Audio Path configure function type + * Each Audio Path registers this at avirt_audiopath_register time. + * It is then called by the core once AVIRT has been configured + */ +typedef int (*avirt_audiopath_configure)(struct snd_card *card, + struct config_group *stream_group, + unsigned int stream_count); + +/** + * AVIRT Audio Path info + */ +struct avirt_audiopath { + const char *uid; /* Unique identifier */ + const char *name; /* Pretty name */ + unsigned int version[3]; /* Version - Major.Minor.Ext */ + const struct snd_pcm_hardware *hw; /* ALSA PCM HW conf */ + const struct snd_pcm_ops *pcm_ops; /* ALSA PCM op table */ + avirt_audiopath_configure configure; /* Configure callback function */ + + void *context; +}; + +/* + * Audio stream configuration + */ +struct avirt_stream { + char name[MAX_NAME_LEN]; /* Stream name */ + char map[MAX_NAME_LEN]; /* Stream Audio Path mapping */ + unsigned int channels; /* Stream channel count */ + unsigned int device; /* Stream PCM device no. */ + unsigned int direction; /* Stream direction */ + struct snd_pcm *pcm; /* ALSA PCM */ + struct config_item item; /* configfs item reference */ +}; + +/** + * AVIRT core info + */ +struct avirt_coreinfo { + unsigned int version[3]; +}; + +/** + * avirt_audiopath_register - register Audio Path with ALSA virtual driver + * @audiopath: Audio Path to be registered + * @core: ALSA virtual driver core info + * @return: 0 on success or error code otherwise + */ +int avirt_audiopath_register(struct avirt_audiopath *audiopath, + struct avirt_coreinfo **coreinfo); + +/** + * avirt_audiopath_deregister - deregister Audio Path with ALSA virtual driver + * @audiopath: Audio Path to be deregistered + * @return: 0 on success or error code otherwise + */ +int avirt_audiopath_deregister(struct avirt_audiopath *audiopath); + +/** + * avirt_audiopath_get - retrieves the Audio Path by it's UID + * @uid: Unique ID for the Audio Path + * @return: Corresponding Audio Path + */ +struct avirt_audiopath *avirt_audiopath_get(const char *uid); + +/** + * avirt_stream_count - get the stream count for the given direction + * @direction: The direction to get the stream count for + * @return: The stream count + */ +int avirt_stream_count(unsigned int direction); + +/** + * avirt_stream_from_config_item - Convert a config_item to an avirt_stream + * @item: The config_item to convert from + * @return: The item's avirt_stream if successful, NULL otherwise + */ +static inline struct avirt_stream * +avirt_stream_from_config_item(struct config_item *item) +{ + return item ? container_of(item, struct avirt_stream, item) : NULL; +} + +/** + * avirt_pcm_period_elapsed - PCM buffer complete callback + * @substream: pointer to ALSA PCM substream + * + * This should be called from a child Audio Path once it has finished processing + * the PCM buffer + */ +void avirt_pcm_period_elapsed(struct snd_pcm_substream *substream); + +#endif // __SOUND_AVIRT_H diff --git a/utils.h b/utils.h index 34e273f..844ee55 100644 --- a/utils.h +++ b/utils.h @@ -1,14 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* - * ALSA Virtual Soundcard + * AVIRT - ALSA Virtual Soundcard * - * utils.h - Some useful utilities for AVIRT - * - * Copyright (C) 2010-2018 Fiberdyne Systems Pty Ltd + * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd + * + * utils.h - Some useful macros/utils for AVIRT */ -#ifndef __AVIRT_UTILS_H__ -#define __AVIRT_UTILS_H__ +#ifndef __SOUND_AVIRT_UTILS_H +#define __SOUND_AVIRT_UTILS_H #include @@ -46,4 +46,4 @@ } \ } while (0) -#endif +#endif /* __SOUND_AVIRT_UTILS_H */ -- cgit 1.2.3-korg From e734ca6ed2bb99343fd32c369a7eb3679447b115 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 25 Oct 2018 15:57:34 +1100 Subject: Optimize CHK_ macros No need to be using kasprintf in a debug macro! Signed-off-by: Mark Farrugia --- alsa-pcm.c | 3 +-- utils.h | 23 ++++++++--------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/alsa-pcm.c b/alsa-pcm.c index 0c39a4d..f174ef0 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -55,8 +55,7 @@ static int pcm_open(struct snd_pcm_substream *substream) stream = __avirt_stream_find_by_device(substream->pcm->device); audiopath = avirt_audiopath_get(stream->map); - CHK_NULL_V(audiopath, -EFAULT, "Cannot find Audio Path uid: '%s'!", - stream->map); + CHK_NULL_V(audiopath, "Cannot find Audio Path uid: '%s'!", stream->map); substream->private_data = audiopath; // Copy the hw params from the audiopath to the pcm diff --git a/utils.h b/utils.h index 844ee55..0931e82 100644 --- a/utils.h +++ b/utils.h @@ -10,39 +10,32 @@ #ifndef __SOUND_AVIRT_UTILS_H #define __SOUND_AVIRT_UTILS_H -#include - -#define PRINT_ERR(errno, errmsg) \ - pr_err("[%s]:[ERRNO:%d]: %s \n", __func__, errno, (errmsg)); - #define CHK_ERR(errno) \ do { \ if ((errno) < 0) \ return (errno); \ } while (0) -#define CHK_ERR_V(errno, errmsg, ...) \ +#define CHK_ERR_V(errno, fmt, args...) \ do { \ if ((errno) < 0) { \ - PRINT_ERR((errno), (errmsg), ##__VA_ARGS__) \ + DERROR(D_LOGNAME, (fmt), ##args) \ return (errno); \ } \ } while (0) -#define CHK_NULL(x, errno) \ +#define CHK_NULL(x) \ do { \ if (!(x)) \ - return errno; \ + return -EFAULT; \ } while (0) -#define CHK_NULL_V(x, errno, errmsg, ...) \ +#define CHK_NULL_V(x, fmt, args...) \ do { \ if (!(x)) { \ - char *errmsg_done = \ - kasprintf(GFP_KERNEL, errmsg, ##__VA_ARGS__); \ - PRINT_ERR(EFAULT, errmsg_done); \ - kfree(errmsg_done); \ - return errno; \ + DERROR(D_LOGNAME, "[ERRNO: %d] " fmt, -EFAULT, \ + ##args); \ + return -EFAULT; \ } \ } while (0) -- cgit 1.2.3-korg From d8ff159e37cb3768358b350b2dc80118d81d4b2c Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 25 Oct 2018 16:13:19 +1100 Subject: Refactor core_internal.h to core.h Signed-off-by: Mark Farrugia --- alsa-pcm.c | 2 +- configfs.c | 4 ++-- core.c | 2 +- core.h | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ core_internal.h | 67 --------------------------------------------------------- 5 files changed, 71 insertions(+), 71 deletions(-) create mode 100644 core.h delete mode 100644 core_internal.h diff --git a/alsa-pcm.c b/alsa-pcm.c index f174ef0..4d7b549 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -7,7 +7,7 @@ * alsa-pcm.c - AVIRT PCM interface */ -#include "core_internal.h" +#include "core.h" #define D_LOGNAME "pcm" diff --git a/configfs.c b/configfs.c index 49eaa78..1a7e7c5 100644 --- a/configfs.c +++ b/configfs.c @@ -9,7 +9,7 @@ #include -#include "core_internal.h" +#include "core.h" #define D_LOGNAME "configfs" @@ -105,7 +105,7 @@ static struct config_item_type cfg_avirt_stream_type = { }; static struct config_item * -cfg_avirt_stream_make_item(struct config_group *group, const char *name) + cfg_avirt_stream_make_item(struct config_group *group, const char *name) { char *split; int direction; diff --git a/core.c b/core.c index 6509b55..08381e5 100644 --- a/core.c +++ b/core.c @@ -12,7 +12,7 @@ #include #include -#include "core_internal.h" +#include "core.h" MODULE_AUTHOR("James O'Shannessy "); MODULE_AUTHOR("Mark Farrugia "); diff --git a/core.h b/core.h new file mode 100644 index 0000000..ca47067 --- /dev/null +++ b/core.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AVIRT - ALSA Virtual Soundcard + * + * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd + * + * core.h - AVIRT internal header + */ + +#ifndef __SOUND_AVIRT_CORE_H +#define __SOUND_AVIRT_CORE_H + +#include + +#include "utils.h" + +struct avirt_core { + struct snd_card *card; + struct device *dev; + struct class *avirt_class; + struct config_group *stream_group; + unsigned int stream_count; + bool streams_sealed; +}; + +/** + * __avirt_configfs_init - Initialise the configfs system + * @core: The avirt_core pointer + * @return: 0 on success, negative ERRNO on failure + */ +int __init __avirt_configfs_init(struct avirt_core *core); + +/** + * __avirt_configfs_exit - Clean up and exit the configfs system + * @core: The avirt_core pointer + */ +void __exit __avirt_configfs_exit(struct avirt_core *core); + +/** + * __avirt_streams_seal - Register the sound card to user space + * @return: 0 on success, negative ERRNO on failure + */ +int __avirt_streams_seal(void); + +/** + * __avirt_streams_sealed - Check whether the streams have been sealed or not + * @return: true if sealed, false otherwise + */ +bool __avirt_streams_sealed(void); + +/** + * __avirt_stream_find_by_device - Get audio stream from device number + * @device: The PCM device number corresponding to the desired stream + * @return: The audio stream if found, or an error pointer otherwise + */ +struct avirt_stream *__avirt_stream_find_by_device(unsigned int device); + +/** + * __avirt_stream_create - Create audio stream, including it's ALSA PCM device + * @name: The name designated to the audio stream + * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or + * SNDRV_PCM_STREAM_CAPTURE) + * @return: The newly created audio stream if successful, or an error pointer + */ +struct avirt_stream *__avirt_stream_create(const char *name, int direction); + +#endif /* __SOUND_AVIRT_CORE_H */ diff --git a/core_internal.h b/core_internal.h deleted file mode 100644 index f3fcb48..0000000 --- a/core_internal.h +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * AVIRT - ALSA Virtual Soundcard - * - * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd - * - * core_internal.h - AVIRT internal header - */ - -#ifndef __SOUND_AVIRT_CORE_H -#define __SOUND_AVIRT_CORE_H - -#include - -#include "utils.h" - -struct avirt_core { - struct snd_card *card; - struct device *dev; - struct class *avirt_class; - struct config_group *stream_group; - unsigned int stream_count; - bool streams_sealed; -}; - -/** - * __avirt_configfs_init - Initialise the configfs system - * @core: The avirt_core pointer - * @return: 0 on success, negative ERRNO on failure - */ -int __init __avirt_configfs_init(struct avirt_core *core); - -/** - * __avirt_configfs_exit - Clean up and exit the configfs system - * @core: The avirt_core pointer - */ -void __exit __avirt_configfs_exit(struct avirt_core *core); - -/** - * __avirt_streams_seal - Register the sound card to user space - * @return: 0 on success, negative ERRNO on failure - */ -int __avirt_streams_seal(void); - -/** - * __avirt_streams_sealed - Check whether the streams have been sealed or not - * @return: true if sealed, false otherwise - */ -bool __avirt_streams_sealed(void); - -/** - * __avirt_stream_find_by_device - Get audio stream from device number - * @device: The PCM device number corresponding to the desired stream - * @return: The audio stream if found, or an error pointer otherwise - */ -struct avirt_stream *__avirt_stream_find_by_device(unsigned int device); - -/** - * __avirt_stream_create - Create audio stream, including it's ALSA PCM device - * @name: The name designated to the audio stream - * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or - * SNDRV_PCM_STREAM_CAPTURE) - * @return: The newly created audio stream if successful, or an error pointer - */ -struct avirt_stream *__avirt_stream_create(const char *name, int direction); - -#endif /* __SOUND_AVIRT_CORE_H */ -- cgit 1.2.3-korg From d66dbf0820e9e7e14a6f723c1ce92b7ac4e0280d Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 25 Oct 2018 17:37:07 +1100 Subject: Refactor all avirt_ symbols to have have snd prefix, as per Linux sound This will aid the eventual migration the the upstream mainline Linux kernel Fix some checkpatch warnings, and other compiler warnings Signed-off-by: Mark Farrugia --- Makefile | 16 ++-- alsa-pcm.c | 70 ++++++++--------- configfs.c | 116 +++++++++++++++------------- core.c | 197 ++++++++++++++++++++++++----------------------- core.h | 33 ++++---- docs/2.Building.md | 2 +- docs/3.Usage.md | 40 +++++----- dummy/Makefile | 4 +- dummy/dummy.c | 22 +++--- loopback/Makefile | 4 +- loopback/loopback.c | 32 ++++---- scripts/load.sh | 6 +- scripts/make-agl.sh | 6 +- scripts/test_configfs.sh | 26 +++---- scripts/unload.sh | 10 +-- sound/avirt.h | 54 ++++++------- 16 files changed, 325 insertions(+), 313 deletions(-) diff --git a/Makefile b/Makefile index 791a60a..84b8b4d 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_AVIRT) += avirt_core.o -avirt_core-y := core.o -avirt_core-y += alsa-pcm.o -avirt_core-y += configfs.o +obj-$(CONFIG_AVIRT) += snd-avirt-core.o +snd-avirt-core-y := core.o +snd-avirt-core-y += alsa-pcm.o +snd-avirt-core-y += configfs.o ifeq ($(CONFIG_AVIRT_BUILDLOCAL),) CCFLAGS_AVIRT := "drivers/staging/" @@ -13,8 +13,8 @@ endif ccflags-y += -I${CCFLAGS_AVIRT} $(info $(KERNELRELEASE)) -obj-$(CONFIG_AVIRT_DUMMYAP) += dummy/ -obj-$(CONFIG_AVIRT_LOOPBACKAP) += loopback/ +obj-$(CONFIG_AVIRT_AP_DUMMY) += dummy/ +obj-$(CONFIG_AVIRT_AP_LOOPBACK) += loopback/ ### # For out-of-tree building @@ -29,8 +29,8 @@ endif all: CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y \ - CONFIG_AVIRT_DUMMYAP=m \ - CONFIG_AVIRT_LOOPBACKAP=m \ + CONFIG_AVIRT_AP_DUMMY=m \ + CONFIG_AVIRT_AP_LOOPBACK=m \ make -C $(KERNEL_SRC) M=$(PWD) clean: diff --git a/alsa-pcm.c b/alsa-pcm.c index 4d7b549..603c096 100644 --- a/alsa-pcm.c +++ b/alsa-pcm.c @@ -3,7 +3,7 @@ * AVIRT - ALSA Virtual Soundcard * * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd - * + * * alsa-pcm.c - AVIRT PCM interface */ @@ -15,24 +15,24 @@ #define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args) #define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args) -#define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ - (((ap)->pcm_ops->callback) ? \ - (ap)->pcm_ops->callback((substream), ##__VA_ARGS__) : \ +#define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ + (((ap)->pcm_ops->callback) ? \ + (ap)->pcm_ops->callback((substream), ##__VA_ARGS__) : \ 0) /** - * avirt_pcm_period_elapsed - PCM buffer complete callback + * snd_avirt_pcm_period_elapsed - PCM buffer complete callback * @substreamid: pointer to ALSA PCM substream * - * This should be called from a child Audio Path once it has finished processing - * the pcm buffer + * This should be called from a child Audio Path once it has finished + * processing the pcm buffer */ -void avirt_pcm_period_elapsed(struct snd_pcm_substream *substream) +void snd_avirt_pcm_period_elapsed(struct snd_pcm_substream *substream) { // Notify ALSA middle layer of the elapsed period boundary snd_pcm_period_elapsed(substream); } -EXPORT_SYMBOL_GPL(avirt_pcm_period_elapsed); +EXPORT_SYMBOL_GPL(snd_avirt_pcm_period_elapsed); /******************************************************************************* * ALSA PCM Callbacks @@ -41,20 +41,20 @@ EXPORT_SYMBOL_GPL(avirt_pcm_period_elapsed); * pcm_open - Implements 'open' callback for PCM middle layer * @substream: pointer to ALSA PCM substream * - * This is called when an ALSA PCM substream is opened. The substream device is - * configured here. + * This is called when an ALSA PCM substream is opened. The substream device + * is configured here. * * Returns 0 on success or error code otherwise. */ static int pcm_open(struct snd_pcm_substream *substream) { - struct avirt_audiopath *audiopath; - struct avirt_stream *stream; + struct snd_avirt_audiopath *audiopath; + struct snd_avirt_stream *stream; struct snd_pcm_hardware *hw; unsigned int chans = 0; - stream = __avirt_stream_find_by_device(substream->pcm->device); - audiopath = avirt_audiopath_get(stream->map); + stream = snd_avirt_stream_find_by_device(substream->pcm->device); + audiopath = snd_avirt_audiopath_get(stream->map); CHK_NULL_V(audiopath, "Cannot find Audio Path uid: '%s'!", stream->map); substream->private_data = audiopath; @@ -62,7 +62,7 @@ static int pcm_open(struct snd_pcm_substream *substream) hw = &substream->runtime->hw; memcpy(hw, audiopath->hw, sizeof(struct snd_pcm_hardware)); - stream = __avirt_stream_find_by_device(substream->pcm->device); + stream = snd_avirt_stream_find_by_device(substream->pcm->device); if (IS_ERR_VALUE(stream) || !stream) return PTR_ERR(stream); @@ -87,7 +87,7 @@ static int pcm_close(struct snd_pcm_substream *substream) { // Do additional Audio Path 'close' callback return DO_AUDIOPATH_CB( - ((struct avirt_audiopath *)substream->private_data), close, + ((struct snd_avirt_audiopath *)substream->private_data), close, substream); } @@ -106,10 +106,10 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, { int retval; size_t bufsz; - struct avirt_audiopath *audiopath; - struct avirt_stream *stream; + struct snd_avirt_audiopath *audiopath; + struct snd_avirt_stream *stream; - stream = __avirt_stream_find_by_device(substream->pcm->device); + stream = snd_avirt_stream_find_by_device(substream->pcm->device); if (IS_ERR_VALUE(stream) || !stream) return PTR_ERR(stream); @@ -120,7 +120,7 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - audiopath = ((struct avirt_audiopath *)substream->private_data); + audiopath = ((struct snd_avirt_audiopath *)substream->private_data); bufsz = params_buffer_bytes(hw_params) * audiopath->hw->periods_max; retval = snd_pcm_lib_alloc_vmalloc_buffer(substream, bufsz); @@ -145,8 +145,8 @@ static int pcm_hw_free(struct snd_pcm_substream *substream) // Do additional Audio Path 'hw_free' callback err = DO_AUDIOPATH_CB( - ((struct avirt_audiopath *)substream->private_data), hw_free, - substream); + ((struct snd_avirt_audiopath *)substream->private_data), + hw_free, substream); return snd_pcm_lib_free_vmalloc_buffer(substream); } @@ -165,8 +165,8 @@ static int pcm_prepare(struct snd_pcm_substream *substream) { // Do additional Audio Path 'prepare' callback return DO_AUDIOPATH_CB( - ((struct avirt_audiopath *)substream->private_data), prepare, - substream); + ((struct snd_avirt_audiopath *)substream->private_data), + prepare, substream); } /** @@ -194,8 +194,8 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) // Do additional Audio Path 'trigger' callback return DO_AUDIOPATH_CB( - ((struct avirt_audiopath *)substream->private_data), trigger, - substream, cmd); + ((struct snd_avirt_audiopath *)substream->private_data), + trigger, substream, cmd); } /** @@ -212,8 +212,8 @@ static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) { // Do additional Audio Path 'pointer' callback return DO_AUDIOPATH_CB( - ((struct avirt_audiopath *)substream->private_data), pointer, - substream); + ((struct snd_avirt_audiopath *)substream->private_data), + pointer, substream); } /** @@ -235,7 +235,7 @@ static int pcm_get_time_info( struct snd_pcm_audio_tstamp_report *audio_tstamp_report) { return DO_AUDIOPATH_CB( - ((struct avirt_audiopath *)substream->private_data), + ((struct snd_avirt_audiopath *)substream->private_data), get_time_info, substream, system_ts, audio_ts, audio_tstamp_config, audio_tstamp_report); } @@ -265,8 +265,8 @@ static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, // Do additional Audio Path 'copy_user' callback return DO_AUDIOPATH_CB( - ((struct avirt_audiopath *)substream->private_data), copy_user, - substream, channel, pos, src, count); + ((struct snd_avirt_audiopath *)substream->private_data), + copy_user, substream, channel, pos, src, count); } /** @@ -286,7 +286,7 @@ static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, unsigned long pos, void *buf, unsigned long count) { return DO_AUDIOPATH_CB( - ((struct avirt_audiopath *)substream->private_data), + ((struct snd_avirt_audiopath *)substream->private_data), copy_kernel, substream, channel, pos, buf, count); } @@ -302,7 +302,7 @@ static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, static int pcm_ack(struct snd_pcm_substream *substream) { return DO_AUDIOPATH_CB( - ((struct avirt_audiopath *)substream->private_data), ack, + ((struct snd_avirt_audiopath *)substream->private_data), ack, substream); } @@ -310,7 +310,7 @@ static int pcm_silence(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) { return DO_AUDIOPATH_CB( - ((struct avirt_audiopath *)substream->private_data), + ((struct snd_avirt_audiopath *)substream->private_data), fill_silence, substream, channel, pos, count); } diff --git a/configfs.c b/configfs.c index 1a7e7c5..12e95a5 100644 --- a/configfs.c +++ b/configfs.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 -/* +/* * AVIRT - ALSA Virtual Soundcard * * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd - * + * * configfs.c - AVIRT configfs support */ @@ -17,54 +17,61 @@ #define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args) #define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args) -static ssize_t cfg_avirt_stream_direction_show(struct config_item *item, - char *page) +static ssize_t cfg_snd_avirt_stream_direction_show(struct config_item *item, + char *page) { ssize_t count; - struct avirt_stream *stream = avirt_stream_from_config_item(item); + struct snd_avirt_stream *stream = + snd_avirt_stream_from_config_item(item); count = sprintf(page, "%d\n", stream->direction); return count; } -CONFIGFS_ATTR_RO(cfg_avirt_stream_, direction); +CONFIGFS_ATTR_RO(cfg_snd_avirt_stream_, direction); -static ssize_t cfg_avirt_stream_map_show(struct config_item *item, char *page) +static ssize_t cfg_snd_avirt_stream_map_show(struct config_item *item, + char *page) { - struct avirt_stream *stream = avirt_stream_from_config_item(item); + struct snd_avirt_stream *stream = + snd_avirt_stream_from_config_item(item); return sprintf(page, "%s\n", stream->map); } -static ssize_t cfg_avirt_stream_map_store(struct config_item *item, - const char *page, size_t count) +static ssize_t cfg_snd_avirt_stream_map_store(struct config_item *item, + const char *page, size_t count) { char *split; - struct avirt_stream *stream = avirt_stream_from_config_item(item); + struct snd_avirt_stream *stream = + snd_avirt_stream_from_config_item(item); split = strsep((char **)&page, "\n"); memcpy(stream->map, (char *)split, count); return count; } -CONFIGFS_ATTR(cfg_avirt_stream_, map); +CONFIGFS_ATTR(cfg_snd_avirt_stream_, map); -static ssize_t cfg_avirt_stream_channels_show(struct config_item *item, - char *page) +static ssize_t cfg_snd_avirt_stream_channels_show(struct config_item *item, + char *page) { ssize_t count; - struct avirt_stream *stream = avirt_stream_from_config_item(item); + struct snd_avirt_stream *stream = + snd_avirt_stream_from_config_item(item); count = sprintf(page, "%d\n", stream->channels); return count; } -static ssize_t cfg_avirt_stream_channels_store(struct config_item *item, - const char *page, size_t count) +static ssize_t cfg_snd_avirt_stream_channels_store(struct config_item *item, + const char *page, + size_t count) { int err; - struct avirt_stream *stream = avirt_stream_from_config_item(item); + struct snd_avirt_stream *stream = + snd_avirt_stream_from_config_item(item); unsigned long tmp; char *p = (char *)page; @@ -79,37 +86,37 @@ static ssize_t cfg_avirt_stream_channels_store(struct config_item *item, return count; } -CONFIGFS_ATTR(cfg_avirt_stream_, channels); +CONFIGFS_ATTR(cfg_snd_avirt_stream_, channels); -static struct configfs_attribute *cfg_avirt_stream_attrs[] = { - &cfg_avirt_stream_attr_channels, - &cfg_avirt_stream_attr_map, - &cfg_avirt_stream_attr_direction, +static struct configfs_attribute *cfg_snd_avirt_stream_attrs[] = { + &cfg_snd_avirt_stream_attr_channels, + &cfg_snd_avirt_stream_attr_map, + &cfg_snd_avirt_stream_attr_direction, NULL, }; -static void cfg_avirt_stream_release(struct config_item *item) +static void cfg_snd_avirt_stream_release(struct config_item *item) { D_INFOK("item->name:%s", item->ci_namebuf); - kfree(avirt_stream_from_config_item(item)); + kfree(snd_avirt_stream_from_config_item(item)); } -static struct configfs_item_operations cfg_avirt_stream_ops = { - .release = cfg_avirt_stream_release, +static struct configfs_item_operations cfg_snd_avirt_stream_ops = { + .release = cfg_snd_avirt_stream_release, }; -static struct config_item_type cfg_avirt_stream_type = { - .ct_item_ops = &cfg_avirt_stream_ops, - .ct_attrs = cfg_avirt_stream_attrs, +static struct config_item_type cfg_snd_avirt_stream_type = { + .ct_item_ops = &cfg_snd_avirt_stream_ops, + .ct_attrs = cfg_snd_avirt_stream_attrs, .ct_owner = THIS_MODULE, }; static struct config_item * - cfg_avirt_stream_make_item(struct config_group *group, const char *name) +cfg_snd_avirt_stream_make_item(struct config_group *group, const char *name) { char *split; int direction; - struct avirt_stream *stream; + struct snd_avirt_stream *stream; // Get prefix (playback_ or capture_) split = strsep((char **)&name, "_"); @@ -130,24 +137,25 @@ static struct config_item * // Get stream name, and create PCM for stream split = strsep((char **)&name, "\n"); - stream = __avirt_stream_create(split, direction); + stream = snd_avirt_stream_create(split, direction); if (IS_ERR(stream)) return ERR_PTR(PTR_ERR(stream)); - config_item_init_type_name(&stream->item, name, &cfg_avirt_stream_type); + config_item_init_type_name(&stream->item, name, + &cfg_snd_avirt_stream_type); return &stream->item; } -static ssize_t cfg_avirt_stream_group_sealed_show(struct config_item *item, - char *page) +static ssize_t cfg_snd_avirt_stream_group_sealed_show(struct config_item *item, + char *page) { - return snprintf(page, PAGE_SIZE, "%d\n", __avirt_streams_sealed()); + return snprintf(page, PAGE_SIZE, "%d\n", snd_avirt_streams_sealed()); } -static ssize_t cfg_avirt_stream_group_sealed_store(struct config_item *item, - const char *page, - size_t count) +static ssize_t cfg_snd_avirt_stream_group_sealed_store(struct config_item *item, + const char *page, + size_t count) { unsigned long tmp; char *p = (char *)page; @@ -159,24 +167,24 @@ static ssize_t cfg_avirt_stream_group_sealed_store(struct config_item *item, return -ERANGE; } - __avirt_streams_seal(); + snd_avirt_streams_seal(); return count; } -CONFIGFS_ATTR(cfg_avirt_stream_group_, sealed); +CONFIGFS_ATTR(cfg_snd_avirt_stream_group_, sealed); -static struct configfs_attribute *cfg_avirt_stream_group_attrs[] = { - &cfg_avirt_stream_group_attr_sealed, +static struct configfs_attribute *cfg_snd_avirt_stream_group_attrs[] = { + &cfg_snd_avirt_stream_group_attr_sealed, NULL, }; -static struct configfs_group_operations cfg_avirt_stream_group_ops = { - .make_item = cfg_avirt_stream_make_item +static struct configfs_group_operations cfg_snd_avirt_stream_group_ops = { + .make_item = cfg_snd_avirt_stream_make_item }; static struct config_item_type cfg_stream_group_type = { - .ct_group_ops = &cfg_avirt_stream_group_ops, - .ct_attrs = cfg_avirt_stream_group_attrs, + .ct_group_ops = &cfg_snd_avirt_stream_group_ops, + .ct_attrs = cfg_snd_avirt_stream_group_attrs, .ct_owner = THIS_MODULE }; @@ -185,17 +193,15 @@ static struct config_item_type cfg_avirt_group_type = { }; static struct configfs_subsystem cfg_subsys = { - .su_group = - { - .cg_item = - { - .ci_namebuf = "avirt", + .su_group = { + .cg_item = { + .ci_namebuf = "snd-avirt", .ci_type = &cfg_avirt_group_type, }, }, }; -int __init __avirt_configfs_init(struct avirt_core *core) +int __init snd_avirt_configfs_init(struct snd_avirt_core *core) { int err; @@ -222,7 +228,7 @@ exit_configfs: return err; } -void __exit __avirt_configfs_exit(struct avirt_core *core) +void __exit snd_avirt_configfs_exit(struct snd_avirt_core *core) { configfs_unregister_default_group(core->stream_group); configfs_unregister_subsystem(&cfg_subsys); diff --git a/core.c b/core.c index 08381e5..aacc0a1 100644 --- a/core.c +++ b/core.c @@ -3,7 +3,7 @@ * AVIRT - ALSA Virtual Soundcard * * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd - * + * * core.c - AVIRT core internals */ @@ -27,44 +27,43 @@ MODULE_LICENSE("GPL v2"); #define SND_AVIRTUAL_DRIVER "snd_avirt" -extern struct snd_pcm_ops pcm_ops; - -static struct avirt_core core = { +static struct snd_avirt_core core = { .stream_count = 0, .streams_sealed = false, }; -struct avirt_coreinfo coreinfo = { - .version = {0, 0, 1}, +struct snd_avirt_coreinfo coreinfo = { + .version = { 0, 0, 1 }, }; static LIST_HEAD(audiopath_list); -struct avirt_audiopath_obj { +struct snd_avirt_audiopath_obj { struct kobject kobj; struct list_head list; - struct avirt_audiopath *path; + struct snd_avirt_audiopath *path; }; -static struct kset *avirt_audiopath_kset; +static struct kset *snd_avirt_audiopath_kset; static struct kobject *kobj; -#define to_audiopath_obj(d) container_of(d, struct avirt_audiopath_obj, kobj) -#define to_audiopath_attr(a) \ - container_of(a, struct avirt_audiopath_attribute, attr) +#define to_audiopath_obj(d) \ + container_of(d, struct snd_avirt_audiopath_obj, kobj) +#define to_audiopath_attr(a) \ + container_of(a, struct snd_avirt_audiopath_attribute, attr) /** - * struct avirt_audiopath_attribute - access the attributes of Audio Path + * struct snd_avirt_audiopath_attribute - access the attributes of Audio Path * @attr: attributes of an Audio Path * @show: pointer to the show function * @store: pointer to the store function */ -struct avirt_audiopath_attribute { +struct snd_avirt_audiopath_attribute { struct attribute attr; - ssize_t (*show)(struct avirt_audiopath_obj *d, - struct avirt_audiopath_attribute *attr, char *buf); - ssize_t (*store)(struct avirt_audiopath_obj *d, - struct avirt_audiopath_attribute *attr, + ssize_t (*show)(struct snd_avirt_audiopath_obj *d, + struct snd_avirt_audiopath_attribute *attr, char *buf); + ssize_t (*store)(struct snd_avirt_audiopath_obj *d, + struct snd_avirt_audiopath_attribute *attr, const char *buf, size_t count); }; @@ -77,8 +76,8 @@ struct avirt_audiopath_attribute { static ssize_t audiopath_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { - struct avirt_audiopath_attribute *audiopath_attr; - struct avirt_audiopath_obj *audiopath_obj; + struct snd_avirt_audiopath_attribute *audiopath_attr; + struct snd_avirt_audiopath_obj *audiopath_obj; audiopath_attr = to_audiopath_attr(attr); audiopath_obj = to_audiopath_obj(kobj); @@ -100,8 +99,8 @@ static ssize_t audiopath_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) { - struct avirt_audiopath_attribute *audiopath_attr; - struct avirt_audiopath_obj *audiopath_obj; + struct snd_avirt_audiopath_attribute *audiopath_attr; + struct snd_avirt_audiopath_obj *audiopath_obj; audiopath_attr = to_audiopath_attr(attr); audiopath_obj = to_audiopath_obj(kobj); @@ -111,84 +110,88 @@ static ssize_t audiopath_attr_store(struct kobject *kobj, return audiopath_attr->store(audiopath_obj, audiopath_attr, buf, len); } -static const struct sysfs_ops avirt_audiopath_sysfs_ops = { +static const struct sysfs_ops snd_avirt_audiopath_sysfs_ops = { .show = audiopath_attr_show, .store = audiopath_attr_store, }; /** - * avirt_audiopath_release - Audio Path release function + * snd_avirt_audiopath_release - Audio Path release function * @kobj: pointer to Audio Paths's kobject */ -static void avirt_audiopath_release(struct kobject *kobj) +static void snd_avirt_audiopath_release(struct kobject *kobj) { - struct avirt_audiopath_obj *audiopath_obj = to_audiopath_obj(kobj); + struct snd_avirt_audiopath_obj *audiopath_obj = to_audiopath_obj(kobj); kfree(audiopath_obj); } -static ssize_t audiopath_name_show(struct avirt_audiopath_obj *audiopath_obj, - struct avirt_audiopath_attribute *attr, - char *buf) +static ssize_t + audiopath_name_show(struct snd_avirt_audiopath_obj *audiopath_obj, + struct snd_avirt_audiopath_attribute *attr, + char *buf) { return sprintf(buf, "%s\n", audiopath_obj->path->name); } -static ssize_t audiopath_version_show(struct avirt_audiopath_obj *audiopath_obj, - struct avirt_audiopath_attribute *attr, - char *buf) +static ssize_t + audiopath_version_show(struct snd_avirt_audiopath_obj *audiopath_obj, + struct snd_avirt_audiopath_attribute *attr, + char *buf) { - struct avirt_audiopath *audiopath = audiopath_obj->path; + struct snd_avirt_audiopath *audiopath = audiopath_obj->path; return sprintf(buf, "%d.%d.%d\n", audiopath->version[0], audiopath->version[1], audiopath->version[2]); } -static struct avirt_audiopath_attribute avirt_audiopath_attrs[] = { +static struct snd_avirt_audiopath_attribute snd_avirt_audiopath_attrs[] = { __ATTR_RO(audiopath_name), __ATTR_RO(audiopath_version), }; -static struct attribute *avirt_audiopath_def_attrs[] = { - &avirt_audiopath_attrs[0].attr, - &avirt_audiopath_attrs[1].attr, +static struct attribute *snd_avirt_audiopath_def_attrs[] = { + &snd_avirt_audiopath_attrs[0].attr, + &snd_avirt_audiopath_attrs[1].attr, NULL, }; -static struct kobj_type avirt_audiopath_ktype = { - .sysfs_ops = &avirt_audiopath_sysfs_ops, - .release = avirt_audiopath_release, - .default_attrs = avirt_audiopath_def_attrs, +static struct kobj_type snd_avirt_audiopath_ktype = { + .sysfs_ops = &snd_avirt_audiopath_sysfs_ops, + .release = snd_avirt_audiopath_release, + .default_attrs = snd_avirt_audiopath_def_attrs, }; /** - * create_avirt_audiopath_obj - creates an Audio Path object + * create_snd_avirt_audiopath_obj - creates an Audio Path object * @uid: Unique ID of the Audio Path * * This creates an Audio Path object and assigns the kset and registers * it with sysfs. * @return: Pointer to the Audio Path object or NULL if it failed. */ -static struct avirt_audiopath_obj *create_avirt_audiopath_obj(const char *uid) +static struct snd_avirt_audiopath_obj * + create_snd_avirt_audiopath_obj(const char *uid) { - struct avirt_audiopath_obj *avirt_audiopath; + struct snd_avirt_audiopath_obj *snd_avirt_audiopath; int retval; - avirt_audiopath = kzalloc(sizeof(*avirt_audiopath), GFP_KERNEL); - if (!avirt_audiopath) + snd_avirt_audiopath = kzalloc(sizeof(*snd_avirt_audiopath), GFP_KERNEL); + if (!snd_avirt_audiopath) return NULL; - avirt_audiopath->kobj.kset = avirt_audiopath_kset; - retval = kobject_init_and_add(&avirt_audiopath->kobj, - &avirt_audiopath_ktype, kobj, "%s", uid); + snd_avirt_audiopath->kobj.kset = snd_avirt_audiopath_kset; + retval = kobject_init_and_add(&snd_avirt_audiopath->kobj, + &snd_avirt_audiopath_ktype, kobj, "%s", + uid); if (retval) { - kobject_put(&avirt_audiopath->kobj); + kobject_put(&snd_avirt_audiopath->kobj); return NULL; } - kobject_uevent(&avirt_audiopath->kobj, KOBJ_ADD); - return avirt_audiopath; + kobject_uevent(&snd_avirt_audiopath->kobj, KOBJ_ADD); + return snd_avirt_audiopath; } -static struct snd_pcm *pcm_create(struct avirt_stream *stream) +static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream) { bool playback = false, capture = false; struct snd_pcm *pcm; @@ -224,25 +227,24 @@ static struct snd_pcm *pcm_create(struct avirt_stream *stream) } /** - * destroy_avirt_audiopath_obj - destroys an Audio Path object + * destroy_snd_avirt_audiopath_obj - destroys an Audio Path object * @name: the Audio Path object */ -static void destroy_avirt_audiopath_obj(struct avirt_audiopath_obj *p) +static void destroy_snd_avirt_audiopath_obj(struct snd_avirt_audiopath_obj *p) { kobject_put(&p->kobj); } /** - * avirt_audiopath_get - retrieves the Audio Path by its UID + * snd_avirt_audiopath_get - retrieves the Audio Path by its UID * @uid: Unique ID for the Audio Path * @return: Corresponding Audio Path */ -struct avirt_audiopath *avirt_audiopath_get(const char *uid) +struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid) { - struct avirt_audiopath_obj *ap_obj; + struct snd_avirt_audiopath_obj *ap_obj; - list_for_each_entry(ap_obj, &audiopath_list, list) - { + list_for_each_entry(ap_obj, &audiopath_list, list) { // pr_info("get_ap %s\n", ap_obj->path->uid); if (!strcmp(ap_obj->path->uid, uid)) return ap_obj->path; @@ -252,22 +254,22 @@ struct avirt_audiopath *avirt_audiopath_get(const char *uid) } /** - * avirt_audiopath_register - register Audio Path with ALSA virtual driver + * snd_avirt_audiopath_register - register Audio Path with AVIRT * @audiopath: Audio Path to be registered * @core: ALSA virtual driver core info * @return: 0 on success or error code otherwise */ -int avirt_audiopath_register(struct avirt_audiopath *audiopath, - struct avirt_coreinfo **info) +int snd_avirt_audiopath_register(struct snd_avirt_audiopath *audiopath, + struct snd_avirt_coreinfo **info) { - struct avirt_audiopath_obj *audiopath_obj; + struct snd_avirt_audiopath_obj *audiopath_obj; if (!audiopath) { D_ERRORK("Audio Path is NULL!"); return -EINVAL; } - audiopath_obj = create_avirt_audiopath_obj(audiopath->uid); + audiopath_obj = create_snd_avirt_audiopath_obj(audiopath->uid); if (!audiopath_obj) { D_INFOK("Failed to alloc driver object"); return -ENOMEM; @@ -288,16 +290,16 @@ int avirt_audiopath_register(struct avirt_audiopath *audiopath, return 0; } -EXPORT_SYMBOL_GPL(avirt_audiopath_register); +EXPORT_SYMBOL_GPL(snd_avirt_audiopath_register); /** - * avirt_audiopath_deregister - deregister Audio Path with ALSA virtual driver + * snd_avirt_audiopath_deregister - deregister Audio Path with AVIRT * @audiopath: Audio Path to be deregistered * @return: 0 on success or error code otherwise */ -int avirt_audiopath_deregister(struct avirt_audiopath *audiopath) +int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath) { - struct avirt_audiopath_obj *audiopath_obj; + struct snd_avirt_audiopath_obj *audiopath_obj; // Check if audio path is registered if (!audiopath) { @@ -312,32 +314,31 @@ int avirt_audiopath_deregister(struct avirt_audiopath *audiopath) } list_del(&audiopath_obj->list); - destroy_avirt_audiopath_obj(audiopath_obj); + destroy_snd_avirt_audiopath_obj(audiopath_obj); D_INFOK("Deregistered Audio Path %s", audiopath->uid); return 0; } -EXPORT_SYMBOL_GPL(avirt_audiopath_deregister); +EXPORT_SYMBOL_GPL(snd_avirt_audiopath_deregister); /** - * avirt_stream_count - get the stream count for the given direction + * snd_avirt_stream_count - get the stream count for the given direction * @direction: The direction to get the stream count for * @return: The stream count */ -int avirt_stream_count(unsigned int direction) +int snd_avirt_stream_count(unsigned int direction) { struct list_head *entry; struct config_item *item; - struct avirt_stream *stream; + struct snd_avirt_stream *stream; unsigned int count = 0; if (direction > 1) return -ERANGE; - list_for_each(entry, &core.stream_group->cg_children) - { + list_for_each(entry, &core.stream_group->cg_children) { item = container_of(entry, struct config_item, ci_entry); - stream = avirt_stream_from_config_item(item); + stream = snd_avirt_stream_from_config_item(item); if (!stream) return -EFAULT; if (stream->direction == direction) @@ -346,18 +347,19 @@ int avirt_stream_count(unsigned int direction) return count; } -EXPORT_SYMBOL_GPL(avirt_stream_count); +EXPORT_SYMBOL_GPL(snd_avirt_stream_count); /** - * __avirt_stream_create - Create audio stream, including it's ALSA PCM device + * snd_avirt_stream_create - Create audio stream, including it's ALSA PCM device * @name: The name designated to the audio stream * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or * SNDRV_PCM_STREAM_CAPTURE) * @return: The newly created audio stream if successful, or an error pointer */ -struct avirt_stream *__avirt_stream_create(const char *name, int direction) +struct snd_avirt_stream *snd_avirt_stream_create(const char *name, + int direction) { - struct avirt_stream *stream; + struct snd_avirt_stream *stream; stream = kzalloc(sizeof(*stream), GFP_KERNEL); if (!stream) @@ -374,11 +376,11 @@ struct avirt_stream *__avirt_stream_create(const char *name, int direction) return stream; } -int __avirt_streams_seal(void) +int snd_avirt_streams_seal(void) { int err = 0; - struct avirt_audiopath_obj *ap_obj; - struct avirt_stream *stream; + struct snd_avirt_audiopath_obj *ap_obj; + struct snd_avirt_stream *stream; struct config_item *item; struct list_head *entry; @@ -387,9 +389,9 @@ int __avirt_streams_seal(void) return -1; } - list_for_each (entry, &core.stream_group->cg_children) { + list_for_each(entry, &core.stream_group->cg_children) { item = container_of(entry, struct config_item, ci_entry); - stream = avirt_stream_from_config_item(item); + stream = snd_avirt_stream_from_config_item(item); if (!stream) return -EFAULT; stream->pcm = pcm_create(stream); @@ -397,7 +399,7 @@ int __avirt_streams_seal(void) return (PTR_ERR(stream->pcm)); } - list_for_each_entry (ap_obj, &audiopath_list, list) { + list_for_each_entry(ap_obj, &audiopath_list, list) { D_INFOK("configure() AP uid: %s", ap_obj->path->uid); ap_obj->path->configure(core.card, core.stream_group, core.stream_count); @@ -414,14 +416,14 @@ int __avirt_streams_seal(void) return err; } -bool __avirt_streams_sealed(void) +bool snd_avirt_streams_sealed(void) { return core.streams_sealed; } -struct avirt_stream *__avirt_stream_find_by_device(unsigned int device) +struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device) { - struct avirt_stream *stream; + struct snd_avirt_stream *stream; struct config_item *item; struct list_head *entry; @@ -430,10 +432,9 @@ struct avirt_stream *__avirt_stream_find_by_device(unsigned int device) return ERR_PTR(-EINVAL); } - list_for_each(entry, &core.stream_group->cg_children) - { + list_for_each(entry, &core.stream_group->cg_children) { item = container_of(entry, struct config_item, ci_entry); - stream = avirt_stream_from_config_item(item); + stream = snd_avirt_stream_from_config_item(item); if (!stream) return ERR_PTR(-EFAULT); if (stream->device == device) @@ -478,14 +479,14 @@ static int __init core_init(void) strncpy(core.card->longname, "A virtual sound card driver for ALSA", 80); - avirt_audiopath_kset = + snd_avirt_audiopath_kset = kset_create_and_add("audiopaths", NULL, &core.dev->kobj); - if (!avirt_audiopath_kset) { + if (!snd_avirt_audiopath_kset) { err = -ENOMEM; goto exit_snd_card; } - err = __avirt_configfs_init(&core); + err = snd_avirt_configfs_init(&core); if (err < 0) goto exit_snd_card; @@ -506,9 +507,9 @@ exit_class: */ static void __exit core_exit(void) { - __avirt_configfs_exit(&core); + snd_avirt_configfs_exit(&core); - kset_unregister(avirt_audiopath_kset); + kset_unregister(snd_avirt_audiopath_kset); snd_card_free(core.card); device_destroy(core.avirt_class, 0); class_destroy(core.avirt_class); diff --git a/core.h b/core.h index ca47067..735df65 100644 --- a/core.h +++ b/core.h @@ -14,7 +14,9 @@ #include "utils.h" -struct avirt_core { +extern struct snd_pcm_ops pcm_ops; + +struct snd_avirt_core { struct snd_card *card; struct device *dev; struct class *avirt_class; @@ -24,44 +26,45 @@ struct avirt_core { }; /** - * __avirt_configfs_init - Initialise the configfs system - * @core: The avirt_core pointer + * snd_avirt_configfs_init - Initialise the configfs system + * @core: The snd_avirt_core pointer * @return: 0 on success, negative ERRNO on failure */ -int __init __avirt_configfs_init(struct avirt_core *core); +int __init snd_avirt_configfs_init(struct snd_avirt_core *core); /** - * __avirt_configfs_exit - Clean up and exit the configfs system - * @core: The avirt_core pointer + * snd_avirt_configfs_exit - Clean up and exit the configfs system + * @core: The snd_avirt_core pointer */ -void __exit __avirt_configfs_exit(struct avirt_core *core); +void __exit snd_avirt_configfs_exit(struct snd_avirt_core *core); /** - * __avirt_streams_seal - Register the sound card to user space + * snd_avirt_streams_seal - Register the sound card to user space * @return: 0 on success, negative ERRNO on failure */ -int __avirt_streams_seal(void); +int snd_avirt_streams_seal(void); /** - * __avirt_streams_sealed - Check whether the streams have been sealed or not + * snd_avirt_streams_sealed - Check if the streams have been sealed or not * @return: true if sealed, false otherwise */ -bool __avirt_streams_sealed(void); +bool snd_avirt_streams_sealed(void); /** - * __avirt_stream_find_by_device - Get audio stream from device number + * snd_avirt_stream_find_by_device - Get audio stream from device number * @device: The PCM device number corresponding to the desired stream * @return: The audio stream if found, or an error pointer otherwise */ -struct avirt_stream *__avirt_stream_find_by_device(unsigned int device); +struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device); /** - * __avirt_stream_create - Create audio stream, including it's ALSA PCM device + * snd_avirt_stream_create - Create audio stream, including it's ALSA PCM device * @name: The name designated to the audio stream * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or * SNDRV_PCM_STREAM_CAPTURE) * @return: The newly created audio stream if successful, or an error pointer */ -struct avirt_stream *__avirt_stream_create(const char *name, int direction); +struct snd_avirt_stream *snd_avirt_stream_create(const char *name, + int direction); #endif /* __SOUND_AVIRT_CORE_H */ diff --git a/docs/2.Building.md b/docs/2.Building.md index 3e42746..5fd9d23 100644 --- a/docs/2.Building.md +++ b/docs/2.Building.md @@ -28,7 +28,7 @@ $ make or ```sh -$ CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) +$ CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_AP_DUMMY=m CONFIG_AVIRT_AP_LOOPBACK=m make -C /lib/modules/$(uname -r)/build/ M=$(pwd) ``` The latter is executed internally with the make file. diff --git a/docs/3.Usage.md b/docs/3.Usage.md index d65d50d..f535a07 100644 --- a/docs/3.Usage.md +++ b/docs/3.Usage.md @@ -18,9 +18,9 @@ As root, load the required `avirt_core.ko` and subsequent audio path. As an example, the Dummy Audiopath is being loaded here. ```sh -insmod avirt_core.ko -insmod dummy/avirt_dummyap.ko -insmod loopback/avirt_loopbackap.ko +insmod snd-avirt-core.ko +insmod dummy/snd-avirt-ap-dummy.ko +insmod loopback/snd-avirt-ap-loopback.ko ``` ### Note: @@ -47,16 +47,16 @@ For example, in AGL: ```sh mkdir -p /lib/modules/$(uname -r)/extra -cp avirt_core.ko avirt_dummyap.ko avirt_loopbackap.ko /lib/modules/$(uname -r)/extra +cp snd-avirt-core.ko snd-avirt-ap-dummy.ko snd-avirt-ap-loopback.ko /lib/modules/$(uname -r)/extra depmod ``` Once the modules are in place, we can load the modules using: ```sh -modprobe avirt_core -modprobe avirt_dummyap -modprobe avirt_loopbackap +modprobe snd-avirt-core +modprobe snd-avirt-ap-dummy +modprobe snd-avirt-ap-loopback ``` @@ -93,24 +93,24 @@ Finally, we can configure AVIRT, for example: ```sh # Set up each of the cards channels -mkdir /config/avirt/streams/playback_media -echo "2">/config/avirt/streams/playback_media/channels -echo "ap_dummy">/config/avirt/streams/playback_media/map +mkdir /config/snd-avirt/streams/playback_media +echo "2">/config/snd-avirt/streams/playback_media/channels +echo "ap_dummy">/config/snd-avirt/streams/playback_media/map -mkdir /config/avirt/streams/playback_navigation -echo "1">/config/avirt/streams/playback_navigation/channels -echo "ap_dummy">/config/avirt/streams/playback_navigation/map +mkdir /config/snd-avirt/streams/playback_navigation +echo "1">/config/snd-avirt/streams/playback_navigation/channels +echo "ap_dummy">/config/snd-avirt/streams/playback_navigation/map -mkdir /config/avirt/streams/playback_emergency -echo "1">/config/avirt/streams/playback_emergency/channels -echo "ap_dummy">/config/avirt/streams/playback_emergency/map +mkdir /config/snd-avirt/streams/playback_emergency +echo "1">/config/snd-avirt/streams/playback_emergency/channels +echo "ap_dummy">/config/snd-avirt/streams/playback_emergency/map -mkdir /config/avirt/streams/capture_voice -echo "1">/config/avirt/streams/capture_voice/channels -echo "ap_dummy">/config/avirt/streams/capture_voice/map +mkdir /config/snd-avirt/streams/capture_voice +echo "1">/config/snd-avirt/streams/capture_voice/channels +echo "ap_dummy">/config/snd-avirt/streams/capture_voice/map # Finally, seal the card, and initiate configuration -echo "1">/config/avirt/streams/sealed +echo "1">/config/snd-avirt/streams/sealed ``` Alternatively, the test script at `scripts/test_configfs.sh` can be used. diff --git a/dummy/Makefile b/dummy/Makefile index 78579d7..11ceceb 100644 --- a/dummy/Makefile +++ b/dummy/Makefile @@ -1,6 +1,6 @@ -obj-$(CONFIG_AVIRT_DUMMYAP) += avirt_dummyap.o +obj-$(CONFIG_AVIRT_AP_DUMMY) += snd-avirt-ap-dummy.o $(info $(src)) -avirt_dummyap-objs := dummy.o +snd-avirt-ap-dummy-objs := dummy.o ccflags-y += -Idrivers/staging/ ccflags-y += -I$(src)/../ diff --git a/dummy/dummy.c b/dummy/dummy.c index 100972a..5f19564 100644 --- a/dummy/dummy.c +++ b/dummy/dummy.c @@ -34,7 +34,7 @@ MODULE_LICENSE("GPL v2"); #define get_dummy_ops(substream) \ (*(const struct dummy_timer_ops **)(substream)->runtime->private_data) -static struct avirt_coreinfo *coreinfo; +static struct snd_avirt_coreinfo *coreinfo; /******************************************************************************* * System Timer Interface @@ -140,7 +140,7 @@ static void dummy_systimer_callback(struct timer_list *t) dpcm->elapsed = 0; spin_unlock_irqrestore(&dpcm->lock, flags); if (elapsed) - avirt_pcm_period_elapsed(dpcm->substream); + snd_avirt_pcm_period_elapsed(dpcm->substream); } static snd_pcm_uframes_t @@ -241,18 +241,18 @@ static struct snd_pcm_ops dummyap_pcm_ops = { /******************************************************************************* * Dummy Audio Path AVIRT registration ******************************************************************************/ -int dummy_configure(struct snd_card *card, - struct config_group *avirt_stream_group, - unsigned int stream_count) +static int dummy_configure(struct snd_card *card, + struct config_group *snd_avirt_stream_group, + unsigned int stream_count) { // Do something with streams struct list_head *entry; - list_for_each (entry, &avirt_stream_group->cg_children) { + list_for_each (entry, &snd_avirt_stream_group->cg_children) { struct config_item *item = container_of(entry, struct config_item, ci_entry); - struct avirt_stream *stream = - avirt_stream_from_config_item(item); + struct snd_avirt_stream *stream = + snd_avirt_stream_from_config_item(item); AP_INFOK("stream name:%s device:%d channels:%d", stream->name, stream->device, stream->channels); } @@ -272,7 +272,7 @@ static struct snd_pcm_hardware dummyap_hw = { .periods_max = DUMMY_PERIODS_MAX, }; -static struct avirt_audiopath dummyap_module = { +static struct snd_avirt_audiopath dummyap_module = { .uid = "ap_dummy", .name = "Dummy Audio Path", .version = { 0, 0, 1 }, @@ -287,7 +287,7 @@ static int __init dummy_init(void) pr_info("init()\n"); - err = avirt_audiopath_register(&dummyap_module, &coreinfo); + err = snd_avirt_audiopath_register(&dummyap_module, &coreinfo); if ((err < 0) || (!coreinfo)) { pr_err("%s: coreinfo is NULL!\n", __func__); return err; @@ -300,7 +300,7 @@ static void __exit dummy_exit(void) { pr_info("exit()\n"); - avirt_audiopath_deregister(&dummyap_module); + snd_avirt_audiopath_deregister(&dummyap_module); } module_init(dummy_init); diff --git a/loopback/Makefile b/loopback/Makefile index 9d3c606..0546168 100644 --- a/loopback/Makefile +++ b/loopback/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_AVIRT_LOOPBACKAP) += avirt_loopbackap.o +obj-$(CONFIG_AVIRT_AP_LOOPBACK) += snd-avirt-ap-loopback.o $(info $(src)) -avirt_loopbackap-objs := loopback.o +snd-avirt-ap-loopback-objs := loopback.o ccflags-y += -Idrivers/staging/ ccflags-y += -I$(src)/../ diff --git a/loopback/loopback.c b/loopback/loopback.c index ba9225e..b4a82e5 100644 --- a/loopback/loopback.c +++ b/loopback/loopback.c @@ -46,7 +46,7 @@ MODULE_LICENSE("GPL"); #define NO_PITCH 100000 -static struct avirt_coreinfo *coreinfo; +static struct snd_avirt_coreinfo *coreinfo; static struct loopback *loopback; struct loopback_pcm; @@ -509,7 +509,7 @@ static void loopback_timer_function(struct timer_list *t) dpcm->period_update_pending = 0; spin_unlock_irqrestore(&dpcm->cable->lock, flags); /* need to unlock before calling below */ - avirt_pcm_period_elapsed(dpcm->substream); + snd_avirt_pcm_period_elapsed(dpcm->substream); return; } } @@ -529,7 +529,7 @@ static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(runtime, pos); } -static const struct snd_pcm_hardware loopback_pcm_hardware = { +static const struct snd_pcm_hardware loopbackap_pcm_hardware = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), @@ -661,7 +661,7 @@ static int loopback_open(struct snd_pcm_substream *substream) goto unlock; } spin_lock_init(&cable->lock); - cable->hw = loopback_pcm_hardware; + cable->hw = loopbackap_pcm_hardware; loopback->cables[substream->pcm->device] = cable; } dpcm->cable = cable; @@ -689,7 +689,7 @@ static int loopback_open(struct snd_pcm_substream *substream) runtime->private_data = dpcm; runtime->private_free = loopback_runtime_free; if (get_notify(dpcm)) - runtime->hw = loopback_pcm_hardware; + runtime->hw = loopbackap_pcm_hardware; else runtime->hw = cable->hw; @@ -936,7 +936,7 @@ static int loopback_mixer_new(struct loopback *loopback, int notify) int dev, dev_count, idx, err; strcpy(card->mixername, "Loopback Mixer"); - dev_count = avirt_stream_count(SNDRV_PCM_STREAM_PLAYBACK); + dev_count = snd_avirt_stream_count(SNDRV_PCM_STREAM_PLAYBACK); for (dev = 0; dev < dev_count; dev++) { pcm = loopback->pcm[dev]; setup = &loopback->setup[dev]; @@ -1043,9 +1043,9 @@ static int loopback_proc_new(struct loopback *loopback, int cidx) return 0; } -int loopbackap_configure(struct snd_card *card, - struct config_group *avirt_stream_group, - unsigned int stream_count) +static int loopbackap_configure(struct snd_card *card, + struct config_group *snd_avirt_stream_group, + unsigned int stream_count) { int err; struct list_head *entry; @@ -1056,11 +1056,11 @@ int loopbackap_configure(struct snd_card *card, loopback->card = card; mutex_init(&loopback->cable_lock); - list_for_each (entry, &avirt_stream_group->cg_children) { + list_for_each (entry, &snd_avirt_stream_group->cg_children) { struct config_item *item = container_of(entry, struct config_item, ci_entry); - struct avirt_stream *stream = - avirt_stream_from_config_item(item); + struct snd_avirt_stream *stream = + snd_avirt_stream_from_config_item(item); loopback->pcm[stream->device] = stream->pcm; AP_INFOK("stream name:%s device:%d channels:%d", stream->name, @@ -1080,11 +1080,11 @@ int loopbackap_configure(struct snd_card *card, /******************************************************************************* * Loopback Audio Path AVIRT registration ******************************************************************************/ -static struct avirt_audiopath loopbackap_module = { +static struct snd_avirt_audiopath loopbackap_module = { .uid = AP_UID, .name = "Loopback Audio Path", .version = { 0, 0, 1 }, - .hw = &loopback_pcm_hardware, + .hw = &loopbackap_pcm_hardware, .pcm_ops = &loopbackap_pcm_ops, .configure = loopbackap_configure, }; @@ -1093,7 +1093,7 @@ static int __init alsa_card_loopback_init(void) { int err = 0; - err = avirt_audiopath_register(&loopbackap_module, &coreinfo); + err = snd_avirt_audiopath_register(&loopbackap_module, &coreinfo); if ((err < 0) || (!coreinfo)) { AP_ERRORK("coreinfo is NULL!"); return err; @@ -1104,7 +1104,7 @@ static int __init alsa_card_loopback_init(void) static void __exit alsa_card_loopback_exit(void) { - avirt_audiopath_deregister(&loopbackap_module); + snd_avirt_audiopath_deregister(&loopbackap_module); } module_init(alsa_card_loopback_init); diff --git a/scripts/load.sh b/scripts/load.sh index 82f613a..103a63b 100755 --- a/scripts/load.sh +++ b/scripts/load.sh @@ -1,11 +1,11 @@ #!/bin/sh # Load the core -insmod avirt_core.ko +insmod snd-avirt-core.ko # Load the additional Audio Paths -insmod dummy/avirt_dummyap.ko -insmod loopback/avirt_loopbackap.ko +insmod dummy/snd-avirt-ap-dummy.ko +insmod loopback/snd-avirt-ap-loopback.ko # Run the test script ./scripts/test_configfs.sh diff --git a/scripts/make-agl.sh b/scripts/make-agl.sh index 4421af8..6c94fbb 100755 --- a/scripts/make-agl.sh +++ b/scripts/make-agl.sh @@ -7,6 +7,8 @@ long_sdkpath=$(xds-cli sdks get $sdk_id | grep Path) sdkpath=${long_sdkpath:4} # Build -/opt/AGL/bin/xds-cli exec --config xds-project.conf -- \ - LDFLAGS= CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y CONFIG_AVIRT_DUMMYAP=m \ +/opt/AGL/bin/xds-cli exec --config xds-project.conf -- \ + LDFLAGS= CONFIG_AVIRT=m CONFIG_AVIRT_BUILDLOCAL=y \ + CONFIG_AVIRT_AP_DUMMY=m \ + CONFIG_AVIRT_AP_LOOPBACK=m \ make -C $sdkpath/sysroots/aarch64-agl-linux/usr/src/kernel M=$(pwd) $@ diff --git a/scripts/test_configfs.sh b/scripts/test_configfs.sh index baa5e58..b6a56f6 100755 --- a/scripts/test_configfs.sh +++ b/scripts/test_configfs.sh @@ -2,20 +2,20 @@ mkdir -p /config && mount -t configfs none /config -mkdir /config/avirt/streams/playback_media -echo "2">/config/avirt/streams/playback_media/channels -echo "ap_loopback">/config/avirt/streams/playback_media/map +mkdir /config/snd-avirt/streams/playback_media +echo "2">/config/snd-avirt/streams/playback_media/channels +echo "ap_loopback">/config/snd-avirt/streams/playback_media/map -mkdir /config/avirt/streams/playback_navigation -echo "1">/config/avirt/streams/playback_navigation/channels -echo "ap_loopback">/config/avirt/streams/playback_navigation/map +mkdir /config/snd-avirt/streams/playback_navigation +echo "1">/config/snd-avirt/streams/playback_navigation/channels +echo "ap_loopback">/config/snd-avirt/streams/playback_navigation/map -mkdir /config/avirt/streams/playback_emergency -echo "1">/config/avirt/streams/playback_emergency/channels -echo "ap_loopback">/config/avirt/streams/playback_emergency/map +mkdir /config/snd-avirt/streams/playback_emergency +echo "1">/config/snd-avirt/streams/playback_emergency/channels +echo "ap_loopback">/config/snd-avirt/streams/playback_emergency/map -mkdir /config/avirt/streams/capture_voice -echo "1">/config/avirt/streams/capture_voice/channels -echo "ap_loopback">/config/avirt/streams/capture_voice/map +mkdir /config/snd-avirt/streams/capture_voice +echo "1">/config/snd-avirt/streams/capture_voice/channels +echo "ap_loopback">/config/snd-avirt/streams/capture_voice/map -echo "1">/config/avirt/streams/sealed +echo "1">/config/snd-avirt/streams/sealed diff --git a/scripts/unload.sh b/scripts/unload.sh index 2c158d5..a18e1f5 100755 --- a/scripts/unload.sh +++ b/scripts/unload.sh @@ -1,13 +1,13 @@ #!/bin/sh -rmdir /config/avirt/streams/playback_* -rmdir /config/avirt/streams/capture_* +rmdir /config/snd-avirt/streams/playback_* +rmdir /config/snd-avirt/streams/capture_* rm_module() { lsmod |grep "^$1\>" && rmmod $1 || true } -rm_module avirt_loopbackap -rm_module avirt_dummyap -rm_module avirt_core +rm_module snd_avirt_ap_loopback +rm_module snd_avirt_ap_dummy +rm_module snd_avirt_core echo "Drivers Removed!" diff --git a/sound/avirt.h b/sound/avirt.h index 5df84fe..1a5c546 100644 --- a/sound/avirt.h +++ b/sound/avirt.h @@ -17,34 +17,34 @@ #define MAX_STREAMS 16 #define MAX_NAME_LEN 80 -#define DINFO(logname, fmt, args...) \ +#define DINFO(logname, fmt, args...) \ snd_printk(KERN_INFO "AVIRT: %s: " fmt "\n", logname, ##args) -#define DERROR(logname, fmt, args...) \ +#define DERROR(logname, fmt, args...) \ snd_printk(KERN_ERR "AVIRT: %s: " fmt "\n", logname, ##args) -#define DDEBUG(logname, fmt, args...) \ +#define DDEBUG(logname, fmt, args...) \ snd_printk(KERN_DEBUG "AVIRT: %s: " fmt "\n", logname, ##args) /** * AVIRT Audio Path configure function type - * Each Audio Path registers this at avirt_audiopath_register time. + * Each Audio Path registers this at snd_avirt_audiopath_register time. * It is then called by the core once AVIRT has been configured */ -typedef int (*avirt_audiopath_configure)(struct snd_card *card, - struct config_group *stream_group, - unsigned int stream_count); +typedef int (*snd_avirt_audiopath_configure)(struct snd_card *card, + struct config_group *stream_group, + unsigned int stream_count); /** * AVIRT Audio Path info */ -struct avirt_audiopath { +struct snd_avirt_audiopath { const char *uid; /* Unique identifier */ const char *name; /* Pretty name */ unsigned int version[3]; /* Version - Major.Minor.Ext */ const struct snd_pcm_hardware *hw; /* ALSA PCM HW conf */ const struct snd_pcm_ops *pcm_ops; /* ALSA PCM op table */ - avirt_audiopath_configure configure; /* Configure callback function */ + snd_avirt_audiopath_configure configure; /* Config callback function */ void *context; }; @@ -52,7 +52,7 @@ struct avirt_audiopath { /* * Audio stream configuration */ -struct avirt_stream { +struct snd_avirt_stream { char name[MAX_NAME_LEN]; /* Stream name */ char map[MAX_NAME_LEN]; /* Stream Audio Path mapping */ unsigned int channels; /* Stream channel count */ @@ -65,58 +65,58 @@ struct avirt_stream { /** * AVIRT core info */ -struct avirt_coreinfo { +struct snd_avirt_coreinfo { unsigned int version[3]; }; /** - * avirt_audiopath_register - register Audio Path with ALSA virtual driver + * snd_avirt_audiopath_register - register Audio Path with AVIRT * @audiopath: Audio Path to be registered * @core: ALSA virtual driver core info * @return: 0 on success or error code otherwise */ -int avirt_audiopath_register(struct avirt_audiopath *audiopath, - struct avirt_coreinfo **coreinfo); +int snd_avirt_audiopath_register(struct snd_avirt_audiopath *audiopath, + struct snd_avirt_coreinfo **coreinfo); /** - * avirt_audiopath_deregister - deregister Audio Path with ALSA virtual driver + * snd_avirt_audiopath_deregister - deregister Audio Path with AVIRT * @audiopath: Audio Path to be deregistered * @return: 0 on success or error code otherwise */ -int avirt_audiopath_deregister(struct avirt_audiopath *audiopath); +int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath); /** - * avirt_audiopath_get - retrieves the Audio Path by it's UID + * snd_avirt_audiopath_get - retrieves the Audio Path by it's UID * @uid: Unique ID for the Audio Path * @return: Corresponding Audio Path */ -struct avirt_audiopath *avirt_audiopath_get(const char *uid); +struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid); /** - * avirt_stream_count - get the stream count for the given direction + * snd_avirt_stream_count - get the stream count for the given direction * @direction: The direction to get the stream count for * @return: The stream count */ -int avirt_stream_count(unsigned int direction); +int snd_avirt_stream_count(unsigned int direction); /** - * avirt_stream_from_config_item - Convert a config_item to an avirt_stream + * snd_avirt_stream_from_config_item - Convert config_item to a snd_avirt_stream * @item: The config_item to convert from - * @return: The item's avirt_stream if successful, NULL otherwise + * @return: The item's snd_avirt_stream if successful, NULL otherwise */ -static inline struct avirt_stream * -avirt_stream_from_config_item(struct config_item *item) +static inline struct snd_avirt_stream * + snd_avirt_stream_from_config_item(struct config_item *item) { - return item ? container_of(item, struct avirt_stream, item) : NULL; + return item ? container_of(item, struct snd_avirt_stream, item) : NULL; } /** - * avirt_pcm_period_elapsed - PCM buffer complete callback + * snd_avirt_pcm_period_elapsed - PCM buffer complete callback * @substream: pointer to ALSA PCM substream * * This should be called from a child Audio Path once it has finished processing * the PCM buffer */ -void avirt_pcm_period_elapsed(struct snd_pcm_substream *substream); +void snd_avirt_pcm_period_elapsed(struct snd_pcm_substream *substream); #endif // __SOUND_AVIRT_H -- cgit 1.2.3-korg From cce221e938509ac9ccf3b59b943930aeac27c311 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 25 Oct 2018 17:37:17 +1100 Subject: Update .clang-format This is taken from the mainline Linux kernel This is used for automatically formatting the source to Linus' strict standards Signed-off-by: Mark Farrugia --- .clang-format | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.clang-format b/.clang-format index 4b283db..1d5da22 100644 --- a/.clang-format +++ b/.clang-format @@ -13,7 +13,7 @@ AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false -AlignEscapedNewlines: Left +#AlignEscapedNewlines: Left # Unknown to clang-format-4.0 AlignOperands: true AlignTrailingComments: false AllowAllParametersOfDeclarationOnNextLine: false @@ -382,7 +382,7 @@ IncludeIsMainRegex: '(Test)?$' IndentCaseLabels: false #IndentPPDirectives: None # Unknown to clang-format-5.0 IndentWidth: 8 -IndentWrappedFunctionNames: true +IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: false -- cgit 1.2.3-korg From 2351ee5ce29c814ba2861d619e89fa8b6bda0484 Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Thu, 25 Oct 2018 17:38:44 +1100 Subject: Refactor alsa-pcm.c to pcm.c No need for alsa- prefix any longer Signed-off-by: Mark Farrugia --- Makefile | 2 +- alsa-pcm.c | 332 ------------------------------------------------------------- pcm.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 333 insertions(+), 333 deletions(-) delete mode 100644 alsa-pcm.c create mode 100644 pcm.c diff --git a/Makefile b/Makefile index 84b8b4d..e2a154e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_AVIRT) += snd-avirt-core.o snd-avirt-core-y := core.o -snd-avirt-core-y += alsa-pcm.o +snd-avirt-core-y += pcm.o snd-avirt-core-y += configfs.o ifeq ($(CONFIG_AVIRT_BUILDLOCAL),) diff --git a/alsa-pcm.c b/alsa-pcm.c deleted file mode 100644 index 603c096..0000000 --- a/alsa-pcm.c +++ /dev/null @@ -1,332 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * AVIRT - ALSA Virtual Soundcard - * - * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd - * - * alsa-pcm.c - AVIRT PCM interface - */ - -#include "core.h" - -#define D_LOGNAME "pcm" - -#define D_INFOK(fmt, args...) DINFO(D_LOGNAME, fmt, ##args) -#define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args) -#define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args) - -#define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ - (((ap)->pcm_ops->callback) ? \ - (ap)->pcm_ops->callback((substream), ##__VA_ARGS__) : \ - 0) - -/** - * snd_avirt_pcm_period_elapsed - PCM buffer complete callback - * @substreamid: pointer to ALSA PCM substream - * - * This should be called from a child Audio Path once it has finished - * processing the pcm buffer - */ -void snd_avirt_pcm_period_elapsed(struct snd_pcm_substream *substream) -{ - // Notify ALSA middle layer of the elapsed period boundary - snd_pcm_period_elapsed(substream); -} -EXPORT_SYMBOL_GPL(snd_avirt_pcm_period_elapsed); - -/******************************************************************************* - * ALSA PCM Callbacks - ******************************************************************************/ -/** - * pcm_open - Implements 'open' callback for PCM middle layer - * @substream: pointer to ALSA PCM substream - * - * This is called when an ALSA PCM substream is opened. The substream device - * is configured here. - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_avirt_audiopath *audiopath; - struct snd_avirt_stream *stream; - struct snd_pcm_hardware *hw; - unsigned int chans = 0; - - stream = snd_avirt_stream_find_by_device(substream->pcm->device); - audiopath = snd_avirt_audiopath_get(stream->map); - CHK_NULL_V(audiopath, "Cannot find Audio Path uid: '%s'!", stream->map); - substream->private_data = audiopath; - - // Copy the hw params from the audiopath to the pcm - hw = &substream->runtime->hw; - memcpy(hw, audiopath->hw, sizeof(struct snd_pcm_hardware)); - - stream = snd_avirt_stream_find_by_device(substream->pcm->device); - if (IS_ERR_VALUE(stream) || !stream) - return PTR_ERR(stream); - - // Setup remaining hw properties - chans = stream->channels; - hw->channels_min = chans; - hw->channels_max = chans; - - // Do additional Audio Path 'open' callback - return DO_AUDIOPATH_CB(audiopath, open, substream); -} - -/** - * pcm_close - Implements 'close' callback for PCM middle layer - * @substream: pointer to ALSA PCM substream - * - * This is called when a PCM substream is closed. - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_close(struct snd_pcm_substream *substream) -{ - // Do additional Audio Path 'close' callback - return DO_AUDIOPATH_CB( - ((struct snd_avirt_audiopath *)substream->private_data), close, - substream); -} - -/** - * pcm_hw_params - Implements 'hw_params' callback for PCM middle layer - * @substream: pointer to ALSA PCM substream - * @hw_params: contains the hardware parameters for the PCM - * - * This is called when the hardware parameters are set, including buffer size, - * the period size, the format, etc. The buffer allocation should be done here. - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - int retval; - size_t bufsz; - struct snd_avirt_audiopath *audiopath; - struct snd_avirt_stream *stream; - - stream = snd_avirt_stream_find_by_device(substream->pcm->device); - if (IS_ERR_VALUE(stream) || !stream) - return PTR_ERR(stream); - - if ((params_channels(hw_params) > stream->channels) || - (params_channels(hw_params) < stream->channels)) { - D_ERRORK("Requested number of channels: %d not supported", - params_channels(hw_params)); - return -EINVAL; - } - - audiopath = ((struct snd_avirt_audiopath *)substream->private_data); - bufsz = params_buffer_bytes(hw_params) * audiopath->hw->periods_max; - - retval = snd_pcm_lib_alloc_vmalloc_buffer(substream, bufsz); - if (retval < 0) - D_ERRORK("pcm: buffer allocation failed: %d", retval); - - return retval; -} - -/** - * pcm_hw_free - Implements 'hw_free' callback for PCM middle layer - * @substream: pointer to ALSA PCM substream - * - * Release the resources allocated via 'hw_params'. - * This function is always called before the 'close' callback . - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_hw_free(struct snd_pcm_substream *substream) -{ - int err; - - // Do additional Audio Path 'hw_free' callback - err = DO_AUDIOPATH_CB( - ((struct snd_avirt_audiopath *)substream->private_data), - hw_free, substream); - - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -/** - * pcm_prepare - Implements 'prepare' callback for PCM middle layer - * @substream: pointer to ALSA PCM substream - * - * The format rate, sample rate, etc., can be set here. This callback can be - * called many times at each setup. This function is also used to handle overrun - * and underrun conditions when we try and resync the DMA (if we're using DMA). - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_prepare(struct snd_pcm_substream *substream) -{ - // Do additional Audio Path 'prepare' callback - return DO_AUDIOPATH_CB( - ((struct snd_avirt_audiopath *)substream->private_data), - prepare, substream); -} - -/** - * pcm_trigger - Implements 'trigger' callback for PCM middle layer - * @substream: pointer to ALSA PCM substream - * @cmd: action to be performed (start or stop) - * - * This is called when the PCM is started, stopped or paused. The action - * indicated action is specified in the second argument, SNDRV_PCM_TRIGGER_XXX - * - * Returns 0 on success or error code otherwise. - */ -static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - break; - default: - D_ERRORK("Invalid trigger cmd: %d", cmd); - return -EINVAL; - } - - // Do additional Audio Path 'trigger' callback - return DO_AUDIOPATH_CB( - ((struct snd_avirt_audiopath *)substream->private_data), - trigger, substream, cmd); -} - -/** - * pcm_pointer - Implements 'pointer' callback for PCM middle layer - * @substream: pointer to ALSA PCM substream - * - * This gets called when the user space needs a DMA buffer index. IO errors will - * be generated if the index does not increment, or drives beyond the frame - * threshold of the buffer itself. - * - * Returns the current hardware buffer frame index. - */ -static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) -{ - // Do additional Audio Path 'pointer' callback - return DO_AUDIOPATH_CB( - ((struct snd_avirt_audiopath *)substream->private_data), - pointer, substream); -} - -/** - * pcm_pointer - Implements 'get_time_info' callback for PCM middle layer - * @substream: pointer to ALSA PCM substream - * @system_ts - * @audio_ts - * @audio_tstamp_config - * @audio_tstamp_report - * - * Generic way to get system timestamp and audio timestamp info - * - * Returns 0 on success or error code otherwise - */ -static int pcm_get_time_info( - struct snd_pcm_substream *substream, struct timespec *system_ts, - struct timespec *audio_ts, - struct snd_pcm_audio_tstamp_config *audio_tstamp_config, - struct snd_pcm_audio_tstamp_report *audio_tstamp_report) -{ - return DO_AUDIOPATH_CB( - ((struct snd_avirt_audiopath *)substream->private_data), - get_time_info, substream, system_ts, audio_ts, - audio_tstamp_config, audio_tstamp_report); -} - -/** - * pcm_copy_user - Implements 'copy_user' callback for PCM middle layer - * @substream: pointer to ALSA PCM substream - * @channel: - * @pos: The offset in the DMA - * @src: Audio PCM data from the user space - * @count: - * - * This is where we need to copy user audio PCM data into the sound driver - * - * Returns 0 on success or error code otherwise. - * - */ -static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void __user *src, - snd_pcm_uframes_t count) -{ - // struct snd_pcm_runtime *runtime; - // int offset; - - // runtime = substream->runtime; - // offset = frames_to_bytes(runtime, pos); - - // Do additional Audio Path 'copy_user' callback - return DO_AUDIOPATH_CB( - ((struct snd_avirt_audiopath *)substream->private_data), - copy_user, substream, channel, pos, src, count); -} - -/** - * pcm_copy_kernel - Implements 'copy_kernel' callback for PCM middle layer - * @substream: pointer to ALSA PCM substream - * @channel: - * @pos: The offset in the DMA - * @src: Audio PCM data from the user space - * @count: - * - * This is where we need to copy kernel audio PCM data into the sound driver - * - * Returns 0 on success or error code otherwise. - * - */ -static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, - unsigned long pos, void *buf, unsigned long count) -{ - return DO_AUDIOPATH_CB( - ((struct snd_avirt_audiopath *)substream->private_data), - copy_kernel, substream, channel, pos, buf, count); -} - -/** - * pcm_ack - Implements 'ack' callback for PCM middle layer - * @substream: pointer to ALSA PCM substream - * - * This is where we need to ack - * - * Returns 0 on success or error code otherwise. - * - */ -static int pcm_ack(struct snd_pcm_substream *substream) -{ - return DO_AUDIOPATH_CB( - ((struct snd_avirt_audiopath *)substream->private_data), ack, - substream); -} - -static int pcm_silence(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, snd_pcm_uframes_t count) -{ - return DO_AUDIOPATH_CB( - ((struct snd_avirt_audiopath *)substream->private_data), - fill_silence, substream, channel, pos, count); -} - -struct snd_pcm_ops pcm_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_hw_params, - .hw_free = pcm_hw_free, - .prepare = pcm_prepare, - .trigger = pcm_trigger, - .pointer = pcm_pointer, - .get_time_info = pcm_get_time_info, - .fill_silence = pcm_silence, - .copy_user = pcm_copy_user, - .copy_kernel = pcm_copy_kernel, - .page = snd_pcm_lib_get_vmalloc_page, - .ack = pcm_ack, -}; diff --git a/pcm.c b/pcm.c new file mode 100644 index 0000000..babc8b6 --- /dev/null +++ b/pcm.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AVIRT - ALSA Virtual Soundcard + * + * Copyright (c) 2010-2018 Fiberdyne Systems Pty Ltd + * + * pcm.c - AVIRT PCM interface + */ + +#include "core.h" + +#define D_LOGNAME "pcm" + +#define D_INFOK(fmt, args...) DINFO(D_LOGNAME, fmt, ##args) +#define D_PRINTK(fmt, args...) DDEBUG(D_LOGNAME, fmt, ##args) +#define D_ERRORK(fmt, args...) DERROR(D_LOGNAME, fmt, ##args) + +#define DO_AUDIOPATH_CB(ap, callback, substream, ...) \ + (((ap)->pcm_ops->callback) ? \ + (ap)->pcm_ops->callback((substream), ##__VA_ARGS__) : \ + 0) + +/** + * snd_avirt_pcm_period_elapsed - PCM buffer complete callback + * @substreamid: pointer to ALSA PCM substream + * + * This should be called from a child Audio Path once it has finished + * processing the pcm buffer + */ +void snd_avirt_pcm_period_elapsed(struct snd_pcm_substream *substream) +{ + // Notify ALSA middle layer of the elapsed period boundary + snd_pcm_period_elapsed(substream); +} +EXPORT_SYMBOL_GPL(snd_avirt_pcm_period_elapsed); + +/******************************************************************************* + * ALSA PCM Callbacks + ******************************************************************************/ +/** + * pcm_open - Implements 'open' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * This is called when an ALSA PCM substream is opened. The substream device + * is configured here. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_avirt_audiopath *audiopath; + struct snd_avirt_stream *stream; + struct snd_pcm_hardware *hw; + unsigned int chans = 0; + + stream = snd_avirt_stream_find_by_device(substream->pcm->device); + audiopath = snd_avirt_audiopath_get(stream->map); + CHK_NULL_V(audiopath, "Cannot find Audio Path uid: '%s'!", stream->map); + substream->private_data = audiopath; + + // Copy the hw params from the audiopath to the pcm + hw = &substream->runtime->hw; + memcpy(hw, audiopath->hw, sizeof(struct snd_pcm_hardware)); + + stream = snd_avirt_stream_find_by_device(substream->pcm->device); + if (IS_ERR_VALUE(stream) || !stream) + return PTR_ERR(stream); + + // Setup remaining hw properties + chans = stream->channels; + hw->channels_min = chans; + hw->channels_max = chans; + + // Do additional Audio Path 'open' callback + return DO_AUDIOPATH_CB(audiopath, open, substream); +} + +/** + * pcm_close - Implements 'close' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * This is called when a PCM substream is closed. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_close(struct snd_pcm_substream *substream) +{ + // Do additional Audio Path 'close' callback + return DO_AUDIOPATH_CB( + ((struct snd_avirt_audiopath *)substream->private_data), close, + substream); +} + +/** + * pcm_hw_params - Implements 'hw_params' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * @hw_params: contains the hardware parameters for the PCM + * + * This is called when the hardware parameters are set, including buffer size, + * the period size, the format, etc. The buffer allocation should be done here. + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int retval; + size_t bufsz; + struct snd_avirt_audiopath *audiopath; + struct snd_avirt_stream *stream; + + stream = snd_avirt_stream_find_by_device(substream->pcm->device); + if (IS_ERR_VALUE(stream) || !stream) + return PTR_ERR(stream); + + if ((params_channels(hw_params) > stream->channels) || + (params_channels(hw_params) < stream->channels)) { + D_ERRORK("Requested number of channels: %d not supported", + params_channels(hw_params)); + return -EINVAL; + } + + audiopath = ((struct snd_avirt_audiopath *)substream->private_data); + bufsz = params_buffer_bytes(hw_params) * audiopath->hw->periods_max; + + retval = snd_pcm_lib_alloc_vmalloc_buffer(substream, bufsz); + if (retval < 0) + D_ERRORK("pcm: buffer allocation failed: %d", retval); + + return retval; +} + +/** + * pcm_hw_free - Implements 'hw_free' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * Release the resources allocated via 'hw_params'. + * This function is always called before the 'close' callback . + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_hw_free(struct snd_pcm_substream *substream) +{ + int err; + + // Do additional Audio Path 'hw_free' callback + err = DO_AUDIOPATH_CB( + ((struct snd_avirt_audiopath *)substream->private_data), + hw_free, substream); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +/** + * pcm_prepare - Implements 'prepare' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * The format rate, sample rate, etc., can be set here. This callback can be + * called many times at each setup. This function is also used to handle overrun + * and underrun conditions when we try and resync the DMA (if we're using DMA). + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_prepare(struct snd_pcm_substream *substream) +{ + // Do additional Audio Path 'prepare' callback + return DO_AUDIOPATH_CB( + ((struct snd_avirt_audiopath *)substream->private_data), + prepare, substream); +} + +/** + * pcm_trigger - Implements 'trigger' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * @cmd: action to be performed (start or stop) + * + * This is called when the PCM is started, stopped or paused. The action + * indicated action is specified in the second argument, SNDRV_PCM_TRIGGER_XXX + * + * Returns 0 on success or error code otherwise. + */ +static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + break; + default: + D_ERRORK("Invalid trigger cmd: %d", cmd); + return -EINVAL; + } + + // Do additional Audio Path 'trigger' callback + return DO_AUDIOPATH_CB( + ((struct snd_avirt_audiopath *)substream->private_data), + trigger, substream, cmd); +} + +/** + * pcm_pointer - Implements 'pointer' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * This gets called when the user space needs a DMA buffer index. IO errors will + * be generated if the index does not increment, or drives beyond the frame + * threshold of the buffer itself. + * + * Returns the current hardware buffer frame index. + */ +static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) +{ + // Do additional Audio Path 'pointer' callback + return DO_AUDIOPATH_CB( + ((struct snd_avirt_audiopath *)substream->private_data), + pointer, substream); +} + +/** + * pcm_pointer - Implements 'get_time_info' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * @system_ts + * @audio_ts + * @audio_tstamp_config + * @audio_tstamp_report + * + * Generic way to get system timestamp and audio timestamp info + * + * Returns 0 on success or error code otherwise + */ +static int pcm_get_time_info( + struct snd_pcm_substream *substream, struct timespec *system_ts, + struct timespec *audio_ts, + struct snd_pcm_audio_tstamp_config *audio_tstamp_config, + struct snd_pcm_audio_tstamp_report *audio_tstamp_report) +{ + return DO_AUDIOPATH_CB( + ((struct snd_avirt_audiopath *)substream->private_data), + get_time_info, substream, system_ts, audio_ts, + audio_tstamp_config, audio_tstamp_report); +} + +/** + * pcm_copy_user - Implements 'copy_user' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * @channel: + * @pos: The offset in the DMA + * @src: Audio PCM data from the user space + * @count: + * + * This is where we need to copy user audio PCM data into the sound driver + * + * Returns 0 on success or error code otherwise. + * + */ +static int pcm_copy_user(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t pos, void __user *src, + snd_pcm_uframes_t count) +{ + // struct snd_pcm_runtime *runtime; + // int offset; + + // runtime = substream->runtime; + // offset = frames_to_bytes(runtime, pos); + + // Do additional Audio Path 'copy_user' callback + return DO_AUDIOPATH_CB( + ((struct snd_avirt_audiopath *)substream->private_data), + copy_user, substream, channel, pos, src, count); +} + +/** + * pcm_copy_kernel - Implements 'copy_kernel' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * @channel: + * @pos: The offset in the DMA + * @src: Audio PCM data from the user space + * @count: + * + * This is where we need to copy kernel audio PCM data into the sound driver + * + * Returns 0 on success or error code otherwise. + * + */ +static int pcm_copy_kernel(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void *buf, unsigned long count) +{ + return DO_AUDIOPATH_CB( + ((struct snd_avirt_audiopath *)substream->private_data), + copy_kernel, substream, channel, pos, buf, count); +} + +/** + * pcm_ack - Implements 'ack' callback for PCM middle layer + * @substream: pointer to ALSA PCM substream + * + * This is where we need to ack + * + * Returns 0 on success or error code otherwise. + * + */ +static int pcm_ack(struct snd_pcm_substream *substream) +{ + return DO_AUDIOPATH_CB( + ((struct snd_avirt_audiopath *)substream->private_data), ack, + substream); +} + +static int pcm_silence(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +{ + return DO_AUDIOPATH_CB( + ((struct snd_avirt_audiopath *)substream->private_data), + fill_silence, substream, channel, pos, count); +} + +struct snd_pcm_ops pcm_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_prepare, + .trigger = pcm_trigger, + .pointer = pcm_pointer, + .get_time_info = pcm_get_time_info, + .fill_silence = pcm_silence, + .copy_user = pcm_copy_user, + .copy_kernel = pcm_copy_kernel, + .page = snd_pcm_lib_get_vmalloc_page, + .ack = pcm_ack, +}; -- cgit 1.2.3-korg From 188712f58b309d94d26d7468a792326c464a7eda Mon Sep 17 00:00:00 2001 From: Mark Farrugia Date: Fri, 26 Oct 2018 17:30:31 +1100 Subject: Add missing changes after GitHub rebase Some Usage doc changes Signed-off-by: Mark Farrugia --- docs/3.Usage.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/3.Usage.md b/docs/3.Usage.md index f535a07..ba5c3e6 100644 --- a/docs/3.Usage.md +++ b/docs/3.Usage.md @@ -7,10 +7,11 @@ Using AVIRT 1. [Load/Unload AVIRT](#un-load-avirt) 2. [Configuring AVIRT](#configuring-avirt) +3. [Checking AVIRT](#checking-avirt) -## Loading and Unloading AVIRT +## 1. Loading and Unloading AVIRT ### Load Out of Tree @@ -61,7 +62,7 @@ modprobe snd-avirt-ap-loopback -## Configuring AVIRT +## 2. Configuring AVIRT AVIRT is configured using configfs. -- cgit 1.2.3-korg