meta-digi/meta-digi-dey/recipes-digi/dey-examples/files/bt_test/bt_test.c

409 lines
9.7 KiB
C

/*
* bt_test.c
*
* Copyright (C) 2012 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* Description: Bluetooth example application.
*
* This application demonstrates how to use Bluetooth to transfer data
* between two stations.
*
*/
#include <errno.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#define APP_VERSION "1.0"
#define APP_NAME "bt_test"
#define pr_error(...) do { myprintf(ERROR, __VA_ARGS__); } while(0)
#define pr_warning(...) do { myprintf(WARNING, __VA_ARGS__); } while(0)
#define pr_info(...) do { myprintf(INFO, __VA_ARGS__); } while(0)
#define pr_debug(...) do { myprintf(DEBUG, __VA_ARGS__); } while(0)
#define pr_naked(...) do { printf(__VA_ARGS__); } while(0)
/* Operation modes */
typedef enum mode {
MODE_RECEIVER = 0,
MODE_TRANSMITTER,
} mode_type;
/* Used for the configuration variable st.verbose */
typedef enum verbosity_t {
ERROR = 0,
WARNING,
INFO,
DEBUG,
} verbosity_t;
static verbosity_t selected_verbosity;
/* Internal structure */
struct opts_t {
mode_type mode;
bdaddr_t server_address;
char file[128];
verbosity_t verbosity;
int server_address_set;
int s;
};
static struct opts_t *main_opts;
static void myprintf(verbosity_t level, const char *format, ...)
{
va_list lst;
char *marke;
int weg = 0;
if (level > selected_verbosity)
return;
switch (level) {
case ERROR:
marke = "ERROR";
weg = 1;
break;
case WARNING:
marke = "WARNING";
break;
case INFO:
marke = "INFO";
break;
case DEBUG:
marke = "DEBUG";
break;
default:
marke = "UNKNOW";
weg = 1;
break;
}
printf("[ %s ] ", marke);
va_start(lst, format);
vprintf(format, lst);
va_end(lst);
if (weg)
fflush(stdout);
}
static void print_version(void)
{
pr_naked("Bluetooth example application v%s\n", APP_VERSION);
}
static void print_usage(void)
{
fprintf(stdout, "Usage: %s [OPTIONS]\n"
"%s %s Copyright Digi International Inc.\n\n"
"Data transfer using Bluetooth\n"
"\n"
" -m, --master Run the test as master (transmitter)\n"
" -d, --destination= Address of message receiver\n"
" -f, --file= File to send/receive\n"
" -v, --verbosity= Verbosity level (3: loud | 0: quiet)\n"
" -V, --version Show version and exit\n"
" -h, --help Display usage information\n\n",
APP_NAME, APP_NAME, APP_VERSION);
}
static struct opts_t *process_options(int argc, char *argv[])
{
int opt_index, opt;
static const char *short_options = "md:f:v:Vh";
struct opts_t *retval;
static const struct option long_options[] = {
{"master", no_argument, NULL, 'm'},
{"destination", required_argument, NULL, 'd'},
{"file", required_argument, NULL, 'f'},
{"verbosity", required_argument, NULL, 'v'},
{"version", no_argument, NULL, 'V'},
{"help", no_argument, NULL, 'h'},
{0, 0, 0, 0},
};
/* Allocate the space for the internal data structure */
retval = calloc(1, sizeof(struct opts_t));
if (!retval) {
pr_error("calloc failed, %s\n", strerror(errno));
return NULL;
}
retval->mode = MODE_RECEIVER;
for (opt_index = 0;;) {
opt = getopt_long(argc, argv, short_options, long_options, &opt_index);
if (opt == EOF)
break;
switch (opt) {
case 'm':
retval->mode = MODE_TRANSMITTER;
break;
case 'd':
if (str2ba(optarg, &retval->server_address) != 0) {
pr_error("Invalid Bluetooth address: %s\n", optarg);
goto err_free_mem;
}
retval->server_address_set = 1;
break;
case 'f':
if (strlen(optarg) < (sizeof(retval->file))) {
strcpy(retval->file, optarg);
} else {
pr_error("Pathname is too long\n");
goto err_free_mem;
}
break;
case 'v':
selected_verbosity = atoi(optarg);
break;
case 'V':
print_version();
goto err_free_mem;
case '?':
pr_error("Unknown option -- %c\n", opt);
/* FALLTHROUGH */
case 'h':
default:
print_usage();
goto err_free_mem;
}
}
/* Sanity checks */
if (retval->mode == MODE_TRANSMITTER) {
if (!retval->server_address_set) {
pr_error("You must specify a server address in master mode\n");
goto err_free_mem;
}
}
if (retval->file[0] == 0) {
pr_error("You must specify a file for the transfer\n");
goto err_free_mem;
}
/* Print the information about the started mode */
if (retval->mode == MODE_TRANSMITTER)
pr_naked("Running the test as TRANSMITTER\n");
else
pr_naked("Running the test as RECEIVER\n");
return retval;
err_free_mem:
free(retval);
return NULL;
}
int receiver_test(struct opts_t *main_opts)
{
struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
char buf[1024] = { 0 };
int s, client = -1, bytes_read;
socklen_t opt = sizeof(rem_addr);
int bytes_transfered = 0;
FILE *out = fopen(main_opts->file, "wb");
int result = EXIT_FAILURE;
unsigned long permissions;
if (out == NULL) {
pr_error("Error creating %s: %s\n", main_opts->file, strerror(errno));
return EXIT_FAILURE;
}
// allocate socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if (s == -1) {
pr_error("Error opening Bluetooth/RFCOMM socket.\n");
pr_error("Did you forget to enable support for RFCOMM protocol\n");
pr_error("in the kernel configuration menus?\n");
fclose(out);
return EXIT_FAILURE;
}
// bind socket to port 1 of the first available
// local bluetooth adapter
loc_addr.rc_family = AF_BLUETOOTH;
loc_addr.rc_bdaddr = *BDADDR_ANY;
loc_addr.rc_channel = (uint8_t) 1;
bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
if (s == -1) {
pr_error("bind failed with errno=%d\n", errno);
goto receiver_stop;
}
// put socket into listening mode
if (listen(s, 1) == -1) {
pr_error("listen failed with errno %d\n", errno);
goto receiver_stop;
}
// accept one connection
client = accept(s, (struct sockaddr *)&rem_addr, &opt);
if (client == -1) {
pr_error("accept failed with errno %d\n", errno);
goto receiver_stop;
}
ba2str(&rem_addr.rc_bdaddr, buf);
pr_naked("Accepted connection from %s\n", buf);
memset(buf, 0, sizeof(buf));
recv(client, &permissions, sizeof(permissions), 0);
permissions = ntohl(permissions);
// read data from the client
do {
bytes_read = recv(client, buf, sizeof(buf), 0);
if (bytes_read > 0) {
int bytes_written = 0;
pr_info("Received %d bytes\n", bytes_read);
do {
int result = fwrite(&buf[bytes_written], 1, bytes_read - bytes_written, out);
if (result < 0) {
pr_error("Error writing data to file, err = %s\n", strerror(errno));
goto receiver_stop;
} else {
pr_info("Wrote %d bytes to file\n", result);
bytes_written += result;
}
} while (bytes_written < bytes_read);
bytes_transfered += bytes_read;
}
} while (bytes_read > 0);
pr_naked("Received %d bytes and wrote them to %s.\n", bytes_transfered, main_opts->file);
result = EXIT_SUCCESS;
receiver_stop:
if (client != -1)
close(client);
close(s);
fclose(out);
if (result == EXIT_SUCCESS) {
chmod(main_opts->file, permissions);
}
return result;
}
int transmitter_test(struct opts_t *main_opts)
{
struct sockaddr_rc addr = { 0 };
int s, status;
char buf[1024];
int bytes_read;
FILE *fd = NULL;
int result = EXIT_FAILURE;
struct stat file_info;
unsigned long permissions;
// allocate a socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if (s == -1) {
pr_error("Error opening Bluetooth/RFCOMM socket.\n");
pr_error("Did you forget to enable support for RFCOMM protocol\n");
pr_error("in the kernel configuration menus?\n");
return EXIT_FAILURE;
}
// set the connection parameters (who to connect to)
addr.rc_family = AF_BLUETOOTH;
addr.rc_channel = (uint8_t) 1;
memcpy(&addr.rc_bdaddr, &main_opts->server_address, sizeof(addr.rc_bdaddr));
// connect to server
status = connect(s, (struct sockaddr *)&addr, sizeof(addr));
if (status != 0) {
ba2str(&main_opts->server_address, buf);
pr_error("Unable to connect to server %s\n", buf);
goto transmitter_exit;
}
fd = fopen(main_opts->file, "rb");
if (fd == NULL) {
pr_error("Unable to open %s\n", main_opts->file);
goto transmitter_exit;
}
main_opts->s = s;
if (stat(main_opts->file, &file_info)) {
pr_error("Unable to read permissions of %s\n", main_opts->file);
goto transmitter_exit;
}
permissions = htonl(file_info.st_mode);
send(s, &permissions, sizeof(permissions), 0);
do {
bytes_read = fread(buf, 1, sizeof(buf), fd);
if (bytes_read > 0) {
int bytes_written = 0;
do {
status = send(s, &buf[bytes_written], bytes_read - bytes_written, 0);
if (status < 0) {
if (errno == EAGAIN) {
sleep(1);
} else {
pr_error("Encountered error %s sending to remote server\n", strerror(errno));
goto transmitter_exit;
}
}
pr_info("Sent %d bytes.\n", status);
bytes_written += status;
} while (bytes_written < bytes_read);
} else {
if (!feof(fd)) {
pr_error("Encountered error %s reading file.\n", strerror(errno));
goto transmitter_exit;
}
}
} while ((bytes_read > 0) && (!feof(fd)));
pr_naked("Finished sending %s\n", main_opts->file);
result = EXIT_SUCCESS;
transmitter_exit:
close(s);
if (fd != NULL)
fclose(fd);
return result;
}
int main(int argc, char **argv)
{
int result = EXIT_SUCCESS;
/* Create the internal options */
if (!(main_opts = process_options(argc, argv)))
return EXIT_FAILURE;
if (main_opts->mode == MODE_RECEIVER) {
result = receiver_test(main_opts);
} else {
result = transmitter_test(main_opts);
}
return result;
}