1481 lines · 35.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * 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/kernel.h>#include <linux/module.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include <sound/control.h>#include <sound/tlv.h>#include <linux/usb/audio.h> #include "u_audio.h" #define BUFF_SIZE_MAX	(PAGE_SIZE * 16)#define PRD_SIZE_MAX	PAGE_SIZE#define MIN_PERIODS	4 enum {	UAC_FBACK_CTRL,	UAC_P_PITCH_CTRL,	UAC_MUTE_CTRL,	UAC_VOLUME_CTRL,	UAC_RATE_CTRL,}; /* 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; 	unsigned int pitch;	/* Stream pitch ratio to 1000000 */	unsigned int max_psize;	/* MaxPacketSize of endpoint */ 	struct usb_request **reqs; 	struct usb_request *req_fback; /* Feedback endpoint request */	bool fb_ep_enabled; /* if the ep is enabled */   /* Volume/Mute controls and their state */  int fu_id; /* Feature Unit ID */  struct snd_ctl_elem_id snd_kctl_volume_id;  struct snd_ctl_elem_id snd_kctl_mute_id;  s16 volume_min, volume_max, volume_res;  s16 volume;  int mute; 	struct snd_ctl_elem_id snd_kctl_rate_id; /* read-only current rate */	int srate; /* selected samplerate */	int active; /* playback/capture running */   spinlock_t lock; /* lock for control transfers */ }; 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; 	/* pre-calculated values for playback iso completion */	unsigned long long p_residue_mil;	unsigned int p_interval;	unsigned int p_framesize;}; static const struct snd_pcm_hardware uac_pcm_hardware = {	.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_set_fback_frequency(enum usb_device_speed speed,					struct usb_ep *out_ep,					unsigned long long freq,					unsigned int pitch,					void *buf){	u32 ff = 0;	const struct usb_endpoint_descriptor *ep_desc; 	/*	 * Because the pitch base is 1000000, the final divider here	 * will be 1000 * 1000000 = 1953125 << 9	 *	 * Instead of dealing with big numbers lets fold this 9 left shift	 */ 	if (speed == USB_SPEED_FULL) {		/*		 * Full-speed feedback endpoints report frequency		 * in samples/frame		 * Format is encoded in Q10.10 left-justified in the 24 bits,		 * so that it has a Q10.14 format.		 *		 * ff = (freq << 14) / 1000		 */		freq <<= 5;	} else {		/*		 * High-speed feedback endpoints report frequency		 * in samples/microframe.		 * Format is encoded in Q12.13 fitted into four bytes so that		 * the binary point is located between the second and the third		 * byte fromat (that is Q16.16)		 *		 * ff = (freq << 16) / 8000		 *		 * Win10 and OSX UAC2 drivers require number of samples per packet		 * in order to honor the feedback value.		 * Linux snd-usb-audio detects the applied bit-shift automatically.		 */		ep_desc = out_ep->desc;		freq <<= 4 + (ep_desc->bInterval - 1);	} 	ff = DIV_ROUND_CLOSEST_ULL((freq * pitch), 1953125); 	*(__le32 *)buf = cpu_to_le32(ff);} static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req){	unsigned int pending;	unsigned int hw_ptr;	int status = req->status;	struct snd_pcm_substream *substream;	struct snd_pcm_runtime *runtime;	struct uac_rtd_params *prm = req->context;	struct snd_uac_chip *uac = prm->uac;	unsigned int frames, p_pktsize;	unsigned long long pitched_rate_mil, p_pktsize_residue_mil,			residue_frames_mil, div_result; 	/* i/f shutting down */	if (!prm->ep_enabled) {		usb_ep_free_request(ep, req);		return;	} 	if (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; 	snd_pcm_stream_lock(substream); 	runtime = substream->runtime;	if (!runtime || !snd_pcm_running(substream)) {		snd_pcm_stream_unlock(substream);		goto exit;	} 	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.		 */		unsigned long long p_interval_mil = uac->p_interval * 1000000ULL; 		pitched_rate_mil = (unsigned long long) prm->srate * prm->pitch;		div_result = pitched_rate_mil;		do_div(div_result, uac->p_interval);		do_div(div_result, 1000000);		frames = (unsigned int) div_result; 		pr_debug("p_srate %d, pitch %d, interval_mil %llu, frames %d\n",				prm->srate, prm->pitch, p_interval_mil, frames); 		p_pktsize = min_t(unsigned int,					uac->p_framesize * frames,					ep->maxpacket); 		if (p_pktsize < ep->maxpacket) {			residue_frames_mil = pitched_rate_mil - frames * p_interval_mil;			p_pktsize_residue_mil = uac->p_framesize * residue_frames_mil;		} else			p_pktsize_residue_mil = 0; 		req->length = p_pktsize;		uac->p_residue_mil += p_pktsize_residue_mil; 		/*		 * Whenever there are more bytes in the accumulator p_residue_mil than we		 * need to add one more sample frame, increase this packet's		 * size and decrease the accumulator.		 */		div_result = uac->p_residue_mil;		do_div(div_result, uac->p_interval);		do_div(div_result, 1000000);		if ((unsigned int) div_result >= uac->p_framesize) {			req->length += uac->p_framesize;			uac->p_residue_mil -= uac->p_framesize * p_interval_mil;			pr_debug("increased req length to %d\n", req->length);		}		pr_debug("remains uac->p_residue_mil %llu\n", uac->p_residue_mil); 		req->actual = req->length;	} 	hw_ptr = prm->hw_ptr; 	/* Pack USB load in ALSA ring buffer */	pending = runtime->dma_bytes - hw_ptr; 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {		if (unlikely(pending < req->actual)) {			memcpy(req->buf, runtime->dma_area + hw_ptr, pending);			memcpy(req->buf + pending, runtime->dma_area,			       req->actual - pending);		} else {			memcpy(req->buf, runtime->dma_area + hw_ptr,			       req->actual);		}	} else {		if (unlikely(pending < req->actual)) {			memcpy(runtime->dma_area + hw_ptr, req->buf, pending);			memcpy(runtime->dma_area, req->buf + pending,			       req->actual - pending);		} else {			memcpy(runtime->dma_area + hw_ptr, req->buf,			       req->actual);		}	} 	/* update hw_ptr after data is copied to memory */	prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes;	hw_ptr = prm->hw_ptr;	snd_pcm_stream_unlock(substream); 	if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)		snd_pcm_period_elapsed(substream); exit:	if (usb_ep_queue(ep, req, GFP_ATOMIC))		dev_err(uac->card->dev, "%d Error!\n", __LINE__);} static void u_audio_iso_fback_complete(struct usb_ep *ep,				       struct usb_request *req){	struct uac_rtd_params *prm = req->context;	struct snd_uac_chip *uac = prm->uac;	struct g_audio *audio_dev = uac->audio_dev;	int status = req->status; 	/* i/f shutting down */	if (!prm->fb_ep_enabled) {		kfree(req->buf);		usb_ep_free_request(ep, req);		return;	} 	if (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); 	u_audio_set_fback_frequency(audio_dev->gadget->speed, audio_dev->out_ep,				    prm->srate, prm->pitch,				    req->buf); 	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;	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; 	/* 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;	} 	/* 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 u64 uac_ssize_to_fmt(int ssize){	u64 ret; 	switch (ssize) {	case 3:		ret = SNDRV_PCM_FMTBIT_S24_3LE;		break;	case 4:		ret = SNDRV_PCM_FMTBIT_S32_LE;		break;	default:		ret = SNDRV_PCM_FMTBIT_S16_LE;		break;	} 	return ret;} 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;	struct uac_rtd_params *prm;	int p_ssize, c_ssize;	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_chmask = params->p_chmask;	c_chmask = params->c_chmask;	uac->p_residue_mil = 0; 	runtime->hw = uac_pcm_hardware; 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {		runtime->hw.formats = uac_ssize_to_fmt(p_ssize);		runtime->hw.channels_min = num_channels(p_chmask);		prm = &uac->p_prm;	} else {		runtime->hw.formats = uac_ssize_to_fmt(c_ssize);		runtime->hw.channels_min = num_channels(c_chmask);		prm = &uac->c_prm;	} 	runtime->hw.period_bytes_min = 2 * prm->max_psize					/ runtime->hw.periods_min;	runtime->hw.rate_min = prm->srate;	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;} static const struct snd_pcm_ops uac_pcm_ops = {	.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; 	audio_dev = uac->audio_dev;	params = &audio_dev->params; 	for (i = 0; i < params->req_number; i++) {		if (prm->reqs[i]) {			if (usb_ep_dequeue(ep, prm->reqs[i]))				usb_ep_free_request(ep, prm->reqs[i]);			/*			 * If usb_ep_dequeue() cannot successfully dequeue the			 * request, the request will be freed by the completion			 * callback.			 */ 			prm->reqs[i] = NULL;		}	} 	prm->ep_enabled = false; 	if (usb_ep_disable(ep))		dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);} static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep){	struct snd_uac_chip *uac = prm->uac; 	if (!prm->fb_ep_enabled)		return; 	if (prm->req_fback) {		if (usb_ep_dequeue(ep, prm->req_fback)) {			kfree(prm->req_fback->buf);			usb_ep_free_request(ep, prm->req_fback);		}		prm->req_fback = NULL;	} 	prm->fb_ep_enabled = false; 	if (usb_ep_disable(ep))		dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__);} static void set_active(struct uac_rtd_params *prm, bool active){	// notifying through the Rate ctrl	unsigned long flags; 	spin_lock_irqsave(&prm->lock, flags);	if (prm->active != active) {

Lines 1–500 of 1481.