btstack/src/socket_connection.c

296 lines
8.2 KiB
C
Raw Normal View History

/*
* SocketServer.c
*
* Handles multiple connections to a single socket without blocking
*
* Created by Matthias Ringwald on 6/6/09.
*
*/
#include "socket_connection.h"
#include "hci.h"
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdio.h>
2009-07-12 15:33:07 +00:00
#include <stdint.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
2009-07-12 15:33:07 +00:00
#define MAX_PENDING_CONNECTIONS 10
#define DATA_BUF_SIZE 80
/** prototypes */
static int socket_connection_hci_process(struct data_source *ds);
static int socket_connection_dummy_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length);
/** globals */
/** packet header used over socket connections, in front of the HCI packet */
2009-07-12 15:33:07 +00:00
typedef struct packet_header {
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;
struct connection {
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;
uint8_t buffer[3+3+255]; // max HCI CMD + packet_header
};
/** list of socket connections */
static linked_list_t connections = NULL;
2009-07-12 15:33:07 +00:00
/** client packet handler */
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
static int socket_connection_dummy_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length){
return 0;
}
int socket_connection_set_non_blocking(int fd)
{
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;
}
void socket_connection_free_connection(connection_t *conn){
// remove from run_loop
run_loop_remove_data_source(&conn->ds);
// and from connection list
linked_list_remove(&connections, &conn->item);
// destroy
free(conn);
}
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
run_loop_add_data_source( &conn->ds );
// and the connection list
linked_list_add( &connections, &conn->item);
return conn;
}
int socket_connection_hci_process(struct data_source *ds) {
2009-07-12 15:33:07 +00:00
connection_t *conn = (connection_t *) ds;
int bytes_read = read(ds->fd, &conn->buffer[conn->bytes_read], conn->bytes_to_read);
// printf("socket_connection_connection_process: state %u, new %u, read %u, toRead %u\n", conn->state,
// bytes_read, conn->bytes_read, conn->bytes_to_read);
if (bytes_read <= 0){
// free
socket_connection_free_connection(linked_item_get_user(&ds->item));
return 0;
2009-07-12 15:33:07 +00:00
}
conn->bytes_read += bytes_read;
conn->bytes_to_read -= bytes_read;
// 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;
conn->bytes_to_read = READ_BT_16( conn->buffer, 4);
2009-07-12 15:33:07 +00:00
break;
case SOCKET_W4_DATA:
// 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));
// reset state machine
socket_connection_init_statemachine(conn);
2009-07-12 15:33:07 +00:00
break;
}
return 0;
}
static int socket_connection_accept(struct data_source *socket_ds) {
2009-07-12 15:33:07 +00:00
/* New connection coming in! */
int fd = accept(socket_ds->fd, NULL, NULL);
if (fd < 0) {
perror("accept");
return 0;
}
// non-blocking ?
// socket_connection_set_non_blocking(ds->fd);
printf("socket_connection_accept new connection %u\n", fd);
socket_connection_register_new_connection(fd);
return 0;
}
/**
* create socket data_source for tcp socket
*
* @return data_source object. If null, check errno
*/
int socket_connection_create_tcp(int port){
// 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;
// create tcp socket
if ((ds->fd = socket (PF_INET, SOCK_STREAM, 0)) < 0) {
printf ("Error creating socket ...(%s)\n", strerror(errno));
free(ds);
return -1;
}
printf ("Socket created\n");
struct sockaddr_in addr;
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);
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-07-12 19:41:30 +00:00
printf ("Server up and running ...\n");
return 0;
}
/**
* create socket data_source for unix domain socket
*
* @TODO: implement socket_connection_create_unix
*/
int socket_connection_create_unix(char *path){
return 0;
}
/**
* set packet handler for all auto-accepted connections
*/
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) ){
socket_connection_packet_callback = packet_callback;
}
/**
* send HCI packet to single connection
*/
void socket_connection_send_packet(connection_t *conn, uint16_t type, uint16_t channel, uint8_t *packet, uint16_t size){
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);
write(conn->ds.fd, packet, size);
}
/**
* send HCI packet to all connections
*/
void socket_connection_send_packet_all(uint16_t type, uint16_t channel, uint8_t *packet, uint16_t size){
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
socket_connection_send_packet( (connection_t *) linked_item_get_user(it), type, channel, packet, size);
}
}
/**
* 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);
run_loop_remove_data_source(&connection->ds);
free( connection );
return 0;
}