2009-06-06 21:10:20 +00:00
|
|
|
/*
|
|
|
|
* SocketServer.c
|
|
|
|
*
|
|
|
|
* Handles multiple connections to a single socket without blocking
|
|
|
|
*
|
|
|
|
* Created by Matthias Ringwald on 6/6/09.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2009-07-22 20:27:00 +00:00
|
|
|
#include "socket_connection.h"
|
2009-06-06 21:10:20 +00:00
|
|
|
|
2009-07-22 21:31:35 +00:00
|
|
|
#include "hci.h"
|
|
|
|
|
2009-08-28 20:37:48 +00:00
|
|
|
#include "../config.h"
|
|
|
|
|
2009-07-22 21:31:35 +00:00
|
|
|
#include <arpa/inet.h>
|
2009-06-06 21:10:20 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2009-07-22 21:31:35 +00:00
|
|
|
#include <netdb.h>
|
2009-06-06 21:10:20 +00:00
|
|
|
#include <stdio.h>
|
2009-07-12 15:33:07 +00:00
|
|
|
#include <stdint.h>
|
2009-07-22 21:31:35 +00:00
|
|
|
#include <string.h>
|
2009-06-06 21:10:20 +00:00
|
|
|
#include <sys/socket.h>
|
2009-08-19 20:09:30 +00:00
|
|
|
#include <sys/un.h>
|
2009-07-22 21:31:35 +00:00
|
|
|
#include <unistd.h>
|
2009-07-12 15:33:07 +00:00
|
|
|
|
2009-08-28 21:55:59 +00:00
|
|
|
#ifdef USE_LAUNCHD
|
|
|
|
#include <launch.h>
|
2009-08-30 15:19:53 +00:00
|
|
|
#include <asl.h>
|
2009-08-28 21:55:59 +00:00
|
|
|
#endif
|
|
|
|
|
2009-06-06 21:10:20 +00:00
|
|
|
#define MAX_PENDING_CONNECTIONS 10
|
|
|
|
|
2009-07-22 21:31:35 +00:00
|
|
|
/** prototypes */
|
2009-08-09 13:00:09 +00:00
|
|
|
static int socket_connection_hci_process(struct data_source *ds);
|
2009-07-31 21:41:15 +00:00
|
|
|
static int socket_connection_dummy_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length);
|
2009-07-22 21:31:35 +00:00
|
|
|
|
|
|
|
/** globals */
|
|
|
|
|
2009-07-22 20:21:22 +00:00
|
|
|
/** packet header used over socket connections, in front of the HCI packet */
|
2009-07-12 15:33:07 +00:00
|
|
|
typedef struct packet_header {
|
2009-07-31 21:41:15 +00:00
|
|
|
uint16_t type;
|
|
|
|
uint16_t channel;
|
2009-07-12 15:33:07 +00:00
|
|
|
uint16_t length;
|
|
|
|
uint8_t data[0];
|
|
|
|
} packet_header_t;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
SOCKET_W4_HEADER,
|
|
|
|
SOCKET_W4_DATA,
|
|
|
|
} SOCKET_STATE;
|
|
|
|
|
2009-07-22 20:21:22 +00:00
|
|
|
struct connection {
|
2009-07-13 22:47:32 +00:00
|
|
|
data_source_t ds; // used for run loop
|
|
|
|
linked_item_t item; // used for connection list
|
2009-07-12 15:33:07 +00:00
|
|
|
SOCKET_STATE state;
|
|
|
|
uint16_t bytes_read;
|
|
|
|
uint16_t bytes_to_read;
|
2009-07-13 22:47:32 +00:00
|
|
|
uint8_t buffer[3+3+255]; // max HCI CMD + packet_header
|
2009-07-22 20:21:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/** list of socket connections */
|
|
|
|
static linked_list_t connections = NULL;
|
|
|
|
|
2009-07-12 15:33:07 +00:00
|
|
|
|
2009-07-22 20:21:22 +00:00
|
|
|
/** client packet handler */
|
2009-07-22 21:31:35 +00:00
|
|
|
|
2009-07-31 21:41:15 +00:00
|
|
|
static int (*socket_connection_packet_callback)(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length) = socket_connection_dummy_handler;
|
2009-07-12 15:33:07 +00:00
|
|
|
|
2009-07-31 21:41:15 +00:00
|
|
|
static int socket_connection_dummy_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length){
|
2009-07-22 20:21:22 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2009-06-06 21:10:20 +00:00
|
|
|
|
|
|
|
|
2009-07-22 20:27:00 +00:00
|
|
|
int socket_connection_set_non_blocking(int fd)
|
2009-06-06 21:10:20 +00:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
int flags;
|
|
|
|
// According to the man page, F_GETFL can't error!
|
|
|
|
flags = fcntl(fd, F_GETFL, NULL);
|
|
|
|
err = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-07-22 20:27:00 +00:00
|
|
|
void socket_connection_free_connection(connection_t *conn){
|
2009-07-13 22:47:32 +00:00
|
|
|
// remove from run_loop
|
2009-08-09 13:00:09 +00:00
|
|
|
run_loop_remove_data_source(&conn->ds);
|
2009-07-13 22:47:32 +00:00
|
|
|
|
|
|
|
// and from connection list
|
|
|
|
linked_list_remove(&connections, &conn->item);
|
|
|
|
|
|
|
|
// destroy
|
|
|
|
free(conn);
|
|
|
|
}
|
|
|
|
|
2009-07-22 21:31:35 +00:00
|
|
|
void socket_connection_init_statemachine(connection_t *connection){
|
|
|
|
// wait for next packet
|
|
|
|
connection->state = SOCKET_W4_HEADER;
|
|
|
|
connection->bytes_read = 0;
|
|
|
|
connection->bytes_to_read = sizeof(packet_header_t);
|
|
|
|
}
|
|
|
|
|
|
|
|
connection_t * socket_connection_register_new_connection(int fd){
|
|
|
|
// create connection objec
|
|
|
|
connection_t * conn = malloc( sizeof(connection_t));
|
|
|
|
if (conn == NULL) return 0;
|
|
|
|
linked_item_set_user( &conn->ds.item, conn);
|
|
|
|
linked_item_set_user( &conn->item, conn);
|
|
|
|
conn->ds.fd = fd;
|
|
|
|
conn->ds.process = socket_connection_hci_process;
|
|
|
|
|
|
|
|
// prepare state machine and
|
|
|
|
socket_connection_init_statemachine(conn);
|
|
|
|
|
|
|
|
// add this socket to the run_loop
|
2009-08-09 13:00:09 +00:00
|
|
|
run_loop_add_data_source( &conn->ds );
|
2009-07-22 21:31:35 +00:00
|
|
|
|
|
|
|
// and the connection list
|
|
|
|
linked_list_add( &connections, &conn->item);
|
|
|
|
|
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
|
2009-08-09 20:18:34 +00:00
|
|
|
void static socket_connection_emit_nr_connections(){
|
|
|
|
linked_item_t *it;
|
|
|
|
uint8_t nr_connections = 0;
|
|
|
|
for (it = (linked_item_t *) connections; it != NULL ; it = it->next, nr_connections++);
|
|
|
|
|
|
|
|
uint8_t event[2];
|
|
|
|
event[0] = DAEMON_NR_CONNECTIONS_CHANGED;
|
|
|
|
event[1] = nr_connections;
|
|
|
|
(*socket_connection_packet_callback)(NULL, DAEMON_EVENT_PACKET, 0, (uint8_t *) &event, 2);
|
|
|
|
// printf("Nr connections changed,.. new %u\n", nr_connections);
|
|
|
|
}
|
|
|
|
|
2009-08-09 13:00:09 +00:00
|
|
|
int socket_connection_hci_process(struct data_source *ds) {
|
2009-07-12 15:33:07 +00:00
|
|
|
connection_t *conn = (connection_t *) ds;
|
2009-07-16 22:04:11 +00:00
|
|
|
int bytes_read = read(ds->fd, &conn->buffer[conn->bytes_read], conn->bytes_to_read);
|
2009-07-13 21:51:42 +00:00
|
|
|
if (bytes_read <= 0){
|
2009-08-09 17:17:00 +00:00
|
|
|
// connection broken (no particular channel, no date yet)
|
2009-08-09 20:18:34 +00:00
|
|
|
uint8_t event[1];
|
|
|
|
event[0] = DAEMON_CONNECTION_CLOSED;
|
|
|
|
(*socket_connection_packet_callback)(conn, DAEMON_EVENT_PACKET, 0, (uint8_t *) &event, 1);
|
2009-08-09 17:17:00 +00:00
|
|
|
|
|
|
|
// free connection
|
2009-07-22 21:31:35 +00:00
|
|
|
socket_connection_free_connection(linked_item_get_user(&ds->item));
|
2009-08-09 20:18:34 +00:00
|
|
|
|
|
|
|
socket_connection_emit_nr_connections();
|
2009-07-13 22:47:32 +00:00
|
|
|
return 0;
|
2009-07-12 15:33:07 +00:00
|
|
|
}
|
|
|
|
conn->bytes_read += bytes_read;
|
|
|
|
conn->bytes_to_read -= bytes_read;
|
2009-07-22 21:31:35 +00:00
|
|
|
// hexdump( conn->buffer, conn->bytes_read);
|
2009-07-12 15:33:07 +00:00
|
|
|
if (conn->bytes_to_read > 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
switch (conn->state){
|
|
|
|
case SOCKET_W4_HEADER:
|
|
|
|
conn->state = SOCKET_W4_DATA;
|
2009-07-31 21:41:15 +00:00
|
|
|
conn->bytes_to_read = READ_BT_16( conn->buffer, 4);
|
2009-07-12 15:33:07 +00:00
|
|
|
break;
|
|
|
|
case SOCKET_W4_DATA:
|
2009-07-31 21:41:15 +00:00
|
|
|
// dispatch packet !!! connection, type, channel, data, size
|
|
|
|
(*socket_connection_packet_callback)(conn, READ_BT_16( conn->buffer, 0), READ_BT_16( conn->buffer, 2),
|
|
|
|
&conn->buffer[sizeof(packet_header_t)], READ_BT_16( conn->buffer, 4));
|
2009-07-22 21:31:35 +00:00
|
|
|
// reset state machine
|
|
|
|
socket_connection_init_statemachine(conn);
|
2009-07-12 15:33:07 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2009-07-13 22:47:32 +00:00
|
|
|
|
2009-08-09 13:00:09 +00:00
|
|
|
static int socket_connection_accept(struct data_source *socket_ds) {
|
2009-06-06 21:10:20 +00:00
|
|
|
|
2009-07-12 15:33:07 +00:00
|
|
|
/* New connection coming in! */
|
2009-07-22 21:31:35 +00:00
|
|
|
int fd = accept(socket_ds->fd, NULL, NULL);
|
|
|
|
if (fd < 0) {
|
2009-06-06 21:10:20 +00:00
|
|
|
perror("accept");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// non-blocking ?
|
2009-07-22 20:27:00 +00:00
|
|
|
// socket_connection_set_non_blocking(ds->fd);
|
2009-07-13 22:47:32 +00:00
|
|
|
|
2009-07-22 21:31:35 +00:00
|
|
|
printf("socket_connection_accept new connection %u\n", fd);
|
2009-07-16 22:04:11 +00:00
|
|
|
|
2009-07-22 21:31:35 +00:00
|
|
|
socket_connection_register_new_connection(fd);
|
2009-08-09 20:18:34 +00:00
|
|
|
socket_connection_emit_nr_connections();
|
2009-07-13 22:47:32 +00:00
|
|
|
|
2009-06-06 21:10:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* create socket data_source for tcp socket
|
|
|
|
*
|
|
|
|
* @return data_source object. If null, check errno
|
|
|
|
*/
|
2009-07-22 20:27:00 +00:00
|
|
|
int socket_connection_create_tcp(int port){
|
2009-06-06 21:10:20 +00:00
|
|
|
|
|
|
|
// create data_source_t
|
|
|
|
data_source_t *ds = malloc( sizeof(data_source_t));
|
2009-07-22 20:21:22 +00:00
|
|
|
if (ds == NULL) return -1;
|
2009-06-06 21:10:20 +00:00
|
|
|
ds->fd = 0;
|
2009-07-22 20:27:00 +00:00
|
|
|
ds->process = socket_connection_accept;
|
2009-06-06 21:10:20 +00:00
|
|
|
|
|
|
|
// create tcp socket
|
|
|
|
if ((ds->fd = socket (PF_INET, SOCK_STREAM, 0)) < 0) {
|
|
|
|
printf ("Error creating socket ...(%s)\n", strerror(errno));
|
|
|
|
free(ds);
|
2009-07-22 20:21:22 +00:00
|
|
|
return -1;
|
2009-06-06 21:10:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
printf ("Socket created\n");
|
|
|
|
|
2009-07-15 22:12:54 +00:00
|
|
|
struct sockaddr_in addr;
|
2009-06-06 21:10:20 +00:00
|
|
|
addr.sin_family = AF_INET;
|
|
|
|
addr.sin_port = htons (port);
|
|
|
|
memset (&addr.sin_addr, 0, sizeof (addr.sin_addr));
|
|
|
|
|
|
|
|
const int y = 1;
|
|
|
|
setsockopt(ds->fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(int));
|
|
|
|
|
|
|
|
if (bind ( ds->fd, (struct sockaddr *) &addr, sizeof (addr) ) ) {
|
|
|
|
printf ("Error on bind() ...(%s)\n", strerror(errno));
|
|
|
|
free(ds);
|
2009-07-22 20:21:22 +00:00
|
|
|
return -1;
|
2009-06-06 21:10:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (listen (ds->fd, MAX_PENDING_CONNECTIONS)) {
|
|
|
|
printf ("Error on listen() ...(%s)\n", strerror(errno));
|
|
|
|
free(ds);
|
2009-07-22 20:21:22 +00:00
|
|
|
return -1;
|
2009-06-06 21:10:20 +00:00
|
|
|
}
|
|
|
|
|
2009-08-09 13:00:09 +00:00
|
|
|
run_loop_add_data_source(ds);
|
2009-07-12 19:41:30 +00:00
|
|
|
|
2009-06-06 21:10:20 +00:00
|
|
|
printf ("Server up and running ...\n");
|
2009-07-22 20:21:22 +00:00
|
|
|
return 0;
|
2009-06-06 21:10:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* create socket data_source for unix domain socket
|
2009-08-19 20:09:30 +00:00
|
|
|
* */
|
2009-07-22 20:27:00 +00:00
|
|
|
int socket_connection_create_unix(char *path){
|
2009-08-19 20:09:30 +00:00
|
|
|
|
2009-08-28 21:55:59 +00:00
|
|
|
#ifdef USE_LAUNCHD
|
|
|
|
|
2009-08-30 15:19:53 +00:00
|
|
|
launch_data_t sockets_dict, checkin_response;
|
|
|
|
launch_data_t checkin_request;
|
|
|
|
launch_data_t listening_fd_array;
|
|
|
|
size_t i;
|
|
|
|
aslclient asl = NULL;
|
|
|
|
aslmsg log_msg = NULL;
|
|
|
|
int retval = EXIT_SUCCESS;
|
2009-08-28 21:55:59 +00:00
|
|
|
|
2009-08-30 15:19:53 +00:00
|
|
|
/*
|
|
|
|
* Create a new ASL log
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
asl = asl_open("SampleD", "Daemon", ASL_OPT_STDERR);
|
|
|
|
log_msg = asl_new(ASL_TYPE_MSG);
|
|
|
|
asl_set(log_msg, ASL_KEY_SENDER, "SampleD");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Register ourselves with launchd.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
if ((checkin_request = launch_data_new_string(LAUNCH_KEY_CHECKIN)) == NULL) {
|
|
|
|
asl_log(asl, log_msg, ASL_LEVEL_ERR, "launch_data_new_string(\"" LAUNCH_KEY_CHECKIN "\") Unable to create string.");
|
|
|
|
retval = EXIT_FAILURE;
|
|
|
|
exit(0);
|
|
|
|
}
|
2009-08-28 21:55:59 +00:00
|
|
|
|
2009-08-30 15:19:53 +00:00
|
|
|
if ((checkin_response = launch_msg(checkin_request)) == NULL) {
|
|
|
|
asl_log(asl, log_msg, ASL_LEVEL_ERR, "launch_msg(\"" LAUNCH_KEY_CHECKIN "\") IPC failure: %m");
|
|
|
|
retval = EXIT_FAILURE;
|
|
|
|
exit(0);
|
|
|
|
}
|
2009-08-28 21:55:59 +00:00
|
|
|
|
2009-08-30 15:19:53 +00:00
|
|
|
if (LAUNCH_DATA_ERRNO == launch_data_get_type(checkin_response)) {
|
|
|
|
errno = launch_data_get_errno(checkin_response);
|
|
|
|
asl_log(asl, log_msg, ASL_LEVEL_ERR, "Check-in failed: %m");
|
|
|
|
retval = EXIT_FAILURE;
|
|
|
|
exit(0);
|
|
|
|
}
|
2009-08-28 21:55:59 +00:00
|
|
|
|
2009-08-30 15:19:53 +00:00
|
|
|
launch_data_t the_label = launch_data_dict_lookup(checkin_response, LAUNCH_JOBKEY_LABEL);
|
|
|
|
if (NULL == the_label) {
|
|
|
|
asl_log(asl, log_msg, ASL_LEVEL_ERR, "No label found");
|
|
|
|
retval = EXIT_FAILURE;
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
asl_log(asl, log_msg, ASL_LEVEL_NOTICE, "Label: %s", launch_data_get_string(the_label));
|
2009-08-28 21:55:59 +00:00
|
|
|
|
2009-08-30 15:19:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Retrieve the dictionary of Socket entries in the config file
|
|
|
|
*/
|
|
|
|
sockets_dict = launch_data_dict_lookup(checkin_response, LAUNCH_JOBKEY_SOCKETS);
|
|
|
|
if (NULL == sockets_dict) {
|
|
|
|
asl_log(asl, log_msg, ASL_LEVEL_ERR, "No sockets found to answer requests on!");
|
|
|
|
retval = EXIT_FAILURE;
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (launch_data_dict_get_count(sockets_dict) > 1) {
|
|
|
|
asl_log(asl, log_msg, ASL_LEVEL_WARNING, "Some sockets will be ignored!");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the dictionary value from the key "MyListenerSocket", as defined in the com.apple.dts.SampleD.plist file.
|
|
|
|
*/
|
|
|
|
listening_fd_array = launch_data_dict_lookup(sockets_dict, "Listeners");
|
|
|
|
if (NULL == listening_fd_array) {
|
|
|
|
asl_log(asl, log_msg, ASL_LEVEL_ERR, "No known sockets found to answer requests on!");
|
|
|
|
retval = EXIT_FAILURE;
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize a new kernel event. This will trigger when
|
|
|
|
* a connection occurs on our listener socket.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
int nr_fds = launch_data_array_get_count(listening_fd_array);
|
|
|
|
asl_log(asl, log_msg, ASL_LEVEL_NOTICE, "%u file descriptors", nr_fds);
|
|
|
|
for (i = 0; i < nr_fds; i++) {
|
|
|
|
// get fd
|
|
|
|
launch_data_t tempi = launch_data_array_get_index (listening_fd_array, i);
|
|
|
|
int listening_fd = launch_data_get_fd(tempi); // identifier
|
2009-08-28 21:55:59 +00:00
|
|
|
launch_data_free (tempi);
|
2009-08-30 15:19:53 +00:00
|
|
|
asl_log(asl, log_msg, ASL_LEVEL_NOTICE, "%u. file descriptor = %u",(unsigned int) i, listening_fd);
|
|
|
|
|
|
|
|
// create data_source_t for fd
|
|
|
|
data_source_t *ds = malloc( sizeof(data_source_t));
|
|
|
|
if (ds == NULL) return -1;
|
|
|
|
ds->process = socket_connection_accept;
|
|
|
|
ds->fd = listening_fd;
|
|
|
|
run_loop_add_data_source(ds);
|
|
|
|
}
|
|
|
|
|
|
|
|
launch_data_free(checkin_response);
|
|
|
|
|
2009-08-28 21:55:59 +00:00
|
|
|
#else
|
2009-08-30 15:19:53 +00:00
|
|
|
|
|
|
|
// create data_source_t
|
|
|
|
data_source_t *ds = malloc( sizeof(data_source_t));
|
|
|
|
if (ds == NULL) return -1;
|
|
|
|
ds->fd = 0;
|
|
|
|
ds->process = socket_connection_accept;
|
|
|
|
|
2009-08-28 21:55:59 +00:00
|
|
|
// create unix socket
|
2009-08-19 20:09:30 +00:00
|
|
|
if ((ds->fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
|
|
|
|
printf ("Error creating socket ...(%s)\n", strerror(errno));
|
|
|
|
free(ds);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf ("Socket created\n");
|
|
|
|
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
bzero(&addr, sizeof(addr));
|
|
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
strcpy(addr.sun_path, path);
|
|
|
|
unlink(path);
|
2009-08-28 21:55:59 +00:00
|
|
|
|
2009-08-19 20:09:30 +00:00
|
|
|
const int y = 1;
|
|
|
|
setsockopt(ds->fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(int));
|
|
|
|
|
|
|
|
if (bind ( ds->fd, (struct sockaddr *) &addr, sizeof (addr) ) ) {
|
|
|
|
printf ("Error on bind() ...(%s)\n", strerror(errno));
|
|
|
|
free(ds);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (listen (ds->fd, MAX_PENDING_CONNECTIONS)) {
|
|
|
|
printf ("Error on listen() ...(%s)\n", strerror(errno));
|
|
|
|
free(ds);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
run_loop_add_data_source(ds);
|
2009-08-30 15:19:53 +00:00
|
|
|
|
|
|
|
#endif
|
2009-08-19 20:09:30 +00:00
|
|
|
|
|
|
|
printf ("Server up and running ...\n");
|
2009-07-22 20:21:22 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set packet handler for all auto-accepted connections
|
|
|
|
*/
|
2009-07-31 21:41:15 +00:00
|
|
|
void socket_connection_register_packet_callback( int (*packet_callback)(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length) ){
|
2009-07-22 20:27:00 +00:00
|
|
|
socket_connection_packet_callback = packet_callback;
|
2009-07-22 20:21:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* send HCI packet to single connection
|
|
|
|
*/
|
2009-07-31 21:41:15 +00:00
|
|
|
void socket_connection_send_packet(connection_t *conn, uint16_t type, uint16_t channel, uint8_t *packet, uint16_t size){
|
2009-07-22 20:21:22 +00:00
|
|
|
|
2009-07-31 21:41:15 +00:00
|
|
|
uint8_t header[sizeof(packet_header_t)];
|
|
|
|
bt_store_16(header, 0, type);
|
|
|
|
bt_store_16(header, 2, channel);
|
|
|
|
bt_store_16(header, 4, size);
|
|
|
|
write(conn->ds.fd, header, 6);
|
2009-07-22 20:21:22 +00:00
|
|
|
write(conn->ds.fd, packet, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* send HCI packet to all connections
|
|
|
|
*/
|
2009-07-31 21:41:15 +00:00
|
|
|
void socket_connection_send_packet_all(uint16_t type, uint16_t channel, uint8_t *packet, uint16_t size){
|
2009-07-22 20:21:22 +00:00
|
|
|
linked_item_t *next;
|
|
|
|
linked_item_t *it;
|
|
|
|
for (it = (linked_item_t *) connections; it != NULL ; it = next){
|
|
|
|
next = it->next; // cache pointer to next connection_t to allow for removal
|
2009-07-31 21:41:15 +00:00
|
|
|
socket_connection_send_packet( (connection_t *) linked_item_get_user(it), type, channel, packet, size);
|
2009-07-22 20:21:22 +00:00
|
|
|
}
|
|
|
|
}
|
2009-07-22 21:31:35 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* create socket connection to BTdaemon
|
|
|
|
*/
|
|
|
|
connection_t * socket_connection_open_tcp(){
|
|
|
|
// TCP
|
|
|
|
struct protoent* tcp = getprotobyname("tcp");
|
|
|
|
|
|
|
|
int btsocket = socket(PF_INET, SOCK_STREAM, tcp->p_proto);
|
|
|
|
if(btsocket == -1){
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
// localhost
|
|
|
|
struct sockaddr_in btdaemon_address;
|
|
|
|
btdaemon_address.sin_family = AF_INET;
|
|
|
|
btdaemon_address.sin_port = htons(BTSTACK_PORT);
|
|
|
|
struct hostent* localhost = gethostbyname("localhost");
|
|
|
|
if(!localhost){
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
// connect
|
|
|
|
char* addr = localhost->h_addr_list[0];
|
|
|
|
memcpy(&btdaemon_address.sin_addr.s_addr, addr, sizeof addr);
|
|
|
|
if(connect(btsocket, (struct sockaddr*)&btdaemon_address, sizeof btdaemon_address) == -1){
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return socket_connection_register_new_connection(btsocket);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* close socket connection to BTdaemon
|
|
|
|
*/
|
|
|
|
int socket_connection_close_tcp(connection_t * connection){
|
|
|
|
if (!connection) return -1;
|
|
|
|
shutdown(connection->ds.fd, SHUT_RDWR);
|
2009-08-09 13:00:09 +00:00
|
|
|
run_loop_remove_data_source(&connection->ds);
|
2009-07-22 21:31:35 +00:00
|
|
|
free( connection );
|
|
|
|
return 0;
|
2009-08-19 20:09:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* create socket connection to BTdaemon
|
|
|
|
*/
|
|
|
|
connection_t * socket_connection_open_unix(){
|
|
|
|
|
|
|
|
int btsocket = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
if(btsocket == -1){
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sockaddr_un server;
|
|
|
|
bzero(&server, sizeof(server));
|
|
|
|
server.sun_family = AF_UNIX;
|
|
|
|
strcpy(server.sun_path, BTSTACK_UNIX);
|
|
|
|
if (connect(btsocket, (struct sockaddr *)&server, sizeof (server)) == -1){
|
|
|
|
return NULL;
|
|
|
|
};
|
|
|
|
|
|
|
|
return socket_connection_register_new_connection(btsocket);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* close socket connection to BTdaemon
|
|
|
|
*/
|
|
|
|
int socket_connection_close_unix(connection_t * connection){
|
|
|
|
if (!connection) return -1;
|
|
|
|
shutdown(connection->ds.fd, SHUT_RDWR);
|
|
|
|
run_loop_remove_data_source(&connection->ds);
|
|
|
|
free( connection );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|