usbusx2yaudio.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,028 行 · 第 1/2 页

C
1,028
字号
 * that. */static struct s_c2{	char c1, c2;}	SetRate44100[] ={	{ 0x14, 0x08},	// this line sets 44100, well actually a little less	{ 0x18, 0x40},	// only tascam / frontier design knows the further lines .......	{ 0x18, 0x42},	{ 0x18, 0x45},	{ 0x18, 0x46},	{ 0x18, 0x48},	{ 0x18, 0x4A},	{ 0x18, 0x4C},	{ 0x18, 0x4E},	{ 0x18, 0x50},	{ 0x18, 0x52},	{ 0x18, 0x54},	{ 0x18, 0x56},	{ 0x18, 0x58},	{ 0x18, 0x5A},	{ 0x18, 0x5C},	{ 0x18, 0x5E},	{ 0x18, 0x60},	{ 0x18, 0x62},	{ 0x18, 0x64},	{ 0x18, 0x66},	{ 0x18, 0x68},	{ 0x18, 0x6A},	{ 0x18, 0x6C},	{ 0x18, 0x6E},	{ 0x18, 0x70},	{ 0x18, 0x72},	{ 0x18, 0x74},	{ 0x18, 0x76},	{ 0x18, 0x78},	{ 0x18, 0x7A},	{ 0x18, 0x7C},	{ 0x18, 0x7E}};static struct s_c2 SetRate48000[] ={	{ 0x14, 0x09},	// this line sets 48000, well actually a little less	{ 0x18, 0x40},	// only tascam / frontier design knows the further lines .......	{ 0x18, 0x42},	{ 0x18, 0x45},	{ 0x18, 0x46},	{ 0x18, 0x48},	{ 0x18, 0x4A},	{ 0x18, 0x4C},	{ 0x18, 0x4E},	{ 0x18, 0x50},	{ 0x18, 0x52},	{ 0x18, 0x54},	{ 0x18, 0x56},	{ 0x18, 0x58},	{ 0x18, 0x5A},	{ 0x18, 0x5C},	{ 0x18, 0x5E},	{ 0x18, 0x60},	{ 0x18, 0x62},	{ 0x18, 0x64},	{ 0x18, 0x66},	{ 0x18, 0x68},	{ 0x18, 0x6A},	{ 0x18, 0x6C},	{ 0x18, 0x6E},	{ 0x18, 0x70},	{ 0x18, 0x73},	{ 0x18, 0x74},	{ 0x18, 0x76},	{ 0x18, 0x78},	{ 0x18, 0x7A},	{ 0x18, 0x7C},	{ 0x18, 0x7E}};#define NOOF_SETRATE_URBS ARRAY_SIZE(SetRate48000)static int usX2Y_rate_set(usX2Ydev_t *usX2Y, int rate){	int			err = 0, i;	snd_usX2Y_urbSeq_t	*us = NULL;	int			*usbdata = NULL;	DECLARE_WAITQUEUE(wait, current);	struct s_c2		*ra = rate == 48000 ? SetRate48000 : SetRate44100;	if (usX2Y->rate != rate) {		do {			us = kmalloc(sizeof(*us) + sizeof(struct urb*) * NOOF_SETRATE_URBS, GFP_KERNEL);			if (NULL == us) {				err = -ENOMEM;				break;			}			memset(us, 0, sizeof(*us) + sizeof(struct urb*) * NOOF_SETRATE_URBS); 			usbdata = kmalloc(sizeof(int)*NOOF_SETRATE_URBS, GFP_KERNEL);			if (NULL == usbdata) {				err = -ENOMEM;				break;			}			for (i = 0; i < NOOF_SETRATE_URBS; ++i) {				if (NULL == (us->urb[i] = usb_alloc_urb(0, GFP_KERNEL))) {					err = -ENOMEM;					break;				}				((char*)(usbdata + i))[0] = ra[i].c1;				((char*)(usbdata + i))[1] = ra[i].c2;				usb_fill_bulk_urb(us->urb[i], usX2Y->chip.dev, usb_sndbulkpipe(usX2Y->chip.dev, 4),						  usbdata + i, 2, i_usX2Y_04Int, usX2Y);#ifdef OLD_USB				us->urb[i]->transfer_flags = USB_QUEUE_BULK;#endif			}			if (err)				break;			add_wait_queue(&usX2Y->In04WaitQueue, &wait);			set_current_state(TASK_INTERRUPTIBLE);			us->submitted =	0;			us->len =	NOOF_SETRATE_URBS;			usX2Y->US04 =	us;					do {				signed long	timeout = schedule_timeout(HZ/2);                					if (signal_pending(current)) {					err = -ERESTARTSYS;					break;				}				if (0 == timeout) {					err = -ENODEV;					break;				}				usX2Y->rate = rate;				usX2Y->refframes = rate == 48000 ? 47 : 44;			} while (0);					remove_wait_queue(&usX2Y->In04WaitQueue, &wait);		} while (0);		if (us) {			us->submitted =	2*NOOF_SETRATE_URBS;			for (i = 0; i < NOOF_SETRATE_URBS; ++i) {				usb_unlink_urb(us->urb[i]);				usb_free_urb(us->urb[i]);			}			usX2Y->US04 = NULL;			kfree(usbdata);			kfree(us);		}	}	return err;}static int usX2Y_format_set(usX2Ydev_t *usX2Y, snd_pcm_format_t format){	int alternate, unlink_err, err;	struct list_head* p;	if (format == SNDRV_PCM_FORMAT_S24_3LE) {		alternate = 2;		usX2Y->stride = 6;	} else {		alternate = 1;		usX2Y->stride = 4;	}	list_for_each(p, &usX2Y->chip.midi_list) {		snd_usbmidi_input_stop(p);	}	unlink_err = usb_unlink_urb(usX2Y->In04urb);	if ((err = usb_set_interface(usX2Y->chip.dev, 0, alternate))) {		snd_printk("usb_set_interface error \n");		return err;	}	if (0 == unlink_err) {		usX2Y->In04urb->dev = usX2Y->chip.dev;		err = usb_submit_urb(usX2Y->In04urb, GFP_KERNEL);	}	list_for_each(p, &usX2Y->chip.midi_list) {		snd_usbmidi_input_start(p);	}	usX2Y->format = format;	usX2Y->rate = 0;	return err;}static int snd_usX2Y_pcm_hw_params(snd_pcm_substream_t *substream,				   snd_pcm_hw_params_t *hw_params){	int			err = 0;	unsigned int		rate = params_rate(hw_params);	snd_pcm_format_t	format = params_format(hw_params);	snd_printdd("snd_usX2Y_hw_params(%p, %p)\n", substream, hw_params);	{	// all pcm substreams off one usX2Y have to operate at the same rate & format		snd_card_t *card = substream->pstr->pcm->card;		struct list_head *list;		list_for_each(list, &card->devices) {			snd_device_t *dev;			snd_pcm_t *pcm;			int s;			dev = snd_device(list);			if (dev->type != SNDRV_DEV_PCM)				continue;			pcm = dev->device_data;			for (s = 0; s < 2; ++s) {				snd_pcm_substream_t *test_substream;				test_substream = pcm->streams[s].substream;				if (test_substream && test_substream != substream  &&				    test_substream->runtime &&				    ((test_substream->runtime->format &&				      test_substream->runtime->format != format) ||				     (test_substream->runtime->rate &&				      test_substream->runtime->rate != rate)))					return -EINVAL;			}		}	}	if (0 > (err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)))) {		snd_printk("snd_pcm_lib_malloc_pages(%p, %i) returned %i\n", substream, params_buffer_bytes(hw_params), err);		return err;	}	return 0;}/* * free the buffer */static int snd_usX2Y_pcm_hw_free(snd_pcm_substream_t *substream){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;	snd_printdd("snd_usX2Y_hw_free(%p)\n", substream);	if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {		snd_usX2Y_substream_t *cap_subs = subs->usX2Y->substream[SNDRV_PCM_STREAM_CAPTURE];		subs->prepared = 0;		usX2Y_urbs_release(subs);		if (!cap_subs->pcm_substream ||		    !cap_subs->pcm_substream->runtime ||		    !cap_subs->pcm_substream->runtime->status ||		    cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {			cap_subs->prepared = 0;			usX2Y_urbs_release(cap_subs);		}	} else {		snd_usX2Y_substream_t *playback_subs = subs->usX2Y->substream[SNDRV_PCM_STREAM_PLAYBACK];		if (!playback_subs->prepared) {			subs->prepared = 0;			usX2Y_urbs_release(subs);		}	}	return snd_pcm_lib_free_pages(substream);}/* * prepare callback * * set format and initialize urbs */static int snd_usX2Y_pcm_prepare(snd_pcm_substream_t *substream){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;	snd_usX2Y_substream_t *capsubs = subs->usX2Y->substream[SNDRV_PCM_STREAM_CAPTURE];	int err = 0;	snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);// Start hardware streams// SyncStream first....	if (! capsubs->prepared) {		if (subs->usX2Y->format != runtime->format)			if ((err = usX2Y_format_set(subs->usX2Y, runtime->format)) < 0)				return err;		if (subs->usX2Y->rate != runtime->rate)			if ((err = usX2Y_rate_set(subs->usX2Y, runtime->rate)) < 0)				return err;		snd_printdd("starting capture pipe for playpipe\n");		usX2Y_urbs_allocate(capsubs);		capsubs->completed_urb = NULL;		{			DECLARE_WAITQUEUE(wait, current);			add_wait_queue(&capsubs->wait_queue, &wait);			if (0 <= (err = usX2Y_urbs_capt_start(capsubs))) {				signed long timeout;				set_current_state(TASK_INTERRUPTIBLE);				timeout = schedule_timeout(HZ/4);				if (signal_pending(current))					err = -ERESTARTSYS;				else {					snd_printdd("%li\n", HZ/4 - timeout);					if (0 == timeout)						err = -EPIPE;				}			}			remove_wait_queue(&capsubs->wait_queue, &wait);			if (0 > err)				return err;		}	}	if (subs != capsubs) {		int u;		if (!subs->prepared) {			if ((err = usX2Y_urbs_allocate(subs)) < 0)				return err;			subs->prepared = 1;		}		while (subs->submitted_urbs)		for (u = 0; u < NRURBS; u++) {			snd_printdd("%i\n", subs->urb[u]->status);			while(subs->urb[u]->status  ||  NULL != subs->urb[u]->hcpriv) {				signed long timeout;				snd_printdd("ep=%i waiting for urb=%p status=%i hcpriv=%p\n",					   subs->endpoint, subs->urb[u],					   subs->urb[u]->status, subs->urb[u]->hcpriv);				set_current_state(TASK_INTERRUPTIBLE);				timeout = schedule_timeout(HZ/10);				if (signal_pending(current)) {					return -ERESTARTSYS;				}			}		}		subs->completed_urb = NULL;		subs->next_urb_complete = -1;		subs->stalled = 0;	}	usX2Y_substream_prepare(subs);	return err;}static snd_pcm_hardware_t snd_usX2Y_2c ={	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |				 SNDRV_PCM_INFO_BLOCK_TRANSFER |				 SNDRV_PCM_INFO_MMAP_VALID),	.formats =                 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,	.rates =                   SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,	.rate_min =                44100,	.rate_max =                48000,	.channels_min =            2,	.channels_max =            2,	.buffer_bytes_max =	(2*128*1024),	.period_bytes_min =	64,	.period_bytes_max =	(128*1024),	.periods_min =		2,	.periods_max =		1024,	.fifo_size =              0};static int snd_usX2Y_pcm_open(snd_pcm_substream_t *substream){	snd_usX2Y_substream_t	*subs = ((snd_usX2Y_substream_t **)					 snd_pcm_substream_chip(substream))[substream->stream];	snd_pcm_runtime_t	*runtime = substream->runtime;	runtime->hw = snd_usX2Y_2c;	runtime->private_data = subs;	subs->pcm_substream = substream;	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);	return 0;}static int snd_usX2Y_pcm_close(snd_pcm_substream_t *substream){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;	int err = 0;	subs->pcm_substream = NULL;	return err;}static snd_pcm_ops_t snd_usX2Y_pcm_ops = {	.open =		snd_usX2Y_pcm_open,	.close =	snd_usX2Y_pcm_close,	.ioctl =	snd_pcm_lib_ioctl,	.hw_params =	snd_usX2Y_pcm_hw_params,	.hw_free =	snd_usX2Y_pcm_hw_free,	.prepare =	snd_usX2Y_pcm_prepare,	.trigger =	snd_usX2Y_pcm_trigger,	.pointer =	snd_usX2Y_pcm_pointer,};/* * free a usb stream instance */static void usX2Y_audio_stream_free(snd_usX2Y_substream_t **usX2Y_substream){	if (NULL != usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]) {		kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]);		usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL;	}	kfree(usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]);	usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE] = NULL;}static void snd_usX2Y_pcm_private_free(snd_pcm_t *pcm){	snd_usX2Y_substream_t **usX2Y_stream = pcm->private_data;	if (usX2Y_stream) {		snd_pcm_lib_preallocate_free_for_all(pcm);		usX2Y_audio_stream_free(usX2Y_stream);	}}static int usX2Y_audio_stream_new(snd_card_t *card, int playback_endpoint, int capture_endpoint){	snd_pcm_t *pcm;	int err, i;	snd_usX2Y_substream_t **usX2Y_substream =		usX2Y(card)->substream + 2 * usX2Y(card)->chip.pcm_devs;	for (i = playback_endpoint ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;	     i <= SNDRV_PCM_STREAM_CAPTURE; ++i) {		usX2Y_substream[i] = kcalloc(1, sizeof(snd_usX2Y_substream_t), GFP_KERNEL);		if (NULL == usX2Y_substream[i]) {			snd_printk(KERN_ERR "cannot malloc\n");			return -ENOMEM;		}		init_waitqueue_head(&usX2Y_substream[i]->wait_queue);		usX2Y_substream[i]->usX2Y = usX2Y(card);	}	if (playback_endpoint)		usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]->endpoint = playback_endpoint;	usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]->endpoint = capture_endpoint;	err = snd_pcm_new(card, NAME_ALLCAPS" Audio", usX2Y(card)->chip.pcm_devs,			  playback_endpoint ? 1 : 0, 1,			  &pcm);	if (err < 0) {		usX2Y_audio_stream_free(usX2Y_substream);		return err;	}	if (playback_endpoint)		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_pcm_ops);	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_pcm_ops);	pcm->private_data = usX2Y_substream;	pcm->private_free = snd_usX2Y_pcm_private_free;	pcm->info_flags = 0;	sprintf(pcm->name, NAME_ALLCAPS" Audio #%d", usX2Y(card)->chip.pcm_devs);	if ((playback_endpoint &&	     0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,						     SNDRV_DMA_TYPE_CONTINUOUS,						     snd_dma_continuous_data(GFP_KERNEL),						     64*1024, 128*1024))) ||	    0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,	    					     SNDRV_DMA_TYPE_CONTINUOUS,	    					     snd_dma_continuous_data(GFP_KERNEL),						     64*1024, 128*1024))) {		snd_usX2Y_pcm_private_free(pcm);		return err;	}	usX2Y(card)->chip.pcm_devs++;	return 0;}/* * free the chip instance * * here we have to do not much, since pcm and controls are already freed * */static int snd_usX2Y_device_dev_free(snd_device_t *device){	return 0;}/* * create a chip instance and set its names. */int usX2Y_audio_create(snd_card_t* card){	int err = 0;	static snd_device_ops_t ops = {		.dev_free = snd_usX2Y_device_dev_free,	};		INIT_LIST_HEAD(&usX2Y(card)->chip.pcm_list);	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, usX2Y(card), &ops)) < 0) {//		snd_usX2Y_audio_free(usX2Y(card));		return err;	}	if (0 > (err = usX2Y_audio_stream_new(card, 0xA, 0x8)))		return err;	if (usX2Y(card)->chip.dev->descriptor.idProduct == USB_ID_US428)	     if (0 > (err = usX2Y_audio_stream_new(card, 0, 0xA)))		     return err;	if (usX2Y(card)->chip.dev->descriptor.idProduct != USB_ID_US122)		err = usX2Y_rate_set(usX2Y(card), 44100);	// Lets us428 recognize output-volume settings, disturbs us122.	return err;}

⌨️ 快捷键说明

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