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

📄 btsco.c

📁 蓝牙blue tooth sco协议栈
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  Bluetooth SCO soundcard *  Copyright (c) 2003, 2004 by Jonathan Paisley <jp@dcs.gla.ac.uk> * *  Based on dummy.c which is *  Copyright (c) by Jaroslav Kysela <perex@suse.cz> * *   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 * */#define chip_t snd_card_bt_sco_t#include <sound/driver.h>#include <linux/version.h>#include <linux/init.h>#include <linux/jiffies.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/wait.h>#include <linux/socket.h>#include <linux/file.h>#include <linux/completion.h>#include <linux/smp_lock.h>#include <net/sock.h>#include <net/bluetooth/bluetooth.h>#include <sound/core.h>#include <sound/control.h>#include <sound/pcm.h>#include <sound/rawmidi.h>#include <sound/hwdep.h>#define SNDRV_GET_ID#include <sound/initval.h>#ifndef SNDRV_HWDEP_IFACE_BLUETOOTH#define SNDRV_HWDEP_IFACE_BLUETOOTH (SNDRV_HWDEP_IFACE_EMUX_WAVETABLE + 1)#endif#ifndef SNDRV_HWDEP_IFACE_BT_SCO#define SNDRV_HWDEP_IFACE_BT_SCO (SNDRV_HWDEP_IFACE_BLUETOOTH + 1)#endif#define SNDRV_BT_SCO_IOCTL_SET_SCO_SOCKET _IOW ('H', 0x10, int)#define SNDRV_BT_SCO_IOCTL_REQ_INFO _IO ('H', 0x11)MODULE_AUTHOR("Jonathan Paisley <jp@dcs.gla.ac.uk>");MODULE_DESCRIPTION("Bluetooth SCO Headset Soundcard");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{ALSA,Bluetooth SCO Soundcard}}");static char *mod_revision = "$Revision: 1.6 $";#undef dprintk#if 1#define dprintk(fmt...) printk(KERN_INFO "snd-bt-sco: " fmt)#else#define dprintk(fmt...) do {} while(0)#endif#define MAX_BUFFER_SIZE		(32*1024)#define MIXER_ADDR_MASTER	0#define MIXER_ADDR_MIC		1#define MIXER_ADDR_LAST		1#define MIXER_MASK_MASTER	1#define MIXER_MASK_MIC		2#define MIXER_MIN_VOLUME	1#define MIXER_MAX_VOLUME	15struct snd_card_bt_sco_pcm;typedef struct snd_card_bt_sco_info {	int mixer_volume[MIXER_ADDR_LAST + 1];	int playback_count, capture_count;} snd_card_bt_sco_info_t;typedef struct snd_card_bt_sco {	snd_card_t *card;	spinlock_t mixer_lock;	int mixer_volume[MIXER_ADDR_LAST + 1];	snd_kcontrol_t *mixer_controls[MIXER_ADDR_LAST + 2];	/* also loopback */	volatile int loopback;	atomic_t playback_count, capture_count;	volatile int count_changed;	spinlock_t count_changed_lock;	spinlock_t mixer_changed_lock;	volatile int mixer_changed;	wait_queue_head_t hwdep_wait;	int thread_pid;	struct completion thread_done;	volatile int thread_exit;	struct semaphore thread_sem;	volatile struct socket *sco_sock;	struct semaphore sock_sem;	wait_queue_head_t wait;	struct semaphore playback_sem;	struct snd_card_bt_sco_pcm *playback;	struct semaphore capture_sem;	struct snd_card_bt_sco_pcm *capture;} snd_card_bt_sco_t;typedef struct snd_card_bt_sco_pcm {	snd_card_bt_sco_t *bt_sco;	spinlock_t lock;	unsigned int pcm_size;	unsigned int pcm_count;	unsigned int pcm_bps;	/* bytes per second */	unsigned int pcm_irq_pos;	/* IRQ position */	unsigned int pcm_buf_pos;	/* position in buffer */	snd_pcm_substream_t *substream;} snd_card_bt_sco_pcm_t;static snd_card_t *snd_bt_sco_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;static int snd_card_bt_sco_playback_trigger(snd_pcm_substream_t *					    substream, int cmd){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_card_bt_sco_pcm_t *bspcm = runtime->private_data;	snd_card_bt_sco_t *bt_sco = snd_pcm_substream_chip(substream);	dprintk("playback_trigger %d\n", cmd);	if (cmd == SNDRV_PCM_TRIGGER_START) {		bt_sco->playback = bspcm;		dprintk("setting playback to bspcm\n");	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {		bt_sco->playback = NULL;		dprintk("setting playback to NULL\n");	} else {		return -EINVAL;	}	return 0;}static int snd_card_bt_sco_capture_trigger(snd_pcm_substream_t *					   substream, int cmd){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_card_bt_sco_pcm_t *bspcm = runtime->private_data;	snd_card_bt_sco_t *bt_sco = snd_pcm_substream_chip(substream);	dprintk("capture_trigger %d\n", cmd);	if (cmd == SNDRV_PCM_TRIGGER_START) {		bt_sco->capture = bspcm;		dprintk("setting capture to bspcm\n");	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {		bt_sco->capture = NULL;		dprintk("setting capture to NULL\n");	} else {		return -EINVAL;	}	return 0;}static int snd_card_bt_sco_pcm_prepare(snd_pcm_substream_t * substream){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_card_bt_sco_pcm_t *bspcm = runtime->private_data;	unsigned int bps;	bps = runtime->rate * runtime->channels;	bps *= snd_pcm_format_width(runtime->format);	bps /= 8;	if (bps <= 0)		return -EINVAL;	bspcm->pcm_bps = bps;	bspcm->pcm_size = snd_pcm_lib_buffer_bytes(substream);	bspcm->pcm_count = snd_pcm_lib_period_bytes(substream);	bspcm->pcm_irq_pos = 0;	bspcm->pcm_buf_pos = 0;	dprintk("prepare ok bps: %d size: %d count: %d\n",		bspcm->pcm_bps, bspcm->pcm_size, bspcm->pcm_count);	return 0;}static int snd_card_bt_sco_playback_prepare(snd_pcm_substream_t * substream){	return snd_card_bt_sco_pcm_prepare(substream);}static int snd_card_bt_sco_capture_prepare(snd_pcm_substream_t * substream){	dprintk("capture_prepare\n");	return snd_card_bt_sco_pcm_prepare(substream);}static void snd_card_bt_sco_pcm_receive(snd_card_bt_sco_pcm_t * bspcm,					unsigned char *data, unsigned int len){	unsigned long flags;	unsigned int oldptr;	spin_lock_irqsave(&bspcm->lock, flags);	oldptr = bspcm->pcm_buf_pos;	bspcm->pcm_irq_pos += len;	bspcm->pcm_buf_pos += len;	bspcm->pcm_buf_pos %= bspcm->pcm_size;	spin_unlock_irqrestore(&bspcm->lock, flags);	/* copy a data chunk */	if (oldptr + len > bspcm->pcm_size) {		unsigned int cnt = bspcm->pcm_size - oldptr;		memcpy(bspcm->substream->runtime->dma_area + oldptr, data, cnt);		memcpy(bspcm->substream->runtime->dma_area, data + cnt,		       len - cnt);	} else {		memcpy(bspcm->substream->runtime->dma_area + oldptr, data, len);	}	/* update the pointer, call callback if necessary */	spin_lock_irqsave(&bspcm->lock, flags);	if (bspcm->pcm_irq_pos >= bspcm->pcm_count) {		bspcm->pcm_irq_pos %= bspcm->pcm_count;		spin_unlock_irqrestore(&bspcm->lock, flags);		snd_pcm_period_elapsed(bspcm->substream);	} else		spin_unlock_irqrestore(&bspcm->lock, flags);}static void snd_card_bt_sco_pcm_send(snd_card_bt_sco_pcm_t * bspcm,				     unsigned char *data, unsigned int len){	unsigned long flags;	unsigned int oldptr;	spin_lock_irqsave(&bspcm->lock, flags);	oldptr = bspcm->pcm_buf_pos;	bspcm->pcm_irq_pos += len;	bspcm->pcm_buf_pos += len;	bspcm->pcm_buf_pos %= bspcm->pcm_size;	spin_unlock_irqrestore(&bspcm->lock, flags);	/* copy a data chunk */	if (oldptr + len > bspcm->pcm_size) {		unsigned int cnt = bspcm->pcm_size - oldptr;		memcpy(data, bspcm->substream->runtime->dma_area + oldptr, cnt);		memcpy(data + cnt, bspcm->substream->runtime->dma_area,		       len - cnt);	} else {		memcpy(data, bspcm->substream->runtime->dma_area + oldptr, len);	}	/* update the pointer, call callback if necessary */	spin_lock_irqsave(&bspcm->lock, flags);	if (bspcm->pcm_irq_pos >= bspcm->pcm_count) {		bspcm->pcm_irq_pos %= bspcm->pcm_count;		spin_unlock_irqrestore(&bspcm->lock, flags);		snd_pcm_period_elapsed(bspcm->substream);	} else		spin_unlock_irqrestore(&bspcm->lock, flags);}static snd_pcm_uframes_tsnd_card_bt_sco_playback_pointer(snd_pcm_substream_t * substream){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_card_bt_sco_pcm_t *bspcm = runtime->private_data;	return bytes_to_frames(runtime, bspcm->pcm_buf_pos);}static snd_pcm_uframes_tsnd_card_bt_sco_capture_pointer(snd_pcm_substream_t * substream){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_card_bt_sco_pcm_t *bspcm = runtime->private_data;	return bytes_to_frames(runtime, bspcm->pcm_buf_pos);}static snd_pcm_hardware_t snd_card_bt_sco_playback = {	.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |		 SNDRV_PCM_INFO_MMAP_VALID),	.formats = SNDRV_PCM_FMTBIT_S16_LE,	.rates = SNDRV_PCM_RATE_8000,	.rate_min = 8000,	.rate_max = 8000,	.channels_min = 1,	.channels_max = 1,	.buffer_bytes_max = MAX_BUFFER_SIZE,	.period_bytes_min = 24,	.period_bytes_max = MAX_BUFFER_SIZE,	.periods_min = 1,	.periods_max = 4 * 8000 / 24,	.fifo_size = 0,};static snd_pcm_hardware_t snd_card_bt_sco_capture = {	.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |		 SNDRV_PCM_INFO_MMAP_VALID),	.formats = SNDRV_PCM_FMTBIT_S16_LE,	.rates = SNDRV_PCM_RATE_8000,	.rate_min = 8000,	.rate_max = 8000,	.channels_min = 1,	.channels_max = 1,	.buffer_bytes_max = MAX_BUFFER_SIZE,	.period_bytes_min = 24,	.period_bytes_max = MAX_BUFFER_SIZE,	.periods_min = 1,	.periods_max = 4 * 8000 / 24,	.fifo_size = 0,};static void snd_card_bt_sco_runtime_free(snd_pcm_runtime_t * runtime){	snd_card_bt_sco_pcm_t *bspcm = runtime->private_data;	kfree(bspcm);}static int snd_card_bt_sco_playback_open(snd_pcm_substream_t * substream){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_card_bt_sco_pcm_t *bspcm;	snd_card_bt_sco_t *bt_sco = snd_pcm_substream_chip(substream);	dprintk("playback_open\n");	bspcm = kmalloc(sizeof(*bspcm), GFP_KERNEL);	if (bspcm == NULL)		return -ENOMEM;	memset(bspcm, 0, sizeof(*bspcm));	if ((runtime->dma_area =	     snd_malloc_pages(MAX_BUFFER_SIZE, GFP_KERNEL)) == NULL) {		kfree(bspcm);		return -ENOMEM;	}	runtime->dma_bytes = MAX_BUFFER_SIZE;	spin_lock_init(&bspcm->lock);	bspcm->substream = substream;	runtime->private_data = bspcm;	runtime->private_free = snd_card_bt_sco_runtime_free;	runtime->hw = snd_card_bt_sco_playback;	atomic_inc(&bt_sco->playback_count);	spin_lock_irq(&bt_sco->count_changed_lock);	bt_sco->count_changed = 1;	spin_unlock_irq(&bt_sco->count_changed_lock);	wake_up(&bt_sco->hwdep_wait);	return 0;}static int snd_card_bt_sco_capture_open(snd_pcm_substream_t * substream){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_card_bt_sco_pcm_t *bspcm;	snd_card_bt_sco_t *bt_sco = snd_pcm_substream_chip(substream);	dprintk("capture_open\n");	bspcm = kmalloc(sizeof(*bspcm), GFP_KERNEL);	if (bspcm == NULL)		return -ENOMEM;	memset(bspcm, 0, sizeof(*bspcm));	if ((runtime->dma_area =	     snd_malloc_pages(MAX_BUFFER_SIZE, GFP_KERNEL)) == NULL) {		kfree(bspcm);		return -ENOMEM;	}	runtime->dma_bytes = MAX_BUFFER_SIZE;	memset(runtime->dma_area, 0, runtime->dma_bytes);	spin_lock_init(&bspcm->lock);	bspcm->substream = substream;	runtime->private_data = bspcm;	runtime->private_free = snd_card_bt_sco_runtime_free;	runtime->hw = snd_card_bt_sco_capture;	atomic_inc(&bt_sco->capture_count);	spin_lock_irq(&bt_sco->count_changed_lock);	bt_sco->count_changed = 1;	spin_unlock_irq(&bt_sco->count_changed_lock);	wake_up(&bt_sco->hwdep_wait);	return 0;}static int snd_card_bt_sco_playback_close(snd_pcm_substream_t * substream){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_card_bt_sco_t *bt_sco = snd_pcm_substream_chip(substream);	snd_assert(bt_sco->playback == NULL,;	    );	/* Ensure any references to this in our thread have finished */	down(&bt_sco->playback_sem);	up(&bt_sco->playback_sem);	atomic_dec(&bt_sco->playback_count);	spin_lock_irq(&bt_sco->count_changed_lock);	bt_sco->count_changed = 1;	spin_unlock_irq(&bt_sco->count_changed_lock);	wake_up(&bt_sco->hwdep_wait);	snd_free_pages(runtime->dma_area, runtime->dma_bytes);	return 0;}static int snd_card_bt_sco_capture_close(snd_pcm_substream_t * substream){	snd_pcm_runtime_t *runtime = substream->runtime;	struct snd_card_bt_sco *bt_sco =	    (struct snd_card_bt_sco *)substream->private_data;	snd_assert(bt_sco->capture == NULL,;	    );	/* Ensure any references to this in our thread have finished */	down(&bt_sco->capture_sem);	up(&bt_sco->capture_sem);	atomic_dec(&bt_sco->capture_count);	spin_lock_irq(&bt_sco->count_changed_lock);	bt_sco->count_changed = 1;	spin_unlock_irq(&bt_sco->count_changed_lock);	wake_up(&bt_sco->hwdep_wait);	snd_free_pages(runtime->dma_area, runtime->dma_bytes);	return 0;}static snd_pcm_ops_t snd_card_bt_sco_playback_ops = {	.open = snd_card_bt_sco_playback_open,	.close = snd_card_bt_sco_playback_close,	.ioctl = snd_pcm_lib_ioctl,	.prepare = snd_card_bt_sco_playback_prepare,	.trigger = snd_card_bt_sco_playback_trigger,	.pointer = snd_card_bt_sco_playback_pointer,};static snd_pcm_ops_t snd_card_bt_sco_capture_ops = {	.open = snd_card_bt_sco_capture_open,	.close = snd_card_bt_sco_capture_close,	.ioctl = snd_pcm_lib_ioctl,	.prepare = snd_card_bt_sco_capture_prepare,	.trigger = snd_card_bt_sco_capture_trigger,	.pointer = snd_card_bt_sco_capture_pointer,};static int __init snd_card_bt_sco_pcm(snd_card_bt_sco_t * bt_sco){	snd_pcm_t *pcm;	int err;	if ((err =	     snd_pcm_new(bt_sco->card, "Bluetooth SCO PCM", 0, 1, 1, &pcm)) < 0)		return err;	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,			&snd_card_bt_sco_playback_ops);	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,			&snd_card_bt_sco_capture_ops);	pcm->private_data = bt_sco;	pcm->info_flags = 0;	strcpy(pcm->name, "BT SCO PCM");	return 0;}#define BT_SCO_VOLUME(xname, xindex, addr) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \                        .info = snd_bt_sco_volume_info, \                                .get = snd_bt_sco_volume_get, .put = snd_bt_sco_volume_put, \                                                                        .private_value = addr }static int snd_bt_sco_volume_info(snd_kcontrol_t * kcontrol,				  snd_ctl_elem_info_t * uinfo){	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;	uinfo->count = 1;	uinfo->value.integer.min = MIXER_MIN_VOLUME;	uinfo->value.integer.max = MIXER_MAX_VOLUME;	return 0;}static int snd_bt_sco_volume_get(snd_kcontrol_t * kcontrol,				 snd_ctl_elem_value_t * ucontrol){	snd_card_bt_sco_t *bt_sco = snd_kcontrol_chip(kcontrol);	unsigned long flags;	int addr = kcontrol->private_value;	spin_lock_irqsave(&bt_sco->mixer_lock, flags);	ucontrol->value.integer.value[0] = bt_sco->mixer_volume[addr];	spin_unlock_irqrestore(&bt_sco->mixer_lock, flags);	return 0;}static int snd_bt_sco_volume_put(snd_kcontrol_t * kcontrol,				 snd_ctl_elem_value_t * ucontrol){	snd_card_bt_sco_t *bt_sco = snd_kcontrol_chip(kcontrol);	unsigned long flags;	int changed, addr = kcontrol->private_value;	int vol;	vol = ucontrol->value.integer.value[0];	if (vol < MIXER_MIN_VOLUME)		vol = MIXER_MIN_VOLUME;	if (vol > MIXER_MAX_VOLUME)		vol = MIXER_MAX_VOLUME;	spin_lock_irqsave(&bt_sco->mixer_lock, flags);	changed = bt_sco->mixer_volume[addr] != vol;	bt_sco->mixer_volume[addr] = vol;	spin_unlock_irqrestore(&bt_sco->mixer_lock, flags);	if (changed) {		spin_lock_irqsave(&bt_sco->mixer_changed_lock, flags);		bt_sco->mixer_changed = 1;		spin_unlock_irqrestore(&bt_sco->mixer_changed_lock, flags);		wake_up(&bt_sco->hwdep_wait);	}	return changed;}static int snd_bt_sco_boolean_info(snd_kcontrol_t * kcontrol,				   snd_ctl_elem_info_t * uinfo){

⌨️ 快捷键说明

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