Note that the transport only works on Linux. Although it also compiles on Windows, the operating system itself does not support it. FreeBSD and NetBSD do not seem to support ERTM, yet. The code for the last two is not compile-tested. --- include/openobex/obex.h | 4 +- include/openobex/obex_const.h | 3 +- lib/CMakeLists.txt | 1 + lib/Makefile.am | 3 +- lib/bluez_compat.h | 16 ++- lib/btobex.c | 24 +++- lib/btobex.h | 11 ++- lib/btobex_l2cap.c | 315 +++++++++++++++++++++++++++++++++++++++++ lib/obex.c | 16 ++- lib/obex_main.c | 20 +++ lib/obex_main.h | 1 + lib/obex_transport.c | 4 + 12 files changed, 401 insertions(+), 17 deletions(-) create mode 100644 lib/btobex_l2cap.c
diff --git a/include/openobex/obex.h b/include/openobex/obex.h index 6dda861..6bf91d4 100644 --- a/include/openobex/obex.h +++ b/include/openobex/obex.h @@ -147,8 +147,8 @@ OPENOBEX_SYMBOL(int) IrOBEX_TransportConnect(obex_t *self, const char *service); #endif #if defined(bt_addr_t) -OPENOBEX_SYMBOL(int) BtOBEX_ServerRegister(obex_t *self, const bt_addr_t *src, uint8_t channel); -OPENOBEX_SYMBOL(int) BtOBEX_TransportConnect(obex_t *self, const bt_addr_t *src, const bt_addr_t *dst, uint8_t channel); +OPENOBEX_SYMBOL(int) BtOBEX_ServerRegister(obex_t *self, const bt_addr_t *src, uint16_t channel); +OPENOBEX_SYMBOL(int) BtOBEX_TransportConnect(obex_t *self, const bt_addr_t *src, const bt_addr_t *dst, uint16_t channel); #endif /* diff --git a/include/openobex/obex_const.h b/include/openobex/obex_const.h index cb7afeb..b4bad7f 100644 --- a/include/openobex/obex_const.h +++ b/include/openobex/obex_const.h @@ -179,9 +179,10 @@ typedef union { #define OBEX_TRANS_IRDA 1 #define OBEX_TRANS_INET 2 #define OBEX_TRANS_CUSTOM 3 -#define OBEX_TRANS_BLUETOOTH 4 +#define OBEX_TRANS_BLUETOOTH 4 /* Bluetooth RFCOMM */ #define OBEX_TRANS_FD 5 #define OBEX_TRANS_USB 6 +#define OBEX_TRANS_BT_L2CAP 7 /* Bluetooth L2CAP */ /* Standard headers */ #define OBEX_HDR_TYPE_UNICODE (0 << 6) /* zero terminated unicode string (network byte order) */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 6efa6d0..f404529 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -95,6 +95,7 @@ endif ( OPENOBEX_IRDA ) if ( OPENOBEX_BLUETOOTH ) list ( APPEND SOURCES btobex.c + btobex_l2cap.c ) list ( APPEND HEADERS btobex.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 897450d..ae92239 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -12,14 +12,13 @@ libopenobex_la_SOURCES = \ obex_client.c obex_client.h \ irobex.c irobex.h \ inobex.c inobex.h \ - btobex.c btobex.h \ + btobex.c btobex.h btobex_l2cap.c bluez_compat.h \ fdobex.c fdobex.h \ customtrans.c customtrans.h \ databuffer.c databuffer.h \ irda_wrap.h \ usbutils.c usbutils.h \ usbobex.c usb1obex.c usbobex.h \ - bluez_compat.h \ visibility.h defines.h debug.h cloexec.h libopenobex_la_CPPFLAGS = -I$(top_srcdir)/include diff --git a/lib/bluez_compat.h b/lib/bluez_compat.h index c8b046c..6bd7000 100644 --- a/lib/bluez_compat.h +++ b/lib/bluez_compat.h @@ -32,13 +32,17 @@ #include <ws2bth.h> #define bdaddr_t BTH_ADDR #define sockaddr_rc _SOCKADDR_BTH +#define sockaddr_l2 _SOCKADDR_BTH #define rc_family addressFamily #define rc_bdaddr btAddr #define rc_channel port +#define l2_family addressFamily +#define l2_bdaddr btAddr +#define l2_psm port #define PF_BLUETOOTH PF_BTH #define AF_BLUETOOTH AF_BTH #define BTPROTO_RFCOMM BTHPROTO_RFCOMM -/* TODO: should be const */ +#define BTPROTO_L2CAP BTHPROTO_L2CAP extern bdaddr_t bluez_compat_bdaddr_any; #define BDADDR_ANY &bluez_compat_bdaddr_any #define bacpy(dst,src) memcpy((dst),(src),sizeof(bdaddr_t)) @@ -47,23 +51,33 @@ extern bdaddr_t bluez_compat_bdaddr_any; #elif defined(HAVE_BLUETOOTH_LINUX) #include <bluetooth/bluetooth.h> #include <bluetooth/rfcomm.h> +#include <bluetooth/l2cap.h> #elif defined(HAVE_BLUETOOTH_FREEBSD) #include <bluetooth.h> #define sockaddr_rc sockaddr_rfcomm +#define sockaddr_l2 sockaddr_l2cap #define rc_family rfcomm_family #define rc_bdaddr rfcomm_bdaddr #define rc_channel rfcomm_channel +#define l2_family l2cap_family +#define l2_bdaddr l2cap_bdaddr +#define l2_psm l2cap_psm #define BDADDR_ANY NG_HCI_BDADDR_ANY #define BTPROTO_RFCOMM BLUETOOTH_PROTO_RFCOMM +#define BTPROTO_L2CAP BLUETOOTH_PROTO_L2CAP #elif defined(HAVE_BLUETOOTH_NETBSD) #include <bluetooth.h> #include <netbt/rfcomm.h> +#include <netbt/l2cap.h> #define sockaddr_rc sockaddr_bt #define rc_family bt_family #define rc_bdaddr bt_bdaddr #define rc_channel bt_channel +#define l2_family bt_family +#define l2_bdaddr bt_bdaddr +#define l2_psm bt_psm #define bacpy(dst,src) memcpy((dst),(src),sizeof(bdaddr_t)) #define bacmp(a,b) memcmp((a),(b),sizeof(bdaddr_t)) #define str2ba(str, ba) (bt_aton((str), (ba)) == 1 ? 0 : -1) diff --git a/lib/btobex.c b/lib/btobex.c index 810ce91..8df237a 100644 --- a/lib/btobex.c +++ b/lib/btobex.c @@ -108,7 +108,7 @@ static int btobex_set_remote_addr(obex_t *self, struct sockaddr *addr, size_t le * */ void btobex_prepare_connect(obex_t *self, const bdaddr_t *src, - const bdaddr_t *dst, uint8_t channel) + const bdaddr_t *dst, uint16_t channel) { struct obex_transport *trans = &self->trans; @@ -123,6 +123,16 @@ void btobex_prepare_connect(obex_t *self, const bdaddr_t *src, addr.rc_channel = (uint8_t)(channel & 0xFF); obex_transport_set_remote_addr(self, (struct sockaddr *) &addr, sizeof(addr)); + + } else if (trans->type == OBEX_TRANS_BT_L2CAP) { + struct sockaddr_l2 addr; + + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, dst); + addr.l2_psm = channel; + obex_transport_set_remote_addr(self, (struct sockaddr *) &addr, + sizeof(addr)); } } @@ -132,7 +142,7 @@ void btobex_prepare_connect(obex_t *self, const bdaddr_t *src, * Prepare for Bluetooth RFCOMM listen * */ -void btobex_prepare_listen(obex_t *self, const bdaddr_t *src, uint8_t channel) +void btobex_prepare_listen(obex_t *self, const bdaddr_t *src, uint16_t channel) { struct obex_transport *trans = &self->trans; @@ -145,6 +155,16 @@ void btobex_prepare_listen(obex_t *self, const bdaddr_t *src, uint8_t channel) addr.rc_channel = (uint8_t)(channel & 0xFF); obex_transport_set_local_addr(self, (struct sockaddr *) &addr, sizeof(addr)); + + } else if (trans->type == OBEX_TRANS_BT_L2CAP) { + struct sockaddr_l2 addr; + + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, src); + addr.l2_psm = channel; + obex_transport_set_local_addr(self, (struct sockaddr *) &addr, + sizeof(addr)); } } diff --git a/lib/btobex.h b/lib/btobex.h index 4375881..9c4d0c8 100644 --- a/lib/btobex.h +++ b/lib/btobex.h @@ -26,16 +26,23 @@ #include "obex_transport.h" void btobex_get_ops(struct obex_transport_ops* ops); +void btobex_l2cap_get_ops(struct obex_transport_ops *ops); void btobex_prepare_connect(obex_t *self, const bdaddr_t *src, - const bdaddr_t *dst, uint8_t channel); -void btobex_prepare_listen(obex_t *self, const bdaddr_t *src, uint8_t channel); + const bdaddr_t *dst, uint16_t channel); +void btobex_prepare_listen(obex_t *self, const bdaddr_t *src, uint16_t channel); struct btobex_rfcomm_data { struct sockaddr_rc self; struct sockaddr_rc peer; }; +struct btobex_l2cap_data { + struct sockaddr_l2 self; + struct sockaddr_l2 peer; +}; + union btobex_data { struct btobex_rfcomm_data rfcomm; + struct btobex_l2cap_data l2cap; }; #endif diff --git a/lib/btobex_l2cap.c b/lib/btobex_l2cap.c new file mode 100644 index 0000000..a170a42 --- /dev/null +++ b/lib/btobex_l2cap.c @@ -0,0 +1,315 @@ +/** + \file btobex_l2cap.c + Bluetooth L2CAP OBEX, Bluetooth L2CAP transport for OBEX. + OpenOBEX library - Free implementation of the Object Exchange protocol. + + Copyright (c) 2011 Hendrik Sattler, All Rights Reserved. + + OpenOBEX is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with OpenOBEX. If not, see <http://www.gnu.org/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_BLUETOOTH + +#ifdef _WIN32 +/* Windows does not support L2CAP transports, although it compiles, it will + * return an error code on runtime. + */ +#include <winsock2.h> + +#else /* _WIN32 */ +/* Linux/FreeBSD/NetBSD case */ + +#include <string.h> +#include <errno.h> +#include <sys/socket.h> +#endif /* _WIN32 */ + +#include "obex_main.h" +#include "btobex.h" + +#ifdef _WIN32 +#define WSA_VER_MAJOR 2 +#define WSA_VER_MINOR 2 +#endif + +#include "cloexec.h" + +static int btobex_l2cap_init(obex_t *self) +{ +#ifdef _WIN32 + WORD ver = MAKEWORD(WSA_VER_MAJOR,WSA_VER_MINOR); + WSADATA WSAData; + if (WSAStartup(ver, &WSAData) != 0) { + DEBUG(4, "WSAStartup failed (%d)\n",WSAGetLastError()); + return -1; + } + if (LOBYTE(WSAData.wVersion) != WSA_VER_MAJOR || + HIBYTE(WSAData.wVersion) != WSA_VER_MINOR) { + DEBUG(4, "WSA version mismatch\n"); + WSACleanup(); + return -1; + } +#endif + + return 0; +} + +static void btobex_l2cap_cleanup(obex_t *self) +{ + DEBUG(3, "\n"); + +#ifdef _WIN32 + WSACleanup(); +#endif +} + +static int btobex_l2cap_set_opts(obex_t *self, socket_t fd) +{ + int ret = 0; + +#ifdef HAVE_BLUETOOTH_LINUX + struct l2cap_options opts; + socklen_t optlen = sizeof(opts); + + DEBUG(3, "\n"); + + memset(&opts, 0, sizeof(opts)); + ret = getsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen); + if (ret < 0) + return -errno; + + opts.imtu = self->mtu_rx; + opts.mode = L2CAP_MODE_ERTM; + + ret = setsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &opts, optlen); + if (ret < 0) + return -errno; + +#elif HAVE_BLUETOOTH_FREEBSD || HAVE_BLUETOOTH_NETBSD + uint16_t imtu_opt = self->mtu_rx; + socklen_t imtu_optlen = sizeof(imtu_opt); + + DEBUG(3, "\n"); + ret = setsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &imtu_opt, imtu_optlen); + if (ret < 0) + return -errno; + + /* ERTM not available? */ + +#endif /* HAVE_BLUETOOTH_* */ + + return 0; +} + +static int btobex_l2cap_set_local_addr(obex_t *self, struct sockaddr *addr, + size_t len) +{ + struct btobex_l2cap_data *data = &self->trans.data.bt.l2cap; + const struct sockaddr_l2 *local = (struct sockaddr_l2 *) addr; + + DEBUG(3, "\n"); + + if (len == sizeof(*local) && local->l2_family == AF_BLUETOOTH) { + data->self = *local; + return 0; + } + + return -1; +} + +static int btobex_l2cap_set_remote_addr(obex_t *self, struct sockaddr *addr, + size_t len) +{ + struct btobex_l2cap_data *data = &self->trans.data.bt.l2cap; + const struct sockaddr_l2 *remote = (struct sockaddr_l2 *) addr; + + DEBUG(3, "\n"); + + if (len == sizeof(*remote) && remote->l2_family == AF_BLUETOOTH) { + data->peer = *remote; + return 0; + } + + return -1; +} + +static int btobex_l2cap_listen(obex_t *self) +{ + struct obex_transport *trans = &self->trans; + struct btobex_l2cap_data *data = &self->trans.data.bt.l2cap; + int ret = 0; + + DEBUG(3, "\n"); + + trans->serverfd = obex_create_packet_socket(self, AF_BLUETOOTH); + if (trans->serverfd == INVALID_SOCKET) { + DEBUG(0, "Error creating socket\n"); + return -1; + } + + ret = bind(trans->serverfd, (struct sockaddr*) &data->self, + sizeof(data->self)); + if (ret < 0) { + DEBUG(0, "Error doing bind\n"); + goto out_freesock; + } + + ret = btobex_l2cap_set_opts(self, trans->serverfd); + if (ret) { + DEBUG(4, "Setting L2CAP socket options failed: %s\n", + strerror(-ret)); + goto out_freesock; + } + + ret = listen(trans->serverfd, 1); + if (ret < 0) { + DEBUG(0, "Error doing listen\n"); + goto out_freesock; + } + + DEBUG(4, "We are now listening for connections\n"); + return 1; + +out_freesock: + obex_delete_socket(self, trans->serverfd); + trans->serverfd = INVALID_SOCKET; + return -1; +} + +static int btobex_l2cap_accept(obex_t *self) +{ + struct obex_transport *trans = &self->trans; + struct btobex_l2cap_data *data = &self->trans.data.bt.l2cap; + struct sockaddr *addr = (struct sockaddr *) &data->peer; + socklen_t addrlen = sizeof(data->peer); + + DEBUG(3, "\n"); + + // First accept the connection and get the new client socket. + if (self->init_flags & OBEX_FL_CLOEXEC) + trans->fd = accept_cloexec(trans->serverfd, addr, &addrlen); + else + trans->fd = accept(trans->serverfd, addr, &addrlen); + + if (trans->fd == INVALID_SOCKET) + return -1; + + trans->mtu = OBEX_DEFAULT_MTU; + + return 0; +} + +static int btobex_l2cap_connect_request(obex_t *self) +{ + struct obex_transport *trans = &self->trans; + struct btobex_l2cap_data *data = &self->trans.data.bt.l2cap; + int ret = 0; + + DEBUG(3, "\n"); + + if (trans->fd == INVALID_SOCKET) { + trans->fd = obex_create_packet_socket(self, AF_BLUETOOTH); + if (trans->fd == INVALID_SOCKET) + return -1; + } + + ret = bind(trans->fd, (struct sockaddr*) &data->self, + sizeof(data->self)); + if (ret < 0) { + DEBUG(4, "bind(): error %d\n", errno); + goto out_freesock; + } + + ret = btobex_l2cap_set_opts(self, trans->fd); + if (ret) { + DEBUG(4, "Setting L2CAP socket options failed: %s\n", + strerror(-ret)); + goto out_freesock; + } + + ret = connect(trans->fd, (struct sockaddr*) &data->peer, + sizeof(data->peer)); + if (ret < 0) { + DEBUG(4, "connect(): error %d\n", errno); + goto out_freesock; + } + + trans->mtu = OBEX_DEFAULT_MTU; + + DEBUG(2, "transport mtu=%d\n", trans->mtu); + + return 1; + +out_freesock: + obex_delete_socket(self, trans->fd); + trans->fd = INVALID_SOCKET; + return ret; +} + +static int btobex_l2cap_disconnect_request(obex_t *self) +{ + struct obex_transport *trans = &self->trans; + int ret; + + DEBUG(4, "\n"); + + ret = obex_delete_socket(self, trans->fd); + if (ret < 0) + return ret; + + trans->fd = INVALID_SOCKET; + + return ret; +} + +static int btobex_l2cap_disconnect_server(obex_t *self) +{ + struct obex_transport *trans = &self->trans; + int ret; + + DEBUG(4, "\n"); + + ret = obex_delete_socket(self, trans->serverfd); + trans->serverfd = INVALID_SOCKET; + + return ret; +} + +static int btobex_l2cap_read(obex_t *self, void *buf, int buflen) +{ + struct obex_transport *trans = &self->trans; + + return recv(trans->fd, buf, self->mtu_rx, 0); +} + +void btobex_l2cap_get_ops(struct obex_transport_ops *ops) +{ + ops->init = &btobex_l2cap_init; + ops->cleanup = &btobex_l2cap_cleanup; + ops->write = &obex_transport_do_send; + ops->read = &btobex_l2cap_read; + ops->set_local_addr = &btobex_l2cap_set_local_addr; + ops->set_remote_addr = &btobex_l2cap_set_remote_addr; + ops->server.listen = &btobex_l2cap_listen; + ops->server.accept = &btobex_l2cap_accept; + ops->server.disconnect = &btobex_l2cap_disconnect_server; + ops->client.connect = &btobex_l2cap_connect_request; + ops->client.disconnect = &btobex_l2cap_disconnect_request; +} + +#endif /* HAVE_BLUETOOTH */ diff --git a/lib/obex.c b/lib/obex.c index 874cfcd..a2409a2 100644 --- a/lib/obex.c +++ b/lib/obex.c @@ -73,6 +73,7 @@ - #OBEX_TRANS_CUSTOM : Use user provided transport, you must register your own transport with #OBEX_RegisterCTransport() - #OBEX_TRANS_BLUETOOTH: Use regular Bluetooth RFCOMM socket + - #OBEX_TRANS_BT_L2CAP: Use regular Bluetooth L2CAP socket - #OBEX_TRANS_USB: Use USB transport (libusb needed) \param eventcb Function pointer to your event callback. See obex.h for prototype of this callback. @@ -1051,14 +1052,15 @@ int CALLAPI IrOBEX_TransportConnect(obex_t *self, const char *service) /** Start listening for incoming connections. \param self OBEX handle - \param src source address to listen on - \param channel source channel to listen on + \param src bluetooth address to listen on + \param channel RFComm channel or L2CAP PSM to listen on \return -1 or negative error code on error An easier server function to use for Bluetooth (Bluetooth OBEX) only. */ LIB_SYMBOL -int CALLAPI BtOBEX_ServerRegister(obex_t *self, const bt_addr_t *src, uint8_t channel) +int CALLAPI BtOBEX_ServerRegister(obex_t *self, const bt_addr_t *src, + uint16_t channel) { DEBUG(3, "\n"); @@ -1077,16 +1079,16 @@ int CALLAPI BtOBEX_ServerRegister(obex_t *self, const bt_addr_t *src, uint8_t ch /** Connect Bluetooth transport. \param self OBEX handle - \param src source address to connect from - \param dst destination address to connect to - \param channel destination channel to connect to + \param src bluetooth address of local adapter to use + \param dst bluetooth address to connect to + \param channel RFComm channel or L2CAP PSM to connect to \return -1 or negative error code on error An easier connect function to use for Bluetooth (Bluetooth OBEX) only. */ LIB_SYMBOL int CALLAPI BtOBEX_TransportConnect(obex_t *self, const bt_addr_t *src, - const bt_addr_t *dst, uint8_t channel) + const bt_addr_t *dst, uint16_t channel) { DEBUG(4, "\n"); diff --git a/lib/obex_main.c b/lib/obex_main.c index 14a5263..1d39cd8 100644 --- a/lib/obex_main.c +++ b/lib/obex_main.c @@ -83,6 +83,26 @@ socket_t obex_create_socket(obex_t *self, int domain) return fd; } +socket_t obex_create_packet_socket(obex_t *self, int domain) +{ + socket_t fd; + int type = SOCK_SEQPACKET, proto = 0; + + DEBUG(4, "\n"); + +#ifdef HAVE_BLUETOOTH + if (domain == AF_BLUETOOTH) + proto = BTPROTO_L2CAP; +#endif /*HAVE_BLUETOOTH*/ + + if (self->init_flags & OBEX_FL_CLOEXEC) + fd = socket_cloexec(domain, type, proto); + else + fd = socket(domain, type, proto); + + return fd; +} + /* * Function obex_delete_socket() * diff --git a/lib/obex_main.h b/lib/obex_main.h index 86474e5..3c3098c 100644 --- a/lib/obex_main.h +++ b/lib/obex_main.h @@ -62,6 +62,7 @@ struct obex { }; socket_t obex_create_socket(struct obex *self, int domain); +socket_t obex_create_packet_socket(obex_t *self, int domain); int obex_delete_socket(struct obex *self, socket_t fd); void obex_deliver_event(struct obex *self, int event, int cmd, int rsp, int del); diff --git a/lib/obex_transport.c b/lib/obex_transport.c index 695a712..aa113ed 100644 --- a/lib/obex_transport.c +++ b/lib/obex_transport.c @@ -87,6 +87,10 @@ int obex_transport_init(obex_t *self, int transport) case OBEX_TRANS_BLUETOOTH: btobex_get_ops(&trans->ops); break; + + case OBEX_TRANS_BT_L2CAP: + btobex_l2cap_get_ops(&trans->ops); + break; #endif /*HAVE_BLUETOOTH*/ case OBEX_TRANS_FD: -- 1.7.5.4 ------------------------------------------------------------------------------ AppSumo Presents a FREE Video for the SourceForge Community by Eric Ries, the creator of the Lean Startup Methodology on "Lean Startup Secrets Revealed." This video shows you how to validate your ideas, optimize your ideas and identify your business strategy. http://p.sf.net/sfu/appsumosfdev2dev _______________________________________________ Openobex-users mailing list Openobex-users@lists.sourceforge.net http://lists.sourceforge.net/lists/listinfo/openobex-users