2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0+
2017-06-18 16:23:52 +03:00
/*
* u_audio . c - - interface to USB gadget " ALSA sound card " utilities
*
* Copyright ( C ) 2016
* Author : Ruslan Bilovol < ruslan . bilovol @ gmail . com >
*
* Sound card implementation was cut - and - pasted with changes
* from f_uac2 . c and has :
* Copyright ( C ) 2011
* Yadwinder Singh ( yadi . brar01 @ gmail . com )
* Jaswinder Singh ( jaswinder . singh @ linaro . org )
*/
# include <linux/module.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include "u_audio.h"
# define BUFF_SIZE_MAX (PAGE_SIZE * 16)
# define PRD_SIZE_MAX PAGE_SIZE
# define MIN_PERIODS 4
struct uac_req {
struct uac_rtd_params * pp ; /* parent param */
struct usb_request * req ;
} ;
/* Runtime data params for one stream */
struct uac_rtd_params {
struct snd_uac_chip * uac ; /* parent chip */
bool ep_enabled ; /* if the ep is enabled */
struct snd_pcm_substream * ss ;
/* Ring buffer */
ssize_t hw_ptr ;
void * rbuf ;
2019-06-13 11:34:33 +02:00
unsigned int max_psize ; /* MaxPacketSize of endpoint */
2017-06-18 16:23:52 +03:00
struct uac_req * ureq ;
spinlock_t lock ;
} ;
struct snd_uac_chip {
struct g_audio * audio_dev ;
struct uac_rtd_params p_prm ;
struct uac_rtd_params c_prm ;
struct snd_card * card ;
struct snd_pcm * pcm ;
/* timekeeping for the playback endpoint */
unsigned int p_interval ;
unsigned int p_residue ;
/* pre-calculated values for playback iso completion */
unsigned int p_pktsize ;
unsigned int p_pktsize_residue ;
unsigned int p_framesize ;
} ;
2017-08-13 18:13:11 +05:30
static const struct snd_pcm_hardware uac_pcm_hardware = {
2017-06-18 16:23:52 +03:00
. info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER
| SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
| SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME ,
. rates = SNDRV_PCM_RATE_CONTINUOUS ,
. periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX ,
. buffer_bytes_max = BUFF_SIZE_MAX ,
. period_bytes_max = PRD_SIZE_MAX ,
. periods_min = MIN_PERIODS ,
} ;
static void u_audio_iso_complete ( struct usb_ep * ep , struct usb_request * req )
{
2019-06-13 11:34:33 +02:00
unsigned int pending ;
usb: gadget: u_audio: protect stream runtime fields with stream spinlock
The change protects almost the whole body of u_audio_iso_complete()
function by PCM stream lock, this is mainly sufficient to avoid a race
between USB request completion and stream termination, the change
prevents a possibility of invalid memory access in interrupt context
by memcpy():
Unable to handle kernel paging request at virtual address 00004e80
pgd = c0004000
[00004e80] *pgd=00000000
Internal error: Oops: 817 [#1] PREEMPT SMP ARM
CPU: 0 PID: 3 Comm: ksoftirqd/0 Tainted: G C 3.14.54+ #117
task: da180b80 ti: da192000 task.ti: da192000
PC is at memcpy+0x50/0x330
LR is at 0xcdd92b0e
pc : [<c029ef30>] lr : [<cdd92b0e>] psr: 20000193
sp : da193ce4 ip : dd86ae26 fp : 0000b180
r10: daf81680 r9 : 00000000 r8 : d58a01ea
r7 : 2c0b43e4 r6 : acdfb08b r5 : 01a271cf r4 : 87389377
r3 : 69469782 r2 : 00000020 r1 : daf82fe0 r0 : 00004e80
Flags: nzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel
Control: 10c5387d Table: 2b70804a DAC: 00000015
Process ksoftirqd/0 (pid: 3, stack limit = 0xda192238)
Also added a check for potential !runtime condition, commonly it is
done by PCM_RUNTIME_CHECK(substream) in the beginning, however this
does not completely prevent from oopses in u_audio_iso_complete(),
because the proper protection scheme must be implemented in PCM
library functions.
An example of *not fixed* oops due to substream->runtime->*
dereference by snd_pcm_running(substream) from
snd_pcm_period_elapsed(), where substream->runtime is gone while
waiting the substream lock:
Unable to handle kernel paging request at virtual address 6b6b6b6b
pgd = db7e4000
[6b6b6b6b] *pgd=00000000
CPU: 0 PID: 193 Comm: klogd Tainted: G C 3.14.54+ #118
task: db5ac500 ti: db60c000 task.ti: db60c000
PC is at snd_pcm_period_elapsed+0x48/0xd8 [snd_pcm]
LR is at snd_pcm_period_elapsed+0x40/0xd8 [snd_pcm]
pc : [<>] lr : [<>] psr: 60000193
Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user
Control: 10c5387d Table: 2b7e404a DAC: 00000015
Process klogd (pid: 193, stack limit = 0xdb60c238)
[<>] (snd_pcm_period_elapsed [snd_pcm]) from [<>] (udc_irq+0x500/0xbbc)
[<>] (udc_irq) from [<>] (ci_irq+0x280/0x304)
[<>] (ci_irq) from [<>] (handle_irq_event_percpu+0xa4/0x40c)
[<>] (handle_irq_event_percpu) from [<>] (handle_irq_event+0x3c/0x5c)
[<>] (handle_irq_event) from [<>] (handle_fasteoi_irq+0xc4/0x110)
[<>] (handle_fasteoi_irq) from [<>] (generic_handle_irq+0x20/0x30)
[<>] (generic_handle_irq) from [<>] (handle_IRQ+0x80/0xc0)
[<>] (handle_IRQ) from [<>] (gic_handle_irq+0x3c/0x60)
[<>] (gic_handle_irq) from [<>] (__irq_svc+0x44/0x78)
Signed-off-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>
[erosca: W/o this patch, with minimal instrumentation [1], I can
consistently reproduce BUG: KASAN: use-after-free [2]]
[1] Instrumentation to reproduce issue [2]:
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index a72295c953bb..bd0b308024fe 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -16,6 +16,7 @@
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <linux/delay.h>
#include "u_audio.h"
@@ -147,6 +148,8 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
spin_unlock_irqrestore(&prm->lock, flags);
+ udelay(500); //delay here to increase probability of parallel activities
+
/* Pack USB load in ALSA ring buffer */
pending = prm->dma_bytes - hw_ptr;
[2] After applying [1], below BUG occurs on Rcar-H3-Salvator-X board:
==================================================================
BUG: KASAN: use-after-free in u_audio_iso_complete+0x24c/0x520 [u_audio]
Read of size 8 at addr ffff8006cafcc248 by task swapper/0/0
CPU: 0 PID: 0 Comm: swapper/0 Tainted: G WC 4.14.47+ #160
Hardware name: Renesas Salvator-X board based on r8a7795 ES2.0+ (DT)
Call trace:
[<ffff2000080925ac>] dump_backtrace+0x0/0x364
[<ffff200008092924>] show_stack+0x14/0x1c
[<ffff200008f8dbcc>] dump_stack+0x108/0x174
[<ffff2000083c71b8>] print_address_description+0x7c/0x32c
[<ffff2000083c78e8>] kasan_report+0x324/0x354
[<ffff2000083c6114>] __asan_load8+0x24/0x94
[<ffff2000021d1b34>] u_audio_iso_complete+0x24c/0x520 [u_audio]
[<ffff20000152fe50>] usb_gadget_giveback_request+0x480/0x4d0 [udc_core]
[<ffff200001860ab8>] usbhsg_queue_done+0x100/0x130 [renesas_usbhs]
[<ffff20000185f814>] usbhsf_pkt_handler+0x1a4/0x298 [renesas_usbhs]
[<ffff20000185fb38>] usbhsf_irq_ready+0x128/0x178 [renesas_usbhs]
[<ffff200001859cc8>] usbhs_interrupt+0x440/0x490 [renesas_usbhs]
[<ffff2000081a0288>] __handle_irq_event_percpu+0x594/0xa58
[<ffff2000081a07d0>] handle_irq_event_percpu+0x84/0x12c
[<ffff2000081a0928>] handle_irq_event+0xb0/0x10c
[<ffff2000081a8384>] handle_fasteoi_irq+0x1e0/0x2ec
[<ffff20000819e5f8>] generic_handle_irq+0x2c/0x44
[<ffff20000819f0d0>] __handle_domain_irq+0x190/0x194
[<ffff20000808177c>] gic_handle_irq+0x80/0xac
Exception stack(0xffff200009e97c80 to 0xffff200009e97dc0)
7c80: 0000000000000000 0000000000000000 0000000000000003 ffff200008179298
7ca0: ffff20000ae1c180 dfff200000000000 0000000000000000 ffff2000081f9a88
7cc0: ffff200009eb5960 ffff200009e97cf0 0000000000001600 ffff0400041b064b
7ce0: 0000000000000000 0000000000000002 0000000200000001 0000000000000001
7d00: ffff20000842197c 0000ffff958c4970 0000000000000000 ffff8006da0d5b80
7d20: ffff8006d4678498 0000000000000000 000000126bde0a8b ffff8006d4678480
7d40: 0000000000000000 000000126bdbea64 ffff200008fd0000 ffff8006fffff980
7d60: 00000000495f0018 ffff200009e97dc0 ffff200008b6c4ec ffff200009e97dc0
7d80: ffff200008b6c4f0 0000000020000145 ffff8006da0d5b80 ffff8006d4678498
7da0: ffffffffffffffff ffff8006d4678498 ffff200009e97dc0 ffff200008b6c4f0
[<ffff200008084034>] el1_irq+0xb4/0x12c
[<ffff200008b6c4f0>] cpuidle_enter_state+0x818/0x844
[<ffff200008b6c59c>] cpuidle_enter+0x18/0x20
[<ffff20000815f2e4>] call_cpuidle+0x98/0x9c
[<ffff20000815f674>] do_idle+0x214/0x264
[<ffff20000815facc>] cpu_startup_entry+0x20/0x24
[<ffff200008fb09d8>] rest_init+0x30c/0x320
[<ffff2000095f1338>] start_kernel+0x570/0x5b0
---<-snip->---
Fixes: 132fcb460839 ("usb: gadget: Add Audio Class 2.0 Driver")
Signed-off-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
2018-06-21 17:22:52 +02:00
unsigned long flags , flags2 ;
2017-06-18 16:23:52 +03:00
unsigned int hw_ptr ;
int status = req - > status ;
struct uac_req * ur = req - > context ;
struct snd_pcm_substream * substream ;
2018-06-21 17:22:49 +02:00
struct snd_pcm_runtime * runtime ;
2017-06-18 16:23:52 +03:00
struct uac_rtd_params * prm = ur - > pp ;
struct snd_uac_chip * uac = prm - > uac ;
/* i/f shutting down */
if ( ! prm - > ep_enabled | | req - > status = = - ESHUTDOWN )
return ;
/*
* We can ' t really do much about bad xfers .
* Afterall , the ISOCH xfers could fail legitimately .
*/
if ( status )
pr_debug ( " %s: iso_complete status(%d) %d/%d \n " ,
__func__ , status , req - > actual , req - > length ) ;
substream = prm - > ss ;
/* Do nothing if ALSA isn't active */
if ( ! substream )
goto exit ;
usb: gadget: u_audio: protect stream runtime fields with stream spinlock
The change protects almost the whole body of u_audio_iso_complete()
function by PCM stream lock, this is mainly sufficient to avoid a race
between USB request completion and stream termination, the change
prevents a possibility of invalid memory access in interrupt context
by memcpy():
Unable to handle kernel paging request at virtual address 00004e80
pgd = c0004000
[00004e80] *pgd=00000000
Internal error: Oops: 817 [#1] PREEMPT SMP ARM
CPU: 0 PID: 3 Comm: ksoftirqd/0 Tainted: G C 3.14.54+ #117
task: da180b80 ti: da192000 task.ti: da192000
PC is at memcpy+0x50/0x330
LR is at 0xcdd92b0e
pc : [<c029ef30>] lr : [<cdd92b0e>] psr: 20000193
sp : da193ce4 ip : dd86ae26 fp : 0000b180
r10: daf81680 r9 : 00000000 r8 : d58a01ea
r7 : 2c0b43e4 r6 : acdfb08b r5 : 01a271cf r4 : 87389377
r3 : 69469782 r2 : 00000020 r1 : daf82fe0 r0 : 00004e80
Flags: nzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel
Control: 10c5387d Table: 2b70804a DAC: 00000015
Process ksoftirqd/0 (pid: 3, stack limit = 0xda192238)
Also added a check for potential !runtime condition, commonly it is
done by PCM_RUNTIME_CHECK(substream) in the beginning, however this
does not completely prevent from oopses in u_audio_iso_complete(),
because the proper protection scheme must be implemented in PCM
library functions.
An example of *not fixed* oops due to substream->runtime->*
dereference by snd_pcm_running(substream) from
snd_pcm_period_elapsed(), where substream->runtime is gone while
waiting the substream lock:
Unable to handle kernel paging request at virtual address 6b6b6b6b
pgd = db7e4000
[6b6b6b6b] *pgd=00000000
CPU: 0 PID: 193 Comm: klogd Tainted: G C 3.14.54+ #118
task: db5ac500 ti: db60c000 task.ti: db60c000
PC is at snd_pcm_period_elapsed+0x48/0xd8 [snd_pcm]
LR is at snd_pcm_period_elapsed+0x40/0xd8 [snd_pcm]
pc : [<>] lr : [<>] psr: 60000193
Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user
Control: 10c5387d Table: 2b7e404a DAC: 00000015
Process klogd (pid: 193, stack limit = 0xdb60c238)
[<>] (snd_pcm_period_elapsed [snd_pcm]) from [<>] (udc_irq+0x500/0xbbc)
[<>] (udc_irq) from [<>] (ci_irq+0x280/0x304)
[<>] (ci_irq) from [<>] (handle_irq_event_percpu+0xa4/0x40c)
[<>] (handle_irq_event_percpu) from [<>] (handle_irq_event+0x3c/0x5c)
[<>] (handle_irq_event) from [<>] (handle_fasteoi_irq+0xc4/0x110)
[<>] (handle_fasteoi_irq) from [<>] (generic_handle_irq+0x20/0x30)
[<>] (generic_handle_irq) from [<>] (handle_IRQ+0x80/0xc0)
[<>] (handle_IRQ) from [<>] (gic_handle_irq+0x3c/0x60)
[<>] (gic_handle_irq) from [<>] (__irq_svc+0x44/0x78)
Signed-off-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>
[erosca: W/o this patch, with minimal instrumentation [1], I can
consistently reproduce BUG: KASAN: use-after-free [2]]
[1] Instrumentation to reproduce issue [2]:
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index a72295c953bb..bd0b308024fe 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -16,6 +16,7 @@
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <linux/delay.h>
#include "u_audio.h"
@@ -147,6 +148,8 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
spin_unlock_irqrestore(&prm->lock, flags);
+ udelay(500); //delay here to increase probability of parallel activities
+
/* Pack USB load in ALSA ring buffer */
pending = prm->dma_bytes - hw_ptr;
[2] After applying [1], below BUG occurs on Rcar-H3-Salvator-X board:
==================================================================
BUG: KASAN: use-after-free in u_audio_iso_complete+0x24c/0x520 [u_audio]
Read of size 8 at addr ffff8006cafcc248 by task swapper/0/0
CPU: 0 PID: 0 Comm: swapper/0 Tainted: G WC 4.14.47+ #160
Hardware name: Renesas Salvator-X board based on r8a7795 ES2.0+ (DT)
Call trace:
[<ffff2000080925ac>] dump_backtrace+0x0/0x364
[<ffff200008092924>] show_stack+0x14/0x1c
[<ffff200008f8dbcc>] dump_stack+0x108/0x174
[<ffff2000083c71b8>] print_address_description+0x7c/0x32c
[<ffff2000083c78e8>] kasan_report+0x324/0x354
[<ffff2000083c6114>] __asan_load8+0x24/0x94
[<ffff2000021d1b34>] u_audio_iso_complete+0x24c/0x520 [u_audio]
[<ffff20000152fe50>] usb_gadget_giveback_request+0x480/0x4d0 [udc_core]
[<ffff200001860ab8>] usbhsg_queue_done+0x100/0x130 [renesas_usbhs]
[<ffff20000185f814>] usbhsf_pkt_handler+0x1a4/0x298 [renesas_usbhs]
[<ffff20000185fb38>] usbhsf_irq_ready+0x128/0x178 [renesas_usbhs]
[<ffff200001859cc8>] usbhs_interrupt+0x440/0x490 [renesas_usbhs]
[<ffff2000081a0288>] __handle_irq_event_percpu+0x594/0xa58
[<ffff2000081a07d0>] handle_irq_event_percpu+0x84/0x12c
[<ffff2000081a0928>] handle_irq_event+0xb0/0x10c
[<ffff2000081a8384>] handle_fasteoi_irq+0x1e0/0x2ec
[<ffff20000819e5f8>] generic_handle_irq+0x2c/0x44
[<ffff20000819f0d0>] __handle_domain_irq+0x190/0x194
[<ffff20000808177c>] gic_handle_irq+0x80/0xac
Exception stack(0xffff200009e97c80 to 0xffff200009e97dc0)
7c80: 0000000000000000 0000000000000000 0000000000000003 ffff200008179298
7ca0: ffff20000ae1c180 dfff200000000000 0000000000000000 ffff2000081f9a88
7cc0: ffff200009eb5960 ffff200009e97cf0 0000000000001600 ffff0400041b064b
7ce0: 0000000000000000 0000000000000002 0000000200000001 0000000000000001
7d00: ffff20000842197c 0000ffff958c4970 0000000000000000 ffff8006da0d5b80
7d20: ffff8006d4678498 0000000000000000 000000126bde0a8b ffff8006d4678480
7d40: 0000000000000000 000000126bdbea64 ffff200008fd0000 ffff8006fffff980
7d60: 00000000495f0018 ffff200009e97dc0 ffff200008b6c4ec ffff200009e97dc0
7d80: ffff200008b6c4f0 0000000020000145 ffff8006da0d5b80 ffff8006d4678498
7da0: ffffffffffffffff ffff8006d4678498 ffff200009e97dc0 ffff200008b6c4f0
[<ffff200008084034>] el1_irq+0xb4/0x12c
[<ffff200008b6c4f0>] cpuidle_enter_state+0x818/0x844
[<ffff200008b6c59c>] cpuidle_enter+0x18/0x20
[<ffff20000815f2e4>] call_cpuidle+0x98/0x9c
[<ffff20000815f674>] do_idle+0x214/0x264
[<ffff20000815facc>] cpu_startup_entry+0x20/0x24
[<ffff200008fb09d8>] rest_init+0x30c/0x320
[<ffff2000095f1338>] start_kernel+0x570/0x5b0
---<-snip->---
Fixes: 132fcb460839 ("usb: gadget: Add Audio Class 2.0 Driver")
Signed-off-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
2018-06-21 17:22:52 +02:00
snd_pcm_stream_lock_irqsave ( substream , flags2 ) ;
2018-06-21 17:22:49 +02:00
runtime = substream - > runtime ;
usb: gadget: u_audio: protect stream runtime fields with stream spinlock
The change protects almost the whole body of u_audio_iso_complete()
function by PCM stream lock, this is mainly sufficient to avoid a race
between USB request completion and stream termination, the change
prevents a possibility of invalid memory access in interrupt context
by memcpy():
Unable to handle kernel paging request at virtual address 00004e80
pgd = c0004000
[00004e80] *pgd=00000000
Internal error: Oops: 817 [#1] PREEMPT SMP ARM
CPU: 0 PID: 3 Comm: ksoftirqd/0 Tainted: G C 3.14.54+ #117
task: da180b80 ti: da192000 task.ti: da192000
PC is at memcpy+0x50/0x330
LR is at 0xcdd92b0e
pc : [<c029ef30>] lr : [<cdd92b0e>] psr: 20000193
sp : da193ce4 ip : dd86ae26 fp : 0000b180
r10: daf81680 r9 : 00000000 r8 : d58a01ea
r7 : 2c0b43e4 r6 : acdfb08b r5 : 01a271cf r4 : 87389377
r3 : 69469782 r2 : 00000020 r1 : daf82fe0 r0 : 00004e80
Flags: nzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel
Control: 10c5387d Table: 2b70804a DAC: 00000015
Process ksoftirqd/0 (pid: 3, stack limit = 0xda192238)
Also added a check for potential !runtime condition, commonly it is
done by PCM_RUNTIME_CHECK(substream) in the beginning, however this
does not completely prevent from oopses in u_audio_iso_complete(),
because the proper protection scheme must be implemented in PCM
library functions.
An example of *not fixed* oops due to substream->runtime->*
dereference by snd_pcm_running(substream) from
snd_pcm_period_elapsed(), where substream->runtime is gone while
waiting the substream lock:
Unable to handle kernel paging request at virtual address 6b6b6b6b
pgd = db7e4000
[6b6b6b6b] *pgd=00000000
CPU: 0 PID: 193 Comm: klogd Tainted: G C 3.14.54+ #118
task: db5ac500 ti: db60c000 task.ti: db60c000
PC is at snd_pcm_period_elapsed+0x48/0xd8 [snd_pcm]
LR is at snd_pcm_period_elapsed+0x40/0xd8 [snd_pcm]
pc : [<>] lr : [<>] psr: 60000193
Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user
Control: 10c5387d Table: 2b7e404a DAC: 00000015
Process klogd (pid: 193, stack limit = 0xdb60c238)
[<>] (snd_pcm_period_elapsed [snd_pcm]) from [<>] (udc_irq+0x500/0xbbc)
[<>] (udc_irq) from [<>] (ci_irq+0x280/0x304)
[<>] (ci_irq) from [<>] (handle_irq_event_percpu+0xa4/0x40c)
[<>] (handle_irq_event_percpu) from [<>] (handle_irq_event+0x3c/0x5c)
[<>] (handle_irq_event) from [<>] (handle_fasteoi_irq+0xc4/0x110)
[<>] (handle_fasteoi_irq) from [<>] (generic_handle_irq+0x20/0x30)
[<>] (generic_handle_irq) from [<>] (handle_IRQ+0x80/0xc0)
[<>] (handle_IRQ) from [<>] (gic_handle_irq+0x3c/0x60)
[<>] (gic_handle_irq) from [<>] (__irq_svc+0x44/0x78)
Signed-off-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>
[erosca: W/o this patch, with minimal instrumentation [1], I can
consistently reproduce BUG: KASAN: use-after-free [2]]
[1] Instrumentation to reproduce issue [2]:
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index a72295c953bb..bd0b308024fe 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -16,6 +16,7 @@
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <linux/delay.h>
#include "u_audio.h"
@@ -147,6 +148,8 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
spin_unlock_irqrestore(&prm->lock, flags);
+ udelay(500); //delay here to increase probability of parallel activities
+
/* Pack USB load in ALSA ring buffer */
pending = prm->dma_bytes - hw_ptr;
[2] After applying [1], below BUG occurs on Rcar-H3-Salvator-X board:
==================================================================
BUG: KASAN: use-after-free in u_audio_iso_complete+0x24c/0x520 [u_audio]
Read of size 8 at addr ffff8006cafcc248 by task swapper/0/0
CPU: 0 PID: 0 Comm: swapper/0 Tainted: G WC 4.14.47+ #160
Hardware name: Renesas Salvator-X board based on r8a7795 ES2.0+ (DT)
Call trace:
[<ffff2000080925ac>] dump_backtrace+0x0/0x364
[<ffff200008092924>] show_stack+0x14/0x1c
[<ffff200008f8dbcc>] dump_stack+0x108/0x174
[<ffff2000083c71b8>] print_address_description+0x7c/0x32c
[<ffff2000083c78e8>] kasan_report+0x324/0x354
[<ffff2000083c6114>] __asan_load8+0x24/0x94
[<ffff2000021d1b34>] u_audio_iso_complete+0x24c/0x520 [u_audio]
[<ffff20000152fe50>] usb_gadget_giveback_request+0x480/0x4d0 [udc_core]
[<ffff200001860ab8>] usbhsg_queue_done+0x100/0x130 [renesas_usbhs]
[<ffff20000185f814>] usbhsf_pkt_handler+0x1a4/0x298 [renesas_usbhs]
[<ffff20000185fb38>] usbhsf_irq_ready+0x128/0x178 [renesas_usbhs]
[<ffff200001859cc8>] usbhs_interrupt+0x440/0x490 [renesas_usbhs]
[<ffff2000081a0288>] __handle_irq_event_percpu+0x594/0xa58
[<ffff2000081a07d0>] handle_irq_event_percpu+0x84/0x12c
[<ffff2000081a0928>] handle_irq_event+0xb0/0x10c
[<ffff2000081a8384>] handle_fasteoi_irq+0x1e0/0x2ec
[<ffff20000819e5f8>] generic_handle_irq+0x2c/0x44
[<ffff20000819f0d0>] __handle_domain_irq+0x190/0x194
[<ffff20000808177c>] gic_handle_irq+0x80/0xac
Exception stack(0xffff200009e97c80 to 0xffff200009e97dc0)
7c80: 0000000000000000 0000000000000000 0000000000000003 ffff200008179298
7ca0: ffff20000ae1c180 dfff200000000000 0000000000000000 ffff2000081f9a88
7cc0: ffff200009eb5960 ffff200009e97cf0 0000000000001600 ffff0400041b064b
7ce0: 0000000000000000 0000000000000002 0000000200000001 0000000000000001
7d00: ffff20000842197c 0000ffff958c4970 0000000000000000 ffff8006da0d5b80
7d20: ffff8006d4678498 0000000000000000 000000126bde0a8b ffff8006d4678480
7d40: 0000000000000000 000000126bdbea64 ffff200008fd0000 ffff8006fffff980
7d60: 00000000495f0018 ffff200009e97dc0 ffff200008b6c4ec ffff200009e97dc0
7d80: ffff200008b6c4f0 0000000020000145 ffff8006da0d5b80 ffff8006d4678498
7da0: ffffffffffffffff ffff8006d4678498 ffff200009e97dc0 ffff200008b6c4f0
[<ffff200008084034>] el1_irq+0xb4/0x12c
[<ffff200008b6c4f0>] cpuidle_enter_state+0x818/0x844
[<ffff200008b6c59c>] cpuidle_enter+0x18/0x20
[<ffff20000815f2e4>] call_cpuidle+0x98/0x9c
[<ffff20000815f674>] do_idle+0x214/0x264
[<ffff20000815facc>] cpu_startup_entry+0x20/0x24
[<ffff200008fb09d8>] rest_init+0x30c/0x320
[<ffff2000095f1338>] start_kernel+0x570/0x5b0
---<-snip->---
Fixes: 132fcb460839 ("usb: gadget: Add Audio Class 2.0 Driver")
Signed-off-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
2018-06-21 17:22:52 +02:00
if ( ! runtime | | ! snd_pcm_running ( substream ) ) {
snd_pcm_stream_unlock_irqrestore ( substream , flags2 ) ;
goto exit ;
}
2017-06-18 16:23:52 +03:00
spin_lock_irqsave ( & prm - > lock , flags ) ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
/*
* For each IN packet , take the quotient of the current data
* rate and the endpoint ' s interval as the base packet size .
* If there is a residue from this division , add it to the
* residue accumulator .
*/
req - > length = uac - > p_pktsize ;
uac - > p_residue + = uac - > p_pktsize_residue ;
/*
* Whenever there are more bytes in the accumulator than we
* need to add one more sample frame , increase this packet ' s
* size and decrease the accumulator .
*/
if ( uac - > p_residue / uac - > p_interval > = uac - > p_framesize ) {
req - > length + = uac - > p_framesize ;
uac - > p_residue - = uac - > p_framesize *
uac - > p_interval ;
}
req - > actual = req - > length ;
}
hw_ptr = prm - > hw_ptr ;
spin_unlock_irqrestore ( & prm - > lock , flags ) ;
/* Pack USB load in ALSA ring buffer */
2018-06-21 17:22:49 +02:00
pending = runtime - > dma_bytes - hw_ptr ;
2017-06-18 16:23:52 +03:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
if ( unlikely ( pending < req - > actual ) ) {
2018-06-21 17:22:49 +02:00
memcpy ( req - > buf , runtime - > dma_area + hw_ptr , pending ) ;
memcpy ( req - > buf + pending , runtime - > dma_area ,
2017-06-18 16:23:52 +03:00
req - > actual - pending ) ;
} else {
2018-06-21 17:22:49 +02:00
memcpy ( req - > buf , runtime - > dma_area + hw_ptr ,
req - > actual ) ;
2017-06-18 16:23:52 +03:00
}
} else {
if ( unlikely ( pending < req - > actual ) ) {
2018-06-21 17:22:49 +02:00
memcpy ( runtime - > dma_area + hw_ptr , req - > buf , pending ) ;
memcpy ( runtime - > dma_area , req - > buf + pending ,
2017-06-18 16:23:52 +03:00
req - > actual - pending ) ;
} else {
2018-06-21 17:22:49 +02:00
memcpy ( runtime - > dma_area + hw_ptr , req - > buf ,
req - > actual ) ;
2017-06-18 16:23:52 +03:00
}
}
2018-06-21 17:22:48 +02:00
spin_lock_irqsave ( & prm - > lock , flags ) ;
/* update hw_ptr after data is copied to memory */
2018-06-21 17:22:49 +02:00
prm - > hw_ptr = ( hw_ptr + req - > actual ) % runtime - > dma_bytes ;
2018-06-21 17:22:50 +02:00
hw_ptr = prm - > hw_ptr ;
2018-06-21 17:22:48 +02:00
spin_unlock_irqrestore ( & prm - > lock , flags ) ;
usb: gadget: u_audio: protect stream runtime fields with stream spinlock
The change protects almost the whole body of u_audio_iso_complete()
function by PCM stream lock, this is mainly sufficient to avoid a race
between USB request completion and stream termination, the change
prevents a possibility of invalid memory access in interrupt context
by memcpy():
Unable to handle kernel paging request at virtual address 00004e80
pgd = c0004000
[00004e80] *pgd=00000000
Internal error: Oops: 817 [#1] PREEMPT SMP ARM
CPU: 0 PID: 3 Comm: ksoftirqd/0 Tainted: G C 3.14.54+ #117
task: da180b80 ti: da192000 task.ti: da192000
PC is at memcpy+0x50/0x330
LR is at 0xcdd92b0e
pc : [<c029ef30>] lr : [<cdd92b0e>] psr: 20000193
sp : da193ce4 ip : dd86ae26 fp : 0000b180
r10: daf81680 r9 : 00000000 r8 : d58a01ea
r7 : 2c0b43e4 r6 : acdfb08b r5 : 01a271cf r4 : 87389377
r3 : 69469782 r2 : 00000020 r1 : daf82fe0 r0 : 00004e80
Flags: nzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel
Control: 10c5387d Table: 2b70804a DAC: 00000015
Process ksoftirqd/0 (pid: 3, stack limit = 0xda192238)
Also added a check for potential !runtime condition, commonly it is
done by PCM_RUNTIME_CHECK(substream) in the beginning, however this
does not completely prevent from oopses in u_audio_iso_complete(),
because the proper protection scheme must be implemented in PCM
library functions.
An example of *not fixed* oops due to substream->runtime->*
dereference by snd_pcm_running(substream) from
snd_pcm_period_elapsed(), where substream->runtime is gone while
waiting the substream lock:
Unable to handle kernel paging request at virtual address 6b6b6b6b
pgd = db7e4000
[6b6b6b6b] *pgd=00000000
CPU: 0 PID: 193 Comm: klogd Tainted: G C 3.14.54+ #118
task: db5ac500 ti: db60c000 task.ti: db60c000
PC is at snd_pcm_period_elapsed+0x48/0xd8 [snd_pcm]
LR is at snd_pcm_period_elapsed+0x40/0xd8 [snd_pcm]
pc : [<>] lr : [<>] psr: 60000193
Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user
Control: 10c5387d Table: 2b7e404a DAC: 00000015
Process klogd (pid: 193, stack limit = 0xdb60c238)
[<>] (snd_pcm_period_elapsed [snd_pcm]) from [<>] (udc_irq+0x500/0xbbc)
[<>] (udc_irq) from [<>] (ci_irq+0x280/0x304)
[<>] (ci_irq) from [<>] (handle_irq_event_percpu+0xa4/0x40c)
[<>] (handle_irq_event_percpu) from [<>] (handle_irq_event+0x3c/0x5c)
[<>] (handle_irq_event) from [<>] (handle_fasteoi_irq+0xc4/0x110)
[<>] (handle_fasteoi_irq) from [<>] (generic_handle_irq+0x20/0x30)
[<>] (generic_handle_irq) from [<>] (handle_IRQ+0x80/0xc0)
[<>] (handle_IRQ) from [<>] (gic_handle_irq+0x3c/0x60)
[<>] (gic_handle_irq) from [<>] (__irq_svc+0x44/0x78)
Signed-off-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>
[erosca: W/o this patch, with minimal instrumentation [1], I can
consistently reproduce BUG: KASAN: use-after-free [2]]
[1] Instrumentation to reproduce issue [2]:
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index a72295c953bb..bd0b308024fe 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -16,6 +16,7 @@
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <linux/delay.h>
#include "u_audio.h"
@@ -147,6 +148,8 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
spin_unlock_irqrestore(&prm->lock, flags);
+ udelay(500); //delay here to increase probability of parallel activities
+
/* Pack USB load in ALSA ring buffer */
pending = prm->dma_bytes - hw_ptr;
[2] After applying [1], below BUG occurs on Rcar-H3-Salvator-X board:
==================================================================
BUG: KASAN: use-after-free in u_audio_iso_complete+0x24c/0x520 [u_audio]
Read of size 8 at addr ffff8006cafcc248 by task swapper/0/0
CPU: 0 PID: 0 Comm: swapper/0 Tainted: G WC 4.14.47+ #160
Hardware name: Renesas Salvator-X board based on r8a7795 ES2.0+ (DT)
Call trace:
[<ffff2000080925ac>] dump_backtrace+0x0/0x364
[<ffff200008092924>] show_stack+0x14/0x1c
[<ffff200008f8dbcc>] dump_stack+0x108/0x174
[<ffff2000083c71b8>] print_address_description+0x7c/0x32c
[<ffff2000083c78e8>] kasan_report+0x324/0x354
[<ffff2000083c6114>] __asan_load8+0x24/0x94
[<ffff2000021d1b34>] u_audio_iso_complete+0x24c/0x520 [u_audio]
[<ffff20000152fe50>] usb_gadget_giveback_request+0x480/0x4d0 [udc_core]
[<ffff200001860ab8>] usbhsg_queue_done+0x100/0x130 [renesas_usbhs]
[<ffff20000185f814>] usbhsf_pkt_handler+0x1a4/0x298 [renesas_usbhs]
[<ffff20000185fb38>] usbhsf_irq_ready+0x128/0x178 [renesas_usbhs]
[<ffff200001859cc8>] usbhs_interrupt+0x440/0x490 [renesas_usbhs]
[<ffff2000081a0288>] __handle_irq_event_percpu+0x594/0xa58
[<ffff2000081a07d0>] handle_irq_event_percpu+0x84/0x12c
[<ffff2000081a0928>] handle_irq_event+0xb0/0x10c
[<ffff2000081a8384>] handle_fasteoi_irq+0x1e0/0x2ec
[<ffff20000819e5f8>] generic_handle_irq+0x2c/0x44
[<ffff20000819f0d0>] __handle_domain_irq+0x190/0x194
[<ffff20000808177c>] gic_handle_irq+0x80/0xac
Exception stack(0xffff200009e97c80 to 0xffff200009e97dc0)
7c80: 0000000000000000 0000000000000000 0000000000000003 ffff200008179298
7ca0: ffff20000ae1c180 dfff200000000000 0000000000000000 ffff2000081f9a88
7cc0: ffff200009eb5960 ffff200009e97cf0 0000000000001600 ffff0400041b064b
7ce0: 0000000000000000 0000000000000002 0000000200000001 0000000000000001
7d00: ffff20000842197c 0000ffff958c4970 0000000000000000 ffff8006da0d5b80
7d20: ffff8006d4678498 0000000000000000 000000126bde0a8b ffff8006d4678480
7d40: 0000000000000000 000000126bdbea64 ffff200008fd0000 ffff8006fffff980
7d60: 00000000495f0018 ffff200009e97dc0 ffff200008b6c4ec ffff200009e97dc0
7d80: ffff200008b6c4f0 0000000020000145 ffff8006da0d5b80 ffff8006d4678498
7da0: ffffffffffffffff ffff8006d4678498 ffff200009e97dc0 ffff200008b6c4f0
[<ffff200008084034>] el1_irq+0xb4/0x12c
[<ffff200008b6c4f0>] cpuidle_enter_state+0x818/0x844
[<ffff200008b6c59c>] cpuidle_enter+0x18/0x20
[<ffff20000815f2e4>] call_cpuidle+0x98/0x9c
[<ffff20000815f674>] do_idle+0x214/0x264
[<ffff20000815facc>] cpu_startup_entry+0x20/0x24
[<ffff200008fb09d8>] rest_init+0x30c/0x320
[<ffff2000095f1338>] start_kernel+0x570/0x5b0
---<-snip->---
Fixes: 132fcb460839 ("usb: gadget: Add Audio Class 2.0 Driver")
Signed-off-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
2018-06-21 17:22:52 +02:00
snd_pcm_stream_unlock_irqrestore ( substream , flags2 ) ;
2018-06-21 17:22:48 +02:00
2018-06-21 17:22:50 +02:00
if ( ( hw_ptr % snd_pcm_lib_period_bytes ( substream ) ) < req - > actual )
snd_pcm_period_elapsed ( substream ) ;
2017-06-18 16:23:52 +03:00
exit :
if ( usb_ep_queue ( ep , req , GFP_ATOMIC ) )
dev_err ( uac - > card - > dev , " %d Error! \n " , __LINE__ ) ;
}
static int uac_pcm_trigger ( struct snd_pcm_substream * substream , int cmd )
{
struct snd_uac_chip * uac = snd_pcm_substream_chip ( substream ) ;
struct uac_rtd_params * prm ;
struct g_audio * audio_dev ;
struct uac_params * params ;
unsigned long flags ;
int err = 0 ;
audio_dev = uac - > audio_dev ;
params = & audio_dev - > params ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
prm = & uac - > p_prm ;
else
prm = & uac - > c_prm ;
spin_lock_irqsave ( & prm - > lock , flags ) ;
/* Reset */
prm - > hw_ptr = 0 ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
prm - > ss = substream ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
prm - > ss = NULL ;
break ;
default :
err = - EINVAL ;
}
spin_unlock_irqrestore ( & prm - > lock , flags ) ;
/* Clear buffer after Play stops */
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK & & ! prm - > ss )
memset ( prm - > rbuf , 0 , prm - > max_psize * params - > req_number ) ;
return err ;
}
static snd_pcm_uframes_t uac_pcm_pointer ( struct snd_pcm_substream * substream )
{
struct snd_uac_chip * uac = snd_pcm_substream_chip ( substream ) ;
struct uac_rtd_params * prm ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
prm = & uac - > p_prm ;
else
prm = & uac - > c_prm ;
return bytes_to_frames ( substream - > runtime , prm - > hw_ptr ) ;
}
static int uac_pcm_open ( struct snd_pcm_substream * substream )
{
struct snd_uac_chip * uac = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct g_audio * audio_dev ;
struct uac_params * params ;
int p_ssize , c_ssize ;
int p_srate , c_srate ;
int p_chmask , c_chmask ;
audio_dev = uac - > audio_dev ;
params = & audio_dev - > params ;
p_ssize = params - > p_ssize ;
c_ssize = params - > c_ssize ;
p_srate = params - > p_srate ;
c_srate = params - > c_srate ;
p_chmask = params - > p_chmask ;
c_chmask = params - > c_chmask ;
uac - > p_residue = 0 ;
runtime - > hw = uac_pcm_hardware ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
spin_lock_init ( & uac - > p_prm . lock ) ;
runtime - > hw . rate_min = p_srate ;
switch ( p_ssize ) {
case 3 :
runtime - > hw . formats = SNDRV_PCM_FMTBIT_S24_3LE ;
break ;
case 4 :
runtime - > hw . formats = SNDRV_PCM_FMTBIT_S32_LE ;
break ;
default :
runtime - > hw . formats = SNDRV_PCM_FMTBIT_S16_LE ;
break ;
}
runtime - > hw . channels_min = num_channels ( p_chmask ) ;
runtime - > hw . period_bytes_min = 2 * uac - > p_prm . max_psize
/ runtime - > hw . periods_min ;
} else {
spin_lock_init ( & uac - > c_prm . lock ) ;
runtime - > hw . rate_min = c_srate ;
switch ( c_ssize ) {
case 3 :
runtime - > hw . formats = SNDRV_PCM_FMTBIT_S24_3LE ;
break ;
case 4 :
runtime - > hw . formats = SNDRV_PCM_FMTBIT_S32_LE ;
break ;
default :
runtime - > hw . formats = SNDRV_PCM_FMTBIT_S16_LE ;
break ;
}
runtime - > hw . channels_min = num_channels ( c_chmask ) ;
runtime - > hw . period_bytes_min = 2 * uac - > c_prm . max_psize
/ runtime - > hw . periods_min ;
}
runtime - > hw . rate_max = runtime - > hw . rate_min ;
runtime - > hw . channels_max = runtime - > hw . channels_min ;
snd_pcm_hw_constraint_integer ( runtime , SNDRV_PCM_HW_PARAM_PERIODS ) ;
return 0 ;
}
/* ALSA cries without these function pointers */
static int uac_pcm_null ( struct snd_pcm_substream * substream )
{
return 0 ;
}
2017-08-09 13:16:51 +05:30
static const struct snd_pcm_ops uac_pcm_ops = {
2017-06-18 16:23:52 +03:00
. open = uac_pcm_open ,
. close = uac_pcm_null ,
. trigger = uac_pcm_trigger ,
. pointer = uac_pcm_pointer ,
. prepare = uac_pcm_null ,
} ;
static inline void free_ep ( struct uac_rtd_params * prm , struct usb_ep * ep )
{
struct snd_uac_chip * uac = prm - > uac ;
struct g_audio * audio_dev ;
struct uac_params * params ;
int i ;
if ( ! prm - > ep_enabled )
return ;
prm - > ep_enabled = false ;
audio_dev = uac - > audio_dev ;
params = & audio_dev - > params ;
for ( i = 0 ; i < params - > req_number ; i + + ) {
if ( prm - > ureq [ i ] . req ) {
usb_ep_dequeue ( ep , prm - > ureq [ i ] . req ) ;
usb_ep_free_request ( ep , prm - > ureq [ i ] . req ) ;
prm - > ureq [ i ] . req = NULL ;
}
}
if ( usb_ep_disable ( ep ) )
dev_err ( uac - > card - > dev , " %s:%d Error! \n " , __func__ , __LINE__ ) ;
}
int u_audio_start_capture ( struct g_audio * audio_dev )
{
struct snd_uac_chip * uac = audio_dev - > uac ;
struct usb_gadget * gadget = audio_dev - > gadget ;
struct device * dev = & gadget - > dev ;
struct usb_request * req ;
struct usb_ep * ep ;
struct uac_rtd_params * prm ;
struct uac_params * params = & audio_dev - > params ;
int req_len , i ;
ep = audio_dev - > out_ep ;
prm = & uac - > c_prm ;
config_ep_by_speed ( gadget , & audio_dev - > func , ep ) ;
2020-01-17 10:40:22 +00:00
req_len = ep - > maxpacket ;
2017-06-18 16:23:52 +03:00
prm - > ep_enabled = true ;
usb_ep_enable ( ep ) ;
for ( i = 0 ; i < params - > req_number ; i + + ) {
if ( ! prm - > ureq [ i ] . req ) {
req = usb_ep_alloc_request ( ep , GFP_ATOMIC ) ;
if ( req = = NULL )
return - ENOMEM ;
prm - > ureq [ i ] . req = req ;
prm - > ureq [ i ] . pp = prm ;
req - > zero = 0 ;
req - > context = & prm - > ureq [ i ] ;
req - > length = req_len ;
req - > complete = u_audio_iso_complete ;
2020-01-17 10:40:22 +00:00
req - > buf = prm - > rbuf + i * ep - > maxpacket ;
2017-06-18 16:23:52 +03:00
}
if ( usb_ep_queue ( ep , prm - > ureq [ i ] . req , GFP_ATOMIC ) )
dev_err ( dev , " %s:%d Error! \n " , __func__ , __LINE__ ) ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( u_audio_start_capture ) ;
void u_audio_stop_capture ( struct g_audio * audio_dev )
{
struct snd_uac_chip * uac = audio_dev - > uac ;
free_ep ( & uac - > c_prm , audio_dev - > out_ep ) ;
}
EXPORT_SYMBOL_GPL ( u_audio_stop_capture ) ;
int u_audio_start_playback ( struct g_audio * audio_dev )
{
struct snd_uac_chip * uac = audio_dev - > uac ;
struct usb_gadget * gadget = audio_dev - > gadget ;
struct device * dev = & gadget - > dev ;
struct usb_request * req ;
struct usb_ep * ep ;
struct uac_rtd_params * prm ;
struct uac_params * params = & audio_dev - > params ;
2020-01-10 11:28:14 +00:00
unsigned int factor ;
2017-06-18 16:23:52 +03:00
const struct usb_endpoint_descriptor * ep_desc ;
int req_len , i ;
ep = audio_dev - > in_ep ;
prm = & uac - > p_prm ;
config_ep_by_speed ( gadget , & audio_dev - > func , ep ) ;
ep_desc = ep - > desc ;
/* pre-calculate the playback endpoint's interval */
if ( gadget - > speed = = USB_SPEED_FULL )
factor = 1000 ;
else
factor = 8000 ;
/* pre-compute some values for iso_complete() */
uac - > p_framesize = params - > p_ssize *
num_channels ( params - > p_chmask ) ;
uac - > p_interval = factor / ( 1 < < ( ep_desc - > bInterval - 1 ) ) ;
2020-01-10 11:28:14 +00:00
uac - > p_pktsize = min_t ( unsigned int ,
uac - > p_framesize *
( params - > p_srate / uac - > p_interval ) ,
2020-01-17 10:40:22 +00:00
ep - > maxpacket ) ;
2017-06-18 16:23:52 +03:00
2020-01-17 10:40:22 +00:00
if ( uac - > p_pktsize < ep - > maxpacket )
2020-01-10 11:28:14 +00:00
uac - > p_pktsize_residue = uac - > p_framesize *
( params - > p_srate % uac - > p_interval ) ;
2017-06-18 16:23:52 +03:00
else
uac - > p_pktsize_residue = 0 ;
req_len = uac - > p_pktsize ;
uac - > p_residue = 0 ;
prm - > ep_enabled = true ;
usb_ep_enable ( ep ) ;
for ( i = 0 ; i < params - > req_number ; i + + ) {
if ( ! prm - > ureq [ i ] . req ) {
req = usb_ep_alloc_request ( ep , GFP_ATOMIC ) ;
if ( req = = NULL )
return - ENOMEM ;
prm - > ureq [ i ] . req = req ;
prm - > ureq [ i ] . pp = prm ;
req - > zero = 0 ;
req - > context = & prm - > ureq [ i ] ;
req - > length = req_len ;
req - > complete = u_audio_iso_complete ;
2020-01-17 10:40:22 +00:00
req - > buf = prm - > rbuf + i * ep - > maxpacket ;
2017-06-18 16:23:52 +03:00
}
if ( usb_ep_queue ( ep , prm - > ureq [ i ] . req , GFP_ATOMIC ) )
dev_err ( dev , " %s:%d Error! \n " , __func__ , __LINE__ ) ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( u_audio_start_playback ) ;
void u_audio_stop_playback ( struct g_audio * audio_dev )
{
struct snd_uac_chip * uac = audio_dev - > uac ;
free_ep ( & uac - > p_prm , audio_dev - > in_ep ) ;
}
EXPORT_SYMBOL_GPL ( u_audio_stop_playback ) ;
int g_audio_setup ( struct g_audio * g_audio , const char * pcm_name ,
const char * card_name )
{
struct snd_uac_chip * uac ;
struct snd_card * card ;
struct snd_pcm * pcm ;
struct uac_params * params ;
int p_chmask , c_chmask ;
int err ;
if ( ! g_audio )
return - EINVAL ;
uac = kzalloc ( sizeof ( * uac ) , GFP_KERNEL ) ;
if ( ! uac )
return - ENOMEM ;
g_audio - > uac = uac ;
uac - > audio_dev = g_audio ;
params = & g_audio - > params ;
p_chmask = params - > p_chmask ;
c_chmask = params - > c_chmask ;
if ( c_chmask ) {
struct uac_rtd_params * prm = & uac - > c_prm ;
uac - > c_prm . uac = uac ;
prm - > max_psize = g_audio - > out_ep_maxpsize ;
prm - > ureq = kcalloc ( params - > req_number , sizeof ( struct uac_req ) ,
GFP_KERNEL ) ;
if ( ! prm - > ureq ) {
err = - ENOMEM ;
goto fail ;
}
prm - > rbuf = kcalloc ( params - > req_number , prm - > max_psize ,
GFP_KERNEL ) ;
if ( ! prm - > rbuf ) {
prm - > max_psize = 0 ;
err = - ENOMEM ;
goto fail ;
}
}
if ( p_chmask ) {
struct uac_rtd_params * prm = & uac - > p_prm ;
uac - > p_prm . uac = uac ;
prm - > max_psize = g_audio - > in_ep_maxpsize ;
prm - > ureq = kcalloc ( params - > req_number , sizeof ( struct uac_req ) ,
GFP_KERNEL ) ;
if ( ! prm - > ureq ) {
err = - ENOMEM ;
goto fail ;
}
prm - > rbuf = kcalloc ( params - > req_number , prm - > max_psize ,
GFP_KERNEL ) ;
if ( ! prm - > rbuf ) {
prm - > max_psize = 0 ;
err = - ENOMEM ;
goto fail ;
}
}
/* Choose any slot, with no id */
err = snd_card_new ( & g_audio - > gadget - > dev ,
- 1 , NULL , THIS_MODULE , 0 , & card ) ;
if ( err < 0 )
goto fail ;
uac - > card = card ;
/*
* Create first PCM device
* Create a substream only for non - zero channel streams
*/
err = snd_pcm_new ( uac - > card , pcm_name , 0 ,
p_chmask ? 1 : 0 , c_chmask ? 1 : 0 , & pcm ) ;
if ( err < 0 )
goto snd_fail ;
2018-06-21 17:22:47 +02:00
strlcpy ( pcm - > name , pcm_name , sizeof ( pcm - > name ) ) ;
2017-06-18 16:23:52 +03:00
pcm - > private_data = uac ;
uac - > pcm = pcm ;
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_PLAYBACK , & uac_pcm_ops ) ;
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE , & uac_pcm_ops ) ;
2018-06-21 17:22:47 +02:00
strlcpy ( card - > driver , card_name , sizeof ( card - > driver ) ) ;
strlcpy ( card - > shortname , card_name , sizeof ( card - > shortname ) ) ;
2017-06-18 16:23:52 +03:00
sprintf ( card - > longname , " %s %i " , card_name , card - > dev - > id ) ;
2019-12-10 15:18:21 +01:00
snd_pcm_set_managed_buffer_all ( pcm , SNDRV_DMA_TYPE_CONTINUOUS ,
NULL , 0 , BUFF_SIZE_MAX ) ;
2017-06-18 16:23:52 +03:00
err = snd_card_register ( card ) ;
if ( ! err )
return 0 ;
snd_fail :
snd_card_free ( card ) ;
fail :
kfree ( uac - > p_prm . ureq ) ;
kfree ( uac - > c_prm . ureq ) ;
kfree ( uac - > p_prm . rbuf ) ;
kfree ( uac - > c_prm . rbuf ) ;
kfree ( uac ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( g_audio_setup ) ;
void g_audio_cleanup ( struct g_audio * g_audio )
{
struct snd_uac_chip * uac ;
struct snd_card * card ;
if ( ! g_audio | | ! g_audio - > uac )
return ;
uac = g_audio - > uac ;
card = uac - > card ;
if ( card )
snd_card_free ( card ) ;
kfree ( uac - > p_prm . ureq ) ;
kfree ( uac - > c_prm . ureq ) ;
kfree ( uac - > p_prm . rbuf ) ;
kfree ( uac - > c_prm . rbuf ) ;
kfree ( uac ) ;
}
EXPORT_SYMBOL_GPL ( g_audio_cleanup ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " USB gadget \" ALSA sound card \" utilities " ) ;
MODULE_AUTHOR ( " Ruslan Bilovol " ) ;