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

📄 btsco.c

📁 蓝牙blue tooth sco协议栈
💻 C
📖 第 1 页 / 共 2 页
字号:
	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;	uinfo->count = 1;	uinfo->value.integer.min = 0;	uinfo->value.integer.max = 1;	return 0;}static int snd_bt_sco_loopback_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;	spin_lock_irqsave(&bt_sco->mixer_lock, flags);	ucontrol->value.integer.value[0] = bt_sco->loopback;	spin_unlock_irqrestore(&bt_sco->mixer_lock, flags);	return 0;}static int snd_bt_sco_loopback_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;	int loopback;	loopback = !!ucontrol->value.integer.value[0];	spin_lock_irqsave(&bt_sco->mixer_lock, flags);	changed = bt_sco->loopback != loopback;	bt_sco->loopback = loopback;	spin_unlock_irqrestore(&bt_sco->mixer_lock, flags);	return changed;}#define BT_SCO_CONTROLS (sizeof(snd_bt_sco_controls)/sizeof(snd_kcontrol_new_t))static snd_kcontrol_new_t snd_bt_sco_controls[] = {	BT_SCO_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER),	BT_SCO_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC),	{.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	 .name = "Loopback Switch",	 .index = 0,	 .info = snd_bt_sco_boolean_info,	 .get = snd_bt_sco_loopback_get,	 .put = snd_bt_sco_loopback_put,	 }};int __init snd_card_bt_sco_new_mixer(snd_card_bt_sco_t * bt_sco){	snd_card_t *card = bt_sco->card;	unsigned int idx;	int err;	snd_assert(bt_sco != NULL, return -EINVAL);	spin_lock_init(&bt_sco->mixer_lock);	strcpy(card->mixername, "BT Headset Mixer");	for (idx = 0; idx < BT_SCO_CONTROLS; idx++) {		bt_sco->mixer_controls[idx] =		    snd_ctl_new1(&snd_bt_sco_controls[idx], bt_sco);		if ((err = snd_ctl_add(card, bt_sco->mixer_controls[idx])) < 0)			return err;	}	return 0;}static int snd_card_bt_open(snd_hwdep_t * hw, struct file *file){	return 0;}static int snd_card_bt_release(snd_hwdep_t * hw, struct file *file){	return 0;}static int snd_card_bt_ioctl(snd_hwdep_t * hw, struct file *file,			     unsigned int cmd, unsigned long arg){	snd_card_bt_sco_t *bt_sco = hw->card->private_data;	struct socket *sock;	int err = -ENOTTY;	int fd = arg;	switch (cmd) {	case SNDRV_BT_SCO_IOCTL_SET_SCO_SOCKET:		err = 0;		/*  Interrupt any socket operations, so that we may		 *  change the socket */		down(&bt_sco->sock_sem);		kill_proc(bt_sco->thread_pid, SIGINT, 1);		if (bt_sco->sco_sock) {			dprintk("Disposing of previous socket count %d\n",				file_count(bt_sco->sco_sock->file));			/* Extra brackets needed here since sockfd_put is a poorly implemented macro */			sockfd_put(((struct socket *)bt_sco->sco_sock));			bt_sco->sco_sock = NULL;		}		if (fd >= 0) {			err = -EINVAL;			sock = sockfd_lookup(fd, &err);			if (sock) {				if (sock->sk->sk_family == PF_BLUETOOTH &&				    sock->sk->sk_protocol == BTPROTO_SCO) {					bt_sco->sco_sock = sock;					wake_up(&bt_sco->wait);					err = 0;				} else {					dprintk					    ("Not a bluetooth SCO socket %d:%d\n",					     sock->sk->sk_family,					     sock->sk->sk_protocol);					sockfd_put(sock);				}			}		}		up(&bt_sco->sock_sem);		break;	case SNDRV_BT_SCO_IOCTL_REQ_INFO:		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);		break;	}	return err;}static long snd_card_bt_write(snd_hwdep_t * hw, const char *buf, long count,			      loff_t * offset){	snd_card_bt_sco_t *bt_sco = hw->card->private_data;	int mixer_volume[MIXER_ADDR_LAST + 1];	int retval;	int i;	if (count != sizeof(mixer_volume))		return -EINVAL;	if (copy_from_user(mixer_volume, buf, sizeof(mixer_volume)))		return -EFAULT;	retval = sizeof(mixer_volume);	spin_lock_irq(&bt_sco->mixer_lock);	for (i = 0; i <= MIXER_ADDR_LAST; i++) {		int vol = mixer_volume[i];		if (vol > MIXER_MAX_VOLUME)			vol = MIXER_MAX_VOLUME;		if (vol < MIXER_MIN_VOLUME)			vol = MIXER_MIN_VOLUME;		if (bt_sco->mixer_volume[i] != vol) {			bt_sco->mixer_volume[i] = vol;			snd_ctl_notify(bt_sco->card,				       SNDRV_CTL_EVENT_MASK_VALUE,				       &bt_sco->mixer_controls[i]->id);		}	}	spin_unlock_irq(&bt_sco->mixer_lock);	return retval;}static long snd_card_bt_read(snd_hwdep_t * hw, char *buf, long count,			     loff_t * offset){	snd_card_bt_sco_t *bt_sco = hw->card->private_data;	DECLARE_WAITQUEUE(wait, current);	ssize_t retval;	int changed;	snd_card_bt_sco_info_t infobuf;	if (count < sizeof(bt_sco->mixer_volume))		return -EINVAL;	add_wait_queue(&bt_sco->hwdep_wait, &wait);	current->state = TASK_INTERRUPTIBLE;	do {		changed = 0;		spin_lock_irq(&bt_sco->mixer_changed_lock);		if(bt_sco->mixer_changed)			changed = 1;		bt_sco->mixer_changed = 0;		spin_unlock_irq(&bt_sco->mixer_changed_lock);		spin_lock_irq(&bt_sco->count_changed_lock);		if(bt_sco->count_changed)			changed = 1;		bt_sco->count_changed = 0;		spin_unlock_irq(&bt_sco->count_changed_lock);		if (changed != 0)			break;		if (signal_pending(current)) {			retval = -ERESTARTSYS;			goto out;		}		schedule();	} while (1);		memcpy(infobuf.mixer_volume, bt_sco->mixer_volume, sizeof(infobuf.mixer_volume));	infobuf.playback_count = atomic_read(&bt_sco->playback_count);	infobuf.capture_count = atomic_read(&bt_sco->capture_count);		if (copy_to_user	    (buf, &infobuf, sizeof(infobuf)))		retval = -EFAULT;	else		retval = sizeof(infobuf);      out:	current->state = TASK_RUNNING;	remove_wait_queue(&bt_sco->hwdep_wait, &wait);	return retval;}static unsigned int snd_card_bt_poll(snd_hwdep_t * hw,				     struct file *file, poll_table * wait){	snd_card_bt_sco_t *bt_sco = hw->card->private_data;	int ret;	poll_wait(file, &bt_sco->hwdep_wait, wait);	ret = 0;	spin_lock_irq(&bt_sco->mixer_changed_lock);	if(bt_sco->mixer_changed)		ret |= POLLIN | POLLRDNORM;	spin_unlock_irq(&bt_sco->mixer_changed_lock);	spin_lock_irq(&bt_sco->count_changed_lock);	if(bt_sco->count_changed)		ret |= POLLIN | POLLRDNORM;	spin_unlock_irq(&bt_sco->count_changed_lock);	return ret;}static int snd_card_bt_sco_thread(void *data){	snd_card_t *card = (snd_card_t *) data;	snd_card_bt_sco_t *bt_sco = card->private_data;	struct socket *sock;	int len;#define BUF_SIZE 256	unsigned char buf[BUF_SIZE];	struct msghdr msg;	struct iovec iov;	sigset_t unblocked;	lock_kernel();	daemonize("snd-bt-scod");	sigemptyset(&unblocked);	sigaddset(&unblocked, SIGINT);	sigaddset(&unblocked, SIGTERM);	sigprocmask(SIG_UNBLOCK, &unblocked, NULL);	/* Pretend so that copy_to_user and friends work */	set_fs(KERNEL_DS);	dprintk("snd-bt-scod thread starting\n");	up(&bt_sco->thread_sem);	do {		if (signal_pending(current))			flush_signals(current);		/*      This may be woken up by a wake_up() when		 *      a new socket is installed, or by a signal.		 *      Signals are sent to terminate the thread,		 *      in which case thread_exit is set, and to force		 *      recvmesg() to wake up (from the ioctl handler)		 */		wait_event_interruptible(bt_sco->wait, bt_sco->sco_sock != 0);		if (bt_sco->thread_exit)			break;		down(&bt_sco->sock_sem);		sock = (struct socket *)bt_sco->sco_sock;		if (sock)			get_file(sock->file);		up(&bt_sco->sock_sem);		if (!sock)			continue;		/* We have a socket, let's read from it and write to it... */		memset(&msg, 0, sizeof(msg));		msg.msg_iov = &iov;		iov.iov_base = buf;		iov.iov_len = BUF_SIZE;		/* This will block until we receive data or a signal */		len = sock_recvmsg(sock, &msg, BUF_SIZE, 0);		if (len > 0) {			down(&bt_sco->capture_sem);			if (bt_sco->capture) {				snd_card_bt_sco_pcm_receive				    (bt_sco->capture, buf, len);			}			up(&bt_sco->capture_sem);			down(&bt_sco->playback_sem);			if (bt_sco->playback || !bt_sco->loopback) {				memset(buf, 0, len);#if 0				/* fill with tone instead of silence */				int i;				for (i = 0; i < len / 2; i++) {					buf[i] = 0;				}				for (i = len / 2; i < len; i++) {					buf[i] = 127;				}#endif			}			if (bt_sco->playback) {				int i, notzero = -1;				snd_card_bt_sco_pcm_send				    (bt_sco->playback, buf, len);				/* Strangely, when the device is open but no audio is				   being written by the app, there's an occasional glitch				   in the silence data. This hack eliminates it. */				for (i = 0; i < len; i++) {					if (buf[i] != 0) {						if (notzero >= 0)							break;						notzero = i;					}				}				if (notzero >= 0 && i >= len) {					buf[notzero] = 0;				}			}			up(&bt_sco->playback_sem);#if 0			/* This chunk of code lets us record (using arecord)			   what data alsa is sending out.			   e.g., when idle, we'd expect something like:			   8080 8080 8080 8080 8483 8281 8182 8384			   8080 8080 8080 8080 8080 8080 8080 8080			   8080 8080 8080 8080 8483 8281 8182 8384			   8080 8080 8080 8080 8080 8080 8080 8080			   (this is from 'xxd' of a wav file, that data in			   which is unsigned, whereas we are dealing with signed).			 */			down(&bt_sco->capture_sem);			if (bt_sco->capture) {				snd_card_bt_sco_pcm_receive				    (bt_sco->capture, "\001\002\003\004", 4);				snd_card_bt_sco_pcm_receive				    (bt_sco->capture, buf, len);				snd_card_bt_sco_pcm_receive				    (bt_sco->capture, "\004\003\002\001", 4);			}			up(&bt_sco->capture_sem);#endif			msg.msg_flags = 0;			msg.msg_iov = &iov;			iov.iov_base = buf;			iov.iov_len = BUF_SIZE;			sock_sendmsg(sock, &msg, len);		}		/* Expect this to be 3 because we (this thead) have a copy,		   the driver process keeps one, and the app has the socket open.		 */		if (file_count(sock->file) != 3) {			dprintk("file_count is %d (expected 3)\n",				file_count(sock->file));		}		fput(sock->file);		schedule();	} while (!bt_sco->thread_exit);	dprintk("thread exiting\n");	unlock_kernel();	complete_and_exit(&bt_sco->thread_done, 0);}static void snd_card_bt_private_free(snd_card_t * card){	snd_card_bt_sco_t *bt_sco = card->private_data;	dprintk("private_free, killing thread\n");	bt_sco->thread_exit = 1;	kill_proc(bt_sco->thread_pid, SIGTERM, 1);	wait_for_completion(&bt_sco->thread_done);	dprintk("private_free, thread exited\n");	if (bt_sco->sco_sock) {		dprintk("shutdown: freeing socket count %d\n",			file_count(bt_sco->sco_sock->file));		sockfd_put(((struct socket *)bt_sco->sco_sock));	}	kfree(bt_sco);}static int __init snd_card_bt_sco_probe(int dev){	snd_card_t *card;	snd_card_bt_sco_t *bt_sco;	int err;	snd_hwdep_t *hw;	card =	    snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,			 THIS_MODULE, 0);	if (card == NULL)		return -ENOMEM;	bt_sco = kmalloc(sizeof(*bt_sco), GFP_KERNEL);	if(bt_sco == NULL)		return -ENOMEM;	memset(bt_sco, 0, sizeof(*bt_sco));	card->private_data = bt_sco;	card->private_free = snd_card_bt_private_free;	bt_sco->card = card;	init_completion(&bt_sco->thread_done);	init_MUTEX_LOCKED(&bt_sco->thread_sem);	init_MUTEX(&bt_sco->sock_sem);	init_MUTEX(&bt_sco->capture_sem);	init_MUTEX(&bt_sco->playback_sem);	init_waitqueue_head(&bt_sco->wait);	init_waitqueue_head(&bt_sco->hwdep_wait);	spin_lock_init(&bt_sco->mixer_changed_lock);	spin_lock_init(&bt_sco->count_changed_lock);	/* These clone flags copied from some other driver.	   Not sure that they're really correct... */	bt_sco->thread_pid =	    kernel_thread(snd_card_bt_sco_thread, card, CLONE_KERNEL);	if (bt_sco->thread_pid < 0) {		err = bt_sco->thread_pid;		goto __nodev;	}	down(&bt_sco->thread_sem);	if ((err = snd_card_bt_sco_pcm(bt_sco)) < 0)		goto __nodev;	if ((err = snd_card_bt_sco_new_mixer(bt_sco)) < 0)		goto __nodev;	strcpy(card->driver, "Bluetooth SCO");	strcpy(card->shortname, "BT Headset");	sprintf(card->longname, "BT Headset %i", dev + 1);	err = snd_hwdep_new(card, "BTSCO", 0, &hw);	if (err < 0)		goto __nodev;	sprintf(hw->name, "BTSCO");	hw->iface = SNDRV_HWDEP_IFACE_BT_SCO;	hw->ops.open = snd_card_bt_open;	hw->ops.ioctl = snd_card_bt_ioctl;	hw->ops.release = snd_card_bt_release;	hw->ops.read = snd_card_bt_read;	hw->ops.write = snd_card_bt_write;	hw->ops.poll = snd_card_bt_poll;	if ((err = snd_card_register(card)) == 0) {		snd_bt_sco_cards[dev] = card;		return 0;	}      __nodev:	snd_card_free(card);	return err;}static int __init alsa_card_bt_sco_init(void){	printk(KERN_INFO "snd-bt-sco revision %s\n", mod_revision + 11);	if (snd_card_bt_sco_probe(0) < 0) {#ifdef MODULE		printk(KERN_ERR		       "Bluetooth SCO soundcard not found or device busy\n");#endif		return -ENODEV;	}	return 0;}static void __exit alsa_card_bt_sco_exit(void){	int idx;	for (idx = 0; idx < SNDRV_CARDS; idx++)		snd_card_free(snd_bt_sco_cards[idx]);}module_init(alsa_card_bt_sco_init)    module_exit(alsa_card_bt_sco_exit)#ifndef MODULEstatic int __init alsa_card_bt_sco_setup(char *str){	static unsigned __initdata nr_dev = 0;	if (nr_dev >= SNDRV_CARDS)		return 0;	nr_dev++;	return 1;}__setup("snd-bt-sco=", alsa_card_bt_sco_setup);#endif				/* ifndef MODULE */

⌨️ 快捷键说明

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