| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2009-2010 Marcel Holtmann <marcel@holtmann.org>
- * Copyright (C) 2009-2010 Nokia Corporation
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include <stdarg.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <errno.h>
- #include <poll.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <glib.h>
- #include "lib/bluetooth.h"
- #include "lib/l2cap.h"
- #include "lib/rfcomm.h"
- #include "lib/sco.h"
- #include "btio.h"
- #ifndef BT_FLUSHABLE
- #define BT_FLUSHABLE 8
- #endif
- #define ERROR_FAILED(gerr, str, err) \
- g_set_error(gerr, BT_IO_ERROR, err, \
- str ": %s (%d)", strerror(err), err)
- #define DEFAULT_DEFER_TIMEOUT 30
- typedef enum {
- BT_IO_L2CAP,
- BT_IO_RFCOMM,
- BT_IO_SCO,
- BT_IO_INVALID,
- } BtIOType;
- struct set_opts {
- bdaddr_t src;
- bdaddr_t dst;
- BtIOType type;
- uint8_t src_type;
- uint8_t dst_type;
- int defer;
- int sec_level;
- uint8_t channel;
- uint16_t psm;
- uint16_t cid;
- uint16_t mtu;
- uint16_t imtu;
- uint16_t omtu;
- int central;
- uint8_t mode;
- int flushable;
- uint32_t priority;
- uint16_t voice;
- };
- struct connect {
- BtIOConnect connect;
- gpointer user_data;
- GDestroyNotify destroy;
- bdaddr_t dst;
- };
- struct accept {
- BtIOConnect connect;
- gpointer user_data;
- GDestroyNotify destroy;
- };
- struct server {
- BtIOConnect connect;
- BtIOConfirm confirm;
- gpointer user_data;
- GDestroyNotify destroy;
- };
- static BtIOType bt_io_get_type(GIOChannel *io, GError **gerr)
- {
- int sk = g_io_channel_unix_get_fd(io);
- int domain, proto, err;
- socklen_t len;
- domain = 0;
- len = sizeof(domain);
- err = getsockopt(sk, SOL_SOCKET, SO_DOMAIN, &domain, &len);
- if (err < 0) {
- ERROR_FAILED(gerr, "getsockopt(SO_DOMAIN)", errno);
- return BT_IO_INVALID;
- }
- if (domain != AF_BLUETOOTH) {
- g_set_error(gerr, BT_IO_ERROR, EINVAL,
- "BtIO socket domain not AF_BLUETOOTH");
- return BT_IO_INVALID;
- }
- proto = 0;
- len = sizeof(proto);
- err = getsockopt(sk, SOL_SOCKET, SO_PROTOCOL, &proto, &len);
- if (err < 0) {
- ERROR_FAILED(gerr, "getsockopt(SO_PROTOCOL)", errno);
- return BT_IO_INVALID;
- }
- switch (proto) {
- case BTPROTO_RFCOMM:
- return BT_IO_RFCOMM;
- case BTPROTO_SCO:
- return BT_IO_SCO;
- case BTPROTO_L2CAP:
- return BT_IO_L2CAP;
- default:
- g_set_error(gerr, BT_IO_ERROR, EINVAL,
- "Unknown BtIO socket type");
- return BT_IO_INVALID;
- }
- }
- static void server_remove(struct server *server)
- {
- if (server->destroy)
- server->destroy(server->user_data);
- g_free(server);
- }
- static void connect_remove(struct connect *conn)
- {
- if (conn->destroy)
- conn->destroy(conn->user_data);
- g_free(conn);
- }
- static void accept_remove(struct accept *accept)
- {
- if (accept->destroy)
- accept->destroy(accept->user_data);
- g_free(accept);
- }
- static gboolean check_nval(GIOChannel *io)
- {
- struct pollfd fds;
- memset(&fds, 0, sizeof(fds));
- fds.fd = g_io_channel_unix_get_fd(io);
- fds.events = POLLNVAL;
- if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
- return TRUE;
- return FALSE;
- }
- static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
- {
- struct accept *accept = user_data;
- GError *gerr = NULL;
- /* If the user aborted this accept attempt */
- if ((cond & G_IO_NVAL) || check_nval(io))
- return FALSE;
- if (cond & (G_IO_HUP | G_IO_ERR)) {
- int err, sk_err, sock = g_io_channel_unix_get_fd(io);
- socklen_t len = sizeof(sk_err);
- if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
- err = -errno;
- else
- err = -sk_err;
- if (err < 0)
- ERROR_FAILED(&gerr, "HUP or ERR on socket", -err);
- }
- accept->connect(io, gerr, accept->user_data);
- g_clear_error(&gerr);
- return FALSE;
- }
- static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
- {
- struct connect *conn = user_data;
- GError *gerr = NULL;
- int err, sk_err, sock;
- socklen_t len = sizeof(sk_err);
- char addr[18];
- /* If the user aborted this connect attempt */
- if ((cond & G_IO_NVAL) || check_nval(io))
- return FALSE;
- sock = g_io_channel_unix_get_fd(io);
- if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
- err = -errno;
- else
- err = -sk_err;
- if (err < 0) {
- ba2str(&conn->dst, addr);
- g_set_error(&gerr, BT_IO_ERROR, -err,
- "connect to %s: %s (%d)", addr, strerror(-err), -err);
- }
- conn->connect(io, gerr, conn->user_data);
- g_clear_error(&gerr);
- return FALSE;
- }
- static gboolean server_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
- {
- struct server *server = user_data;
- int srv_sock, cli_sock;
- GIOChannel *cli_io;
- /* If the user closed the server */
- if ((cond & G_IO_NVAL) || check_nval(io))
- return FALSE;
- srv_sock = g_io_channel_unix_get_fd(io);
- cli_sock = accept(srv_sock, NULL, NULL);
- if (cli_sock < 0)
- return TRUE;
- cli_io = g_io_channel_unix_new(cli_sock);
- g_io_channel_set_close_on_unref(cli_io, TRUE);
- g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
- if (server->confirm)
- server->confirm(cli_io, server->user_data);
- else
- server->connect(cli_io, NULL, server->user_data);
- g_io_channel_unref(cli_io);
- return TRUE;
- }
- static void server_add(GIOChannel *io, BtIOConnect connect,
- BtIOConfirm confirm, gpointer user_data,
- GDestroyNotify destroy)
- {
- struct server *server;
- GIOCondition cond;
- server = g_new0(struct server, 1);
- server->connect = connect;
- server->confirm = confirm;
- server->user_data = user_data;
- server->destroy = destroy;
- cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
- g_io_add_watch_full(io, G_PRIORITY_HIGH, cond, server_cb, server,
- (GDestroyNotify) server_remove);
- }
- static void connect_add(GIOChannel *io, BtIOConnect connect, bdaddr_t dst,
- gpointer user_data, GDestroyNotify destroy)
- {
- struct connect *conn;
- GIOCondition cond;
- conn = g_new0(struct connect, 1);
- conn->connect = connect;
- conn->user_data = user_data;
- conn->destroy = destroy;
- conn->dst = dst;
- cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
- g_io_add_watch_full(io, G_PRIORITY_HIGH, cond, connect_cb, conn,
- (GDestroyNotify) connect_remove);
- }
- static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data,
- GDestroyNotify destroy)
- {
- struct accept *accept;
- GIOCondition cond;
- accept = g_new0(struct accept, 1);
- accept->connect = connect;
- accept->user_data = user_data;
- accept->destroy = destroy;
- cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
- g_io_add_watch_full(io, G_PRIORITY_HIGH, cond, accept_cb, accept,
- (GDestroyNotify) accept_remove);
- }
- static int l2cap_bind(int sock, const bdaddr_t *src, uint8_t src_type,
- uint16_t psm, uint16_t cid, GError **err)
- {
- struct sockaddr_l2 addr;
- memset(&addr, 0, sizeof(addr));
- addr.l2_family = AF_BLUETOOTH;
- bacpy(&addr.l2_bdaddr, src);
- if (cid)
- addr.l2_cid = htobs(cid);
- else
- addr.l2_psm = htobs(psm);
- addr.l2_bdaddr_type = src_type;
- if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- int error = -errno;
- ERROR_FAILED(err, "l2cap_bind", errno);
- return error;
- }
- return 0;
- }
- static int l2cap_connect(int sock, const bdaddr_t *dst, uint8_t dst_type,
- uint16_t psm, uint16_t cid)
- {
- int err;
- struct sockaddr_l2 addr;
- memset(&addr, 0, sizeof(addr));
- addr.l2_family = AF_BLUETOOTH;
- bacpy(&addr.l2_bdaddr, dst);
- if (cid)
- addr.l2_cid = htobs(cid);
- else
- addr.l2_psm = htobs(psm);
- addr.l2_bdaddr_type = dst_type;
- err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
- if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
- return -errno;
- return 0;
- }
- static int l2cap_set_central(int sock, int central)
- {
- int flags;
- socklen_t len;
- len = sizeof(flags);
- if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
- return -errno;
- if (central) {
- if (flags & L2CAP_LM_MASTER)
- return 0;
- flags |= L2CAP_LM_MASTER;
- } else {
- if (!(flags & L2CAP_LM_MASTER))
- return 0;
- flags &= ~L2CAP_LM_MASTER;
- }
- if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
- return -errno;
- return 0;
- }
- static int rfcomm_set_central(int sock, int central)
- {
- int flags;
- socklen_t len;
- len = sizeof(flags);
- if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
- return -errno;
- if (central) {
- if (flags & RFCOMM_LM_MASTER)
- return 0;
- flags |= RFCOMM_LM_MASTER;
- } else {
- if (!(flags & RFCOMM_LM_MASTER))
- return 0;
- flags &= ~RFCOMM_LM_MASTER;
- }
- if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
- return -errno;
- return 0;
- }
- static int l2cap_set_lm(int sock, int level)
- {
- int lm_map[] = {
- 0,
- L2CAP_LM_AUTH,
- L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
- L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
- }, opt = lm_map[level];
- if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
- return -errno;
- return 0;
- }
- static int rfcomm_set_lm(int sock, int level)
- {
- int lm_map[] = {
- 0,
- RFCOMM_LM_AUTH,
- RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
- RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
- }, opt = lm_map[level];
- if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
- return -errno;
- return 0;
- }
- static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
- {
- struct bt_security sec;
- int ret;
- if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
- g_set_error(err, BT_IO_ERROR, EINVAL,
- "Valid security level range is %d-%d",
- BT_SECURITY_LOW, BT_SECURITY_HIGH);
- return FALSE;
- }
- memset(&sec, 0, sizeof(sec));
- sec.level = level;
- if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
- sizeof(sec)) == 0)
- return TRUE;
- if (errno != ENOPROTOOPT) {
- ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
- return FALSE;
- }
- if (type == BT_IO_L2CAP)
- ret = l2cap_set_lm(sock, level);
- else
- ret = rfcomm_set_lm(sock, level);
- if (ret < 0) {
- ERROR_FAILED(err, "setsockopt(LM)", -ret);
- return FALSE;
- }
- return TRUE;
- }
- static int l2cap_get_lm(int sock, int *sec_level)
- {
- int opt;
- socklen_t len;
- len = sizeof(opt);
- if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
- return -errno;
- *sec_level = 0;
- if (opt & L2CAP_LM_AUTH)
- *sec_level = BT_SECURITY_LOW;
- if (opt & L2CAP_LM_ENCRYPT)
- *sec_level = BT_SECURITY_MEDIUM;
- if (opt & L2CAP_LM_SECURE)
- *sec_level = BT_SECURITY_HIGH;
- return 0;
- }
- static int rfcomm_get_lm(int sock, int *sec_level)
- {
- int opt;
- socklen_t len;
- len = sizeof(opt);
- if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
- return -errno;
- *sec_level = 0;
- if (opt & RFCOMM_LM_AUTH)
- *sec_level = BT_SECURITY_LOW;
- if (opt & RFCOMM_LM_ENCRYPT)
- *sec_level = BT_SECURITY_MEDIUM;
- if (opt & RFCOMM_LM_SECURE)
- *sec_level = BT_SECURITY_HIGH;
- return 0;
- }
- static gboolean get_sec_level(int sock, BtIOType type, int *level,
- GError **err)
- {
- struct bt_security sec;
- socklen_t len;
- int ret;
- memset(&sec, 0, sizeof(sec));
- len = sizeof(sec);
- if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
- *level = sec.level;
- return TRUE;
- }
- if (errno != ENOPROTOOPT) {
- ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
- return FALSE;
- }
- if (type == BT_IO_L2CAP)
- ret = l2cap_get_lm(sock, level);
- else
- ret = rfcomm_get_lm(sock, level);
- if (ret < 0) {
- ERROR_FAILED(err, "getsockopt(LM)", -ret);
- return FALSE;
- }
- return TRUE;
- }
- static int l2cap_set_flushable(int sock, gboolean flushable)
- {
- int f;
- f = flushable;
- if (setsockopt(sock, SOL_BLUETOOTH, BT_FLUSHABLE, &f, sizeof(f)) < 0)
- return -errno;
- return 0;
- }
- static int set_priority(int sock, uint32_t prio)
- {
- if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0)
- return -errno;
- return 0;
- }
- static gboolean get_key_size(int sock, int *size, GError **err)
- {
- struct bt_security sec;
- socklen_t len;
- memset(&sec, 0, sizeof(sec));
- len = sizeof(sec);
- if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
- *size = sec.key_size;
- return TRUE;
- }
- return FALSE;
- }
- static uint8_t mode_l2mode(uint8_t mode)
- {
- switch (mode) {
- case BT_IO_MODE_BASIC:
- return L2CAP_MODE_BASIC;
- case BT_IO_MODE_ERTM:
- return L2CAP_MODE_ERTM;
- case BT_IO_MODE_STREAMING:
- return L2CAP_MODE_STREAMING;
- default:
- return UINT8_MAX;
- }
- }
- static gboolean set_l2opts(int sock, uint16_t imtu, uint16_t omtu,
- uint8_t mode, GError **err)
- {
- struct l2cap_options l2o;
- socklen_t len;
- memset(&l2o, 0, sizeof(l2o));
- len = sizeof(l2o);
- if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
- ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
- return FALSE;
- }
- if (imtu)
- l2o.imtu = imtu;
- if (omtu)
- l2o.omtu = omtu;
- if (mode) {
- l2o.mode = mode_l2mode(mode);
- if (l2o.mode == UINT8_MAX) {
- ERROR_FAILED(err, "Unsupported mode", errno);
- return FALSE;
- }
- }
- if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) {
- ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
- return FALSE;
- }
- return TRUE;
- }
- static gboolean set_le_imtu(int sock, uint16_t imtu, GError **err)
- {
- if (setsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, &imtu,
- sizeof(imtu)) < 0) {
- ERROR_FAILED(err, "setsockopt(BT_RCVMTU)", errno);
- return FALSE;
- }
- return TRUE;
- }
- static gboolean set_le_mode(int sock, uint8_t mode, GError **err)
- {
- if (setsockopt(sock, SOL_BLUETOOTH, BT_MODE, &mode,
- sizeof(mode)) < 0) {
- ERROR_FAILED(err, "setsockopt(BT_MODE)", errno);
- return FALSE;
- }
- return TRUE;
- }
- static gboolean l2cap_set(int sock, uint8_t src_type, int sec_level,
- uint16_t imtu, uint16_t omtu, uint8_t mode,
- int central, int flushable, uint32_t priority,
- GError **err)
- {
- if (imtu || omtu || mode) {
- gboolean ret = FALSE;
- if (src_type == BDADDR_BREDR)
- ret = set_l2opts(sock, imtu, omtu, mode, err);
- else {
- if (imtu)
- ret = set_le_imtu(sock, imtu, err);
- if (ret && mode)
- ret = set_le_mode(sock, mode, err);
- }
- if (!ret)
- return ret;
- }
- if (central >= 0 && l2cap_set_central(sock, central) < 0) {
- ERROR_FAILED(err, "l2cap_set_central", errno);
- return FALSE;
- }
- if (flushable >= 0 && l2cap_set_flushable(sock, flushable) < 0) {
- ERROR_FAILED(err, "l2cap_set_flushable", errno);
- return FALSE;
- }
- if (priority > 0 && set_priority(sock, priority) < 0) {
- ERROR_FAILED(err, "set_priority", errno);
- return FALSE;
- }
- if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
- return FALSE;
- return TRUE;
- }
- static int rfcomm_bind(int sock,
- const bdaddr_t *src, uint8_t channel, GError **err)
- {
- struct sockaddr_rc addr;
- memset(&addr, 0, sizeof(addr));
- addr.rc_family = AF_BLUETOOTH;
- bacpy(&addr.rc_bdaddr, src);
- addr.rc_channel = channel;
- if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- int error = -errno;
- ERROR_FAILED(err, "rfcomm_bind", errno);
- return error;
- }
- return 0;
- }
- static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
- {
- int err;
- struct sockaddr_rc addr;
- memset(&addr, 0, sizeof(addr));
- addr.rc_family = AF_BLUETOOTH;
- bacpy(&addr.rc_bdaddr, dst);
- addr.rc_channel = channel;
- err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
- if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
- return -errno;
- return 0;
- }
- static gboolean rfcomm_set(int sock, int sec_level, int central, GError **err)
- {
- if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
- return FALSE;
- if (central >= 0 && rfcomm_set_central(sock, central) < 0) {
- ERROR_FAILED(err, "rfcomm_set_central", errno);
- return FALSE;
- }
- return TRUE;
- }
- static int sco_bind(int sock, const bdaddr_t *src, GError **err)
- {
- struct sockaddr_sco addr;
- memset(&addr, 0, sizeof(addr));
- addr.sco_family = AF_BLUETOOTH;
- bacpy(&addr.sco_bdaddr, src);
- if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- int error = -errno;
- ERROR_FAILED(err, "sco_bind", errno);
- return error;
- }
- return 0;
- }
- static int sco_connect(int sock, const bdaddr_t *dst)
- {
- struct sockaddr_sco addr;
- int err;
- memset(&addr, 0, sizeof(addr));
- addr.sco_family = AF_BLUETOOTH;
- bacpy(&addr.sco_bdaddr, dst);
- err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
- if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
- return -errno;
- return 0;
- }
- static gboolean sco_set(int sock, uint16_t mtu, uint16_t voice, GError **err)
- {
- struct sco_options sco_opt;
- struct bt_voice bt_voice;
- socklen_t len;
- if (!mtu)
- goto voice;
- len = sizeof(sco_opt);
- memset(&sco_opt, 0, len);
- if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
- ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
- return FALSE;
- }
- sco_opt.mtu = mtu;
- if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
- sizeof(sco_opt)) < 0) {
- ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
- return FALSE;
- }
- voice:
- if (!voice)
- return TRUE;
- memset(&bt_voice, 0, sizeof(bt_voice));
- bt_voice.setting = voice;
- if (setsockopt(sock, SOL_BLUETOOTH, BT_VOICE, &bt_voice,
- sizeof(bt_voice)) < 0) {
- ERROR_FAILED(err, "setsockopt(BT_VOICE)", errno);
- return FALSE;
- }
- return TRUE;
- }
- static gboolean parse_set_opts(struct set_opts *opts, GError **err,
- BtIOOption opt1, va_list args)
- {
- BtIOOption opt = opt1;
- const char *str;
- memset(opts, 0, sizeof(*opts));
- /* Set defaults */
- opts->type = BT_IO_SCO;
- opts->defer = DEFAULT_DEFER_TIMEOUT;
- opts->central = -1;
- opts->mode = L2CAP_MODE_BASIC;
- opts->flushable = -1;
- opts->priority = 0;
- opts->src_type = BDADDR_BREDR;
- opts->dst_type = BDADDR_BREDR;
- while (opt != BT_IO_OPT_INVALID) {
- switch (opt) {
- case BT_IO_OPT_SOURCE:
- str = va_arg(args, const char *);
- str2ba(str, &opts->src);
- break;
- case BT_IO_OPT_SOURCE_BDADDR:
- bacpy(&opts->src, va_arg(args, const bdaddr_t *));
- break;
- case BT_IO_OPT_SOURCE_TYPE:
- opts->src_type = va_arg(args, int);
- break;
- case BT_IO_OPT_DEST:
- str2ba(va_arg(args, const char *), &opts->dst);
- break;
- case BT_IO_OPT_DEST_BDADDR:
- bacpy(&opts->dst, va_arg(args, const bdaddr_t *));
- break;
- case BT_IO_OPT_DEST_TYPE:
- opts->dst_type = va_arg(args, int);
- break;
- case BT_IO_OPT_DEFER_TIMEOUT:
- opts->defer = va_arg(args, int);
- break;
- case BT_IO_OPT_SEC_LEVEL:
- opts->sec_level = va_arg(args, int);
- break;
- case BT_IO_OPT_CHANNEL:
- opts->type = BT_IO_RFCOMM;
- opts->channel = va_arg(args, int);
- break;
- case BT_IO_OPT_PSM:
- opts->type = BT_IO_L2CAP;
- opts->psm = va_arg(args, int);
- break;
- case BT_IO_OPT_CID:
- opts->type = BT_IO_L2CAP;
- opts->cid = va_arg(args, int);
- break;
- case BT_IO_OPT_MTU:
- opts->mtu = va_arg(args, int);
- opts->imtu = opts->mtu;
- opts->omtu = opts->mtu;
- break;
- case BT_IO_OPT_OMTU:
- opts->omtu = va_arg(args, int);
- if (!opts->mtu)
- opts->mtu = opts->omtu;
- break;
- case BT_IO_OPT_IMTU:
- opts->imtu = va_arg(args, int);
- if (!opts->mtu)
- opts->mtu = opts->imtu;
- break;
- case BT_IO_OPT_CENTRAL:
- opts->central = va_arg(args, gboolean);
- break;
- case BT_IO_OPT_MODE:
- opts->mode = va_arg(args, int);
- break;
- case BT_IO_OPT_FLUSHABLE:
- opts->flushable = va_arg(args, gboolean);
- break;
- case BT_IO_OPT_PRIORITY:
- opts->priority = va_arg(args, int);
- break;
- case BT_IO_OPT_VOICE:
- opts->voice = va_arg(args, int);
- break;
- case BT_IO_OPT_INVALID:
- case BT_IO_OPT_KEY_SIZE:
- case BT_IO_OPT_SOURCE_CHANNEL:
- case BT_IO_OPT_DEST_CHANNEL:
- case BT_IO_OPT_HANDLE:
- case BT_IO_OPT_CLASS:
- case BT_IO_OPT_PHY:
- default:
- g_set_error(err, BT_IO_ERROR, EINVAL,
- "Unknown option %d", opt);
- return FALSE;
- }
- opt = va_arg(args, int);
- }
- return TRUE;
- }
- static gboolean get_src(int sock, void *src, socklen_t len, GError **err)
- {
- socklen_t olen;
- memset(src, 0, len);
- olen = len;
- if (getsockname(sock, src, &olen) < 0) {
- ERROR_FAILED(err, "getsockname", errno);
- return FALSE;
- }
- return TRUE;
- }
- static gboolean get_dst(int sock, void *dst, socklen_t len, GError **err)
- {
- socklen_t olen;
- memset(dst, 0, len);
- olen = len;
- if (getpeername(sock, dst, &olen) < 0) {
- ERROR_FAILED(err, "getpeername", errno);
- return FALSE;
- }
- return TRUE;
- }
- static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
- {
- struct l2cap_conninfo info;
- socklen_t len;
- len = sizeof(info);
- if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
- return -errno;
- if (handle)
- *handle = info.hci_handle;
- if (dev_class)
- memcpy(dev_class, info.dev_class, 3);
- return 0;
- }
- static int l2cap_get_flushable(int sock, gboolean *flushable)
- {
- int f;
- socklen_t len;
- f = 0;
- len = sizeof(f);
- if (getsockopt(sock, SOL_BLUETOOTH, BT_FLUSHABLE, &f, &len) < 0)
- return -errno;
- if (f)
- *flushable = TRUE;
- else
- *flushable = FALSE;
- return 0;
- }
- static int get_priority(int sock, uint32_t *prio)
- {
- socklen_t len;
- len = sizeof(*prio);
- if (getsockopt(sock, SOL_SOCKET, SO_PRIORITY, prio, &len) < 0)
- return -errno;
- return 0;
- }
- static int get_phy(int sock, uint32_t *phy)
- {
- socklen_t len;
- len = sizeof(*phy);
- if (getsockopt(sock, SOL_BLUETOOTH, BT_PHY, phy, &len) < 0)
- return -errno;
- return 0;
- }
- static int get_le_imtu(int sock, uint16_t *mtu)
- {
- socklen_t len;
- len = sizeof(*mtu);
- if (getsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, mtu, &len) < 0)
- return -errno;
- return 0;
- }
- static int get_le_mode(int sock, uint8_t *mode)
- {
- socklen_t len;
- len = sizeof(*mode);
- if (getsockopt(sock, SOL_BLUETOOTH, BT_MODE, mode, &len) < 0)
- return -errno;
- return 0;
- }
- static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
- va_list args)
- {
- BtIOOption opt = opt1;
- struct sockaddr_l2 src, dst;
- struct l2cap_options l2o;
- int flags;
- uint8_t dev_class[3];
- uint16_t handle = 0;
- socklen_t len;
- gboolean flushable = FALSE, have_dst = FALSE;
- uint32_t priority, phy;
- if (!get_src(sock, &src, sizeof(src), err))
- return FALSE;
- memset(&l2o, 0, sizeof(l2o));
- if (src.l2_bdaddr_type != BDADDR_BREDR) {
- if (get_le_imtu(sock, &l2o.imtu) == 0) {
- /* Older kernels may not support BT_MODE */
- get_le_mode(sock, &l2o.mode);
- goto parse_opts;
- }
- /* Non-LE CoC enabled kernels will return one of these
- * in which case we need to fall back to L2CAP_OPTIONS.
- */
- if (errno != EPROTONOSUPPORT && errno != ENOPROTOOPT) {
- ERROR_FAILED(err, "getsockopt(BT_RCVMTU)", errno);
- return FALSE;
- }
- }
- len = sizeof(l2o);
- if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
- ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
- return FALSE;
- }
- parse_opts:
- while (opt != BT_IO_OPT_INVALID) {
- switch (opt) {
- case BT_IO_OPT_SOURCE:
- ba2str(&src.l2_bdaddr, va_arg(args, char *));
- break;
- case BT_IO_OPT_SOURCE_BDADDR:
- bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
- break;
- case BT_IO_OPT_DEST:
- if (!have_dst)
- have_dst = get_dst(sock, &dst, sizeof(dst),
- err);
- if (!have_dst)
- return FALSE;
- ba2str(&dst.l2_bdaddr, va_arg(args, char *));
- break;
- case BT_IO_OPT_DEST_BDADDR:
- if (!have_dst)
- have_dst = get_dst(sock, &dst, sizeof(dst),
- err);
- if (!have_dst)
- return FALSE;
- bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
- break;
- case BT_IO_OPT_DEST_TYPE:
- if (!have_dst)
- have_dst = get_dst(sock, &dst, sizeof(dst),
- err);
- if (!have_dst)
- return FALSE;
- *(va_arg(args, uint8_t *)) = dst.l2_bdaddr_type;
- break;
- case BT_IO_OPT_DEFER_TIMEOUT:
- len = sizeof(int);
- if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
- va_arg(args, int *), &len) < 0) {
- ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
- errno);
- return FALSE;
- }
- break;
- case BT_IO_OPT_SEC_LEVEL:
- if (!get_sec_level(sock, BT_IO_L2CAP,
- va_arg(args, int *), err))
- return FALSE;
- break;
- case BT_IO_OPT_KEY_SIZE:
- if (!get_key_size(sock, va_arg(args, int *), err))
- return FALSE;
- break;
- case BT_IO_OPT_PSM:
- if (src.l2_psm) {
- *(va_arg(args, uint16_t *)) = btohs(src.l2_psm);
- break;
- }
- if (!have_dst)
- have_dst = get_dst(sock, &dst, sizeof(dst),
- err);
- if (!have_dst)
- return FALSE;
- *(va_arg(args, uint16_t *)) = btohs(dst.l2_psm);
- break;
- case BT_IO_OPT_CID:
- if (src.l2_cid) {
- *(va_arg(args, uint16_t *)) = btohs(src.l2_cid);
- break;
- }
- if (!have_dst)
- have_dst = get_dst(sock, &dst, sizeof(dst),
- err);
- if (!have_dst)
- return FALSE;
- *(va_arg(args, uint16_t *)) = btohs(dst.l2_cid);
- break;
- case BT_IO_OPT_OMTU:
- if (src.l2_bdaddr_type == BDADDR_BREDR) {
- *(va_arg(args, uint16_t *)) = l2o.omtu;
- break;
- }
- len = sizeof(l2o.omtu);
- if (getsockopt(sock, SOL_BLUETOOTH, BT_SNDMTU,
- &l2o.omtu, &len) < 0) {
- ERROR_FAILED(err, "getsockopt(BT_SNDMTU)",
- errno);
- return FALSE;
- }
- *(va_arg(args, uint16_t *)) = l2o.omtu;
- break;
- case BT_IO_OPT_IMTU:
- *(va_arg(args, uint16_t *)) = l2o.imtu;
- break;
- case BT_IO_OPT_CENTRAL:
- len = sizeof(flags);
- if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
- &len) < 0) {
- ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
- errno);
- return FALSE;
- }
- *(va_arg(args, gboolean *)) =
- (flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
- break;
- case BT_IO_OPT_HANDLE:
- if (l2cap_get_info(sock, &handle, dev_class) < 0) {
- ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
- return FALSE;
- }
- *(va_arg(args, uint16_t *)) = handle;
- break;
- case BT_IO_OPT_CLASS:
- if (l2cap_get_info(sock, &handle, dev_class) < 0) {
- ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
- return FALSE;
- }
- memcpy(va_arg(args, uint8_t *), dev_class, 3);
- break;
- case BT_IO_OPT_MODE:
- *(va_arg(args, uint8_t *)) = l2o.mode;
- break;
- case BT_IO_OPT_FLUSHABLE:
- if (l2cap_get_flushable(sock, &flushable) < 0) {
- ERROR_FAILED(err, "get_flushable", errno);
- return FALSE;
- }
- *(va_arg(args, gboolean *)) = flushable;
- break;
- case BT_IO_OPT_PRIORITY:
- if (get_priority(sock, &priority) < 0) {
- ERROR_FAILED(err, "get_priority", errno);
- return FALSE;
- }
- *(va_arg(args, uint32_t *)) = priority;
- break;
- case BT_IO_OPT_PHY:
- if (get_phy(sock, &phy) < 0) {
- ERROR_FAILED(err, "get_phy", errno);
- return FALSE;
- }
- *(va_arg(args, uint32_t *)) = phy;
- break;
- case BT_IO_OPT_INVALID:
- case BT_IO_OPT_SOURCE_TYPE:
- case BT_IO_OPT_CHANNEL:
- case BT_IO_OPT_SOURCE_CHANNEL:
- case BT_IO_OPT_DEST_CHANNEL:
- case BT_IO_OPT_MTU:
- case BT_IO_OPT_VOICE:
- default:
- g_set_error(err, BT_IO_ERROR, EINVAL,
- "Unknown option %d", opt);
- return FALSE;
- }
- opt = va_arg(args, int);
- }
- return TRUE;
- }
- static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
- {
- struct rfcomm_conninfo info;
- socklen_t len;
- len = sizeof(info);
- if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
- return -errno;
- if (handle)
- *handle = info.hci_handle;
- if (dev_class)
- memcpy(dev_class, info.dev_class, 3);
- return 0;
- }
- static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
- va_list args)
- {
- BtIOOption opt = opt1;
- struct sockaddr_rc src, dst;
- gboolean have_dst = FALSE;
- int flags;
- socklen_t len;
- uint8_t dev_class[3];
- uint16_t handle = 0;
- uint32_t phy;
- if (!get_src(sock, &src, sizeof(src), err))
- return FALSE;
- while (opt != BT_IO_OPT_INVALID) {
- switch (opt) {
- case BT_IO_OPT_SOURCE:
- ba2str(&src.rc_bdaddr, va_arg(args, char *));
- break;
- case BT_IO_OPT_SOURCE_BDADDR:
- bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
- break;
- case BT_IO_OPT_DEST:
- if (!have_dst)
- have_dst = get_dst(sock, &dst, sizeof(dst),
- err);
- if (!have_dst)
- return FALSE;
- ba2str(&dst.rc_bdaddr, va_arg(args, char *));
- break;
- case BT_IO_OPT_DEST_BDADDR:
- if (!have_dst)
- have_dst = get_dst(sock, &dst, sizeof(dst),
- err);
- if (!have_dst)
- return FALSE;
- bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
- break;
- case BT_IO_OPT_DEFER_TIMEOUT:
- len = sizeof(int);
- if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
- va_arg(args, int *), &len) < 0) {
- ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
- errno);
- return FALSE;
- }
- break;
- case BT_IO_OPT_SEC_LEVEL:
- if (!get_sec_level(sock, BT_IO_RFCOMM,
- va_arg(args, int *), err))
- return FALSE;
- break;
- case BT_IO_OPT_CHANNEL:
- if (src.rc_channel) {
- *(va_arg(args, uint8_t *)) = src.rc_channel;
- break;
- }
- if (!have_dst)
- have_dst = get_dst(sock, &dst, sizeof(dst),
- err);
- if (!have_dst)
- return FALSE;
- *(va_arg(args, uint8_t *)) = dst.rc_channel;
- break;
- case BT_IO_OPT_SOURCE_CHANNEL:
- *(va_arg(args, uint8_t *)) = src.rc_channel;
- break;
- case BT_IO_OPT_DEST_CHANNEL:
- if (!have_dst)
- have_dst = get_dst(sock, &dst, sizeof(dst),
- err);
- if (!have_dst)
- return FALSE;
- *(va_arg(args, uint8_t *)) = dst.rc_channel;
- break;
- case BT_IO_OPT_CENTRAL:
- len = sizeof(flags);
- if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
- &len) < 0) {
- ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
- errno);
- return FALSE;
- }
- *(va_arg(args, gboolean *)) =
- (flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
- break;
- case BT_IO_OPT_HANDLE:
- if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
- ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
- return FALSE;
- }
- *(va_arg(args, uint16_t *)) = handle;
- break;
- case BT_IO_OPT_CLASS:
- if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
- ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
- return FALSE;
- }
- memcpy(va_arg(args, uint8_t *), dev_class, 3);
- break;
- case BT_IO_OPT_PHY:
- if (get_phy(sock, &phy) < 0) {
- ERROR_FAILED(err, "get_phy", errno);
- return FALSE;
- }
- *(va_arg(args, uint32_t *)) = phy;
- break;
- case BT_IO_OPT_SOURCE_TYPE:
- case BT_IO_OPT_DEST_TYPE:
- case BT_IO_OPT_KEY_SIZE:
- case BT_IO_OPT_PSM:
- case BT_IO_OPT_CID:
- case BT_IO_OPT_MTU:
- case BT_IO_OPT_OMTU:
- case BT_IO_OPT_IMTU:
- case BT_IO_OPT_MODE:
- case BT_IO_OPT_FLUSHABLE:
- case BT_IO_OPT_PRIORITY:
- case BT_IO_OPT_VOICE:
- case BT_IO_OPT_INVALID:
- default:
- g_set_error(err, BT_IO_ERROR, EINVAL,
- "Unknown option %d", opt);
- return FALSE;
- }
- opt = va_arg(args, int);
- }
- return TRUE;
- }
- static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
- {
- struct sco_conninfo info;
- socklen_t len;
- len = sizeof(info);
- if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
- return -errno;
- if (handle)
- *handle = info.hci_handle;
- if (dev_class)
- memcpy(dev_class, info.dev_class, 3);
- return 0;
- }
- static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
- {
- BtIOOption opt = opt1;
- struct sockaddr_sco src, dst;
- struct sco_options sco_opt;
- socklen_t len;
- uint8_t dev_class[3];
- uint16_t handle = 0;
- uint32_t phy;
- len = sizeof(sco_opt);
- memset(&sco_opt, 0, len);
- if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
- ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
- return FALSE;
- }
- if (!get_src(sock, &src, sizeof(src), err))
- return FALSE;
- if (!get_dst(sock, &dst, sizeof(dst), err))
- return FALSE;
- while (opt != BT_IO_OPT_INVALID) {
- switch (opt) {
- case BT_IO_OPT_SOURCE:
- ba2str(&src.sco_bdaddr, va_arg(args, char *));
- break;
- case BT_IO_OPT_SOURCE_BDADDR:
- bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr);
- break;
- case BT_IO_OPT_DEST:
- ba2str(&dst.sco_bdaddr, va_arg(args, char *));
- break;
- case BT_IO_OPT_DEST_BDADDR:
- bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr);
- break;
- case BT_IO_OPT_MTU:
- case BT_IO_OPT_IMTU:
- case BT_IO_OPT_OMTU:
- *(va_arg(args, uint16_t *)) = sco_opt.mtu;
- break;
- case BT_IO_OPT_HANDLE:
- if (sco_get_info(sock, &handle, dev_class) < 0) {
- ERROR_FAILED(err, "SCO_CONNINFO", errno);
- return FALSE;
- }
- *(va_arg(args, uint16_t *)) = handle;
- break;
- case BT_IO_OPT_CLASS:
- if (sco_get_info(sock, &handle, dev_class) < 0) {
- ERROR_FAILED(err, "SCO_CONNINFO", errno);
- return FALSE;
- }
- memcpy(va_arg(args, uint8_t *), dev_class, 3);
- break;
- case BT_IO_OPT_PHY:
- if (get_phy(sock, &phy) < 0) {
- ERROR_FAILED(err, "get_phy", errno);
- return FALSE;
- }
- *(va_arg(args, uint32_t *)) = phy;
- break;
- case BT_IO_OPT_SOURCE_TYPE:
- case BT_IO_OPT_DEST_TYPE:
- case BT_IO_OPT_DEFER_TIMEOUT:
- case BT_IO_OPT_SEC_LEVEL:
- case BT_IO_OPT_KEY_SIZE:
- case BT_IO_OPT_CHANNEL:
- case BT_IO_OPT_SOURCE_CHANNEL:
- case BT_IO_OPT_DEST_CHANNEL:
- case BT_IO_OPT_PSM:
- case BT_IO_OPT_CID:
- case BT_IO_OPT_CENTRAL:
- case BT_IO_OPT_MODE:
- case BT_IO_OPT_FLUSHABLE:
- case BT_IO_OPT_PRIORITY:
- case BT_IO_OPT_VOICE:
- case BT_IO_OPT_INVALID:
- default:
- g_set_error(err, BT_IO_ERROR, EINVAL,
- "Unknown option %d", opt);
- return FALSE;
- }
- opt = va_arg(args, int);
- }
- return TRUE;
- }
- static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
- BtIOOption opt1, va_list args)
- {
- int sock;
- sock = g_io_channel_unix_get_fd(io);
- switch (type) {
- case BT_IO_L2CAP:
- return l2cap_get(sock, err, opt1, args);
- case BT_IO_RFCOMM:
- return rfcomm_get(sock, err, opt1, args);
- case BT_IO_SCO:
- return sco_get(sock, err, opt1, args);
- case BT_IO_INVALID:
- default:
- g_set_error(err, BT_IO_ERROR, EINVAL,
- "Unknown BtIO type %d", type);
- return FALSE;
- }
- }
- gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
- GDestroyNotify destroy, GError **err)
- {
- int sock;
- char c;
- struct pollfd pfd;
- sock = g_io_channel_unix_get_fd(io);
- memset(&pfd, 0, sizeof(pfd));
- pfd.fd = sock;
- pfd.events = POLLOUT;
- if (poll(&pfd, 1, 0) < 0) {
- ERROR_FAILED(err, "poll", errno);
- return FALSE;
- }
- if (!(pfd.revents & POLLOUT)) {
- if (read(sock, &c, 1) < 0) {
- ERROR_FAILED(err, "read", errno);
- return FALSE;
- }
- }
- accept_add(io, connect, user_data, destroy);
- return TRUE;
- }
- gboolean bt_io_set(GIOChannel *io, GError **err, BtIOOption opt1, ...)
- {
- va_list args;
- gboolean ret;
- struct set_opts opts;
- int sock;
- BtIOType type;
- va_start(args, opt1);
- ret = parse_set_opts(&opts, err, opt1, args);
- va_end(args);
- if (!ret)
- return ret;
- type = bt_io_get_type(io, err);
- if (type == BT_IO_INVALID)
- return FALSE;
- sock = g_io_channel_unix_get_fd(io);
- switch (type) {
- case BT_IO_L2CAP:
- return l2cap_set(sock, opts.src_type, opts.sec_level, opts.imtu,
- opts.omtu, opts.mode, opts.central,
- opts.flushable, opts.priority, err);
- case BT_IO_RFCOMM:
- return rfcomm_set(sock, opts.sec_level, opts.central, err);
- case BT_IO_SCO:
- return sco_set(sock, opts.mtu, opts.voice, err);
- case BT_IO_INVALID:
- default:
- g_set_error(err, BT_IO_ERROR, EINVAL,
- "Unknown BtIO type %d", type);
- return FALSE;
- }
- }
- gboolean bt_io_get(GIOChannel *io, GError **err, BtIOOption opt1, ...)
- {
- va_list args;
- gboolean ret;
- BtIOType type;
- type = bt_io_get_type(io, err);
- if (type == BT_IO_INVALID)
- return FALSE;
- va_start(args, opt1);
- ret = get_valist(io, type, err, opt1, args);
- va_end(args);
- return ret;
- }
- static GIOChannel *create_io(gboolean server, struct set_opts *opts,
- GError **err)
- {
- int sock;
- GIOChannel *io;
- switch (opts->type) {
- case BT_IO_L2CAP:
- sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
- if (sock < 0) {
- ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
- return NULL;
- }
- if (l2cap_bind(sock, &opts->src, opts->src_type,
- server ? opts->psm : 0, opts->cid, err) < 0)
- goto failed;
- if (!l2cap_set(sock, opts->src_type, opts->sec_level,
- opts->imtu, opts->omtu, opts->mode,
- opts->central, opts->flushable, opts->priority,
- err))
- goto failed;
- break;
- case BT_IO_RFCOMM:
- sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
- if (sock < 0) {
- ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
- return NULL;
- }
- if (rfcomm_bind(sock, &opts->src,
- server ? opts->channel : 0, err) < 0)
- goto failed;
- if (!rfcomm_set(sock, opts->sec_level, opts->central, err))
- goto failed;
- break;
- case BT_IO_SCO:
- sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
- if (sock < 0) {
- ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
- return NULL;
- }
- if (sco_bind(sock, &opts->src, err) < 0)
- goto failed;
- if (!sco_set(sock, opts->mtu, opts->voice, err))
- goto failed;
- break;
- case BT_IO_INVALID:
- default:
- g_set_error(err, BT_IO_ERROR, EINVAL,
- "Unknown BtIO type %d", opts->type);
- return NULL;
- }
- io = g_io_channel_unix_new(sock);
- g_io_channel_set_close_on_unref(io, TRUE);
- g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
- return io;
- failed:
- close(sock);
- return NULL;
- }
- GIOChannel *bt_io_connect(BtIOConnect connect, gpointer user_data,
- GDestroyNotify destroy, GError **gerr,
- BtIOOption opt1, ...)
- {
- GIOChannel *io;
- va_list args;
- struct set_opts opts;
- int err, sock;
- gboolean ret;
- char addr[18];
- va_start(args, opt1);
- ret = parse_set_opts(&opts, gerr, opt1, args);
- va_end(args);
- if (ret == FALSE)
- return NULL;
- io = create_io(FALSE, &opts, gerr);
- if (io == NULL)
- return NULL;
- sock = g_io_channel_unix_get_fd(io);
- /* Use DEFER_SETUP when connecting using Ext-Flowctl */
- if (opts.mode == BT_IO_MODE_EXT_FLOWCTL && opts.defer) {
- if (setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
- &opts.defer, sizeof(opts.defer)) < 0) {
- ERROR_FAILED(gerr, "setsockopt(BT_DEFER_SETUP)", errno);
- return NULL;
- }
- }
- switch (opts.type) {
- case BT_IO_L2CAP:
- err = l2cap_connect(sock, &opts.dst, opts.dst_type,
- opts.psm, opts.cid);
- break;
- case BT_IO_RFCOMM:
- err = rfcomm_connect(sock, &opts.dst, opts.channel);
- break;
- case BT_IO_SCO:
- err = sco_connect(sock, &opts.dst);
- break;
- case BT_IO_INVALID:
- default:
- g_set_error(gerr, BT_IO_ERROR, EINVAL,
- "Unknown BtIO type %d", opts.type);
- return NULL;
- }
- if (err < 0) {
- ba2str(&opts.dst, addr);
- g_set_error(gerr, BT_IO_ERROR, -err,
- "connect to %s: %s (%d)", addr, strerror(-err),
- -err);
- g_io_channel_unref(io);
- return NULL;
- }
- connect_add(io, connect, opts.dst, user_data, destroy);
- return io;
- }
- GIOChannel *bt_io_listen(BtIOConnect connect, BtIOConfirm confirm,
- gpointer user_data, GDestroyNotify destroy,
- GError **err, BtIOOption opt1, ...)
- {
- GIOChannel *io;
- va_list args;
- struct set_opts opts;
- int sock;
- gboolean ret;
- va_start(args, opt1);
- ret = parse_set_opts(&opts, err, opt1, args);
- va_end(args);
- if (ret == FALSE)
- return NULL;
- io = create_io(TRUE, &opts, err);
- if (io == NULL)
- return NULL;
- sock = g_io_channel_unix_get_fd(io);
- if (confirm)
- if (setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
- &opts.defer, sizeof(opts.defer)) < 0) {
- ERROR_FAILED(err, "setsockopt(BT_DEFER_SETUP)", errno);
- return NULL;
- }
- if (listen(sock, 5) < 0) {
- ERROR_FAILED(err, "listen", errno);
- g_io_channel_unref(io);
- return NULL;
- }
- server_add(io, connect, confirm, user_data, destroy);
- return io;
- }
- GQuark bt_io_error_quark(void)
- {
- return g_quark_from_static_string("bt-io-error-quark");
- }
|