⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usbusx2yaudio.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *   US-X2Y AUDIO *   Copyright (c) 2002-2004 by Karsten Wiese * *   based on * *   (Tentative) USB Audio Driver for ALSA * *   Main and PCM part * *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> * *   Many codes borrowed from audio.c by  *	    Alan Cox (alan@lxorguk.ukuu.org.uk) *	    Thomas Sailer (sailer@ife.ee.ethz.ch) * * *   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 */#include <sound/driver.h>#include <linux/interrupt.h>#include <linux/usb.h>#include <sound/core.h>#include <sound/info.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include "usx2y.h"#include "usbusx2y.h"#define USX2Y_NRPACKS 4			/* Default value used for nr of packs per urb.					  1 to 4 have been tested ok on uhci.					  To use 3 on ohci, you'd need a patch:					  look for "0000425-linux-2.6.9-rc4-mm1_ohci-hcd.patch.gz" on					  "https://bugtrack.alsa-project.org/alsa-bug/bug_view_page.php?bug_id=0000425"					  .					  1, 2 and 4 work out of the box on ohci, if I recall correctly.					  Bigger is safer operation,					  smaller gives lower latencies.					*/#define USX2Y_NRPACKS_VARIABLE y	/* If your system works ok with this module's parameter					   nrpacks set to 1, you might as well comment 					   this #define out, and thereby produce smaller, faster code.					   You'd also set USX2Y_NRPACKS to 1 then.					*/#ifdef USX2Y_NRPACKS_VARIABLE static int nrpacks = USX2Y_NRPACKS; /* number of packets per urb */ #define  nr_of_packs() nrpacks module_param(nrpacks, int, 0444); MODULE_PARM_DESC(nrpacks, "Number of packets per URB.");#else #define nr_of_packs() USX2Y_NRPACKS#endifstatic int usX2Y_urb_capt_retire(snd_usX2Y_substream_t *subs){	struct urb	*urb = subs->completed_urb;	snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;	unsigned char	*cp;	int 		i, len, lens = 0, hwptr_done = subs->hwptr_done;	usX2Ydev_t	*usX2Y = subs->usX2Y;	for (i = 0; i < nr_of_packs(); i++) {		cp = (unsigned char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset;		if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */			snd_printk("activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status);			return urb->iso_frame_desc[i].status;		}		len = urb->iso_frame_desc[i].actual_length / usX2Y->stride;		if (! len) {			snd_printd("0 == len ERROR!\n");			continue;		}		/* copy a data chunk */		if ((hwptr_done + len) > runtime->buffer_size) {			int cnt = runtime->buffer_size - hwptr_done;			int blen = cnt * usX2Y->stride;			memcpy(runtime->dma_area + hwptr_done * usX2Y->stride, cp, blen);			memcpy(runtime->dma_area, cp + blen, len * usX2Y->stride - blen);		} else {			memcpy(runtime->dma_area + hwptr_done * usX2Y->stride, cp, len * usX2Y->stride);		}		lens += len;		if ((hwptr_done += len) >= runtime->buffer_size)			hwptr_done -= runtime->buffer_size;	}	subs->hwptr_done = hwptr_done;	subs->transfer_done += lens;	/* update the pointer, call callback if necessary */	if (subs->transfer_done >= runtime->period_size) {		subs->transfer_done -= runtime->period_size;		snd_pcm_period_elapsed(subs->pcm_substream);	}	return 0;}/* * prepare urb for playback data pipe * * we copy the data directly from the pcm buffer. * the current position to be copied is held in hwptr field. * since a urb can handle only a single linear buffer, if the total * transferred area overflows the buffer boundary, we cannot send * it directly from the buffer.  thus the data is once copied to * a temporary buffer and urb points to that. */static int usX2Y_urb_play_prepare(snd_usX2Y_substream_t *subs,				  struct urb *cap_urb,				  struct urb *urb){	int count, counts, pack;	usX2Ydev_t* usX2Y = subs->usX2Y;	snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;	count = 0;	for (pack = 0; pack <  nr_of_packs(); pack++) {		/* calculate the size of a packet */		counts = cap_urb->iso_frame_desc[pack].actual_length / usX2Y->stride;		count += counts;		if (counts < 43 || counts > 50) {			snd_printk("should not be here with counts=%i\n", counts);			return -EPIPE;		}		/* set up descriptor */		urb->iso_frame_desc[pack].offset = pack ?			urb->iso_frame_desc[pack - 1].offset + urb->iso_frame_desc[pack - 1].length :			0;		urb->iso_frame_desc[pack].length = cap_urb->iso_frame_desc[pack].actual_length;	}	if (atomic_read(&subs->state) >= state_PRERUNNING)		if (subs->hwptr + count > runtime->buffer_size) {			/* err, the transferred area goes over buffer boundary.			 * copy the data to the temp buffer.			 */			int len;			len = runtime->buffer_size - subs->hwptr;			urb->transfer_buffer = subs->tmpbuf;			memcpy(subs->tmpbuf, runtime->dma_area + subs->hwptr * usX2Y->stride, len * usX2Y->stride);			memcpy(subs->tmpbuf + len * usX2Y->stride, runtime->dma_area, (count - len) * usX2Y->stride);			subs->hwptr += count;			subs->hwptr -= runtime->buffer_size;		} else {			/* set the buffer pointer */			urb->transfer_buffer = runtime->dma_area + subs->hwptr * usX2Y->stride;			if ((subs->hwptr += count) >= runtime->buffer_size)			subs->hwptr -= runtime->buffer_size;					}	else		urb->transfer_buffer = subs->tmpbuf;	urb->transfer_buffer_length = count * usX2Y->stride;	return 0;}/* * process after playback data complete * * update the current position and call callback if a period is processed. */static void usX2Y_urb_play_retire(snd_usX2Y_substream_t *subs, struct urb *urb){	snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;	int		len = urb->actual_length / subs->usX2Y->stride;	subs->transfer_done += len;	subs->hwptr_done +=  len;	if (subs->hwptr_done >= runtime->buffer_size)		subs->hwptr_done -= runtime->buffer_size;	if (subs->transfer_done >= runtime->period_size) {		subs->transfer_done -= runtime->period_size;		snd_pcm_period_elapsed(subs->pcm_substream);	}}static int usX2Y_urb_submit(snd_usX2Y_substream_t *subs, struct urb *urb, int frame){	int err;	if (!urb)		return -ENODEV;	urb->start_frame = (frame + NRURBS * nr_of_packs());  // let hcd do rollover sanity checks	urb->hcpriv = NULL;	urb->dev = subs->usX2Y->chip.dev; /* we need to set this at each time */	if ((err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {		snd_printk("usb_submit_urb() returned %i\n", err);		return err;	}	return 0;}static inline int usX2Y_usbframe_complete(snd_usX2Y_substream_t *capsubs, snd_usX2Y_substream_t *playbacksubs, int frame){	int err, state;	{		struct urb *urb = playbacksubs->completed_urb;		state = atomic_read(&playbacksubs->state);		if (NULL != urb) {			if (state == state_RUNNING)				usX2Y_urb_play_retire(playbacksubs, urb);			else				if (state >= state_PRERUNNING) {					atomic_inc(&playbacksubs->state);				}		} else {			switch (state) {			case state_STARTING1:				urb = playbacksubs->urb[0];				atomic_inc(&playbacksubs->state);				break;			case state_STARTING2:				urb = playbacksubs->urb[1];				atomic_inc(&playbacksubs->state);				break;			}		}		if (urb) {			if ((err = usX2Y_urb_play_prepare(playbacksubs, capsubs->completed_urb, urb)) ||			    (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {				return err;			}		}		playbacksubs->completed_urb = NULL;	}	state = atomic_read(&capsubs->state);	if (state >= state_PREPARED) {		if (state == state_RUNNING) {			if ((err = usX2Y_urb_capt_retire(capsubs)))				return err;		} else			if (state >= state_PRERUNNING) {				atomic_inc(&capsubs->state);			}		if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))			return err;	}	capsubs->completed_urb = NULL;	return 0;}static void usX2Y_clients_stop(usX2Ydev_t *usX2Y){	int s, u;	for (s = 0; s < 4; s++) {		snd_usX2Y_substream_t *subs = usX2Y->subs[s];		if (subs) {			snd_printdd("%i %p state=%i\n", s, subs, atomic_read(&subs->state));			atomic_set(&subs->state, state_STOPPED);		}	}	for (s = 0; s < 4; s++) {		snd_usX2Y_substream_t *subs = usX2Y->subs[s];		if (subs) {			if (atomic_read(&subs->state) >= state_PRERUNNING) {				snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);			}			for (u = 0; u < NRURBS; u++) {				struct urb *urb = subs->urb[u];				if (NULL != urb)					snd_printdd("%i status=%i start_frame=%i\n", u, urb->status, urb->start_frame);			}		}	}	usX2Y->prepare_subs = NULL;	wake_up(&usX2Y->prepare_wait_queue);}static void usX2Y_error_urb_status(usX2Ydev_t *usX2Y, snd_usX2Y_substream_t *subs, struct urb *urb){	snd_printk("ep=%i stalled with status=%i\n", subs->endpoint, urb->status);	urb->status = 0;	usX2Y_clients_stop(usX2Y);}static void usX2Y_error_sequence(usX2Ydev_t *usX2Y, snd_usX2Y_substream_t *subs, struct urb *urb){	snd_printk("Sequence Error!(hcd_frame=%i ep=%i%s;wait=%i,frame=%i).\n"		   "Most propably some urb of usb-frame %i is still missing.\n"		   "Cause could be too long delays in usb-hcd interrupt handling.\n",		   usb_get_current_frame_number(usX2Y->chip.dev),		   subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", usX2Y->wait_iso_frame, urb->start_frame, usX2Y->wait_iso_frame);	usX2Y_clients_stop(usX2Y);}static void i_usX2Y_urb_complete(struct urb *urb, struct pt_regs *regs){	snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;	usX2Ydev_t *usX2Y = subs->usX2Y;	if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {		snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n", usb_get_current_frame_number(usX2Y->chip.dev), subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", urb->status, urb->start_frame);		return;	}	if (unlikely(urb->status)) {		usX2Y_error_urb_status(usX2Y, subs, urb);		return;	}	if (likely((0xFFFF & urb->start_frame) == usX2Y->wait_iso_frame))		subs->completed_urb = urb;	else {		usX2Y_error_sequence(usX2Y, subs, urb);		return;	}	{		snd_usX2Y_substream_t *capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE],			*playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];		if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&		    (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {			if (!usX2Y_usbframe_complete(capsubs, playbacksubs, urb->start_frame)) {				if (nr_of_packs() <= urb->start_frame &&				    urb->start_frame <= (2 * nr_of_packs() - 1))	// uhci and ohci					usX2Y->wait_iso_frame = urb->start_frame - nr_of_packs();				else					usX2Y->wait_iso_frame +=  nr_of_packs();			} else {				snd_printdd("\n");				usX2Y_clients_stop(usX2Y);			}		}	}}static void usX2Y_urbs_set_complete(usX2Ydev_t * usX2Y, void (*complete)(struct urb *, struct pt_regs *)){	int s, u;	for (s = 0; s < 4; s++) {		snd_usX2Y_substream_t *subs = usX2Y->subs[s];		if (NULL != subs)			for (u = 0; u < NRURBS; u++) {				struct urb * urb = subs->urb[u];				if (NULL != urb)					urb->complete = complete;			}	}}static void usX2Y_subs_startup_finish(usX2Ydev_t * usX2Y){	usX2Y_urbs_set_complete(usX2Y, i_usX2Y_urb_complete);	usX2Y->prepare_subs = NULL;}static void i_usX2Y_subs_startup(struct urb *urb, struct pt_regs *regs){	snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;	usX2Ydev_t *usX2Y = subs->usX2Y;	snd_usX2Y_substream_t *prepare_subs = usX2Y->prepare_subs;	if (NULL != prepare_subs)		if (urb->start_frame == prepare_subs->urb[0]->start_frame) {			usX2Y_subs_startup_finish(usX2Y);			atomic_inc(&prepare_subs->state);			wake_up(&usX2Y->prepare_wait_queue);		}	i_usX2Y_urb_complete(urb, regs);}static void usX2Y_subs_prepare(snd_usX2Y_substream_t *subs){	snd_printdd("usX2Y_substream_prepare(%p) ep=%i urb0=%p urb1=%p\n", subs, subs->endpoint, subs->urb[0], subs->urb[1]);	/* reset the pointer */	subs->hwptr = 0;	subs->hwptr_done = 0;	subs->transfer_done = 0;}static void usX2Y_urb_release(struct urb** urb, int free_tb){	if (*urb) {		usb_kill_urb(*urb);		if (free_tb)			kfree((*urb)->transfer_buffer);		usb_free_urb(*urb);		*urb = NULL;	}}/* * release a substreams urbs */static void usX2Y_urbs_release(snd_usX2Y_substream_t *subs){	int i;	snd_printdd("usX2Y_urbs_release() %i\n", subs->endpoint);	for (i = 0; i < NRURBS; i++)		usX2Y_urb_release(subs->urb + i, subs != subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK]);	kfree(subs->tmpbuf);	subs->tmpbuf = NULL;}/* * initialize a substream's urbs */static int usX2Y_urbs_allocate(snd_usX2Y_substream_t *subs){	int i;	unsigned int pipe;	int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];	struct usb_device *dev = subs->usX2Y->chip.dev;	pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :			usb_rcvisocpipe(dev, subs->endpoint);	subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);	if (!subs->maxpacksize)		return -EINVAL;	if (is_playback && NULL == subs->tmpbuf) {	/* allocate a temporary buffer for playback */		subs->tmpbuf = kcalloc(nr_of_packs(), subs->maxpacksize, GFP_KERNEL);		if (NULL == subs->tmpbuf) {			snd_printk(KERN_ERR "cannot malloc tmpbuf\n");			return -ENOMEM;		}	}	/* allocate and initialize data urbs */	for (i = 0; i < NRURBS; i++) {		struct urb** purb = subs->urb + i;		if (*purb) {			usb_kill_urb(*purb);			continue;		}		*purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);		if (NULL == *purb) {			usX2Y_urbs_release(subs);			return -ENOMEM;		}		if (!is_playback && !(*purb)->transfer_buffer) {			/* allocate a capture buffer per urb */			(*purb)->transfer_buffer = kmalloc(subs->maxpacksize * nr_of_packs(), GFP_KERNEL);			if (NULL == (*purb)->transfer_buffer) {				usX2Y_urbs_release(subs);				return -ENOMEM;			}		}		(*purb)->dev = dev;		(*purb)->pipe = pipe;		(*purb)->number_of_packets = nr_of_packs();		(*purb)->context = subs;		(*purb)->interval = 1;		(*purb)->complete = i_usX2Y_subs_startup;	}	return 0;}static void usX2Y_subs_startup(snd_usX2Y_substream_t *subs){	usX2Ydev_t *usX2Y = subs->usX2Y;	usX2Y->prepare_subs = subs;	subs->urb[0]->start_frame = -1;	wmb();	usX2Y_urbs_set_complete(usX2Y, i_usX2Y_subs_startup);}static int usX2Y_urbs_start(snd_usX2Y_substream_t *subs){	int i, err;	usX2Ydev_t *usX2Y = subs->usX2Y;	if ((err = usX2Y_urbs_allocate(subs)) < 0)		return err;	subs->completed_urb = NULL;	for (i = 0; i < 4; i++) {		snd_usX2Y_substream_t *subs = usX2Y->subs[i];		if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)			goto start;	}	usX2Y->wait_iso_frame = -1; start:	{		usX2Y_subs_startup(subs);		for (i = 0; i < NRURBS; i++) {			struct urb *urb = subs->urb[i];			if (usb_pipein(urb->pipe)) {				unsigned long pack;				if (0 == i)					atomic_set(&subs->state, state_STARTING3);				urb->dev = usX2Y->chip.dev;				urb->transfer_flags = URB_ISO_ASAP;				for (pack = 0; pack < nr_of_packs(); pack++) {					urb->iso_frame_desc[pack].offset = subs->maxpacksize * pack;					urb->iso_frame_desc[pack].length = subs->maxpacksize;				}				urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs(); 				if ((err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {					snd_printk (KERN_ERR "cannot submit datapipe for urb %d, err = %d\n", i, err);					err = -EPIPE;					goto cleanup;				} else {					if (0 > usX2Y->wait_iso_frame)						usX2Y->wait_iso_frame = urb->start_frame;				}				urb->transfer_flags = 0;			} else {				atomic_set(&subs->state, state_STARTING1);				break;			}		}		err = 0;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -