📄 btsco.c
字号:
/* * Userspace management of snd-bt-sco * * Copyright (c) 2003 by Jonathan Paisley <jp@dcs.gla.ac.uk> * * Daemon enhancements (c) 2004 by Lars Grunewaldt <lgw@dark-reality.de> * * Based on sb16_csp/cspctl.c and hstest.c from bluez-utils/test. * * 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 <getopt.h>#include <stdarg.h>#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include <assert.h>#include <string.h>#include <errno.h>#include <regex.h>#include <ctype.h>#include <sys/wait.h>#include <sys/time.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/poll.h>#include <bluetooth/bluetooth.h>#include <bluetooth/hci.h>#include <bluetooth/hci_lib.h>#include <bluetooth/sco.h>#include <bluetooth/rfcomm.h>#include <bluetooth/sdp.h>#include <bluetooth/sdp_lib.h>#include <alsa/asoundlib.h>#define SNDRV_BT_SCO_IOCTL_SET_SCO_SOCKET _IOW ('H', 0x10, int)#define SNDRV_BT_SCO_IOCTL_REQ_INFO _IO ('H', 0x11)#ifndef SND_HWDEP_IFACE_EMUX_WAVETABLE#define SND_HWDEP_IFACE_EMUX_WAVETABLE (SND_HWDEP_IFACE_USX2Y + 1)#endif#ifndef SND_HWDEP_IFACE_BLUETOOTH#define SND_HWDEP_IFACE_BLUETOOTH (SND_HWDEP_IFACE_EMUX_WAVETABLE + 1)#endif#ifndef SNDRV_HWDEP_IFACE_BT_SCO#define SNDRV_HWDEP_IFACE_BT_SCO (SND_HWDEP_IFACE_BLUETOOTH + 1)#endif#define NOT_CONNECTED 0#define CONNECTED 1typedef struct snd_card_bt_sco_info { int mixer_volume[2]; int playback_count, capture_count;} snd_card_bt_sco_info_t;struct action { struct action *next; regex_t regex; char *cmd;};static volatile int terminate = 0, ring = 0, hupped = 0;static int verbose = 0;static void sig_term(int sig){ terminate = 1;}static void sig_ring(int sig){ ring = 1;}static void sig_hup(int sig){ hupped = 1;}static int rfcomm_connect(bdaddr_t * src, bdaddr_t * dst, uint8_t channel){ struct sockaddr_rc addr; int s; if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) { return -1; } memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, src); addr.rc_channel = 0; if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(s); return -1; } memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, dst); addr.rc_channel = channel; if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(s); return -1; } return s;}static int sco_connect(bdaddr_t * src, bdaddr_t * dst, uint16_t * handle, uint16_t * mtu){ struct sockaddr_sco addr; struct sco_conninfo conn; struct sco_options opts; int s, size; if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) { return -1; } memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, src); if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(s); return -1; } memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, dst); if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(s); return -1; } size = sizeof(conn); if (getsockopt(s, SOL_SCO, SCO_CONNINFO, &conn, &size) < 0) { close(s); return -1; } size = sizeof(opts); if (getsockopt(s, SOL_SCO, SCO_OPTIONS, &opts, &size) < 0) { close(s); return -1; } if (handle) *handle = conn.hci_handle; if (mtu) *mtu = opts.mtu; return s;}static void error(const char *fmt, ...){ va_list va; va_start(va, fmt); fprintf(stderr, "Error: "); vfprintf(stderr, fmt, va); fprintf(stderr, "\n"); va_end(va);}static int bt_sco_set_fd(snd_hwdep_t * handle, int sco_fd){ if (snd_hwdep_ioctl (handle, SNDRV_BT_SCO_IOCTL_SET_SCO_SOCKET, (void *)sco_fd) < 0) { error("unable to set fd"); return 1; } return 0;}int find_hwdep_device(int *cardP, int *devP){ snd_ctl_t *ctl_handle; snd_ctl_card_info_t *card_info; snd_hwdep_info_t *hwdep_info; int card; int dev; int err; char card_id[32]; ctl_handle = NULL; snd_ctl_card_info_alloca(&card_info); snd_hwdep_info_alloca(&hwdep_info); for (card = 0; card < 7; card++) { *cardP = card; if (ctl_handle) { snd_ctl_close(ctl_handle); ctl_handle = NULL; } // Get control handle for selected card sprintf(card_id, "hw:%i", card); if ((err = snd_ctl_open(&ctl_handle, card_id, 0)) < 0) { error("control open (%s): %s", card_id, snd_strerror(err)); return -1; } // Read control hardware info from card if ((err = snd_ctl_card_info(ctl_handle, card_info)) < 0) { error("control hardware info (%s): %s", card_id, snd_strerror(err)); continue; } //if (strcmp(snd_ctl_card_info_get_driver(card_info),"BT SCO (d)")) // continue; dev = -1; err = 1; while (1) { if (snd_ctl_hwdep_next_device(ctl_handle, &dev) < 0) error("hwdep next device (%s): %s", card_id, snd_strerror(err)); if (dev < 0) break; snd_hwdep_info_set_device(hwdep_info, dev); if (snd_ctl_hwdep_info(ctl_handle, hwdep_info) < 0) { if (err != -ENOENT) error ("control hwdep info (%s): %s", card_id, snd_strerror(err)); continue; } if (snd_hwdep_info_get_iface(hwdep_info) == SNDRV_HWDEP_IFACE_BT_SCO) { snd_ctl_close(ctl_handle); *devP = dev; return 0; } } } if (ctl_handle) snd_ctl_close(ctl_handle); return -1;}static void usage(void){ printf("\nbtsco bluetooth audio handler\n"); printf("Usage:\n" "\tbtsco [options] <bdaddr> [channel]\n"); printf("Options:\n"); printf(" -v print verbose output\n"); printf(" -f fork and run as a daemon\n"); printf(" -c clear filehandle and exit\n"); printf(" -h print this usage and exit\n"); printf("\nThe headset channel will be automatically detected if not specified\n\n");}int detect_channel(bdaddr_t * bdaddr){ // equivalent to running: // sdptool search --bdaddr 00:0A:D9:74:B4:EA 0x1108 // and parsing out the channel number uuid_t group; bdaddr_t interface; sdp_list_t *attrid, *search, *seq, *next; uint32_t range = 0x0000ffff; sdp_session_t *sess; int channel = 2; int searchresult; bacpy(&interface, BDADDR_ANY); sdp_uuid16_create(&group, 0x1108); sess = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY); if (!sess) { error ("Failed to connect to SDP server: %s\nAssuming channel %d\n", strerror(errno), channel); return channel; } attrid = sdp_list_append(0, &range); search = sdp_list_append(0, &group); searchresult = sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq); sdp_list_free(attrid, 0); sdp_list_free(search, 0); if (searchresult) { error("Service Search failed: %s\nAssuming channel %d\n", strerror(errno), channel); sdp_close(sess); return channel; } for (; seq; seq = next) { sdp_record_t *rec = (sdp_record_t *) seq->data; sdp_list_t *list = 0; if (sdp_get_access_protos(rec, &list) == 0) { channel = sdp_get_proto_port(list, RFCOMM_UUID); } next = seq->next; free(seq); sdp_record_free(rec); } sdp_close(sess); return channel;}static void free_actions(struct action *list){ struct action *cur; while(list != NULL) { cur = list; list = cur->next; regfree(&cur->regex); free(cur->cmd); free(cur); }}static struct action *read_actions(void){ int state, retval, len; struct action *ret, *cur, *new; char buf[1024]; char *p; FILE *cf; ret = NULL; cur = NULL; new = NULL; if(getenv("HOME") == NULL) return(NULL); snprintf(buf, sizeof(buf), "%s/.btscorc", getenv("HOME")); if((cf = fopen(buf, "r")) == NULL) { if(errno != ENOENT) perror(buf); return(NULL); } state = 0; while(!feof(cf)) { if(fgets(buf, sizeof(buf), cf) == NULL) { if(ferror(cf)) { error("reading cf: %s", strerror(ferror(cf))); free_actions(ret); return(NULL); } else { continue; } } if(buf[0] == '#') continue; for(p = buf; isspace(*p); p++); memmove(buf, p, strlen(buf) + 1 - (p - buf)); if(strlen(buf) == 0) continue; for(p = buf + strlen(buf) - 1; isspace(*p); p--); p[1] = 0; switch(state) { case 0: new = malloc(sizeof(*new)); new->next = NULL; new->cmd = NULL; if((retval = regcomp(&new->regex, buf, REG_EXTENDED)) != 0) { error("could not compile regex `%s'", buf); free_actions(ret); free(new); return(NULL); } state = 1; break; case 1: len = strlen(buf); if(buf[len - 1] == '\\') { buf[len - 1] = 0; } else { state = 0; if(ret == NULL) { ret = cur = new; } else { cur->next = new; cur = new; } } if(new->cmd == NULL) { new->cmd = strdup(buf); } else { len = strlen(new->cmd); new->cmd = realloc(new->cmd, len + strlen(buf) + 1); memcpy(new->cmd + len, buf, strlen(buf)); new->cmd[len + strlen(buf)] = 0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -